<commit_before>use std::ffi::OsString;
use std::fs::{self, File, OpenOptions};
use std::{io, ptr};
use std::os::windows::prelude::*;
use std::path::{Path, PathBuf};

use winapi::*;
use kernel::*;

pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122;
pub const VOLUME_NAME_DOS: DWORD = 0x0;

struct RmdirContext<'a> {
    base_dir: &'a Path,
    readonly: bool,
    counter: u64,
}

/// Reliably removes directory and all of it's children.
/// ```rust
/// extern crate remove_dir_all;
///
/// use remove_dir_all::*;
///
/// fn main() {
///     remove_dir_all("./temp/").unwrap();
/// }
/// ```
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
    // On Windows it is not enough to just recursively remove the contents of a
    // directory and then the directory itself. Deleting does not happen
    // instantaneously, but is scheduled.
    // To work around this, we move the file or directory to some `base_dir`
    // right before deletion to avoid races.
    //
    // As `base_dir` we choose the parent dir of the directory we want to
    // remove. We very probably have permission to create files here, as we
    // already need write permission in this dir to delete the directory. And it
    // should be on the same volume.
    //
    // To handle files with names like `CON` and `morse .. .`,  and when a
    // directory structure is so deep it needs long path names the path is first
    // converted to a `//?/`-path with `get_path()`.
    //
    // To make sure we don't leave a moved file laying around if the process
    // crashes before we can delete the file, we do all operations on an file
    // handle. By opening a file with `FILE_FLAG_DELETE_ON_CLOSE` Windows will
    // always delete the file when the handle closes.
    //
    // All files are renamed to be in the `base_dir`, and have their name
    // changed to "rm-<counter>". After every rename the counter is increased.
    // Rename should not overwrite possibly existing files in the base dir. So
    // if it fails with `AlreadyExists`, we just increase the counter and try
    // again.
    //
    // For read-only files and directories we first have to remove the read-only
    // attribute before we can move or delete them. This also removes the
    // attribute from possible hardlinks to the file, so just before closing we
    // restore the read-only attribute.
    //
    // If 'path' points to a directory symlink or junction we should not
    // recursively remove the target of the link, but only the link itself.
    //
    // Moving and deleting is guaranteed to succeed if we are able to open the
    // file with `DELETE` permission. If others have the file open we only have
    // `DELETE` permission if they have specified `FILE_SHARE_DELETE`. We can
    // also delete the file now, but it will not disappear until all others have
    // closed the file. But no-one can open the file after we have flagged it
    // for deletion.

    // Open the path once to get the canonical path, file type and attributes.
    let (path, metadata) = {
        let mut opts = OpenOptions::new();
        opts.access_mode(FILE_READ_ATTRIBUTES);
        opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS |
                          FILE_FLAG_OPEN_REPARSE_POINT);
        let file = opts.open(path)?;
        (get_path(&file)?, path.metadata()?)
    };

    let mut ctx = RmdirContext {
        base_dir: match path.parent() {
            Some(dir) => dir,
            None => return Err(io::Error::new(io::ErrorKind::PermissionDenied,
                                              "Can't delete root directory"))
        },
        readonly: metadata.permissions().readonly(),
        counter: 0,
    };

    let filetype = metadata.file_type();
    if filetype.is_dir() {
        if !filetype.is_symlink() {
            remove_dir_all_recursive(path.as_ref(), &mut ctx)
        } else {
            remove_item(path.as_ref(), &mut ctx)
        }
    } else {
        Err(io::Error::new(io::ErrorKind::PermissionDenied, "Not a directory"))
    }
}

fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> {
    if !ctx.readonly {
        let mut opts = OpenOptions::new();
        opts.access_mode(DELETE);
        opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | // delete directory
                          FILE_FLAG_OPEN_REPARSE_POINT | // delete symlink
                          FILE_FLAG_DELETE_ON_CLOSE);
        let file = opts.open(path)?;
        move_item(&file, ctx)
    } else {
        // remove read-only permision
        let mut permissions = path.metadata()?.permissions();
        permissions.set_readonly(false);

        fs::set_permissions(path, permissions)?;
        let file = File::open(path)?;
        move_item(&file, ctx)?;

        // restore read-only flag just in case there are other hard links
        let mut perm = fs::metadata(&path)?.permissions();
        perm.set_readonly(true);
        fs::set_permissions(&path, perm)?;
        Ok(())
    }
}

fn move_item(file: &File, ctx: &mut RmdirContext) -> io::Result<()> {
    let mut tmpname = ctx.base_dir.join(format!{"rm-{}", ctx.counter});
    ctx.counter += 1;

    // Try to rename the file. If it already exists, just retry with an other
    // filename.
    while let Err(err) = rename(file, &tmpname, false) {
        if err.kind() != io::ErrorKind::AlreadyExists { return Err(err) };
        tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter));
        ctx.counter += 1;
    }

    Ok(())
}

fn rename(file: &File, new: &Path, replace: bool) -> io::Result<()> {
    // &self must be opened with DELETE permission
    use std::iter;
    #[cfg(target_arch = "x86")]
    const STRUCT_SIZE: usize = 12;
    #[cfg(target_arch = "x86_64")]
    const STRUCT_SIZE: usize = 20;

    // FIXME: check for internal NULs in 'new'
    let mut data: Vec<u16> = iter::repeat(0u16).take(STRUCT_SIZE/2)
        .chain(new.as_os_str().encode_wide())
        .collect();
    data.push(0);
    let size = data.len() * 2;

    unsafe {
        // Thanks to alignment guarantees on Windows this works
        // (8 for 32-bit and 16 for 64-bit)
        let mut info = data.as_mut_ptr() as *mut FILE_RENAME_INFO;
        // The type of ReplaceIfExists is BOOL, but it actually expects a
        // BOOLEAN. This means true is -1, not c::TRUE.
        (*info).ReplaceIfExists = if replace { -1 } else { FALSE };
        (*info).RootDirectory = ptr::null_mut();
        (*info).FileNameLength = (size - STRUCT_SIZE) as DWORD;
        let result = SetFileInformationByHandle(file.as_raw_handle(),
        FileRenameInfo,
        data.as_mut_ptr() as *mut _ as *mut _,
        size as DWORD);

        if result == 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

fn get_path(f: &File) -> io::Result<PathBuf> {

    fill_utf16_buf(|buf, sz| unsafe {
        GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, VOLUME_NAME_DOS)
    }, |buf| {
        PathBuf::from(OsString::from_wide(buf))
    })
}

fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext)
                            -> io::Result<()> {
    let dir_readonly = ctx.readonly;
    for child in try!(fs::read_dir(path)) {
        let child = try!(child);
        let child_type = try!(child.file_type());
        ctx.readonly = try!(child.metadata()).permissions().readonly();
        if child_type.is_dir() {
            try!(remove_dir_all_recursive(&child.path(), ctx));
        } else {
            try!(remove_item(&child.path().as_ref(), ctx));
        }
    }
    ctx.readonly = dir_readonly;
    remove_item(path, ctx)
}

fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T>
    where F1: FnMut(*mut u16, DWORD) -> DWORD,
          F2: FnOnce(&[u16]) -> T
{
    // Start off with a stack buf but then spill over to the heap if we end up
    // needing more space.
    let mut stack_buf = [0u16; 512];
    let mut heap_buf = Vec::new();
    unsafe {
        let mut n = stack_buf.len();

        loop {
            let buf = if n <= stack_buf.len() {
                &mut stack_buf[..]
            } else {
                let extra = n - heap_buf.len();
                heap_buf.reserve(extra);
                heap_buf.set_len(n);
                &mut heap_buf[..]
            };

            // This function is typically called on windows API functions which
            // will return the correct length of the string, but these functions
            // also return the `0` on error. In some cases, however, the
            // returned "correct length" may actually be 0!
            //
            // To handle this case we call `SetLastError` to reset it to 0 and
            // then check it again if we get the "0 error value". If the "last
            // error" is still 0 then we interpret it as a 0 length buffer and
            // not an actual error.
            SetLastError(0);
            let k = match f1(buf.as_mut_ptr(), n as DWORD) {
                0 if GetLastError() == 0 => 0,
                0 => return Err(io::Error::last_os_error()),
                n => n,
            } as usize;
            if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER {
                n *= 2;
            } else if k >= n {
                n = k;
            } else {
                return Ok(f2(&buf[..k]))
            }
        }
    }
}

<commit_msg>remove duplicate const (#2)<commit_after>use std::ffi::OsString;
use std::fs::{self, File, OpenOptions};
use std::{io, ptr};
use std::os::windows::prelude::*;
use std::path::{Path, PathBuf};

use winapi::*;
use kernel::*;

pub const VOLUME_NAME_DOS: DWORD = 0x0;

struct RmdirContext<'a> {
    base_dir: &'a Path,
    readonly: bool,
    counter: u64,
}

