<commit_before>use super::{metadata::{read_metafile,
                       MetaFile},
            PackageIdent,
            PackageTarget};
use crate::error::{Error,
                   Result};
use std::{ffi::OsStr,
          fs,
          io,
          path::{Path,
                 PathBuf},
          str::FromStr};
use tempfile::{Builder,
               TempDir};

pub const INSTALL_TMP_PREFIX: &str = ".hab-pkg-install";

/// Return a directory which can be used as a temp dir during package install/
/// uninstall
///
/// It returns a path which is in the same parent directory as `path`
/// but with TempDir style randomization
pub fn temp_package_directory(path: &Path) -> Result<TempDir> {
    let base = path.parent().ok_or_else(|| {
                                 Error::PackageUnpackFailed(
            "Could not determine parent directory for temporary package directory".to_owned(),
        )
                             })?;
    fs::create_dir_all(base)?;
    let cannon_base = base.canonicalize()?;
    let temp_install_prefix =
        path.file_name()
            .and_then(OsStr::to_str)
            .map(|dirname| format!("{}-{}", INSTALL_TMP_PREFIX, dirname))
            .ok_or_else(|| {
                Error::PackageUnpackFailed("Could not generate prefix for temporary package \
                                            directory"
                                                      .to_owned())
            })?;
    Ok(Builder::new().prefix(&temp_install_prefix)
                     .tempdir_in(cannon_base)?)
}

/// Returns a list of package structs built from the contents of the given directory.
pub fn all_packages(path: &Path) -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    if fs::metadata(path)?.is_dir() {
        walk_origins(&path, &mut package_list)?;
    }
    Ok(package_list)
}

/// Returns a vector of package idents built from the contents of
/// the given directory, using the given origin to restrict the
/// search.
///
/// The search is restricted by assuming the package directory
/// structure is:
///
///    /base/ORIGIN/NAME/VERSION/RELEASE/
pub fn package_list_for_origin(base_pkg_path: &Path, origin: &str) -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    let mut package_path = PathBuf::from(base_pkg_path);
    package_path.push(&origin);

    if !is_existing_dir(&package_path)? {
        return Ok(package_list);
    };

    walk_names(&origin, &package_path, &mut package_list)?;
    Ok(package_list)
}

/// Returns a vector of package structs built from the contents of
/// the given directory, using the given ident to restrict the
/// search.
///
/// The search is restricted by assuming the package directory
/// structure is:
///
///    /base/ORIGIN/NAME/VERSION/RELEASE/
pub fn package_list_for_ident(base_pkg_path: &Path,
                              ident: &PackageIdent)
                              -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    let mut package_path = PathBuf::from(base_pkg_path);
    package_path.push(&ident.origin);
    package_path.push(&ident.name);

    if !is_existing_dir(&package_path)? {
        return Ok(package_list);
    }

    match (&ident.version, &ident.release) {
        // origin/name
        (None, _) => walk_versions(&ident.origin, &ident.name, &package_path, &mut package_list)?,
        // origin/name/version
        (Some(version), None) => {
            package_path.push(version);
            if !is_existing_dir(&package_path)? {
                return Ok(package_list);
            }
            walk_releases(&ident.origin,
                          &ident.name,
                          &version,
                          &package_path,
                          &mut package_list)?
        }
        // origin/name/version/release
        (Some(version), Some(release)) => {
            package_path.push(version);
            package_path.push(release);
            if !is_existing_dir(&package_path)? {
                return Ok(package_list);
            }

            let active_target = PackageTarget::active_target();
            if let Some(new_ident) = package_ident_from_dir(&ident.origin,
                                                            &ident.name,
                                                            &version,
                                                            active_target,
                                                            &package_path)
            {
                package_list.push(new_ident)
            }
        }
    }
    Ok(package_list)
}