/// Reliably removes directory and all of it's children.
/// ```rust
/// extern crate remove_dir_all;
///
/// use remove_dir_all::*;
///
/// fn main() {
///     remove_dir_all("./temp/").unwrap();
/// }
/// ```
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
    // On Windows it is not enough to just recursively remove the contents of a
    // directory and then the directory itself. Deleting does not happen
    // instantaneously, but is scheduled.
    // To work around this, we move the file or directory to some `base_dir`
    // right before deletion to avoid races.
    //
    // As `base_dir` we choose the parent dir of the directory we want to
    // remove. We very probably have permission to create files here, as we
    // already need write permission in this dir to delete the directory. And it
    // should be on the same volume.
    //
    // To handle files with names like `CON` and `morse .. .`,  and when a
    // directory structure is so deep it needs long path names the path is first
    // converted to a `//?/`-path with `get_path()`.
    //
    // To make sure we don't leave a moved file laying around if the process
    // crashes before we can delete the file, we do all operations on an file
    // handle. By opening a file with `FILE_FLAG_DELETE_ON_CLOSE` Windows will
    // always delete the file when the handle closes.
    //
    // All files are renamed to be in the `base_dir`, and have their name
    // changed to "rm-<counter>". After every rename the counter is increased.
    // Rename should not overwrite possibly existing files in the base dir. So
    // if it fails with `AlreadyExists`, we just increase the counter and try
    // again.
    //
    // For read-only files and directories we first have to remove the read-only
    // attribute before we can move or delete them. This also removes the
    // attribute from possible hardlinks to the file, so just before closing we
    // restore the read-only attribute.
    //
    // If 'path' points to a directory symlink or junction we should not
    // recursively remove the target of the link, but only the link itself.
    //
    // Moving and deleting is guaranteed to succeed if we are able to open the
    // file with `DELETE` permission. If others have the file open we only have
    // `DELETE` permission if they have specified `FILE_SHARE_DELETE`. We can
    // also delete the file now, but it will not disappear until all others have
    // closed the file. But no-one can open the file after we have flagged it
    // for deletion.

    // Open the path once to get the canonical path, file type and attributes.
    let (path, metadata) = {
        let mut opts = OpenOptions::new();
        opts.access_mode(FILE_READ_ATTRIBUTES);
        opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS |
                          FILE_FLAG_OPEN_REPARSE_POINT);
        let file = opts.open(path)?;
        (get_path(&file)?, path.metadata()?)
    };

    let mut ctx = RmdirContext {
        base_dir: match path.parent() {
            Some(dir) => dir,
            None => return Err(io::Error::new(io::ErrorKind::PermissionDenied,
                                              "Can't delete root directory"))
        },
        readonly: metadata.permissions().readonly(),
        counter: 0,
    };

    let filetype = metadata.file_type();
    if filetype.is_dir() {
        if !filetype.is_symlink() {
            remove_dir_all_recursive(path.as_ref(), &mut ctx)
        } else {
            remove_item(path.as_ref(), &mut ctx)
        }
    } else {
        Err(io::Error::new(io::ErrorKind::PermissionDenied, "Not a directory"))
    }
}

fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> {
    if !ctx.readonly {
        let mut opts = OpenOptions::new();
        opts.access_mode(DELETE);
        opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | // delete directory
                          FILE_FLAG_OPEN_REPARSE_POINT | // delete symlink
                          FILE_FLAG_DELETE_ON_CLOSE);
        let file = opts.open(path)?;
        move_item(&file, ctx)
    } else {
        // remove read-only permision
        let mut permissions = path.metadata()?.permissions();
        permissions.set_readonly(false);

        fs::set_permissions(path, permissions)?;
        let file = File::open(path)?;
        move_item(&file, ctx)?;

        // restore read-only flag just in case there are other hard links
        let mut perm = fs::metadata(&path)?.permissions();
        perm.set_readonly(true);
        fs::set_permissions(&path, perm)?;
        Ok(())
    }
}

fn move_item(file: &File, ctx: &mut RmdirContext) -> io::Result<()> {
    let mut tmpname = ctx.base_dir.join(format!{"rm-{}", ctx.counter});
    ctx.counter += 1;

    // Try to rename the file. If it already exists, just retry with an other
    // filename.
    while let Err(err) = rename(file, &tmpname, false) {
        if err.kind() != io::ErrorKind::AlreadyExists { return Err(err) };
        tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter));
        ctx.counter += 1;
    }

    Ok(())
}

fn rename(file: &File, new: &Path, replace: bool) -> io::Result<()> {
    // &self must be opened with DELETE permission
    use std::iter;
    #[cfg(target_arch = "x86")]
    const STRUCT_SIZE: usize = 12;
    #[cfg(target_arch = "x86_64")]
    const STRUCT_SIZE: usize = 20;

    // FIXME: check for internal NULs in 'new'
    let mut data: Vec<u16> = iter::repeat(0u16).take(STRUCT_SIZE/2)
        .chain(new.as_os_str().encode_wide())
        .collect();
    data.push(0);
    let size = data.len() * 2;

    unsafe {
        // Thanks to alignment guarantees on Windows this works
        // (8 for 32-bit and 16 for 64-bit)
        let mut info = data.as_mut_ptr() as *mut FILE_RENAME_INFO;
        // The type of ReplaceIfExists is BOOL, but it actually expects a
        // BOOLEAN. This means true is -1, not c::TRUE.
        (*info).ReplaceIfExists = if replace { -1 } else { FALSE };
        (*info).RootDirectory = ptr::null_mut();
        (*info).FileNameLength = (size - STRUCT_SIZE) as DWORD;
        let result = SetFileInformationByHandle(file.as_raw_handle(),
        FileRenameInfo,
        data.as_mut_ptr() as *mut _ as *mut _,
        size as DWORD);

        if result == 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

fn get_path(f: &File) -> io::Result<PathBuf> {

    fill_utf16_buf(|buf, sz| unsafe {
        GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, VOLUME_NAME_DOS)
    }, |buf| {
        PathBuf::from(OsString::from_wide(buf))
    })
}

fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext)
                            -> io::Result<()> {
    let dir_readonly = ctx.readonly;
    for child in try!(fs::read_dir(path)) {
        let child = try!(child);
        let child_type = try!(child.file_type());
        ctx.readonly = try!(child.metadata()).permissions().readonly();
        if child_type.is_dir() {
            try!(remove_dir_all_recursive(&child.path(), ctx));
        } else {
            try!(remove_item(&child.path().as_ref(), ctx));
        }
    }
    ctx.readonly = dir_readonly;
    remove_item(path, ctx)
}

fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T>
    where F1: FnMut(*mut u16, DWORD) -> DWORD,
          F2: FnOnce(&[u16]) -> T
{
    // Start off with a stack buf but then spill over to the heap if we end up
    // needing more space.
    let mut stack_buf = [0u16; 512];
    let mut heap_buf = Vec::new();
    unsafe {
        let mut n = stack_buf.len();

        loop {
            let buf = if n <= stack_buf.len() {
                &mut stack_buf[..]
            } else {
                let extra = n - heap_buf.len();
                heap_buf.reserve(extra);
                heap_buf.set_len(n);
                &mut heap_buf[..]
            };

            // This function is typically called on windows API functions which
            // will return the correct length of the string, but these functions
            // also return the `0` on error. In some cases, however, the
            // returned "correct length" may actually be 0!
            //
            // To handle this case we call `SetLastError` to reset it to 0 and
            // then check it again if we get the "0 error value". If the "last
            // error" is still 0 then we interpret it as a 0 length buffer and
            // not an actual error.
            SetLastError(0);
            let k = match f1(buf.as_mut_ptr(), n as DWORD) {
                0 if GetLastError() == 0 => 0,
                0 => return Err(io::Error::last_os_error()),
                n => n,
            } as usize;
            if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER {
                n *= 2;
            } else if k >= n {
                n = k;
            } else {
                return Ok(f2(&buf[..k]))
            }
        }
    }
}

<|endoftext|>


<commit_before>use std::env::home_dir;
use std::fs::{create_dir_all, OpenOptions, read_dir, remove_dir_all, remove_file, rename};
use std::io::Write;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};

use error::{ErrorKind, Result};
use process::{exec_command, run_command};

const DEFAULT_HOME_DIR: &str = ".monger";
const DEFAULT_BIN_DIR: &str = "mongodb-versions";
const DEFAULT_DB_DIR: &str = "db";

#[derive(Debug)]
pub struct Fs {
    home_dir: PathBuf,
    bin_dir: PathBuf,
    db_dir: PathBuf,
}

impl Fs {
    pub fn new() -> FsBuilder {
        Default::default()
    }

    #[inline]
    fn get_file<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.home_dir.join(filename)
    }

    #[inline]
    fn get_bin_file_rel<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.bin_dir.join(filename)
    }

    #[inline]
    fn get_bin_file_abs<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.get_file(self.get_bin_file_rel(filename))
    }

    #[inline]
    fn get_bin_dir(&self) -> PathBuf {
        self.get_file(self.bin_dir.as_path())
    }

    #[inline]
    fn get_db_file_rel<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.db_dir.join(filename)
    }

    #[inline]
    fn get_version_dir(&self, version: &str) -> PathBuf {
        self.get_bin_file_abs(version)
    }

    #[inline]
    fn get_version_bin_dir<P: AsRef<Path>>(&self, version: P) -> PathBuf {
        self.get_bin_file_abs(version.as_ref().join("bin"))
    }

    fn create(&self) -> Result<()> {
        create_dir_all(self.get_file(self.bin_dir.as_path()).as_path())?;
        create_dir_all(self.get_file(self.db_dir.as_path()).as_path())?;

        Ok(())
    }

    fn decompress_download<P: AsRef<Path>>(&self, filename: P, version: P) -> Result<()> {
        run_command(
            "tar",
            vec!["xf".as_ref(), filename.as_ref().as_os_str()],
            self.get_bin_dir(),
        )?;

        // TODO: Deal with unwrap in a non-messy way.
        let old_name = self.get_bin_file_abs(filename.as_ref().file_stem().unwrap());
        let new_name = self.get_bin_file_abs(version);
        rename(old_name, new_name)?;

        Ok(())
    }

    fn delete_file<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
        let path = self.home_dir.join(filename);

        if !path.exists() {
            return Ok(());
        }

        remove_file(path)?;

        Ok(())
    }

    fn delete_directory<P: AsRef<Path>>(&self, dirname: P) -> Result<bool> {
        let path = self.home_dir.join(dirname);

        if !path.exists() {
            return Ok(false);
        }

        remove_dir_all(path)?;

        Ok(true)
    }

    fn write_file<P: AsRef<Path>>(&self, filename: P, bytes: &[u8]) -> Result<()> {
        self.create()?;

        let filepath = self.home_dir.join(filename);
        let mut file = OpenOptions::new().write(true).create(true).open(filepath)?;
        file.write_all(bytes)?;

        Ok(())
    }

    #[inline]
    pub fn version_exists(&self, version: &str) -> bool {
        self.get_version_dir(version).is_dir()
    }

    pub fn create_or_get_db_dir(&self, version: &str) -> Result<PathBuf> {
        let db_dir = self.get_file(self.get_db_file_rel(version));
        create_dir_all(db_dir.as_path())?;
        Ok(db_dir)
    }

    pub fn delete_mongodb_version(&self, version: &str) -> Result<bool> {
        self.delete_directory(self.get_version_dir(version))
    }

    pub fn write_mongodb_download(
        &self,
        filename: &str,
        bytes: &[u8],
        version: &str,
    ) -> Result<()> {
        let bin_file = self.get_bin_file_rel(filename);

        println!("writing {}...", bin_file.display());
        self.write_file(&bin_file, bytes)?;

        println!("decompressing...");
        self.decompress_download(filename, version)?;

        println!("cleaning up...");
        self.delete_file(&bin_file)?;

        Ok(())
    }

    pub fn list_versions(&self) -> Result<Vec<OsString>> {
        self.create()?;

        let mut versions = Vec::new();

        for e in read_dir(self.get_bin_dir())? {
            let entry = e?;

            if entry.file_type()?.is_dir() {
                versions.push(entry.file_name());
            }
        }

        Ok(versions)
    }

    pub fn exec<I, S>(&self, binary_name: &str, args: I, version: &str) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        exec_command(&format!("./{}", binary_name), args, self.get_version_bin_dir(version))
    }
}

#[derive(Debug, Default)]
pub struct FsBuilder {
    home_dir: Option<String>,
    bin_dir: Option<String>,
    db_dir: Option<String>,
}

impl FsBuilder {
    #[allow(dead_code)]
    pub fn with_home_dir(&mut self, home_dir: &str) -> &mut Self {
        self.home_dir = Some(home_dir.into());
        self
    }

    #[allow(dead_code)]
    pub fn with_bin_dir(&mut self, bin_dir: &str) -> &mut Self {
        self.bin_dir = Some(bin_dir.into());
        self
    }

    #[allow(dead_code)]
    pub fn with_db_dir(&mut self, db_dir: &str) -> &mut Self {
        self.db_dir = Some(db_dir.into());
        self
    }

    pub fn build(self) -> Result<Fs> {
        match home_dir() {
            Some(mut home_dir) => {
                home_dir.push(self.home_dir.unwrap_or(DEFAULT_HOME_DIR.into()));

                let bin_dir = Path::new(&self.bin_dir.unwrap_or(DEFAULT_BIN_DIR.into()))
                    .to_path_buf();

                let db_dir = Path::new(&self.db_dir.unwrap_or(DEFAULT_DB_DIR.into())).to_path_buf();

                Ok(Fs {
                    home_dir,
                    bin_dir,
                    db_dir,
                })
            }
            None => bail!(ErrorKind::UnknownHomeDirectory),
        }
    }
}
<commit_msg>format fix<commit_after>use std::env::home_dir;
use std::fs::{create_dir_all, OpenOptions, read_dir, remove_dir_all, remove_file, rename};
use std::io::Write;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};

use error::{ErrorKind, Result};
use process::{exec_command, run_command};

const DEFAULT_HOME_DIR: &str = ".monger";
const DEFAULT_BIN_DIR: &str = "mongodb-versions";
const DEFAULT_DB_DIR: &str = "db";

#[derive(Debug)]
pub struct Fs {
    home_dir: PathBuf,
    bin_dir: PathBuf,
    db_dir: PathBuf,
}

impl Fs {
    pub fn new() -> FsBuilder {
        Default::default()
    }

    #[inline]
    fn get_file<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.home_dir.join(filename)
    }

    #[inline]
    fn get_bin_file_rel<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.bin_dir.join(filename)
    }

    #[inline]
    fn get_bin_file_abs<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.get_file(self.get_bin_file_rel(filename))
    }

    #[inline]
    fn get_bin_dir(&self) -> PathBuf {
        self.get_file(self.bin_dir.as_path())
    }

    #[inline]
    fn get_db_file_rel<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.db_dir.join(filename)
    }

    #[inline]
    fn get_version_dir(&self, version: &str) -> PathBuf {
        self.get_bin_file_abs(version)
    }

    #[inline]
    fn get_version_bin_dir<P: AsRef<Path>>(&self, version: P) -> PathBuf {
        self.get_bin_file_abs(version.as_ref().join("bin"))
    }

    fn create(&self) -> Result<()> {
        create_dir_all(self.get_file(self.bin_dir.as_path()).as_path())?;
        create_dir_all(self.get_file(self.db_dir.as_path()).as_path())?;

        Ok(())
    }

    fn decompress_download<P: AsRef<Path>>(&self, filename: P, version: P) -> Result<()> {
        run_command(
            "tar",
            vec!["xf".as_ref(), filename.as_ref().as_os_str()],
            self.get_bin_dir(),
        )?;

        // TODO: Deal with unwrap in a non-messy way.
        let old_name = self.get_bin_file_abs(filename.as_ref().file_stem().unwrap());
        let new_name = self.get_bin_file_abs(version);
        rename(old_name, new_name)?;

        Ok(())
    }

    fn delete_file<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
        let path = self.home_dir.join(filename);

        if !path.exists() {
            return Ok(());
        }

        remove_file(path)?;

        Ok(())
    }

    fn delete_directory<P: AsRef<Path>>(&self, dirname: P) -> Result<bool> {
        let path = self.home_dir.join(dirname);

        if !path.exists() {
            return Ok(false);
        }

        remove_dir_all(path)?;

        Ok(true)
    }

    fn write_file<P: AsRef<Path>>(&self, filename: P, bytes: &[u8]) -> Result<()> {
        self.create()?;

        let filepath = self.home_dir.join(filename);
        let mut file = OpenOptions::new().write(true).create(true).open(filepath)?;
        file.write_all(bytes)?;

        Ok(())
    }

    #[inline]
    pub fn version_exists(&self, version: &str) -> bool {
        self.get_version_dir(version).is_dir()
    }

    pub fn create_or_get_db_dir(&self, version: &str) -> Result<PathBuf> {
        let db_dir = self.get_file(self.get_db_file_rel(version));
        create_dir_all(db_dir.as_path())?;
        Ok(db_dir)
    }

    pub fn delete_mongodb_version(&self, version: &str) -> Result<bool> {
        self.delete_directory(self.get_version_dir(version))
    }

    pub fn write_mongodb_download(
        &self,
        filename: &str,
        bytes: &[u8],
        version: &str,
    ) -> Result<()> {
        let bin_file = self.get_bin_file_rel(filename);

        println!("writing {}...", bin_file.display());
        self.write_file(&bin_file, bytes)?;

        println!("decompressing...");
        self.decompress_download(filename, version)?;

        println!("cleaning up...");
        self.delete_file(&bin_file)?;

        Ok(())
    }

    pub fn list_versions(&self) -> Result<Vec<OsString>> {
        self.create()?;

        let mut versions = Vec::new();

        for e in read_dir(self.get_bin_dir())? {
            let entry = e?;

            if entry.file_type()?.is_dir() {
                versions.push(entry.file_name());
            }
        }

        Ok(versions)
    }

    pub fn exec<I, S>(&self, binary_name: &str, args: I, version: &str) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        exec_command(
            &format!("./{}", binary_name),
            args,
            self.get_version_bin_dir(version),
        )
    }
}

#[derive(Debug, Default)]
pub struct FsBuilder {
    home_dir: Option<String>,
    bin_dir: Option<String>,
    db_dir: Option<String>,
}

impl FsBuilder {
    #[allow(dead_code)]
    pub fn with_home_dir(&mut self, home_dir: &str) -> &mut Self {
        self.home_dir = Some(home_dir.into());
        self
    }

    #[allow(dead_code)]
    pub fn with_bin_dir(&mut self, bin_dir: &str) -> &mut Self {
        self.bin_dir = Some(bin_dir.into());
        self
    }

    #[allow(dead_code)]
    pub fn with_db_dir(&mut self, db_dir: &str) -> &mut Self {
        self.db_dir = Some(db_dir.into());
        self
    }

    pub fn build(self) -> Result<Fs> {
        match home_dir() {
            Some(mut home_dir) => {
                home_dir.push(self.home_dir.unwrap_or(DEFAULT_HOME_DIR.into()));

                let bin_dir = Path::new(&self.bin_dir.unwrap_or(DEFAULT_BIN_DIR.into()))
                    .to_path_buf();

                let db_dir = Path::new(&self.db_dir.unwrap_or(DEFAULT_DB_DIR.into())).to_path_buf();

                Ok(Fs {
                    home_dir,
                    bin_dir,
                    db_dir,
                })
            }
            None => bail!(ErrorKind::UnknownHomeDirectory),
        }
    }
}
<|endoftext|>


<commit_before>use docopt::Docopt;
use docopt::Error as DocoptError;
use std::error::Error as StdError;
use std::ffi::OsStr;
use std::fmt::{self, Display, Formatter};
use std::fs::File;
use std::io::Error as IoError;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use toml;

use gobjects;
use version::Version;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WorkMode {
    Normal,     //generate widgets etc.
    Sys,        //generate -sys with ffi
}

impl Default for WorkMode {
    fn default() -> WorkMode { WorkMode::Normal }
}

impl FromStr for WorkMode {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "normal" => Ok(WorkMode::Normal),
            "sys" => Ok(WorkMode::Sys),
            _ => Err("Wrong work mode".into())
        }
    }
}

#[derive(Debug)]
pub enum Error {
    CommandLine(DocoptError),
    Io(IoError, PathBuf),
    Toml(String, PathBuf),
    Options(String, PathBuf),
}

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            CommandLine(ref e) => e.description(),
            Io(ref e, _) => e.description(),
            Toml(ref s, _) => s,
            Options(ref s, _) => s,
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::Error::*;
        match *self {
            CommandLine(ref err) => err.fmt(f),
            Io(ref err, ref filename) => {
                try!(write!(f, "Failed to read config \"{}\": ", filename.display()));
                err.fmt(f)
            }
            Toml(ref err, ref filename) => {
                write!(f, "\"{}\": {}", filename.display(), err)
            }
            Options(ref err, ref filename) => {
                write!(f, "\"{}\": {}", filename.display(), err)
            }
        }
    }
}