/// Helper function for all_packages. Walks the directory at the given
/// Path for origin directories and builds on the given package list
/// by recursing into name, version, and release directories.
fn walk_origins(path: &Path, packages: &mut Vec<PackageIdent>) -> Result<()> {
    for entry in fs::read_dir(path)? {
        let origin_dir = entry?;
        let origin_path = origin_dir.path();
        if fs::metadata(&origin_path)?.is_dir() {
            let origin = filename_from_entry(&origin_dir);
            walk_names(&origin, &origin_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_origins. Walks the direcotry at the given
/// Path for name directories and recurses into them to find version
/// and release directories.
fn walk_names(origin: &str, dir: &Path, packages: &mut Vec<PackageIdent>) -> Result<()> {
    for entry in fs::read_dir(dir)? {
        let name_dir = entry?;
        let name_path = name_dir.path();
        if fs::metadata(&name_path)?.is_dir() {
            let name = filename_from_entry(&name_dir);
            walk_versions(&origin, &name, &name_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_names. Walks the directory at the given
/// Path and recurses into them to find release directories.
fn walk_versions(origin: &str,
                 name: &str,
                 dir: &Path,
                 packages: &mut Vec<PackageIdent>)
                 -> Result<()> {
    for entry in fs::read_dir(dir)? {
        let version_dir = entry?;
        let version_path = version_dir.path();
        if fs::metadata(&version_path)?.is_dir() {
            let version = filename_from_entry(&version_dir);
            walk_releases(origin, name, &version, &version_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_versions. Walks the directory at the
/// given Path and constructs a Package struct if the directory is a
/// valid package directory. Any resulting packages are pushed onto
/// the given packages vector, assuming the given origin, name, and
/// version.
fn walk_releases(origin: &str,
                 name: &str,
                 version: &str,
                 dir: &Path,
                 packages: &mut Vec<PackageIdent>)
                 -> Result<()> {
    let active_target = PackageTarget::active_target();
    for entry in fs::read_dir(dir)? {
        let release_dir = entry?;
        let release_path = release_dir.path();
        if fs::metadata(&release_path)?.is_dir() {
            if let Some(ident) =
                package_ident_from_dir(origin, name, version, active_target, &release_path)
            {
                packages.push(ident)
            }
        }
    }
    Ok(())
}

/// package_ident_from_dir returns a PackageIdent if the given
/// path contains a valid package for the given active_target.
///
/// Returns None when
///    - The directory is a temporary install directroy
///    - An error occurs reading the package metadata
///    - An error occurs reading the package target
///    - The package target doesn't match the given active target
fn package_ident_from_dir(origin: &str,
                          name: &str,
                          version: &str,
                          active_target: PackageTarget,
                          dir: &Path)
                          -> Option<PackageIdent> {
    let release = dir.file_name().and_then(OsStr::to_str)?;

    if release.starts_with(INSTALL_TMP_PREFIX) {
        debug!("PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate \
                because it matches installation temporary directory prefix: {}",
               dir.display());
        return None;
    }

    let metafile_content = read_metafile(dir, MetaFile::Target);
    // If there is an error reading the target metafile, then skip the candidate
    if let Err(e) = metafile_content {
        debug!("PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate due \
                to error reading TARGET metafile, found={}, reason={:?}",
               dir.display(),
               e,);
        return None;
    }

    // Any errors have been cleared, so unwrap is safe
    let metafile_content = metafile_content.unwrap();
    let install_target = PackageTarget::from_str(&metafile_content);
    // If there is an error parsing the target as a valid PackageTarget, then skip the
    // candidate
    if let Err(e) = install_target {
        debug!("PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate due \
                to error parsing TARGET metafile as a valid PackageTarget, found={}, reason={:?}",
               dir.display(),
               e,);
        return None;
    }
    // Any errors have been cleared, so unwrap is safe
    let install_target = install_target.unwrap();

    // Ensure that the installed package's target matches the active `PackageTarget`,
    // otherwise skip the candidate
    if active_target == install_target {
        Some(PackageIdent::new(origin.to_string(),
                               name.to_string(),
                               Some(version.to_string()),
                               Some(release.to_owned())))
    } else {
        debug!("PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate, \
                found={}, installed_target={}, active_target={}",
               dir.display(),
               install_target,
               active_target,);
        None
    }
}

fn filename_from_entry(entry: &fs::DirEntry) -> String {
    entry.file_name().to_string_lossy().into_owned()
}

fn is_existing_dir(path: &Path) -> Result<bool> {
    match fs::metadata(&path) {
        Err(err) => {
            if err.kind() == io::ErrorKind::NotFound {
                return Ok(false);
            }
            Err(Error::from(err))
        }
        Ok(metadata) => Ok(metadata.is_dir()),
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::package::test_support::testing_package_install;

    use crate::fs;
    use std::fs::File;
    use tempfile::Builder;

    #[test]
    fn empty_dir_gives_empty_list() {
        let package_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let packages = all_packages(&package_root.path()).unwrap();

        assert_eq!(0, packages.len());
    }

    #[test]
    fn not_a_dir_gives_empty_list() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let file_path = fs_root.path().join("not-a-dir");
        let _tmp_file = File::create(&file_path).unwrap();

        let packages = all_packages(&file_path).unwrap();

        assert_eq!(0, packages.len());
    }
    #[test]
    fn can_read_single_package() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let package_install = testing_package_install("core/redis", fs_root.path());

        let packages = all_packages(&package_root).unwrap();

        assert_eq!(1, packages.len());
        assert_eq!(package_install.ident, packages[0]);
    }

    #[test]
    fn can_read_multiple_packages() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let expected = vec![testing_package_install("core/redis/1.0.0", fs_root.path()),
                            testing_package_install("test/foobar", fs_root.path()),
                            testing_package_install("core/redis/1.1.0", fs_root.path()),];

        let packages = all_packages(&package_root).unwrap();

        assert_eq!(3, packages.len());
        for p in &expected {
            assert!(packages.contains(&p.ident));
        }
    }

    #[test]
    fn create_temp_package_directory_in_same_parentdir() {
        let p = Builder::new().tempdir().unwrap();
        let temp_dir = temp_package_directory(p.path()).unwrap();
        let temp_path = temp_dir.path();

        assert_eq!(&p.path().parent().unwrap().canonicalize().unwrap(),
                   &temp_path.parent().unwrap());
    }

    #[test]
    fn temp_package_directory_starts_with_prefix() {
        let p = Builder::new().tempdir().unwrap();
        let temp_dir = temp_package_directory(p.path()).unwrap();
        let temp_filename = temp_dir.path().file_name().unwrap().to_str().unwrap();

        assert!(&temp_filename.starts_with(INSTALL_TMP_PREFIX));
    }

    #[test]
    fn temp_package_directory_changes() {
        let p = Builder::new().tempdir().unwrap();
        let temp_dir1 = temp_package_directory(p.path()).unwrap();
        let temp_dir2 = temp_package_directory(p.path()).unwrap();

        assert_ne!(&temp_dir1.path(), &temp_dir2.path());
    }

    #[test]
    fn list_for_origin_skips_non_origin_packages() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let test_origin = vec![testing_package_install("test/foobar", fs_root.path())];
        let core_origin = vec![testing_package_install("core/redis/1.0.0", fs_root.path()),
                               testing_package_install("core/redis/1.1.0", fs_root.path()),];

        let packages = package_list_for_origin(&package_root, &String::from("core")).unwrap();

        assert_eq!(2, packages.len());
        for p in &core_origin {
            assert!(packages.contains(&p.ident));
        }

        for p in &test_origin {
            assert!(!packages.contains(&p.ident));
        }
    }

    #[test]
    fn list_for_origin_no_packages() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));

        let packages = package_list_for_origin(&package_root, &String::from("core")).unwrap();

        assert_eq!(0, packages.len());
    }
}
<commit_msg>add comment about path canonicalization<commit_after>use super::{metadata::{read_metafile,
                       MetaFile},
            PackageIdent,
            PackageTarget};
use crate::error::{Error,
                   Result};
use std::{ffi::OsStr,
          fs,
          io,
          path::{Path,
                 PathBuf},
          str::FromStr};
use tempfile::{Builder,
               TempDir};

pub const INSTALL_TMP_PREFIX: &str = ".hab-pkg-install";

/// Return a directory which can be used as a temp dir during package install/
/// uninstall
///
/// It returns a path which is in the same parent directory as `path`
/// but with TempDir style randomization
pub fn temp_package_directory(path: &Path) -> Result<TempDir> {
    let base = path.parent().ok_or_else(|| {
                                 Error::PackageUnpackFailed(
            "Could not determine parent directory for temporary package directory".to_owned(),
        )
                             })?;
    fs::create_dir_all(base)?;

    // If this temp directory is being used for installs, we will be untarring archives
    // into the directory. Depending on the length of the paths included in the archives
    // and the length of this temp directory, the final paths may be over 260 characters.
    // In most cases on Windows, this is the maximum allowable length of a path. We can
    // canonicalize the base path which will prepend a `\\?\` to the path on windows
    // causing windows APIs to treat the path as an extended length path permitting 32,767
    // characters in the path. Now thats a whole lot of path! Note that this is a new
    // concern since migrating from libarchive to tar-rs where libarchive performed this
    // path prepending operation and tar-rs does not.
    let cannon_base = base.canonicalize()?;

    let temp_install_prefix =
        path.file_name()
            .and_then(OsStr::to_str)
            .map(|dirname| format!("{}-{}", INSTALL_TMP_PREFIX, dirname))
            .ok_or_else(|| {
                Error::PackageUnpackFailed("Could not generate prefix for temporary package \
                                            directory"
                                                      .to_owned())
            })?;
    Ok(Builder::new().prefix(&temp_install_prefix)
                     .tempdir_in(cannon_base)?)
}

/// Returns a list of package structs built from the contents of the given directory.
pub fn all_packages(path: &Path) -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    if fs::metadata(path)?.is_dir() {
        walk_origins(&path, &mut package_list)?;
    }
    Ok(package_list)
}

/// Returns a vector of package idents built from the contents of
/// the given directory, using the given origin to restrict the
/// search.
///
/// The search is restricted by assuming the package directory
/// structure is:
///
///    /base/ORIGIN/NAME/VERSION/RELEASE/
pub fn package_list_for_origin(base_pkg_path: &Path, origin: &str) -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    let mut package_path = PathBuf::from(base_pkg_path);
    package_path.push(&origin);

    if !is_existing_dir(&package_path)? {
        return Ok(package_list);
    };

    walk_names(&origin, &package_path, &mut package_list)?;
    Ok(package_list)
}

/// Returns a vector of package structs built from the contents of
/// the given directory, using the given ident to restrict the
/// search.
///
/// The search is restricted by assuming the package directory
/// structure is:
///
///    /base/ORIGIN/NAME/VERSION/RELEASE/
pub fn package_list_for_ident(base_pkg_path: &Path,
                              ident: &PackageIdent)
                              -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    let mut package_path = PathBuf::from(base_pkg_path);
    package_path.push(&ident.origin);
    package_path.push(&ident.name);

    if !is_existing_dir(&package_path)? {
        return Ok(package_list);
    }

    match (&ident.version, &ident.release) {
        // origin/name
        (None, _) => walk_versions(&ident.origin, &ident.name, &package_path, &mut package_list)?,
        // origin/name/version
        (Some(version), None) => {
            package_path.push(version);
            if !is_existing_dir(&package_path)? {
                return Ok(package_list);
            }
            walk_releases(&ident.origin,
                          &ident.name,
                          &version,
                          &package_path,
                          &mut package_list)?
        }
        // origin/name/version/release
        (Some(version), Some(release)) => {
            package_path.push(version);
            package_path.push(release);
            if !is_existing_dir(&package_path)? {
                return Ok(package_list);
            }

            let active_target = PackageTarget::active_target();
            if let Some(new_ident) = package_ident_from_dir(&ident.origin,
                                                            &ident.name,
                                                            &version,
                                                            active_target,
                                                            &package_path)
            {
                package_list.push(new_ident)
            }
        }
    }
    Ok(package_list)
}

/// Helper function for all_packages. Walks the directory at the given
/// Path for origin directories and builds on the given package list
/// by recursing into name, version, and release directories.
fn walk_origins(path: &Path, packages: &mut Vec<PackageIdent>) -> Result<()> {
    for entry in fs::read_dir(path)? {
        let origin_dir = entry?;
        let origin_path = origin_dir.path();
        if fs::metadata(&origin_path)?.is_dir() {
            let origin = filename_from_entry(&origin_dir);
            walk_names(&origin, &origin_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_origins. Walks the direcotry at the given
/// Path for name directories and recurses into them to find version
/// and release directories.
fn walk_names(origin: &str, dir: &Path, packages: &mut Vec<PackageIdent>) -> Result<()> {
    for entry in fs::read_dir(dir)? {
        let name_dir = entry?;
        let name_path = name_dir.path();
        if fs::metadata(&name_path)?.is_dir() {
            let name = filename_from_entry(&name_dir);
            walk_versions(&origin, &name, &name_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_names. Walks the directory at the given
/// Path and recurses into them to find release directories.
fn walk_versions(origin: &str,
                 name: &str,
                 dir: &Path,
                 packages: &mut Vec<PackageIdent>)
                 -> Result<()> {
    for entry in fs::read_dir(dir)? {
        let version_dir = entry?;
        let version_path = version_dir.path();
        if fs::metadata(&version_path)?.is_dir() {
            let version = filename_from_entry(&version_dir);
            walk_releases(origin, name, &version, &version_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_versions. Walks the directory at the
/// given Path and constructs a Package struct if the directory is a
/// valid package directory. Any resulting packages are pushed onto
/// the given packages vector, assuming the given origin, name, and
/// version.
fn walk_releases(origin: &str,
                 name: &str,
                 version: &str,
                 dir: &Path,
                 packages: &mut Vec<PackageIdent>)
                 -> Result<()> {
    let active_target = PackageTarget::active_target();
    for entry in fs::read_dir(dir)? {
        let release_dir = entry?;
        let release_path = release_dir.path();
        if fs::metadata(&release_path)?.is_dir() {
            if let Some(ident) =
                package_ident_from_dir(origin, name, version, active_target, &release_path)
            {
                packages.push(ident)
            }
        }
    }
    Ok(())
}

/// package_ident_from_dir returns a PackageIdent if the given
/// path contains a valid package for the given active_target.
///
/// Returns None when
///    - The directory is a temporary install directroy
///    - An error occurs reading the package metadata
///    - An error occurs reading the package target
///    - The package target doesn't match the given active target
fn package_ident_from_dir(origin: &str,
                          name: &str,
                          version: &str,
                          active_target: PackageTarget,
                          dir: &Path)
                          -> Option<PackageIdent> {
    let release = dir.file_name().and_then(OsStr::to_str)?;

    if release.starts_with(INSTALL_TMP_PREFIX) {
        debug!("PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate \
                because it matches installation temporary directory prefix: {}",
               dir.display());
        return None;
    }

    let metafile_content = read_metafile(dir, MetaFile::Target);
    // If there is an error reading the target metafile, then skip the candidate
    if let Err(e) = metafile_content {
        debug!("PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate due \
                to error reading TARGET metafile, found={}, reason={:?}",
               dir.display(),
               e,);
        return None;
    }

    // Any errors have been cleared, so unwrap is safe
    let metafile_content = metafile_content.unwrap();
    let install_target = PackageTarget::from_str(&metafile_content);
    // If there is an error parsing the target as a valid PackageTarget, then skip the
    // candidate
    if let Err(e) = install_target {
        debug!("PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate due \
                to error parsing TARGET metafile as a valid PackageTarget, found={}, reason={:?}",
               dir.display(),
               e,);
        return None;
    }
    // Any errors have been cleared, so unwrap is safe
    let install_target = install_target.unwrap();

    // Ensure that the installed package's target matches the active `PackageTarget`,
    // otherwise skip the candidate
    if active_target == install_target {
        Some(PackageIdent::new(origin.to_string(),
                               name.to_string(),
                               Some(version.to_string()),
                               Some(release.to_owned())))
    } else {
        debug!("PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate, \
                found={}, installed_target={}, active_target={}",
               dir.display(),
               install_target,
               active_target,);
        None
    }
}

fn filename_from_entry(entry: &fs::DirEntry) -> String {
    entry.file_name().to_string_lossy().into_owned()
}

fn is_existing_dir(path: &Path) -> Result<bool> {
    match fs::metadata(&path) {
        Err(err) => {
            if err.kind() == io::ErrorKind::NotFound {
                return Ok(false);
            }
            Err(Error::from(err))
        }
        Ok(metadata) => Ok(metadata.is_dir()),
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::package::test_support::testing_package_install;

    use crate::fs;
    use std::fs::File;
    use tempfile::Builder;

    #[test]
    fn empty_dir_gives_empty_list() {
        let package_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let packages = all_packages(&package_root.path()).unwrap();

        assert_eq!(0, packages.len());
    }

    #[test]
    fn not_a_dir_gives_empty_list() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let file_path = fs_root.path().join("not-a-dir");
        let _tmp_file = File::create(&file_path).unwrap();

        let packages = all_packages(&file_path).unwrap();

        assert_eq!(0, packages.len());
    }
    #[test]
    fn can_read_single_package() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let package_install = testing_package_install("core/redis", fs_root.path());

        let packages = all_packages(&package_root).unwrap();

        assert_eq!(1, packages.len());
        assert_eq!(package_install.ident, packages[0]);
    }

    #[test]
    fn can_read_multiple_packages() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let expected = vec![testing_package_install("core/redis/1.0.0", fs_root.path()),
                            testing_package_install("test/foobar", fs_root.path()),
                            testing_package_install("core/redis/1.1.0", fs_root.path()),];

        let packages = all_packages(&package_root).unwrap();

        assert_eq!(3, packages.len());
        for p in &expected {
            assert!(packages.contains(&p.ident));
        }
    }

    #[test]
    fn create_temp_package_directory_in_same_parentdir() {
        let p = Builder::new().tempdir().unwrap();
        let temp_dir = temp_package_directory(p.path()).unwrap();
        let temp_path = temp_dir.path();

        assert_eq!(&p.path().parent().unwrap().canonicalize().unwrap(),
                   &temp_path.parent().unwrap());
    }

    #[test]
    fn temp_package_directory_starts_with_prefix() {
        let p = Builder::new().tempdir().unwrap();
        let temp_dir = temp_package_directory(p.path()).unwrap();
        let temp_filename = temp_dir.path().file_name().unwrap().to_str().unwrap();

        assert!(&temp_filename.starts_with(INSTALL_TMP_PREFIX));
    }

    #[test]
    fn temp_package_directory_changes() {
        let p = Builder::new().tempdir().unwrap();
        let temp_dir1 = temp_package_directory(p.path()).unwrap();
        let temp_dir2 = temp_package_directory(p.path()).unwrap();

        assert_ne!(&temp_dir1.path(), &temp_dir2.path());
    }

    #[test]
    fn list_for_origin_skips_non_origin_packages() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let test_origin = vec![testing_package_install("test/foobar", fs_root.path())];
        let core_origin = vec![testing_package_install("core/redis/1.0.0", fs_root.path()),
                               testing_package_install("core/redis/1.1.0", fs_root.path()),];

        let packages = package_list_for_origin(&package_root, &String::from("core")).unwrap();

        assert_eq!(2, packages.len());
        for p in &core_origin {
            assert!(packages.contains(&p.ident));
        }

        for p in &test_origin {
            assert!(!packages.contains(&p.ident));
        }
    }

    #[test]
    fn list_for_origin_no_packages() {
        let fs_root = Builder::new().prefix("fs-root").tempdir().unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));

        let packages = package_list_for_origin(&package_root, &String::from("core")).unwrap();

        assert_eq!(0, packages.len());
    }
}
<|endoftext|>


<commit_before>extern crate rustc_hash;
extern crate salsa;
extern crate secsp_syntax;

use std::fs;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use salsa::ParallelDatabase;

use secsp_syntax::{ast, Parse};

use crate::cancellation::{Canceled, CheckCanceled};
use crate::input::{FileId, FilesDatabase, SourceRoot};
use crate::syntax::SyntaxDatabase;

pub mod cancellation;
pub mod db;
pub mod input;
pub mod syntax;

pub use db::AnalysisDatabase;

#[derive(Debug)]
pub struct AnalysisHost {
    db: AnalysisDatabase,
}

#[derive(Debug)]
pub struct Analysis {
    db: salsa::Snapshot<AnalysisDatabase>,
}

pub type Cancelable<T> = Result<T, Canceled>;

impl Analysis {
    pub fn file_id(&self, path: PathBuf) -> Cancelable<FileId> {
        self.with_db(|db| db.file_path(path))
    }

    pub fn source_file(&self, file_id: FileId) -> Cancelable<Parse<ast::SourceFile>> {
        self.with_db(|db| db.source_file(file_id))
    }

    pub fn source_root(&self) -> Cancelable<SourceRoot> {
        self.with_db(|db| (*db.source_root()).clone())
    }

    fn with_db<F: FnOnce(&AnalysisDatabase) -> T + std::panic::UnwindSafe, T>(
        &self,
        f: F,
    ) -> Cancelable<T> {
        self.db.catch_canceled(f)
    }
}

impl AnalysisHost {
    pub fn new(db: AnalysisDatabase) -> AnalysisHost {
        AnalysisHost { db }
    }

    pub fn from_workspace<P: AsRef<Path>>(ws: P) -> AnalysisHost {
        let db = AnalysisDatabase::from_workspace_root(ws).expect("unable to initialize database");

        AnalysisHost { db }
    }

    pub fn add_file(&mut self, path: PathBuf, contents: String) {
        let id = self.db.file_path(path);
        let mut source_root = (*self.db.source_root()).clone();

        source_root.0.insert(id);

        self.db.set_source_root(Arc::new(source_root));
        self.db.set_file_text(id, Arc::new(contents));
    }

    pub fn analysis(&self) -> Analysis {
        Analysis {
            db: self.db.snapshot(),
        }
    }
}

impl Default for AnalysisHost {
    fn default() -> Self {
        AnalysisHost::new(AnalysisDatabase::default())
    }
}
<commit_msg>Add missing analysis change for csp-editor<commit_after>extern crate rustc_hash;
extern crate salsa;
extern crate secsp_syntax;

use std::fs;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use salsa::ParallelDatabase;

use secsp_syntax::{ast, Parse};

use crate::cancellation::{Canceled, CheckCanceled};
use crate::input::{FileId, FilesDatabase, SourceRoot};
use crate::syntax::SyntaxDatabase;

pub mod cancellation;
pub mod db;
pub mod input;
pub mod syntax;

pub use db::AnalysisDatabase;
use rustc_hash::FxHashSet;

#[derive(Debug)]
pub struct AnalysisHost {
    db: AnalysisDatabase,
}

#[derive(Debug)]
pub struct Analysis {
    db: salsa::Snapshot<AnalysisDatabase>,
}

pub type Cancelable<T> = Result<T, Canceled>;

impl Analysis {
    pub fn file_id(&self, path: PathBuf) -> Cancelable<FileId> {
        self.with_db(|db| db.file_path(path))
    }

    pub fn source_file(&self, file_id: FileId) -> Cancelable<Parse<ast::SourceFile>> {
        self.with_db(|db| db.source_file(file_id))
    }

    pub fn source_root(&self) -> Cancelable<SourceRoot> {
        self.with_db(|db| (*db.source_root()).clone())
    }

    fn with_db<F: FnOnce(&AnalysisDatabase) -> T + std::panic::UnwindSafe, T>(
        &self,
        f: F,
    ) -> Cancelable<T> {
        self.db.catch_canceled(f)
    }
}

impl AnalysisHost {
    pub fn new(db: AnalysisDatabase) -> AnalysisHost {
        AnalysisHost { db }
    }

    pub fn from_workspace<P: AsRef<Path>>(ws: P) -> AnalysisHost {
        let db = AnalysisDatabase::from_workspace_root(ws).expect("unable to initialize database");

        AnalysisHost { db }
    }

    pub fn add_file(&mut self, path: PathBuf, contents: String) -> FileId {
        let id = self.db.file_path(path);
        let mut source_root = (*self.db.source_root()).clone();

        source_root.0.insert(id);

        self.db.set_source_root(Arc::new(source_root));
        self.db.set_file_text(id, Arc::new(contents));

        id
    }

    pub fn analysis(&self) -> Analysis {
        Analysis {
            db: self.db.snapshot(),
        }
    }
}

impl Default for AnalysisHost {
    fn default() -> Self {
        let mut database = AnalysisDatabase::default();
        database.set_source_root(Arc::new(SourceRoot(FxHashSet::default())));

        AnalysisHost::new(database)
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate timely;
extern crate differential_dataflow;
extern crate core_affinity;

use rand::{Rng, SeedableRng, StdRng};

use timely::dataflow::*;
use timely::dataflow::operators::probe::Handle;

use differential_dataflow::input::Input;
use differential_dataflow::Collection;
use differential_dataflow::operators::*;
use differential_dataflow::lattice::Lattice;
use differential_dataflow::operators::iterate::Variable;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::operators::arrange::ArrangeBySelf;

type Node = usize;

fn main() {

    let nodes: usize = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: usize = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: usize = std::env::args().nth(3).unwrap().parse().unwrap();
    let inspect: bool = std::env::args().any(|x| x == "inspect");

    // Our setting involves four read query types, and two updatable base relations.
    //
    //  Q1: Point lookup: reads "state" associated with a node.
    //  Q2: One-hop lookup: reads "state" associated with neighbors of a node.
    //  Q3: Two-hop lookup: reads "state" associated with n-of-n's of a node.
    //  Q4: Shortest path: reports hop count between two query nodes.
    //
    //  R1: "State": a pair of (node, T) for some type T that I don't currently know.
    //  R2: "Graph": pairs (node, node) indicating linkage between the two nodes.

    timely::execute_from_args(std::env::args().skip(3), move |worker| {

        let index = worker.index();
        let peers = worker.peers();
        let timer = ::std::time::Instant::now();

        let core_ids = core_affinity::get_core_ids().unwrap();
        core_affinity::set_for_current(core_ids[index]);

        // define BFS dataflow; return handles to roots and edges inputs
        let mut probe = Handle::new();

        let (mut q1, mut q2, mut q3, mut q4, mut state, mut graph) = worker.dataflow(|scope| {

            let (q1_input, q1) = scope.new_collection();
            let (q2_input, q2) = scope.new_collection();
            let (q3_input, q3) = scope.new_collection();
            let (q4_input, q4) = scope.new_collection();

            let (state_input, state) = scope.new_collection();
            let (graph_input, graph) = scope.new_collection();

            let state_indexed = state.arrange_by_key();
            let graph_indexed = graph.map(|(src, dst)| (dst, src))
                                     .concat(&graph)
                                     .arrange_by_key();

            // Q1: Point lookups on `state`:
            q1  .arrange_by_self()
                .join_core(&state_indexed, |&query, &(), &state| Some((query, state)))
                .filter(move |_| inspect)
                .inspect(|x| println!("Q1: {:?}", x))
                .probe_with(&mut probe);

            // Q2: One-hop lookups on `state`:
            q2  .arrange_by_self()
                .join_core(&graph_indexed, |&query, &(), &friend| Some((friend, query)))
                .join_core(&state_indexed, |_friend, &query, &state| Some((query, state)))
                .filter(move |_| inspect)
                .inspect(|x| println!("Q2: {:?}", x))
                .probe_with(&mut probe);

            // Q3: Two-hop lookups on `state`:
            q3  .arrange_by_self()
                .join_core(&graph_indexed, |&query, &(), &friend| Some((friend, query)))
                .join_core(&graph_indexed, |_friend, &query, &friend2| Some((friend2, query)))
                .join_core(&state_indexed, |_friend2, &query, &state| Some((query, state)))
                .filter(move |_| inspect)
                .consolidate()
                .inspect(|x| println!("Q3: {:?}", x))
                .probe_with(&mut probe);

            // Q4: Shortest path queries:
            bidijkstra(&graph_indexed, &graph_indexed, &q4)
                .filter(move |_| inspect)
                .inspect(|x| println!("Q4: {:?}", x))
                .probe_with(&mut probe);

            (q1_input, q2_input, q3_input, q4_input, state_input, graph_input)
        });

        let seed: &[_] = &[1, 2, 3, index];
        let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
        // let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge deletions
        let mut rng3: StdRng = SeedableRng::from_seed(seed);    // rng for queries

        if index == 0 { println!("performing workload on random graph with {} nodes, {} edges:", nodes, edges); }

        let worker_edges = edges/peers + if index < (edges % peers) { 1 } else { 0 };
        for _ in 0 .. worker_edges {
            graph.insert((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)));
        }
        for node in 0 .. nodes {
            if node % peers == index {
                state.insert((node, node));
            }
        }

        q1.advance_to(1);       q1.flush();     // q1 queries start now.
        q2.advance_to(1001);    q2.flush();     // q2 queries start here.
        q3.advance_to(2001);    q3.flush();     // q3 queries start here.
        q4.advance_to(3001);    q4.flush();     // q4 queries start here.
        state.advance_to(4001); state.flush();
        graph.advance_to(4001); graph.flush();
        // state.close();                          // no changes to state.
        // graph.close();                          // no changes to graph.

        // finish graph loading work.
        while probe.less_than(q1.time()) { worker.step(); }

        if index == 0 { println!("{:?}\tgraph loaded", timer.elapsed()); }

        let worker_batch = batch/peers + if index < batch % peers { 1 } else { 0 };

        // Q1 testing:
        let timer_q1 = ::std::time::Instant::now();
        for round in 1 .. 1001 {
            for _ in 0 .. worker_batch {
                q1.insert(rng3.gen_range(0, nodes));
            }
            q1.advance_to(round);
            q1.flush();
            while probe.less_than(q1.time()) { worker.step(); }
        }
        if index == 0 { println!("{:?}\tq1 eval complete; avg: {:?}", timer.elapsed(), timer_q1.elapsed()/1000); }
        q1.close();

        // Q2 testing:
        let timer_q2 = ::std::time::Instant::now();
        for round in 1001 .. 2001 {
            for _ in 0 .. worker_batch {
                q2.insert(rng3.gen_range(0, nodes));
            }
            q2.advance_to(round);
            q2.flush();
            while probe.less_than(q2.time()) { worker.step(); }
        }
        if index == 0 { println!("{:?}\tq2 eval complete; avg: {:?}", timer.elapsed(), timer_q2.elapsed()/1000); }
        q2.close();

        // Q3 testing:
        let timer_q3 = ::std::time::Instant::now();
        for round in 2001 .. 3001 {
            for _ in 0 .. worker_batch {
                q3.insert(rng3.gen_range(0, nodes));
            }
            q3.advance_to(round);
            q3.flush();
            while probe.less_than(q3.time()) { worker.step(); }
        }
        if index == 0 { println!("{:?}\tq3 eval complete; avg: {:?}", timer.elapsed(), timer_q3.elapsed()/1000); }
        q3.close();

        // Q4 testing:
        let timer_q4 = ::std::time::Instant::now();
        for round in 3001 .. 4001 {
            for _ in 0 .. worker_batch {
               q4.insert((rng3.gen_range(0, nodes), rng3.gen_range(0, nodes)));
            }
            q4.advance_to(round);
            q4.flush();
            while probe.less_than(q4.time()) { worker.step(); }
        }
        if index == 0 { println!("{:?}\tq4 eval complete; avg: {:?}", timer.elapsed(), timer_q4.elapsed()/1000); }
        q4.close();

    }).unwrap();
}

use differential_dataflow::trace::implementations::ord::OrdValSpine as DefaultValTrace;
use differential_dataflow::operators::arrange::TraceAgent;
use differential_dataflow::operators::arrange::Arranged;

type Arrange<G: Scope, K, V, R> = Arranged<G, K, V, R, TraceAgent<K, V, G::Timestamp, R, DefaultValTrace<K, V, G::Timestamp, R>>>;

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn bidijkstra<G: Scope>(
    forward_graph: &Arrange<G, Node, Node, isize>,
    reverse_graph: &Arrange<G, Node, Node, isize>,
    goals: &Collection<G, (Node, Node)>) -> Collection<G, ((Node, Node), u32)>
where G::Timestamp: Lattice+Ord {

    goals.scope().scoped(|inner| {

        // Our plan is to start evolving distances from both sources and destinations.
        // The evolution from a source or destination should continue as long as there
        // is a corresponding destination or source that has not yet been reached.

        // forward and reverse (node, (root, dist))
        let forward = Variable::from(goals.map(|(x,_)| (x,(x,0))).enter(inner));
        let reverse = Variable::from(goals.map(|(_,y)| (y,(y,0))).enter(inner));

        let goals = goals.enter(inner);
        let forward_graph = forward_graph.enter(inner);
        let reverse_graph = reverse_graph.enter(inner);

        // Let's determine which (src, dst) pairs are ready to return.
        //
        //   done(src, dst) := forward(src, med), reverse(dst, med), goal(src, dst).
        //
        // This is a cyclic join, which should scare us a bunch.
        let reached =
        forward
            .join_map(&reverse, |_, &(src,d1), &(dst,d2)| ((src, dst), d1 + d2))
            .group(|_key, s, t| t.push((*s[0].0, 1)))
            .semijoin(&goals);

        let active =
        reached
            .negate()
            .map(|(srcdst,_)| srcdst)
            .concat(&goals)
            .consolidate();

        // Let's expand out forward queries that are active.
        let forward_active = active.map(|(x,_y)| x).distinct();
        let forward_next =
        forward
            .map(|(med, (src, dist))| (src, (med, dist)))
            .semijoin(&forward_active)
            .map(|(src, (med, dist))| (med, (src, dist)))
            .join_core(&forward_graph, |_med, &(src, dist), &next| Some((next, (src, dist+1))))
            .concat(&forward)
            .map(|(next, (src, dist))| ((next, src), dist))
            .group(|_key, s, t| t.push((*s[0].0, 1)))
            .map(|((next, src), dist)| (next, (src, dist)));

        forward.set(&forward_next);

        // Let's expand out reverse queries that are active.
        let reverse_active = active.map(|(_x,y)| y).distinct();
        let reverse_next =
        reverse
            .map(|(med, (rev, dist))| (rev, (med, dist)))
            .semijoin(&reverse_active)
            .map(|(rev, (med, dist))| (med, (rev, dist)))
            .join_core(&reverse_graph, |_med, &(rev, dist), &next| Some((next, (rev, dist+1))))
            .concat(&reverse)
            .map(|(next, (rev, dist))| ((next, rev), dist))
            .group(|_key, s, t| t.push((*s[0].0, 1)))
            .map(|((next,rev), dist)| (next, (rev, dist)));

        reverse.set(&reverse_next);

        reached.leave()
    })
}<commit_msg>randomized queries<commit_after>extern crate rand;
extern crate timely;
extern crate differential_dataflow;
extern crate core_affinity;

use rand::{Rng, SeedableRng, StdRng};

use timely::dataflow::*;
use timely::dataflow::operators::probe::Handle;

use differential_dataflow::input::Input;
use differential_dataflow::Collection;
use differential_dataflow::operators::*;
use differential_dataflow::lattice::Lattice;
use differential_dataflow::operators::iterate::Variable;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::operators::arrange::ArrangeBySelf;

type Node = usize;

fn main() {

    let nodes: usize = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: usize = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: usize = std::env::args().nth(3).unwrap().parse().unwrap();
    let inspect: bool = std::env::args().any(|x| x == "inspect");

    // Our setting involves four read query types, and two updatable base relations.
    //
    //  Q1: Point lookup: reads "state" associated with a node.
    //  Q2: One-hop lookup: reads "state" associated with neighbors of a node.
    //  Q3: Two-hop lookup: reads "state" associated with n-of-n's of a node.
    //  Q4: Shortest path: reports hop count between two query nodes.
    //
    //  R1: "State": a pair of (node, T) for some type T that I don't currently know.
    //  R2: "Graph": pairs (node, node) indicating linkage between the two nodes.

    timely::execute_from_args(std::env::args().skip(3), move |worker| {

        let index = worker.index();
        let peers = worker.peers();
        let timer = ::std::time::Instant::now();

        let core_ids = core_affinity::get_core_ids().unwrap();
        core_affinity::set_for_current(core_ids[index]);

        // define BFS dataflow; return handles to roots and edges inputs
        let mut probe = Handle::new();

        let (mut q1, mut q2, mut q3, mut q4, mut state, mut graph) = worker.dataflow(|scope| {

            let (q1_input, q1) = scope.new_collection();
            let (q2_input, q2) = scope.new_collection();
            let (q3_input, q3) = scope.new_collection();
            let (q4_input, q4) = scope.new_collection();

            let (state_input, state) = scope.new_collection();
            let (graph_input, graph) = scope.new_collection();

            let state_indexed = state.arrange_by_key();
            let graph_indexed = graph.map(|(src, dst)| (dst, src))
                                     .concat(&graph)
                                     .arrange_by_key();

            // Q1: Point lookups on `state`:
            q1  .arrange_by_self()
                .join_core(&state_indexed, |&query, &(), &state| Some((query, state)))
                .filter(move |_| inspect)
                .inspect(|x| println!("Q1: {:?}", x))
                .probe_with(&mut probe);

            // Q2: One-hop lookups on `state`:
            q2  .arrange_by_self()
                .join_core(&graph_indexed, |&query, &(), &friend| Some((friend, query)))
                .join_core(&state_indexed, |_friend, &query, &state| Some((query, state)))
                .filter(move |_| inspect)
                .inspect(|x| println!("Q2: {:?}", x))
                .probe_with(&mut probe);

            // Q3: Two-hop lookups on `state`:
            q3  .arrange_by_self()
                .join_core(&graph_indexed, |&query, &(), &friend| Some((friend, query)))
                .join_core(&graph_indexed, |_friend, &query, &friend2| Some((friend2, query)))
                .join_core(&state_indexed, |_friend2, &query, &state| Some((query, state)))
                .filter(move |_| inspect)
                .consolidate()
                .inspect(|x| println!("Q3: {:?}", x))
                .probe_with(&mut probe);

            // Q4: Shortest path queries:
            bidijkstra(&graph_indexed, &graph_indexed, &q4)
                .filter(move |_| inspect)
                .inspect(|x| println!("Q4: {:?}", x))
                .probe_with(&mut probe);

            (q1_input, q2_input, q3_input, q4_input, state_input, graph_input)
        });

        let seed1: &[_] = &[1, 2, 3, index];
        let mut rng1: StdRng = SeedableRng::from_seed(seed1);    // rng for edge additions
        // let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge deletions
        let seed2: &[_] = &[1, 2, 4, index];
        let mut rng3: StdRng = SeedableRng::from_seed(seed2);    // rng for queries

        if index == 0 { println!("performing workload on random graph with {} nodes, {} edges:", nodes, edges); }

        let worker_edges = edges/peers + if index < (edges % peers) { 1 } else { 0 };
        for _ in 0 .. worker_edges {
            graph.insert((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)));
        }
        for node in 0 .. nodes {
            if node % peers == index {
                state.insert((node, node));
            }
        }

        q1.advance_to(1);       q1.flush();     // q1 queries start now.
        q2.advance_to(1001);    q2.flush();     // q2 queries start here.
        q3.advance_to(2001);    q3.flush();     // q3 queries start here.
        q4.advance_to(3001);    q4.flush();     // q4 queries start here.
        state.advance_to(4001); state.flush();
        graph.advance_to(4001); graph.flush();
        // state.close();                          // no changes to state.
        // graph.close();                          // no changes to graph.

        // finish graph loading work.
        while probe.less_than(q1.time()) { worker.step(); }

        if index == 0 { println!("{:?}\tgraph loaded", timer.elapsed()); }

        let worker_batch = batch/peers + if index < batch % peers { 1 } else { 0 };

        // Q1 testing:
        let timer_q1 = ::std::time::Instant::now();
        for round in 1 .. 1001 {
            for _ in 0 .. worker_batch {
                q1.insert(rng3.gen_range(0, nodes));
            }
            q1.advance_to(round);
            q1.flush();
            while probe.less_than(q1.time()) { worker.step(); }
        }
        if index == 0 { println!("{:?}\tq1 eval complete; avg: {:?}", timer.elapsed(), timer_q1.elapsed()/1000); }
        q1.close();

        // Q2 testing:
        let timer_q2 = ::std::time::Instant::now();
        for round in 1001 .. 2001 {
            for _ in 0 .. worker_batch {
                q2.insert(rng3.gen_range(0, nodes));
            }
            q2.advance_to(round);
            q2.flush();
            while probe.less_than(q2.time()) { worker.step(); }
        }
        if index == 0 { println!("{:?}\tq2 eval complete; avg: {:?}", timer.elapsed(), timer_q2.elapsed()/1000); }
        q2.close();

        // Q3 testing:
        let timer_q3 = ::std::time::Instant::now();
        for round in 2001 .. 3001 {
            for _ in 0 .. worker_batch {
                q3.insert(rng3.gen_range(0, nodes));
            }
            q3.advance_to(round);
            q3.flush();
            while probe.less_than(q3.time()) { worker.step(); }
        }
        if index == 0 { println!("{:?}\tq3 eval complete; avg: {:?}", timer.elapsed(), timer_q3.elapsed()/1000); }
        q3.close();

        // Q4 testing:
        let timer_q4 = ::std::time::Instant::now();
        for round in 3001 .. 4001 {
            for _ in 0 .. worker_batch {
               q4.insert((rng3.gen_range(0, nodes), rng3.gen_range(0, nodes)));
            }
            q4.advance_to(round);
            q4.flush();
            while probe.less_than(q4.time()) { worker.step(); }
        }
        if index == 0 { println!("{:?}\tq4 eval complete; avg: {:?}", timer.elapsed(), timer_q4.elapsed()/1000); }
        q4.close();

    }).unwrap();
}

use differential_dataflow::trace::implementations::ord::OrdValSpine as DefaultValTrace;
use differential_dataflow::operators::arrange::TraceAgent;
use differential_dataflow::operators::arrange::Arranged;

type Arrange<G: Scope, K, V, R> = Arranged<G, K, V, R, TraceAgent<K, V, G::Timestamp, R, DefaultValTrace<K, V, G::Timestamp, R>>>;

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn bidijkstra<G: Scope>(
    forward_graph: &Arrange<G, Node, Node, isize>,
    reverse_graph: &Arrange<G, Node, Node, isize>,
    goals: &Collection<G, (Node, Node)>) -> Collection<G, ((Node, Node), u32)>
where G::Timestamp: Lattice+Ord {

    goals.scope().scoped(|inner| {

        // Our plan is to start evolving distances from both sources and destinations.
        // The evolution from a source or destination should continue as long as there
        // is a corresponding destination or source that has not yet been reached.

        // forward and reverse (node, (root, dist))
        let forward = Variable::from(goals.map(|(x,_)| (x,(x,0))).enter(inner));
        let reverse = Variable::from(goals.map(|(_,y)| (y,(y,0))).enter(inner));

        let goals = goals.enter(inner);
        let forward_graph = forward_graph.enter(inner);
        let reverse_graph = reverse_graph.enter(inner);

        // Let's determine which (src, dst) pairs are ready to return.
        //
        //   done(src, dst) := forward(src, med), reverse(dst, med), goal(src, dst).
        //
        // This is a cyclic join, which should scare us a bunch.
        let reached =
        forward
            .join_map(&reverse, |_, &(src,d1), &(dst,d2)| ((src, dst), d1 + d2))
            .group(|_key, s, t| t.push((*s[0].0, 1)))
            .semijoin(&goals);

        let active =
        reached
            .negate()
            .map(|(srcdst,_)| srcdst)
            .concat(&goals)
            .consolidate();

        // Let's expand out forward queries that are active.
        let forward_active = active.map(|(x,_y)| x).distinct();
        let forward_next =
        forward
            .map(|(med, (src, dist))| (src, (med, dist)))
            .semijoin(&forward_active)
            .map(|(src, (med, dist))| (med, (src, dist)))
            .join_core(&forward_graph, |_med, &(src, dist), &next| Some((next, (src, dist+1))))
            .concat(&forward)
            .map(|(next, (src, dist))| ((next, src), dist))
            .group(|_key, s, t| t.push((*s[0].0, 1)))
            .map(|((next, src), dist)| (next, (src, dist)));

        forward.set(&forward_next);

        // Let's expand out reverse queries that are active.
        let reverse_active = active.map(|(_x,y)| y).distinct();
        let reverse_next =
        reverse
            .map(|(med, (rev, dist))| (rev, (med, dist)))
            .semijoin(&reverse_active)
            .map(|(rev, (med, dist))| (med, (rev, dist)))
            .join_core(&reverse_graph, |_med, &(rev, dist), &next| Some((next, (rev, dist+1))))
            .concat(&reverse)
            .map(|(next, (rev, dist))| ((next, rev), dist))
            .group(|_key, s, t| t.push((*s[0].0, 1)))
            .map(|((next,rev), dist)| (next, (rev, dist)));

        reverse.set(&reverse_next);

        reached.leave()
    })
}<|endoftext|>


<commit_before>import std::cmp::FuzzyEq;
import num::Num;

pure fn Matrix4<T:copy FuzzyEq Num>(m11: T, m12: T, m13: T, m14: T,
                                    m21: T, m22: T, m23: T, m24: T,
                                    m31: T, m32: T, m33: T, m34: T,
                                    m41: T, m42: T, m43: T, m44: T)
                                 -> Matrix4<T> {

    Matrix4 {
        m11: m11, m12: m12, m13: m13, m14: m14,
        m21: m21, m22: m22, m23: m23, m24: m24,
        m31: m31, m32: m32, m33: m33, m34: m34,
        m41: m41, m42: m42, m43: m43, m44: m44
    }
}

struct Matrix4<T:copy FuzzyEq Num> {
    let m11: T; let m12: T; let m13: T; let m14: T;
    let m21: T; let m22: T; let m23: T; let m24: T;
    let m31: T; let m32: T; let m33: T; let m34: T;
    let m41: T; let m42: T; let m43: T; let m44: T;

    pure fn fuzzy_eq(&&other: Matrix4<T>) -> bool {
        self.m11.fuzzy_eq(other.m11) && self.m12.fuzzy_eq(other.m12) &&
        self.m13.fuzzy_eq(other.m13) && self.m14.fuzzy_eq(other.m14) &&
        self.m21.fuzzy_eq(other.m21) && self.m22.fuzzy_eq(other.m22) &&
        self.m23.fuzzy_eq(other.m23) && self.m24.fuzzy_eq(other.m24) &&
        self.m31.fuzzy_eq(other.m31) && self.m32.fuzzy_eq(other.m32) &&
        self.m33.fuzzy_eq(other.m33) && self.m34.fuzzy_eq(other.m34) &&
        self.m41.fuzzy_eq(other.m41) && self.m42.fuzzy_eq(other.m42) &&
        self.m43.fuzzy_eq(other.m43) && self.m44.fuzzy_eq(other.m44)
    }

    pure fn mul(&&m: Matrix4<T>) -> Matrix4<T> {
        return Matrix4(m.m11.mul(self.m11).add(m.m12.mul(self.m21))
                                          .add(m.m13.mul(self.m31))
                                          .add(m.m14.mul(self.m41)),
                       m.m11.mul(self.m12).add(m.m12.mul(self.m22))
                                          .add(m.m13.mul(self.m32))
                                          .add(m.m14.mul(self.m42)),
                       m.m11.mul(self.m13).add(m.m12.mul(self.m23))
                                          .add(m.m13.mul(self.m33))
                                          .add(m.m14.mul(self.m43)),
                       m.m11.mul(self.m14).add(m.m12.mul(self.m24))
                                          .add(m.m13.mul(self.m34))
                                          .add(m.m14.mul(self.m44)),
                       m.m21.mul(self.m11).add(m.m22.mul(self.m21))
                                          .add(m.m23.mul(self.m31))
                                          .add(m.m24.mul(self.m41)),
                       m.m21.mul(self.m12).add(m.m22.mul(self.m22))
                                          .add(m.m23.mul(self.m32))
                                          .add(m.m24.mul(self.m42)),
                       m.m21.mul(self.m13).add(m.m22.mul(self.m23))
                                          .add(m.m23.mul(self.m33))
                                          .add(m.m24.mul(self.m43)),
                       m.m21.mul(self.m14).add(m.m22.mul(self.m24))
                                          .add(m.m23.mul(self.m34))
                                          .add(m.m24.mul(self.m44)),
                       m.m31.mul(self.m11).add(m.m32.mul(self.m21))
                                          .add(m.m33.mul(self.m31))
                                          .add(m.m34.mul(self.m41)),
                       m.m31.mul(self.m12).add(m.m32.mul(self.m22))
                                          .add(m.m33.mul(self.m32))
                                          .add(m.m34.mul(self.m42)),
                       m.m31.mul(self.m13).add(m.m32.mul(self.m23))
                                          .add(m.m33.mul(self.m33))
                                          .add(m.m34.mul(self.m43)),
                       m.m31.mul(self.m14).add(m.m32.mul(self.m24))
                                          .add(m.m33.mul(self.m34))
                                          .add(m.m34.mul(self.m44)),
                       m.m41.mul(self.m11).add(m.m42.mul(self.m21))
                                          .add(m.m43.mul(self.m31))
                                          .add(m.m44.mul(self.m41)),
                       m.m41.mul(self.m12).add(m.m42.mul(self.m22))
                                          .add(m.m43.mul(self.m32))
                                          .add(m.m44.mul(self.m42)),
                       m.m41.mul(self.m13).add(m.m42.mul(self.m23))
                                          .add(m.m43.mul(self.m33))
                                          .add(m.m44.mul(self.m43)),
                       m.m41.mul(self.m14).add(m.m42.mul(self.m24))
                                          .add(m.m43.mul(self.m34))
                                          .add(m.m44.mul(self.m44)));
    }

    pure fn mul_s(&&x: T) -> Matrix4<T> {
        return Matrix4(self.m11.mul(x), self.m12.mul(x), self.m13.mul(x), self.m14.mul(x),
                       self.m21.mul(x), self.m22.mul(x), self.m23.mul(x), self.m24.mul(x),
                       self.m31.mul(x), self.m32.mul(x), self.m33.mul(x), self.m34.mul(x),
                       self.m41.mul(x), self.m42.mul(x), self.m43.mul(x), self.m44.mul(x));
    }

    pure fn scale(&&x: T, &&y: T, &&z: T) -> Matrix4<T> {
        return Matrix4(self.m11.mul(x), self.m12,        self.m13,        self.m14,
                       self.m21,        self.m22.mul(y), self.m23,        self.m24,
                       self.m31,        self.m32,        self.m33.mul(z), self.m34,
                       self.m41,        self.m42,        self.m43,        self.m44);
    }

    pure fn to_array() -> ~[T] {
        return ~[
            self.m11, self.m12, self.m13, self.m14,
            self.m21, self.m22, self.m23, self.m24,
            self.m31, self.m32, self.m33, self.m34,
            self.m41, self.m42, self.m43, self.m44
        ];
    }

    pure fn translate(&&x: T, &&y: T, &&z: T) -> Matrix4<T> {
        let _0 = num::from_int(0);
        let _1 = num::from_int(1);
        let matrix = Matrix4(_1, _0, _0, _0,
                             _0, _1, _0, _0,
                             _0, _0, _1, _0,
                              x,  y,  z, _1);

        return self.mul(matrix);
    }
}

fn ortho<T:copy FuzzyEq Num>(+left: T, +right: T, +bottom: T, +top: T, +near: T, +far: T)
                          -> Matrix4<T> {

    let two: T = num::from_int(2);
    let one: T = num::from_int(1);
    let zero: T = num::from_int(0);
    let minus_two: T = num::from_int(-2);

    let tx = right.add(left).div(right.sub(left)).neg();
    let ty = top.add(bottom).div(top.sub(bottom)).neg();
    let tz = far.add(near).div(far.sub(near)).neg();

    return Matrix4(two.div(right.sub(left)), zero, zero, zero,
                   zero, two.div(top.sub(bottom)), zero, zero,
                   zero, zero, minus_two.div(far.sub(near)), zero,
                   tx, ty, tz, one);
}

fn identity<T:copy FuzzyEq Num>(_0: T) -> Matrix4<T> {
    let _1 = num::from_int(1);
    return Matrix4(_1, _0, _0, _0,
                   _0, _1, _0, _0,
                   _0, _0, _1, _0,
                   _0, _0, _0, _1);
}

#[test]
fn test_ortho() {
    let (left, right, bottom, top) = (0.0, 1.0, 0.1, 1.0);
    let (near, far) = (-1.0, 1.0);
    let result = ortho(left, right, bottom, top, near, far);
    let expected = Matrix4(2.0,  0.0,         0.0,  0.0,
                           0.0,  2.22222222,  0.0,  0.0,
                           0.0,  0.0,         -1.0, 0.0,
                           -1.0, -1.22222222, -0.0, 1.0);
    #debug("result=%? expected=%?", result, expected);
    assert result.fuzzy_eq(expected);
}

<commit_msg>Fix upstream build breaks by specifying more pass-by-reference.<commit_after>import std::cmp::FuzzyEq;
import num::Num;

pure fn Matrix4<T:copy FuzzyEq Num>(m11: T, m12: T, m13: T, m14: T,
                                    m21: T, m22: T, m23: T, m24: T,
                                    m31: T, m32: T, m33: T, m34: T,
                                    m41: T, m42: T, m43: T, m44: T)
                                 -> Matrix4<T> {

    Matrix4 {
        m11: m11, m12: m12, m13: m13, m14: m14,
        m21: m21, m22: m22, m23: m23, m24: m24,
        m31: m31, m32: m32, m33: m33, m34: m34,
        m41: m41, m42: m42, m43: m43, m44: m44
    }
}

struct Matrix4<T:copy FuzzyEq Num> {
    let m11: T; let m12: T; let m13: T; let m14: T;
    let m21: T; let m22: T; let m23: T; let m24: T;
    let m31: T; let m32: T; let m33: T; let m34: T;
    let m41: T; let m42: T; let m43: T; let m44: T;

    pure fn fuzzy_eq(&&other: Matrix4<T>) -> bool {
        self.m11.fuzzy_eq(&other.m11) && self.m12.fuzzy_eq(&other.m12) &&
        self.m13.fuzzy_eq(&other.m13) && self.m14.fuzzy_eq(&other.m14) &&
        self.m21.fuzzy_eq(&other.m21) && self.m22.fuzzy_eq(&other.m22) &&
        self.m23.fuzzy_eq(&other.m23) && self.m24.fuzzy_eq(&other.m24) &&
        self.m31.fuzzy_eq(&other.m31) && self.m32.fuzzy_eq(&other.m32) &&
        self.m33.fuzzy_eq(&other.m33) && self.m34.fuzzy_eq(&other.m34) &&
        self.m41.fuzzy_eq(&other.m41) && self.m42.fuzzy_eq(&other.m42) &&
        self.m43.fuzzy_eq(&other.m43) && self.m44.fuzzy_eq(&other.m44)
    }

    pure fn mul(&&m: Matrix4<T>) -> Matrix4<T> {
        return Matrix4(m.m11.mul(self.m11).add(m.m12.mul(self.m21))
                                          .add(m.m13.mul(self.m31))
                                          .add(m.m14.mul(self.m41)),
                       m.m11.mul(self.m12).add(m.m12.mul(self.m22))
                                          .add(m.m13.mul(self.m32))
                                          .add(m.m14.mul(self.m42)),
                       m.m11.mul(self.m13).add(m.m12.mul(self.m23))
                                          .add(m.m13.mul(self.m33))
                                          .add(m.m14.mul(self.m43)),
                       m.m11.mul(self.m14).add(m.m12.mul(self.m24))
                                          .add(m.m13.mul(self.m34))
                                          .add(m.m14.mul(self.m44)),
                       m.m21.mul(self.m11).add(m.m22.mul(self.m21))
                                          .add(m.m23.mul(self.m31))
                                          .add(m.m24.mul(self.m41)),
                       m.m21.mul(self.m12).add(m.m22.mul(self.m22))
                                          .add(m.m23.mul(self.m32))
                                          .add(m.m24.mul(self.m42)),
                       m.m21.mul(self.m13).add(m.m22.mul(self.m23))
                                          .add(m.m23.mul(self.m33))
                                          .add(m.m24.mul(self.m43)),
                       m.m21.mul(self.m14).add(m.m22.mul(self.m24))
                                          .add(m.m23.mul(self.m34))
                                          .add(m.m24.mul(self.m44)),
                       m.m31.mul(self.m11).add(m.m32.mul(self.m21))
                                          .add(m.m33.mul(self.m31))
                                          .add(m.m34.mul(self.m41)),
                       m.m31.mul(self.m12).add(m.m32.mul(self.m22))
                                          .add(m.m33.mul(self.m32))
                                          .add(m.m34.mul(self.m42)),
                       m.m31.mul(self.m13).add(m.m32.mul(self.m23))
                                          .add(m.m33.mul(self.m33))
                                          .add(m.m34.mul(self.m43)),
                       m.m31.mul(self.m14).add(m.m32.mul(self.m24))
                                          .add(m.m33.mul(self.m34))
                                          .add(m.m34.mul(self.m44)),
                       m.m41.mul(self.m11).add(m.m42.mul(self.m21))
                                          .add(m.m43.mul(self.m31))
                                          .add(m.m44.mul(self.m41)),
                       m.m41.mul(self.m12).add(m.m42.mul(self.m22))
                                          .add(m.m43.mul(self.m32))
                                          .add(m.m44.mul(self.m42)),
                       m.m41.mul(self.m13).add(m.m42.mul(self.m23))
                                          .add(m.m43.mul(self.m33))
                                          .add(m.m44.mul(self.m43)),
                       m.m41.mul(self.m14).add(m.m42.mul(self.m24))
                                          .add(m.m43.mul(self.m34))
                                          .add(m.m44.mul(self.m44)));
    }

    pure fn mul_s(&&x: T) -> Matrix4<T> {
        return Matrix4(self.m11.mul(x), self.m12.mul(x), self.m13.mul(x), self.m14.mul(x),
                       self.m21.mul(x), self.m22.mul(x), self.m23.mul(x), self.m24.mul(x),
                       self.m31.mul(x), self.m32.mul(x), self.m33.mul(x), self.m34.mul(x),
                       self.m41.mul(x), self.m42.mul(x), self.m43.mul(x), self.m44.mul(x));
    }

    pure fn scale(&&x: T, &&y: T, &&z: T) -> Matrix4<T> {
        return Matrix4(self.m11.mul(x), self.m12,        self.m13,        self.m14,
                       self.m21,        self.m22.mul(y), self.m23,        self.m24,
                       self.m31,        self.m32,        self.m33.mul(z), self.m34,
                       self.m41,        self.m42,        self.m43,        self.m44);
    }

    pure fn to_array() -> ~[T] {
        return ~[
            self.m11, self.m12, self.m13, self.m14,
            self.m21, self.m22, self.m23, self.m24,
            self.m31, self.m32, self.m33, self.m34,
            self.m41, self.m42, self.m43, self.m44
        ];
    }

    pure fn translate(&&x: T, &&y: T, &&z: T) -> Matrix4<T> {
        let _0 = num::from_int(0);
        let _1 = num::from_int(1);
        let matrix = Matrix4(_1, _0, _0, _0,
                             _0, _1, _0, _0,
                             _0, _0, _1, _0,
                              x,  y,  z, _1);

        return self.mul(matrix);
    }
}

fn ortho<T:copy FuzzyEq Num>(+left: T, +right: T, +bottom: T, +top: T, +near: T, +far: T)
                          -> Matrix4<T> {

    let two: T = num::from_int(2);
    let one: T = num::from_int(1);
    let zero: T = num::from_int(0);
    let minus_two: T = num::from_int(-2);

    let tx = right.add(left).div(right.sub(left)).neg();
    let ty = top.add(bottom).div(top.sub(bottom)).neg();
    let tz = far.add(near).div(far.sub(near)).neg();

    return Matrix4(two.div(right.sub(left)), zero, zero, zero,
                   zero, two.div(top.sub(bottom)), zero, zero,
                   zero, zero, minus_two.div(far.sub(near)), zero,
                   tx, ty, tz, one);
}

fn identity<T:copy FuzzyEq Num>(_0: T) -> Matrix4<T> {
    let _1 = num::from_int(1);
    return Matrix4(_1, _0, _0, _0,
                   _0, _1, _0, _0,
                   _0, _0, _1, _0,
                   _0, _0, _0, _1);
}

#[test]
fn test_ortho() {
    let (left, right, bottom, top) = (0.0, 1.0, 0.1, 1.0);
    let (near, far) = (-1.0, 1.0);
    let result = ortho(left, right, bottom, top, near, far);
    let expected = Matrix4(2.0,  0.0,         0.0,  0.0,
                           0.0,  2.22222222,  0.0,  0.0,
                           0.0,  0.0,         -1.0, 0.0,
                           -1.0, -1.22222222, -0.0, 1.0);
    #debug("result=%? expected=%?", result, expected);
    assert result.fuzzy_eq(expected);
}

<|endoftext|>


<commit_before>#![feature(scoped)]

extern crate websocket;

fn main() {
    use std::thread;
    use std::sync::mpsc::channel;
    use std::io::stdin;

    use websocket::{Message, Sender, Receiver};
    use websocket::client::request::Url;
    use websocket::Client;

    let url = Url::parse("wss://ws.blockchain.info/inv").unwrap();

    println!("Connecting to {}", url);

    let request = Client::connect(url).unwrap();

    let response = request.send().unwrap(); // Send the request and retrieve a response

    println!("Validating response...");

    response.validate().unwrap(); // Validate the response

    println!("Successfully connected");

    let (mut sender, mut receiver) = response.begin().split();

    let (tx, rx) = channel();

    let tx_1 = tx.clone();

    // request transactions from out bitcoin address
    let message = Message::Text("{\"op\":\"addr_sub\", \"addr\":\"1MtD4wbHnfCtmSg7VFavmfChuWeRrSe9qX\"}".to_string());
    match sender.send_message(message) {
        Ok(()) => (),
        Err(e) => {
            println!("Send Loop: {:?}", e);
            let _ = sender.send_message(Message::Close(None));
            return;
        }
    }

    let receive_loop = thread::scoped(move || {
        // Receive loop
        for message in receiver.incoming_messages() {
            let message = match message {
                Ok(m) => m,
                Err(e) => {
                    println!("Receive Loop: {:?}", e);
                    let _ = tx_1.send(Message::Close(None));
                    return;
                }
            };
            match message {
                Message::Close(_) => {
                    // Got a close message, so send a close message and return
                    let _ = tx_1.send(Message::Close(None));
                    return;
                }
                Message::Ping(data) => match tx_1.send(Message::Pong(data)) {
                    // Send a pong in response
                    Ok(()) => (),
                    Err(e) => {
                        println!("Receive Loop: {:?}", e);
                        return;
                    }
                },
                // Say what we received
                _ => println!("Receive Loop: {:?}", message),
            }
        }
    });

    // We're exiting

    println!("Waiting for child threads to exit");

    let _ = receive_loop.join();

    println!("Exited");
}

<commit_msg>sender instance from websocket connection owned by isolated thread<commit_after>#![feature(scoped)]

extern crate websocket;

fn main() {
    use std::thread;
    use std::sync::mpsc::channel;
    use std::io::stdin;

    use websocket::{Message, Sender, Receiver};
    use websocket::client::request::Url;
    use websocket::Client;

    let url = Url::parse("wss://ws.blockchain.info/inv").unwrap();

    println!("Connecting to {}", url);

    let request = Client::connect(url).unwrap();

    let response = request.send().unwrap(); // Send the request and retrieve a response

    println!("Validating response...");

    response.validate().unwrap(); // Validate the response

    println!("Successfully connected");

    let (mut sender, mut receiver) = response.begin().split();

    let (tx, rx) = channel();

    let tx_1 = tx.clone();

    let send_loop = thread::scoped(move || {
        loop {
            // Send loop
            let message = match rx.recv() {
                Ok(m) => m,
                Err(e) => {
                    println!("Send Loop: {:?}", e);
                    return;
                }
            };
            match message {
                Message::Close(_) => {
                    let _ = sender.send_message(message);
                    // If it's a close message, just send it and then return.
                    return;
                }
                _ => (),
            }
            // Send the message
            match sender.send_message(message) {
                Ok(()) => (),
                Err(e) => {
                    println!("Send Loop: {:?}", e);
                    let _ = sender.send_message(Message::Close(None));
                    return;
                }
            }
        }
    });

    let receive_loop = thread::scoped(move || {
        // Receive loop
        for message in receiver.incoming_messages() {
            let message = match message {
                Ok(m) => m,
                Err(e) => {
                    println!("Receive Loop: {:?}", e);
                    let _ = tx_1.send(Message::Close(None));
                    return;
                }
            };
            match message {
                Message::Close(_) => {
                    // Got a close message, so send a close message and return
                    let _ = tx_1.send(Message::Close(None));
                    return;
                }
                Message::Ping(data) => match tx_1.send(Message::Pong(data)) {
                    // Send a pong in response
                    Ok(()) => (),
                    Err(e) => {
                        println!("Receive Loop: {:?}", e);
                        return;
                    }
                },
                // Say what we received
                _ => println!("Receive Loop: {:?}", message),
            }
        }
    });

    // request transactions from out bitcoin address
    let message = Message::Text("{\"op\":\"addr_sub\", \"addr\":\"1MtD4wbHnfCtmSg7VFavmfChuWeRrSe9qX\"}".to_string());
    tx.send(message);

    // We're exiting

    println!("Waiting for child threads to exit");

    let _ = send_loop.join();
    let _ = receive_loop.join();

    println!("Exited");
}

<|endoftext|>


<commit_before>use std::env;
use std::path::PathBuf;
use std::process;
use std::{cell::RefCell, str::FromStr};

use getopts::Options;
use hprof::Profiler;
use libgir::{self as gir, Config, Library, WorkMode};

fn print_usage(program: &str, opts: Options) {
    let brief = format!(
        "Usage: {program} [options] [<library> <version>]
       {program} (-h | --help)",
        program = program
    );
    print!("{}", opts.usage(&brief));
}

trait OptionStr {
    fn as_str_ref(&self) -> Option<&str>;
}

impl<S: AsRef<str>> OptionStr for Option<S> {
    fn as_str_ref(&self) -> Option<&str> {
        self.as_ref().map(|string| string.as_ref())
    }
}

#[allow(clippy::large_enum_variant)]
enum RunKind {
    Config(Config),
    CheckGirFile(String),
}

fn build_config() -> Result<RunKind, String> {
    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();

    let mut options = Options::new();
    options.optopt(
        "c",
        "config",
        "Config file path (default: Gir.toml)",
        "CONFIG",
    );
    options.optflag("h", "help", "Show this message");
    options.optopt("d", "gir-directory", "Directory for girs", "GIRSPATH");
    options.optopt(
        "m",
        "mode",
        "Work mode: doc, normal, sys or not_bound",
        "MODE",
    );
    options.optopt("o", "target", "Target path", "PATH");
    options.optopt("p", "doc-target-path", "Doc target path", "PATH");
    options.optflag("b", "make-backup", "Make backup before generating");
    options.optflag("s", "stats", "Show statistics");
    options.optflag("", "disable-format", "Disable formatting generated code");
    options.optopt(
        "",
        "check-gir-file",
        "Check if the given `.gir` file is valid",
        "PATH",
    );

    let matches = options.parse(&args[1..]).map_err(|e| e.to_string())?;

    if let Some(check_gir_file) = matches.opt_str("check-gir-file") {
        return Ok(RunKind::CheckGirFile(check_gir_file));
    }

    if matches.opt_present("h") {
        print_usage(&program, options);
        process::exit(0);
    }

    let work_mode = match matches.opt_str("m") {
        None => None,
        Some(s) => match WorkMode::from_str(&s) {
            Ok(w) => Some(w),
            Err(e) => {
                eprintln!("Error (switching to default work mode): {}", e);
                None
            }
        },
    };

    Config::new(
        matches.opt_str("c").as_str_ref(),
        work_mode,
        matches.opt_str("d").as_str_ref(),
        matches.free.get(0).as_str_ref(),
        matches.free.get(1).as_str_ref(),
        matches.opt_str("o").as_str_ref(),
        matches.opt_str("doc-target-path").as_str_ref(),
        matches.opt_present("b"),
        matches.opt_present("s"),
        matches.opt_present("disable-format"),
    )
    .map(RunKind::Config)
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    if let Err(ref e) = do_main() {
        eprintln!("{}", e);

        ::std::process::exit(1);
    }
}

fn run_check(check_gir_file: &str) -> Result<(), String> {
    let path = PathBuf::from(check_gir_file);
    if !path.is_file() {
        return Err(format!("`{}`: file not found", check_gir_file));
    }
    let lib_name = path
        .file_stem()
        .ok_or(format!("Failed to get file stem from `{}`", check_gir_file))?;
    let lib_name = lib_name
        .to_str()
        .ok_or("failed to convert OsStr to str".to_owned())?;
    let mut library = Library::new(lib_name);
    let parent = path.parent().ok_or(format!(
        "Failed to get parent directory from `{}`",
        check_gir_file
    ))?;

    library.read_file(&parent, &mut vec![lib_name.to_owned()])
}

fn do_main() -> Result<(), String> {
    if std::env::var_os("RUST_LOG").is_none() {
        std::env::set_var("RUST_LOG", "gir=warn,libgir=warn");
    }
    env_logger::init();

    let mut cfg = match build_config() {
        Ok(RunKind::CheckGirFile(check_gir_file)) => return run_check(&check_gir_file),
        Ok(RunKind::Config(cfg)) => cfg,
        Err(err) => return Err(err),
    };
    cfg.check_disable_format();

    let statistics = Profiler::new("Gir");
    statistics.start_frame();

    let watcher_total = statistics.enter("Total");

    let mut library = {
        let _watcher = statistics.enter("Loading");

        let mut library = Library::new(&cfg.library_name);
        library.read_file(&cfg.girs_dir, &mut vec![cfg.library_full_name()])?;
        library
    };

    {
        let _watcher = statistics.enter("Preprocessing");
        library.preprocessing(cfg.work_mode);
    }

    {
        let _watcher = statistics.enter("Update library by config");
        gir::update_version::apply_config(&mut library, &cfg);
    }

    {
        let _watcher = statistics.enter("Postprocessing");
        library.postprocessing(&cfg);
    }

    {
        let _watcher = statistics.enter("Resolving type ids");
        cfg.resolve_type_ids(&library);
    }

    {
        let _watcher = statistics.enter("Checking versions");
        gir::update_version::check_function_real_version(&mut library);
    }

    let mut env = {
        let _watcher = statistics.enter("Namespace/symbol/class analysis");

        let namespaces = gir::namespaces_run(&library);
        let symbols = gir::symbols_run(&library, &namespaces);
        let class_hierarchy = gir::class_hierarchy_run(&library);

        gir::Env {
            library,
            config: cfg,
            namespaces,
            symbols: RefCell::new(symbols),
            class_hierarchy,
            analysis: Default::default(),
        }
    };

    if env.config.work_mode != WorkMode::Sys {
        let _watcher = statistics.enter("Analyzing");
        gir::analysis_run(&mut env);
    }

    if env.config.work_mode != WorkMode::DisplayNotBound {
        let _watcher = statistics.enter("Generating");
        gir::codegen_generate(&env);
    }

    if !env.config.disable_format && env.config.work_mode.is_generate_rust_files() {
        let _watcher = statistics.enter("Formatting");
        gir::fmt::format(&env.config.target_path);
    }

    drop(watcher_total);
    statistics.end_frame();

    if env.config.show_statistics {
        statistics.print_timing();
    }
    if env.config.work_mode == WorkMode::DisplayNotBound {
        env.library.show_non_bound_types(&env);
    }

    Ok(())
}
<commit_msg>Fix clippy::or_fun_call on automatically refactored code<commit_after>use std::env;
use std::path::PathBuf;
use std::process;
use std::{cell::RefCell, str::FromStr};

use getopts::Options;
use hprof::Profiler;
use libgir::{self as gir, Config, Library, WorkMode};

fn print_usage(program: &str, opts: Options) {
    let brief = format!(
        "Usage: {program} [options] [<library> <version>]
       {program} (-h | --help)",
        program = program
    );
    print!("{}", opts.usage(&brief));
}

trait OptionStr {
    fn as_str_ref(&self) -> Option<&str>;
}

impl<S: AsRef<str>> OptionStr for Option<S> {
    fn as_str_ref(&self) -> Option<&str> {
        self.as_ref().map(|string| string.as_ref())
    }
}

#[allow(clippy::large_enum_variant)]
enum RunKind {
    Config(Config),
    CheckGirFile(String),
}

fn build_config() -> Result<RunKind, String> {
    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();

    let mut options = Options::new();
    options.optopt(
        "c",
        "config",
        "Config file path (default: Gir.toml)",
        "CONFIG",
    );
    options.optflag("h", "help", "Show this message");
    options.optopt("d", "gir-directory", "Directory for girs", "GIRSPATH");
    options.optopt(
        "m",
        "mode",
        "Work mode: doc, normal, sys or not_bound",
        "MODE",
    );
    options.optopt("o", "target", "Target path", "PATH");
    options.optopt("p", "doc-target-path", "Doc target path", "PATH");
    options.optflag("b", "make-backup", "Make backup before generating");
    options.optflag("s", "stats", "Show statistics");
    options.optflag("", "disable-format", "Disable formatting generated code");
    options.optopt(
        "",
        "check-gir-file",
        "Check if the given `.gir` file is valid",
        "PATH",
    );

    let matches = options.parse(&args[1..]).map_err(|e| e.to_string())?;

    if let Some(check_gir_file) = matches.opt_str("check-gir-file") {
        return Ok(RunKind::CheckGirFile(check_gir_file));
    }

    if matches.opt_present("h") {
        print_usage(&program, options);
        process::exit(0);
    }

    let work_mode = match matches.opt_str("m") {
        None => None,
        Some(s) => match WorkMode::from_str(&s) {
            Ok(w) => Some(w),
            Err(e) => {
                eprintln!("Error (switching to default work mode): {}", e);
                None
            }
        },
    };

    Config::new(
        matches.opt_str("c").as_str_ref(),
        work_mode,
        matches.opt_str("d").as_str_ref(),
        matches.free.get(0).as_str_ref(),
        matches.free.get(1).as_str_ref(),
        matches.opt_str("o").as_str_ref(),
        matches.opt_str("doc-target-path").as_str_ref(),
        matches.opt_present("b"),
        matches.opt_present("s"),
        matches.opt_present("disable-format"),
    )
    .map(RunKind::Config)
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    if let Err(ref e) = do_main() {
        eprintln!("{}", e);

        ::std::process::exit(1);
    }
}

fn run_check(check_gir_file: &str) -> Result<(), String> {
    let path = PathBuf::from(check_gir_file);
    if !path.is_file() {
        return Err(format!("`{}`: file not found", check_gir_file));
    }
    let lib_name = path
        .file_stem()
        .ok_or(format!("Failed to get file stem from `{}`", check_gir_file))?;
    let lib_name = lib_name
        .to_str()
        .ok_or_else(|| "failed to convert OsStr to str".to_owned())?;
    let mut library = Library::new(lib_name);
    let parent = path.parent().ok_or(format!(
        "Failed to get parent directory from `{}`",
        check_gir_file
    ))?;

    library.read_file(&parent, &mut vec![lib_name.to_owned()])
}

fn do_main() -> Result<(), String> {
    if std::env::var_os("RUST_LOG").is_none() {
        std::env::set_var("RUST_LOG", "gir=warn,libgir=warn");
    }
    env_logger::init();

    let mut cfg = match build_config() {
        Ok(RunKind::CheckGirFile(check_gir_file)) => return run_check(&check_gir_file),
        Ok(RunKind::Config(cfg)) => cfg,
        Err(err) => return Err(err),
    };
    cfg.check_disable_format();

    let statistics = Profiler::new("Gir");
    statistics.start_frame();

    let watcher_total = statistics.enter("Total");

    let mut library = {
        let _watcher = statistics.enter("Loading");

        let mut library = Library::new(&cfg.library_name);
        library.read_file(&cfg.girs_dir, &mut vec![cfg.library_full_name()])?;
        library
    };

    {
        let _watcher = statistics.enter("Preprocessing");
        library.preprocessing(cfg.work_mode);
    }

    {
        let _watcher = statistics.enter("Update library by config");
        gir::update_version::apply_config(&mut library, &cfg);
    }

    {
        let _watcher = statistics.enter("Postprocessing");
        library.postprocessing(&cfg);
    }

    {
        let _watcher = statistics.enter("Resolving type ids");
        cfg.resolve_type_ids(&library);
    }

    {
        let _watcher = statistics.enter("Checking versions");
        gir::update_version::check_function_real_version(&mut library);
    }

    let mut env = {
        let _watcher = statistics.enter("Namespace/symbol/class analysis");

        let namespaces = gir::namespaces_run(&library);
        let symbols = gir::symbols_run(&library, &namespaces);
        let class_hierarchy = gir::class_hierarchy_run(&library);

        gir::Env {
            library,
            config: cfg,
            namespaces,
            symbols: RefCell::new(symbols),
            class_hierarchy,
            analysis: Default::default(),
        }
    };

    if env.config.work_mode != WorkMode::Sys {
        let _watcher = statistics.enter("Analyzing");
        gir::analysis_run(&mut env);
    }

    if env.config.work_mode != WorkMode::DisplayNotBound {
        let _watcher = statistics.enter("Generating");
        gir::codegen_generate(&env);
    }

    if !env.config.disable_format && env.config.work_mode.is_generate_rust_files() {
        let _watcher = statistics.enter("Formatting");
        gir::fmt::format(&env.config.target_path);
    }

    drop(watcher_total);
    statistics.end_frame();

    if env.config.show_statistics {
        statistics.print_timing();
    }
    if env.config.work_mode == WorkMode::DisplayNotBound {
        env.library.show_non_bound_types(&env);
    }

    Ok(())
}
<|endoftext|>