impl<'a> From<DocoptError> for Error {
    fn from(e: DocoptError) -> Error {
        Error::CommandLine(e)
    }
}

impl<P: AsRef<OsStr>> From<(IoError, P)> for Error {
    fn from(e: (IoError, P)) -> Error {
        Error::Io(e.0, PathBuf::from(&e.1))
    }
}

impl<'a, P: AsRef<OsStr>> From<(&'a str, P)> for Error {
    fn from(e: (&'a str, P)) -> Error {
        Error::Options(e.0.into(), PathBuf::from(&e.1))
    }
}

impl<P: AsRef<OsStr>> From<(String, P)> for Error {
    fn from(e: (String, P)) -> Error {
        Error::Options(e.0, PathBuf::from(&e.1))
    }
}

trait TomlHelper where Self: Sized {
    fn lookup_str<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, err: &str, config_file: P) -> Result<&'a str, Error>;
    fn as_result_str<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, config_file: P) -> Result<&'a str, Error>;
    fn as_result_slice<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, config_file: P) -> Result<&'a [Self], Error>;
}

impl TomlHelper for toml::Value {
    fn lookup_str<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, err: &str, config_file: P) -> Result<&'a str, Error> {
        let value = try!(self.lookup(option).ok_or((err, &config_file)));
        value.as_result_str(option, config_file)
    }
    fn as_result_str<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, config_file: P) -> Result<&'a str, Error> {
        self.as_str()
            .ok_or(Error::Options(format!("Invalid `{}` value, expected a string, found {}",
                                          option, self.type_str()),
                                  PathBuf::from(&config_file)))
    }
    fn as_result_slice<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, config_file: P) -> Result<&'a [Self], Error> {
        self.as_slice()
            .ok_or(Error::Options(format!("Invalid `{}` value, expected a array, found {}",
                                          option, self.type_str()),
                                  PathBuf::from(&config_file)))
    }
}

static USAGE: &'static str = "
Usage: gir [options] [<library> <version>]
       gir --help

Options:
    -h, --help          Show this message.
    -c CONFIG           Config file path (default: Gir.toml)
    -d GIRSPATH         Directory for girs
    -m MODE             Work mode: normal or sys
    -o PATH             Target path
    -b, --make_backup   Make backup before generating
";

#[derive(Debug)]
pub struct Config {
    pub work_mode: WorkMode,
    pub girs_dir: PathBuf,
    pub library_name: String,
    pub library_version: String,
    pub target_path: PathBuf,
    pub external_libraries: Vec<String>,
    pub objects: gobjects::GObjects,
    pub min_cfg_version: Version,
    pub make_backup: bool,
}

impl Config {
    pub fn new() -> Result<Config, Error> {
        let args = try!(Docopt::new(USAGE)
            .and_then(|dopt| dopt.parse()));

        let config_file: PathBuf = match args.get_str("-c") {
            "" => "Gir.toml",
            a => a,
        }.into();

        //TODO: add check file existence when stable std::fs::PathExt
        let toml = try!(read_toml(&config_file));

        let work_mode_str = match args.get_str("-m") {
            "" => try!(toml.lookup_str("options.work_mode", "No options.work_mode in config", &config_file)),
            a => a,
        };
        let work_mode = WorkMode::from_str(work_mode_str)
            .unwrap_or_else(|e| panic!(e));

        let girs_dir: PathBuf = match args.get_str("-d") {
            "" => try!(toml.lookup_str("options.girs_dir", "No options.girs_dir in config", &config_file)),
            a => a
        }.into();

        let (library_name, library_version) =
            match (args.get_str("<library>"), args.get_str("<version>")) {
            ("", "") => (
                try!(toml.lookup_str("options.library", "No options.library in config", &config_file)),
                try!(toml.lookup_str("options.version", "No options.version in config", &config_file))
            ),
            ("", _) | (_, "") => try!(Err(("Library and version can not be specified separately", &config_file))),
            (a, b) => (a, b)
        };

        let target_path: PathBuf = match args.get_str("-o") {
            "" => try!(toml.lookup_str("options.target_path", "No target path specified", &config_file)),
            a => a
        }.into();

        let mut objects = toml.lookup("object").map(|t| gobjects::parse_toml(t))
            .unwrap_or_else(|| Default::default());
        gobjects::parse_status_shorthands(&mut objects, &toml);

        let external_libraries = match toml.lookup("options.external_libraries") {
            Some(a) => {
                try!(a.as_result_slice("options.external_libraries", &config_file))
                    .iter().filter_map(|v| v.as_str().map(String::from))
                    .collect()
            }
            None => Vec::new(),
        };

        let min_cfg_version = match toml.lookup("options.min_cfg_version") {
            Some(v) => {
                try!(
                    try!(v.as_result_str("options.min_cfg_version", &config_file))
                        .parse().map_err(|e: String| (e, config_file)))
            }
            None => Default::default(),
        };

        let make_backup = args.get_bool("-b");

        Ok(Config {
            work_mode: work_mode,
            girs_dir: girs_dir,
            library_name: library_name.into(),
            library_version: library_version.into(),
            target_path: target_path,
            external_libraries: external_libraries,
            objects: objects,
            min_cfg_version: min_cfg_version,
            make_backup: make_backup,
        })
    }

    pub fn library_full_name(&self) -> String {
        format!("{}-{}", self.library_name, self.library_version)
    }
}

fn read_toml<P: AsRef<OsStr> + AsRef<Path>>(filename: P) -> Result<toml::Value, Error> {
    let mut input = String::new();
    try!(File::open(&filename)
         .and_then(|mut f| f.read_to_string(&mut input))
         .map_err(|e| (e, &filename)));

    let mut parser = toml::Parser::new(&input);
    match parser.parse() {
        Some(toml) => Ok(toml::Value::Table(toml)),
        None => {
            let err = &parser.errors[parser.errors.len() - 1];
            let (loline, locol) = parser.to_linecol(err.lo);
            let (hiline, hicol) = parser.to_linecol(err.hi);
            let s = format!("{}:{}-{}:{} error: {}", loline, locol, hiline, hicol, err.desc);
            Err(Error::Toml(s, PathBuf::from(&filename)))
        }
    }
}
<commit_msg>Auto merge of #138 - EPashkin:relative_path_in_config, r=gkoz<commit_after>use docopt::Docopt;
use docopt::Error as DocoptError;
use std::error::Error as StdError;
use std::ffi::OsStr;
use std::fmt::{self, Display, Formatter};
use std::fs::File;
use std::io::Error as IoError;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use toml;

use gobjects;
use version::Version;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WorkMode {
    Normal,     //generate widgets etc.
    Sys,        //generate -sys with ffi
}

impl Default for WorkMode {
    fn default() -> WorkMode { WorkMode::Normal }
}

impl FromStr for WorkMode {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "normal" => Ok(WorkMode::Normal),
            "sys" => Ok(WorkMode::Sys),
            _ => Err("Wrong work mode".into())
        }
    }
}

#[derive(Debug)]
pub enum Error {
    CommandLine(DocoptError),
    Io(IoError, PathBuf),
    Toml(String, PathBuf),
    Options(String, PathBuf),
}

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            CommandLine(ref e) => e.description(),
            Io(ref e, _) => e.description(),
            Toml(ref s, _) => s,
            Options(ref s, _) => s,
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::Error::*;
        match *self {
            CommandLine(ref err) => err.fmt(f),
            Io(ref err, ref filename) => {
                try!(write!(f, "Failed to read config \"{}\": ", filename.display()));
                err.fmt(f)
            }
            Toml(ref err, ref filename) => {
                write!(f, "\"{}\": {}", filename.display(), err)
            }
            Options(ref err, ref filename) => {
                write!(f, "\"{}\": {}", filename.display(), err)
            }
        }
    }
}

impl<'a> From<DocoptError> for Error {
    fn from(e: DocoptError) -> Error {
        Error::CommandLine(e)
    }
}

impl<P: AsRef<OsStr>> From<(IoError, P)> for Error {
    fn from(e: (IoError, P)) -> Error {
        Error::Io(e.0, PathBuf::from(&e.1))
    }
}

impl<'a, P: AsRef<OsStr>> From<(&'a str, P)> for Error {
    fn from(e: (&'a str, P)) -> Error {
        Error::Options(e.0.into(), PathBuf::from(&e.1))
    }
}

impl<P: AsRef<OsStr>> From<(String, P)> for Error {
    fn from(e: (String, P)) -> Error {
        Error::Options(e.0, PathBuf::from(&e.1))
    }
}

trait TomlHelper where Self: Sized {
    fn lookup_str<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, err: &str, config_file: P) -> Result<&'a str, Error>;
    fn as_result_str<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, config_file: P) -> Result<&'a str, Error>;
    fn as_result_slice<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, config_file: P) -> Result<&'a [Self], Error>;
}

impl TomlHelper for toml::Value {
    fn lookup_str<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, err: &str, config_file: P) -> Result<&'a str, Error> {
        let value = try!(self.lookup(option).ok_or((err, &config_file)));
        value.as_result_str(option, config_file)
    }
    fn as_result_str<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, config_file: P) -> Result<&'a str, Error> {
        self.as_str()
            .ok_or(Error::Options(format!("Invalid `{}` value, expected a string, found {}",
                                          option, self.type_str()),
                                  PathBuf::from(&config_file)))
    }
    fn as_result_slice<'a, P: AsRef<OsStr>>(&'a self, option: &'a str, config_file: P) -> Result<&'a [Self], Error> {
        self.as_slice()
            .ok_or(Error::Options(format!("Invalid `{}` value, expected a array, found {}",
                                          option, self.type_str()),
                                  PathBuf::from(&config_file)))
    }
}

static USAGE: &'static str = "
Usage: gir [options] [<library> <version>]
       gir --help

Options:
    -h, --help          Show this message.
    -c CONFIG           Config file path (default: Gir.toml)
    -d GIRSPATH         Directory for girs
    -m MODE             Work mode: normal or sys
    -o PATH             Target path
    -b, --make_backup   Make backup before generating
";

#[derive(Debug)]
pub struct Config {
    pub work_mode: WorkMode,
    pub girs_dir: PathBuf,
    pub library_name: String,
    pub library_version: String,
    pub target_path: PathBuf,
    pub external_libraries: Vec<String>,
    pub objects: gobjects::GObjects,
    pub min_cfg_version: Version,
    pub make_backup: bool,
}

impl Config {
    pub fn new() -> Result<Config, Error> {
        let args = try!(Docopt::new(USAGE)
            .and_then(|dopt| dopt.parse()));

        let config_file: PathBuf = match args.get_str("-c") {
            "" => "Gir.toml",
            a => a,
        }.into();

        let config_dir = match config_file.parent() {
            Some(path) => path.into(),
            None => PathBuf::new(),
        };

        //TODO: add check file existence when stable std::fs::PathExt
        let toml = try!(read_toml(&config_file));

        let work_mode_str = match args.get_str("-m") {
            "" => try!(toml.lookup_str("options.work_mode",
               "No options.work_mode in config", &config_file)),
            a => a,
        };
        let work_mode = WorkMode::from_str(work_mode_str)
            .unwrap_or_else(|e| panic!(e));

        let girs_dir: PathBuf = match args.get_str("-d") {
            "" => {
                let path = try!(toml.lookup_str("options.girs_dir",
                    "No options.girs_dir in config", &config_file));
                config_dir.join(path)
            }
            a => a.into(),
        };

        let (library_name, library_version) =
            match (args.get_str("<library>"), args.get_str("<version>")) {
            ("", "") => (
                try!(toml.lookup_str("options.library", "No options.library in config", &config_file)),
                try!(toml.lookup_str("options.version", "No options.version in config", &config_file))
            ),
            ("", _) | (_, "") => try!(Err(("Library and version can not be specified separately",
                                           &config_file))),
            (a, b) => (a, b)
        };

        let target_path: PathBuf = match args.get_str("-o") {
            "" => {
                let path = try!(toml.lookup_str("options.target_path",
                    "No target path specified", &config_file));
                config_dir.join(path)
            }
            a => a.into()
        };

        let mut objects = toml.lookup("object").map(|t| gobjects::parse_toml(t))
            .unwrap_or_else(|| Default::default());
        gobjects::parse_status_shorthands(&mut objects, &toml);

        let external_libraries = match toml.lookup("options.external_libraries") {
            Some(a) => {
                try!(a.as_result_slice("options.external_libraries", &config_file))
                    .iter().filter_map(|v| v.as_str().map(String::from))
                    .collect()
            }
            None => Vec::new(),
        };

        let min_cfg_version = match toml.lookup("options.min_cfg_version") {
            Some(v) => {
                try!(
                    try!(v.as_result_str("options.min_cfg_version", &config_file))
                        .parse().map_err(|e: String| (e, config_file)))
            }
            None => Default::default(),
        };

        let make_backup = args.get_bool("-b");

        Ok(Config {
            work_mode: work_mode,
            girs_dir: girs_dir,
            library_name: library_name.into(),
            library_version: library_version.into(),
            target_path: target_path,
            external_libraries: external_libraries,
            objects: objects,
            min_cfg_version: min_cfg_version,
            make_backup: make_backup,
        })
    }

    pub fn library_full_name(&self) -> String {
        format!("{}-{}", self.library_name, self.library_version)
    }
}

fn read_toml<P: AsRef<OsStr> + AsRef<Path>>(filename: P) -> Result<toml::Value, Error> {
    let mut input = String::new();
    try!(File::open(&filename)
         .and_then(|mut f| f.read_to_string(&mut input))
         .map_err(|e| (e, &filename)));

    let mut parser = toml::Parser::new(&input);
    match parser.parse() {
        Some(toml) => Ok(toml::Value::Table(toml)),
        None => {
            let err = &parser.errors[parser.errors.len() - 1];
            let (loline, locol) = parser.to_linecol(err.lo);
            let (hiline, hicol) = parser.to_linecol(err.hi);
            let s = format!("{}:{}-{}:{} error: {}", loline, locol, hiline, hicol, err.desc);
            Err(Error::Toml(s, PathBuf::from(&filename)))
        }
    }
}
<|endoftext|>


<commit_before>use num::Num;

#[inline(always)]
pub fn new<T: Num>(x: T, y: T, z: T, w: T) -> [T; 4] {[x, y, z, w]}
#[inline(always)]
pub fn create<T: Num>(x: T, y: T, z: T, w: T) -> [T; 4] {new(x, y, z, w)}
#[test]
fn test_new() {
    let v = new(1, 2, 3, 4);
    assert!(v[0] == 1);
    assert!(v[1] == 2);
    assert!(v[2] == 3);
    assert!(v[3] == 4);
}

#[inline(always)]
pub fn clone<'b, T: Num>(v: &'b [T; 4]) -> [T; 4] {new(v[0], v[1], v[2], v[3])}

#[inline(always)]
pub fn copy<'a, 'b, T: Num>(out: &'a mut [T; 4], a: &'b [T; 4]) -> &'a mut [T; 4] {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    out
}
#[test]
fn test_copy() {
    let mut v = [0, 0, 0, 0];
    copy(&mut v, &[1, 2, 3, 4]);
    assert!(v == [1, 2, 3, 4]);
}
<commit_msg>clean up<commit_after>use num::Num;

#[inline(always)]
pub fn new<T: Num>(x: T, y: T, z: T, w: T) -> [T; 4] {[x, y, z, w]}
#[inline(always)]
pub fn create<T: Num>(x: T, y: T, z: T, w: T) -> [T; 4] {new(x, y, z, w)}
#[test]
fn test_new() {
    let v = new(1, 2, 3, 4);
    assert!(v[0] == 1);
    assert!(v[1] == 2);
    assert!(v[2] == 3);
    assert!(v[3] == 4);
}

#[inline(always)]
pub fn clone<'b, T: Num>(v: &'b [T; 4]) -> [T; 4] {new(v[0], v[1], v[2], v[3])}

#[inline(always)]
pub fn copy<'a, 'b, T: Num>(out: &'a mut [T; 4], a: &'b [T; 4]) -> &'a mut [T; 4] {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    out
}
#[test]
fn test_copy() {
    let mut v = [0, 0, 0, 0];
    copy(&mut v, &[1, 2, 3, 4]);
    assert!(v == [1, 2, 3, 4]);
}

#[inline(always)]
pub fn from_vec2<'a, 'b, T: Num>(out: &'a mut [T; 4], v: &'b [T; 2]) -> &'a mut [T; 4] {
    out[0] = v[0];
    out[1] = v[1];
    out
}
#[inline(always)]
pub fn from_vec3<'a, 'b, T: Num>(out: &'a mut [T; 4], v: &'b [T; 3]) -> &'a mut [T; 4] {
    out[0] = v[0];
    out[1] = v[1];
    out[2] = v[2];
    out
}
<|endoftext|>


<commit_before>//! I/O port functionality.

/// Write 8 bits to port
pub unsafe fn outb(port: u16, val: u8) {
    asm!("outb %al, %dx" :: "{dx}"(port), "{al}"(val));
}

/// Read 8 bits from port
pub unsafe fn inb(port: u16) -> u8 {
    let ret: u8;
    asm!("inb %dx, %al" : "={ax}"(ret) : "{dx}"(port) :: "volatile");
    ret
}

/// Write 16 bits to port
pub unsafe fn outw(port: u16, val: u16) {
    asm!("outw %ax, %dx" :: "{dx}"(port), "{al}"(val));
}

/// Read 16 bits from port
pub unsafe fn inw(port: u16) -> u16 {
    let ret: u16;
    asm!("inw %dx, %ax" : "={ax}"(ret) : "{dx}"(port) :: "volatile");
    ret
}

/// Write 32 bits to port
pub unsafe fn outl(port: u16, val: u32) {
    asm!("outl %eax, %dx" :: "{dx}"(port), "{al}"(val));
}

/// Read 32 bits from port
pub unsafe fn inl(port: u16) -> u32 {
    let ret: u32;
    asm!("inl %dx, %eax" : "={ax}"(ret) : "{dx}"(port) :: "volatile");
    ret
}

// Write 8-bit array to port
pub unsafe fn outsb(port: u16, buf: &[u8]) {
    asm!("rep outsb (%esi), %dx"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr())
         : "ecx", "edi");
}

// Read 8-bit array from port
pub unsafe fn insb(port: u16, buf: &mut [u8]) {
    asm!("rep insb %dx, (%edi)"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr())
         : "ecx", "edi" : "volatile");
}

// Write 16-bit array to port
pub unsafe fn outsw(port: u16, buf: &[u16]) {
    asm!("rep outsw (%esi), %dx"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr())
         : "ecx", "edi");
}

// Read 16-bit array from port
pub unsafe fn insw(port: u16, buf: &mut [u16]) {
    asm!("rep insw %dx, (%edi)"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr())
         : "ecx", "edi" : "volatile");
}

// Write 32-bit array to port
pub unsafe fn outsl(port: u16, buf: &[u32]) {
    asm!("rep outsl (%esi), %dx"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr())
         : "ecx", "edi");
}

// Read 32-bit array from port
pub unsafe fn insl(port: u16, buf: &mut [u32]) {
    asm!("rep insl %dx, (%edi)"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr())
         : "ecx", "edi" : "volatile");
}
<commit_msg>Make io assembly inlinable and fix doc comments<commit_after>//! I/O port functionality.

/// Write 8 bits to port
#[inline]
pub unsafe fn outb(port: u16, val: u8) {
    asm!("outb %al, %dx" :: "{dx}"(port), "{al}"(val));
}

/// Read 8 bits from port
#[inline]
pub unsafe fn inb(port: u16) -> u8 {
    let ret: u8;
    asm!("inb %dx, %al" : "={ax}"(ret) : "{dx}"(port) :: "volatile");
    ret
}

/// Write 16 bits to port
#[inline]
pub unsafe fn outw(port: u16, val: u16) {
    asm!("outw %ax, %dx" :: "{dx}"(port), "{al}"(val));
}

/// Read 16 bits from port
#[inline]
pub unsafe fn inw(port: u16) -> u16 {
    let ret: u16;
    asm!("inw %dx, %ax" : "={ax}"(ret) : "{dx}"(port) :: "volatile");
    ret
}

/// Write 32 bits to port
#[inline]
pub unsafe fn outl(port: u16, val: u32) {
    asm!("outl %eax, %dx" :: "{dx}"(port), "{al}"(val));
}

/// Read 32 bits from port
#[inline]
pub unsafe fn inl(port: u16) -> u32 {
    let ret: u32;
    asm!("inl %dx, %eax" : "={ax}"(ret) : "{dx}"(port) :: "volatile");
    ret
}

/// Write 8-bit array to port
#[inline]
pub unsafe fn outsb(port: u16, buf: &[u8]) {
    asm!("rep outsb (%esi), %dx"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr())
         : "ecx", "edi");
}

/// Read 8-bit array from port
#[inline]
pub unsafe fn insb(port: u16, buf: &mut [u8]) {
    asm!("rep insb %dx, (%edi)"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr())
         : "ecx", "edi" : "volatile");
}

/// Write 16-bit array to port
#[inline]
pub unsafe fn outsw(port: u16, buf: &[u16]) {
    asm!("rep outsw (%esi), %dx"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr())
         : "ecx", "edi");
}

/// Read 16-bit array from port
#[inline]
pub unsafe fn insw(port: u16, buf: &mut [u16]) {
    asm!("rep insw %dx, (%edi)"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr())
         : "ecx", "edi" : "volatile");
}

/// Write 32-bit array to port
#[inline]
pub unsafe fn outsl(port: u16, buf: &[u32]) {
    asm!("rep outsl (%esi), %dx"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr())
         : "ecx", "edi");
}

/// Read 32-bit array from port
#[inline]
pub unsafe fn insl(port: u16, buf: &mut [u32]) {
    asm!("rep insl %dx, (%edi)"
         :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr())
         : "ecx", "edi" : "volatile");
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::{Read, Write, Result};

pub fn copy_until<R, W, P>(reader: &mut R, writer: &mut W, mut pred: P)
    -> Result<()>
where
    R: Read,
    W: Write,
    P: FnMut(&R, &W) -> bool,
{
    // TODO: Move the magic number to a constant.
    let mut buf = [0; 1024];
    loop {
        use std::io::ErrorKind::*;
        let len = match reader.read(&mut buf[..]) {
            Ok(0) => break,
            Ok(len) => len,
            Err(ref error) if error.kind() == Interrupted => continue,
            Err(error) => return Err(error),
        };

        writer.write_all(&buf[..len])?;
        if pred(reader, writer) {
            break;
        }
    }

    Ok(())
}

// TODO: Write tests.
<commit_msg>Remove magic constant from buffer copying code.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::{Read, Write, Result};

// The same as in the Rust's standard library.
const DEFAULT_BUF_SIZE: usize = 8 * 1024;

pub fn copy_until<R, W, P>(reader: &mut R, writer: &mut W, mut pred: P)
    -> Result<()>
where
    R: Read,
    W: Write,
    P: FnMut(&R, &W) -> bool,
{
    let mut buf = [0; DEFAULT_BUF_SIZE];
    loop {
        use std::io::ErrorKind::*;
        let len = match reader.read(&mut buf[..]) {
            Ok(0) => break,
            Ok(len) => len,
            Err(ref error) if error.kind() == Interrupted => continue,
            Err(error) => return Err(error),
        };

        writer.write_all(&buf[..len])?;
        if pred(reader, writer) {
            break;
        }
    }

    Ok(())
}

// TODO: Write tests.
<|endoftext|>


<commit_before>//! I/O related extensions.

use std::io::Read;

use Result;

pub trait LasStringExt: Read {
    fn read_las_string(&mut self, count: usize) -> Result<String>;
}

impl<R: Read> LasStringExt for R {
    fn read_las_string(&mut self, count: usize) -> Result<String> {
        Ok(String::new())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::io::Cursor;

    #[test]
    fn simple_las_string() {
        let mut cursor = Cursor::new(Vec::from("hi"));
        assert_eq!("hi", cursor.read_las_string(2).unwrap());
    }
}
<commit_msg>Reimplement read_las_string<commit_after>//! I/O related extensions.

use std::io::Read;

use Error;
use Result;

pub trait LasStringExt: Read {
    fn read_las_string(&mut self, count: usize) -> Result<String>;
}

impl<R: Read> LasStringExt for R {
    fn read_las_string(&mut self, count: usize) -> Result<String> {
        let mut character_after_null = false;
        let mut seen_null = false;
        let mut string = String::with_capacity(count);
        for byte in self.take(count as u64).bytes() {
            let byte = try!(byte);
            if byte == 0u8 {
                seen_null = true;
            } else if seen_null {
                character_after_null = true;
            } else {
                string.push(byte as char);
            }
        }
        if character_after_null {
            Err(Error::CharacterAfterNullByte)
        } else {
            Ok(string)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::io::Cursor;

    #[test]
    fn simple_las_string() {
        let mut cursor = Cursor::new(Vec::from("hi"));
        assert_eq!("hi", cursor.read_las_string(2).unwrap());
    }
}
<|endoftext|>


<commit_before>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::pin::{Pin};
use std::sync::{Arc};
use std::marker::{Unpin};
use futures::channel::oneshot;
use futures::future::{Future, BoxFuture};

use std::mem;
use std::result::{Result};

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send+Unpin> {
    /// Queue used for scheduling runtime for this object
    queue:  Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    /// Will be 'None' only briefly when the data has been taken to be dropped
    data:   Option<Pin<Box<T>>>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send+Unpin> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send+Unpin> Sync for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send+Unpin> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:  queue,
            data:   Some(Pin::new(Box::new(data)))
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    #[inline]
    #[deprecated(since="0.3.0", note="please use `desync` instead")]
    pub fn r#async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        self.desync(job)
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn desync<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        // As drop() is the last thing called, we know that this object will still exist at the point where the queue makes the asynchronous callback
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        desync(&self.queue, move || {
            let data = data.0 as *mut T;
            job(unsafe { &mut *data });
        })
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            // Exclusivity is guaranteed because the queue executes only one task at a time
            let data = DataRef::<T>(&**self.data.as_ref().unwrap());

            sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation synchronously on this item,
    ///
    pub fn try_sync<TFn, FnResult>(&self, job: TFn) -> Result<FnResult, TrySyncError>
    where TFn: Send+FnOnce(&mut T) -> FnResult, FnResult: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            let data = DataRef::<T>(&**self.data.as_ref().unwrap());

            try_sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Deprecated name for `future_desync`
    ///
    #[inline]
    #[deprecated(since="0.7.0", note="please use either `future_desync` or `future_sync` to schedule futures")]
    pub fn future<TFn, TOutput>(&self, job: TFn) -> impl Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'static+Send+for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, TOutput>,
            TOutput:    'static+Send {
        self.future_desync(job)
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    /// The future will be scheduled in the background, so it will make progress even if the current scheduler is
    /// blocked for any reason. Additionally, it's not necessary to await the returned future, which can be discarded
    /// if necessary.
    ///
    pub fn future_desync<'a, TFn, TFuture>(&self, job: TFn) -> SchedulerFuture<TFuture::Output>
    where
        TFn:                'static + Send + FnOnce(&'a mut T) -> TFuture,
        TFuture:            'a + Send + Future,
        TFuture::Output:    'static + Send,
    {
        // The future will have a lifetime shorter than the lifetime of this structure, and exclusivity is guaranteed
        // because queues only execute one task at a time
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        scheduler().future_desync(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning a future that must be awaited
    /// before it is dropped.
    ///
    /// The future will be scheduled in the current execution context, so it will only make progress if the current
    /// scheduler is running. The task will be cancelled and will not complete execution if the future is dropped before
    /// completion, so it's usually necessary to await the result of this function for the task to behave correctly.
    ///
    pub fn future_sync<'a, 'b, TFn, TFuture>(&'a self, job: TFn) -> impl 'a + Future<Output=Result<TFuture::Output, oneshot::Canceled>> + Send
    where
        TFn:                'a + Send + FnOnce(&'b mut T) -> TFuture,
        TFuture:            'b + Send + Future,
        TFuture::Output:    'a + Send,
        'b:                 'a,
    {
        // The future will have a lifetime shorter than the lifetime of this structure
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        scheduler().future_sync(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// After the pending operations for this item are performed, waits for the
    /// supplied future to complete and then calls the specified function
    ///
    pub fn after<'a, TFn, Res, Fut>(&self, after: Fut, job: TFn) -> impl 'static + Future<Output=Result<Res, oneshot::Canceled>> + Send
    where
        Res: 'static + Send,
        Fut: 'static + Future + Send,
        TFn: 'static + Send + FnOnce(&mut T, Fut::Output) -> Res
    {
        self.future_desync(move |data| {
            async move {
                let future_result = after.await;
                job(data, future_result)
            }
        })
    }
}

impl<T: Send+Unpin> Drop for Desync<T> {
    fn drop(&mut self) {
        use std::thread;

        // Take the data we're about to drop from the object
        let data = self.data.take();

        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        if thread::panicking() {
            // If the thread is already panicking when we're dropped, do not panic again
            scheduler().sync_no_panic(&self.queue, move || {
                mem::drop(data);
            });
        } else {
            // Thread is not panicking
            sync(&self.queue, move || {
                mem::drop(data);
            });
        }
    }
}
<commit_msg>Add some extra documentation for the future_sync/future_desync methods describing when to use one or the other<commit_after>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::pin::{Pin};
use std::sync::{Arc};
use std::marker::{Unpin};
use futures::channel::oneshot;
use futures::future::{Future, BoxFuture};

use std::mem;
use std::result::{Result};

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send+Unpin> {
    /// Queue used for scheduling runtime for this object
    queue:  Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    /// Will be 'None' only briefly when the data has been taken to be dropped
    data:   Option<Pin<Box<T>>>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send+Unpin> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send+Unpin> Sync for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send+Unpin> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:  queue,
            data:   Some(Pin::new(Box::new(data)))
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    #[inline]
    #[deprecated(since="0.3.0", note="please use `desync` instead")]
    pub fn r#async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        self.desync(job)
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn desync<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        // As drop() is the last thing called, we know that this object will still exist at the point where the queue makes the asynchronous callback
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        desync(&self.queue, move || {
            let data = data.0 as *mut T;
            job(unsafe { &mut *data });
        })
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            // Exclusivity is guaranteed because the queue executes only one task at a time
            let data = DataRef::<T>(&**self.data.as_ref().unwrap());

            sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation synchronously on this item,
    ///
    pub fn try_sync<TFn, FnResult>(&self, job: TFn) -> Result<FnResult, TrySyncError>
    where TFn: Send+FnOnce(&mut T) -> FnResult, FnResult: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            let data = DataRef::<T>(&**self.data.as_ref().unwrap());

            try_sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Deprecated name for `future_desync`
    ///
    #[inline]
    #[deprecated(since="0.7.0", note="please use either `future_desync` or `future_sync` to schedule futures")]
    pub fn future<TFn, TOutput>(&self, job: TFn) -> impl Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'static+Send+for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, TOutput>,
            TOutput:    'static+Send {
        self.future_desync(job)
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    /// The future will be scheduled in the background, so it will make progress even if the current scheduler is
    /// blocked for any reason. Additionally, it's not necessary to await the returned future, which can be discarded
    /// if necessary.
    ///
    /// Call `detach()` on the returned future to fully put it in the background. If awaited the scheduler may try to
    /// 'steal' runtime on the current thread to complete the future, but it will also run the future in the background
    /// if the current thread is not available or a background thread is available faster. The future here has static
    /// lifetime so can be passed around in case something needs to monitor when it completes.
    ///
    /// Note that this has a companion function, `future_sync()` which is more useful for cases where the future is
    /// awaited, as the 'inner' future is able to borrow values from the 'outer' future due to the shorter lifetime
    /// of the returned value.
    ///
    pub fn future_desync<'a, TFn, TFuture>(&self, job: TFn) -> SchedulerFuture<TFuture::Output>
    where
        TFn:                'static + Send + FnOnce(&'a mut T) -> TFuture,
        TFuture:            'a + Send + Future,
        TFuture::Output:    'static + Send,
    {
        // The future will have a lifetime shorter than the lifetime of this structure, and exclusivity is guaranteed
        // because queues only execute one task at a time
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        scheduler().future_desync(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning a future that must be awaited
    /// before it is dropped.
    ///
    /// The future will be scheduled in the current execution context, so it will only make progress if the current
    /// scheduler is running. The task will be cancelled and will not complete execution if the future is dropped before
    /// completion, so it's usually necessary to await the result of this function for the task to behave correctly.
    ///
    /// This is most useful for 'foreground' futures: note that the future can safely borrow values from the outer context
    /// to use while it's running (in much the same way `sync()` can in synchronous contexts). The need to dispatch to
    /// the awaiting context can make this future slightly slower than `future_desync()`
    ///
    pub fn future_sync<'a, 'b, TFn, TFuture>(&'a self, job: TFn) -> impl 'a + Future<Output=Result<TFuture::Output, oneshot::Canceled>> + Send
    where
        TFn:                'a + Send + FnOnce(&'b mut T) -> TFuture,
        TFuture:            'b + Send + Future,
        TFuture::Output:    'a + Send,
        'b:                 'a,
    {
        // The future will have a lifetime shorter than the lifetime of this structure
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        scheduler().future_sync(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// After the pending operations for this item are performed, waits for the
    /// supplied future to complete and then calls the specified function
    ///
    pub fn after<'a, TFn, Res, Fut>(&self, after: Fut, job: TFn) -> impl 'static + Future<Output=Result<Res, oneshot::Canceled>> + Send
    where
        Res: 'static + Send,
        Fut: 'static + Future + Send,
        TFn: 'static + Send + FnOnce(&mut T, Fut::Output) -> Res
    {
        self.future_desync(move |data| {
            async move {
                let future_result = after.await;
                job(data, future_result)
            }
        })
    }
}

impl<T: Send+Unpin> Drop for Desync<T> {
    fn drop(&mut self) {
        use std::thread;

        // Take the data we're about to drop from the object
        let data = self.data.take();

        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        if thread::panicking() {
            // If the thread is already panicking when we're dropped, do not panic again
            scheduler().sync_no_panic(&self.queue, move || {
                mem::drop(data);
            });
        } else {
            // Thread is not panicking
            sync(&self.queue, move || {
                mem::drop(data);
            });
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use Digest::*;

#[allow(non_camel_case_types)]
pub enum Digest {
    Sha1([u8; 20]),
    Sha2_256([u8; 32]),
    Sha2_512([u8; 64]),
    Sha3_512([u8; 64]),
    Sha3_384([u8; 48]),
    Sha3_256([u8; 32]),
    Sha3_224([u8; 28]),
    Shake128([u8; 16]),
    Shake256([u8; 32]),
    Blake2B([u8; 64]),
    Blake2S([u8; 32]),
    ApplicationSpecific {
        code: u8,
        bytes: Vec<u8>,
    },
}

impl Digest {
    // TODO: Real error type
    /// Returns an empty digest of the specified type, validates code and length
    pub fn from_code_and_length(code: u8, length: usize) -> Result<Digest, &'static str> {
        Ok(match code {
            0x11 if length <= 20 => Sha1([0; 20]),
            0x12 if length <= 32 => Sha2_256([0; 32]),
            0x13 if length <= 64 => Sha2_512([0; 64]),
            0x14 if length <= 64 => Sha3_512([0; 64]),
            0x15 if length <= 48 => Sha3_384([0; 48]),
            0x16 if length <= 32 => Sha3_256([0; 32]),
            0x17 if length <= 28 => Sha3_224([0; 28]),
            0x18 if length <= 16 => Shake128([0; 16]),
            0x19 if length <= 32 => Shake256([0; 32]),

            0x40 if length <= 64 => Blake2B([0; 64]),
            0x41 if length <= 32 => Blake2S([0; 32]),

            _ if code < 0x10 && length < 0x7f =>
                ApplicationSpecific { code: code, bytes: vec![0; length] },
            _ if code > 0x7f => {
                return Err("Codes greater than 0x7f are an unsupported future feature")
            }
            _ => {
                return Err("Digest length exceeds allowed length for specified type")
            }
        })
    }

    pub fn code(&self) -> u8 {
        match *self {
            Sha1(_) => 0x11,
            Sha2_256(_) => 0x12,
            Sha2_512(_) => 0x13,
            Sha3_512(_) => 0x14,
            Sha3_384(_) => 0x15,
            Sha3_256(_) => 0x16,
            Sha3_224(_) => 0x17,
            Shake128(_) => 0x18,
            Shake256(_) => 0x19,
            Blake2B(_) => 0x40,
            Blake2S(_) => 0x41,

            ApplicationSpecific { code, .. } if code < 0x10 => code,

            // TODO: could just ignore them being invalid or return Error...
            ApplicationSpecific { code, .. } =>
                panic!("application specific code {:#02x} outside allowed range 0x00-0x0f", code),
        }
    }

    pub fn name(&self) -> &'static str {
        match *self {
            Sha1(_) => "sha1",
            Sha2_256(_) => "sha2-256",
            Sha2_512(_) => "sha2-512",
            Sha3_224(_) => "sha3-224",
            Sha3_256(_) => "sha3-256",
            Sha3_384(_) => "sha3-384",
            Sha3_512(_) => "sha3-512",
            Shake128(_) => "shake-128",
            Shake256(_) => "shake-256",
            Blake2B(_) => "blake2b",
            Blake2S(_) => "blake2s",

            ApplicationSpecific { code, .. } if code < 0x10 => "app-specific",
            _ => panic!("TODO: not panic"),
        }
    }

    pub fn bytes(&self) -> &[u8] {
        match *self {
            Sha1(ref bytes) => bytes,
            Sha2_256(ref bytes) => bytes,
            Sha2_512(ref bytes) => bytes,
            Sha3_224(ref bytes) => bytes,
            Sha3_256(ref bytes) => bytes,
            Sha3_384(ref bytes) => bytes,
            Sha3_512(ref bytes) => bytes,
            Shake128(ref bytes) => bytes,
            Shake256(ref bytes) => bytes,
            Blake2B(ref bytes) => bytes,
            Blake2S(ref bytes) => bytes,
            ApplicationSpecific { ref bytes, .. } => bytes,
        }
    }

    pub fn mut_bytes(&mut self) -> &mut [u8] {
        match *self {
            Sha1(ref mut bytes) => bytes,
            Sha2_256(ref mut bytes) => bytes,
            Sha2_512(ref mut bytes) => bytes,
            Sha3_224(ref mut bytes) => bytes,
            Sha3_256(ref mut bytes) => bytes,
            Sha3_384(ref mut bytes) => bytes,
            Sha3_512(ref mut bytes) => bytes,
            Shake128(ref mut bytes) => bytes,
            Shake256(ref mut bytes) => bytes,
            Blake2B(ref mut bytes) => bytes,
            Blake2S(ref mut bytes) => bytes,
            ApplicationSpecific { ref mut bytes, .. } => bytes,
        }
    }
}

impl fmt::Debug for Digest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple(self.name()).field(&self.bytes().to_vec()).finish()
    }
}

impl Clone for Digest {
    fn clone(&self) -> Digest {
        match *self {
            Sha1(ref bytes) => Sha1(*bytes),
            Sha2_256(ref bytes) => Sha2_256(*bytes),
            Sha2_512(ref bytes) => Sha2_512(*bytes),
            Sha3_224(ref bytes) => Sha3_224(*bytes),
            Sha3_256(ref bytes) => Sha3_256(*bytes),
            Sha3_384(ref bytes) => Sha3_384(*bytes),
            Sha3_512(ref bytes) => Sha3_512(*bytes),
            Shake128(ref bytes) => Shake128(*bytes),
            Shake256(ref bytes) => Shake256(*bytes),
            Blake2B(ref bytes) => Blake2B(*bytes),
            Blake2S(ref bytes) => Blake2S(*bytes),
            ApplicationSpecific { code, ref bytes } => ApplicationSpecific {
                code: code,
                bytes: bytes.clone(),
            },
        }
    }
}

impl Eq for Digest {}
impl PartialEq for Digest {
    fn eq(&self, other: &Digest) -> bool {
        match (self, other) {
            (&Sha1(ref left), &Sha1(ref right)) => left == right,
            (&Sha2_256(ref left), &Sha2_256(ref right)) => left == right,
            (&Sha2_512(ref left), &Sha2_512(ref right)) => left[..] == right[..],
            (&Sha3_224(ref left), &Sha3_224(ref right)) => left == right,
            (&Sha3_256(ref left), &Sha3_256(ref right)) => left == right,
            (&Sha3_384(ref left), &Sha3_384(ref right)) => left[..] == right[..],
            (&Sha3_512(ref left), &Sha3_512(ref right)) => left[..] == right[..],
            (&Shake128(ref left), &Shake128(ref right)) => left == right,
            (&Shake256(ref left), &Shake256(ref right)) => left == right,
            (&Blake2B(ref left), &Blake2B(ref right)) => left[..] == right[..],
            (&Blake2S(ref left), &Blake2S(ref right)) => left == right,
            (
                &ApplicationSpecific { code: left_code, bytes: ref left },
                &ApplicationSpecific { code: right_code, bytes: ref right }
            ) if left_code == right_code => left == right,
            _ => false,
        }
    }
}
<commit_msg>Use base16 encoded string for digest bytes debug format<commit_after>use std::fmt;

use Digest::*;

#[allow(non_camel_case_types)]
pub enum Digest {
    Sha1([u8; 20]),
    Sha2_256([u8; 32]),
    Sha2_512([u8; 64]),
    Sha3_512([u8; 64]),
    Sha3_384([u8; 48]),
    Sha3_256([u8; 32]),
    Sha3_224([u8; 28]),
    Shake128([u8; 16]),
    Shake256([u8; 32]),
    Blake2B([u8; 64]),
    Blake2S([u8; 32]),
    ApplicationSpecific {
        code: u8,
        bytes: Vec<u8>,
    },
}

impl Digest {
    // TODO: Real error type
    /// Returns an empty digest of the specified type, validates code and length
    pub fn from_code_and_length(code: u8, length: usize) -> Result<Digest, &'static str> {
        Ok(match code {
            0x11 if length <= 20 => Sha1([0; 20]),
            0x12 if length <= 32 => Sha2_256([0; 32]),
            0x13 if length <= 64 => Sha2_512([0; 64]),
            0x14 if length <= 64 => Sha3_512([0; 64]),
            0x15 if length <= 48 => Sha3_384([0; 48]),
            0x16 if length <= 32 => Sha3_256([0; 32]),
            0x17 if length <= 28 => Sha3_224([0; 28]),
            0x18 if length <= 16 => Shake128([0; 16]),
            0x19 if length <= 32 => Shake256([0; 32]),

            0x40 if length <= 64 => Blake2B([0; 64]),
            0x41 if length <= 32 => Blake2S([0; 32]),

            _ if code < 0x10 && length < 0x7f =>
                ApplicationSpecific { code: code, bytes: vec![0; length] },
            _ if code > 0x7f => {
                return Err("Codes greater than 0x7f are an unsupported future feature")
            }
            _ => {
                return Err("Digest length exceeds allowed length for specified type")
            }
        })
    }

    pub fn code(&self) -> u8 {
        match *self {
            Sha1(_) => 0x11,
            Sha2_256(_) => 0x12,
            Sha2_512(_) => 0x13,
            Sha3_512(_) => 0x14,
            Sha3_384(_) => 0x15,
            Sha3_256(_) => 0x16,
            Sha3_224(_) => 0x17,
            Shake128(_) => 0x18,
            Shake256(_) => 0x19,
            Blake2B(_) => 0x40,
            Blake2S(_) => 0x41,

            ApplicationSpecific { code, .. } if code < 0x10 => code,

            // TODO: could just ignore them being invalid or return Error...
            ApplicationSpecific { code, .. } =>
                panic!("application specific code {:#02x} outside allowed range 0x00-0x0f", code),
        }
    }

    pub fn name(&self) -> &'static str {
        match *self {
            Sha1(_) => "sha1",
            Sha2_256(_) => "sha2-256",
            Sha2_512(_) => "sha2-512",
            Sha3_224(_) => "sha3-224",
            Sha3_256(_) => "sha3-256",
            Sha3_384(_) => "sha3-384",
            Sha3_512(_) => "sha3-512",
            Shake128(_) => "shake-128",
            Shake256(_) => "shake-256",
            Blake2B(_) => "blake2b",
            Blake2S(_) => "blake2s",

            ApplicationSpecific { code, .. } if code < 0x10 => "app-specific",
            _ => panic!("TODO: not panic"),
        }
    }

    pub fn bytes(&self) -> &[u8] {
        match *self {
            Sha1(ref bytes) => bytes,
            Sha2_256(ref bytes) => bytes,
            Sha2_512(ref bytes) => bytes,
            Sha3_224(ref bytes) => bytes,
            Sha3_256(ref bytes) => bytes,
            Sha3_384(ref bytes) => bytes,
            Sha3_512(ref bytes) => bytes,
            Shake128(ref bytes) => bytes,
            Shake256(ref bytes) => bytes,
            Blake2B(ref bytes) => bytes,
            Blake2S(ref bytes) => bytes,
            ApplicationSpecific { ref bytes, .. } => bytes,
        }
    }

    pub fn mut_bytes(&mut self) -> &mut [u8] {
        match *self {
            Sha1(ref mut bytes) => bytes,
            Sha2_256(ref mut bytes) => bytes,
            Sha2_512(ref mut bytes) => bytes,
            Sha3_224(ref mut bytes) => bytes,
            Sha3_256(ref mut bytes) => bytes,
            Sha3_384(ref mut bytes) => bytes,
            Sha3_512(ref mut bytes) => bytes,
            Shake128(ref mut bytes) => bytes,
            Shake256(ref mut bytes) => bytes,
            Blake2B(ref mut bytes) => bytes,
            Blake2S(ref mut bytes) => bytes,
            ApplicationSpecific { ref mut bytes, .. } => bytes,
        }
    }
}

impl fmt::Debug for Digest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(f.write_str(self.name()));
        try!(f.write_str("(\""));
        for byte in self.bytes() {
            try!(write!(f, "{:x}", byte));
        }
        try!(f.write_str("\")"));
        Ok(())
    }
}

impl Clone for Digest {
    fn clone(&self) -> Digest {
        match *self {
            Sha1(ref bytes) => Sha1(*bytes),
            Sha2_256(ref bytes) => Sha2_256(*bytes),
            Sha2_512(ref bytes) => Sha2_512(*bytes),
            Sha3_224(ref bytes) => Sha3_224(*bytes),
            Sha3_256(ref bytes) => Sha3_256(*bytes),
            Sha3_384(ref bytes) => Sha3_384(*bytes),
            Sha3_512(ref bytes) => Sha3_512(*bytes),
            Shake128(ref bytes) => Shake128(*bytes),
            Shake256(ref bytes) => Shake256(*bytes),
            Blake2B(ref bytes) => Blake2B(*bytes),
            Blake2S(ref bytes) => Blake2S(*bytes),
            ApplicationSpecific { code, ref bytes } => ApplicationSpecific {
                code: code,
                bytes: bytes.clone(),
            },
        }
    }
}

impl Eq for Digest {}
impl PartialEq for Digest {
    fn eq(&self, other: &Digest) -> bool {
        match (self, other) {
            (&Sha1(ref left), &Sha1(ref right)) => left == right,
            (&Sha2_256(ref left), &Sha2_256(ref right)) => left == right,
            (&Sha2_512(ref left), &Sha2_512(ref right)) => left[..] == right[..],
            (&Sha3_224(ref left), &Sha3_224(ref right)) => left == right,
            (&Sha3_256(ref left), &Sha3_256(ref right)) => left == right,
            (&Sha3_384(ref left), &Sha3_384(ref right)) => left[..] == right[..],
            (&Sha3_512(ref left), &Sha3_512(ref right)) => left[..] == right[..],
            (&Shake128(ref left), &Shake128(ref right)) => left == right,
            (&Shake256(ref left), &Shake256(ref right)) => left == right,
            (&Blake2B(ref left), &Blake2B(ref right)) => left[..] == right[..],
            (&Blake2S(ref left), &Blake2S(ref right)) => left == right,
            (
                &ApplicationSpecific { code: left_code, bytes: ref left },
                &ApplicationSpecific { code: right_code, bytes: ref right }
            ) if left_code == right_code => left == right,
            _ => false,
        }
    }
}
<|endoftext|>