<commit_before>// Copyright (c) 2016-2018 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;

use super::metadata::{read_metafile, MetaFile};
use super::{PackageIdent, PackageTarget};

use error::{Error, Result};

use tempdir::TempDir;

const INSTALL_TMP_PREFIX: &'static 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(Error::PackageUnpackFailed(
        "Could not determine parent directory for temporary package directory".to_owned(),
    ))?;
    fs::create_dir_all(base)?;
    let temp_install_prefix = path
        .file_name()
        .and_then(|f| f.to_str())
        .and_then(|dirname| Some(format!("{}-{}", INSTALL_TMP_PREFIX, dirname)))
        .ok_or(Error::PackageUnpackFailed(
            "Could not generate prefix for temporary package directory".to_owned(),
        ))?;
    Ok(TempDir::new_in(base, &temp_install_prefix)?)
}

/// 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 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.clone())
            }
        }
    }
    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: &String, 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: &String,
    name: &String,
    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: &String,
    name: &String,
    version: &String,
    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: &String,
    name: &String,
    version: &String,
    active_target: &PackageTarget,
    dir: &Path,
) -> Option<PackageIdent> {
    let release = if let Some(rel) = dir.file_name().and_then(|f| f.to_str()) {
        rel
    } else {
        return None;
    };

    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 {
        return Some(PackageIdent::new(
            origin.clone(),
            name.clone(),
            Some(version.clone()),
            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,
        );
        return None;
    }
}

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

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

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

    use fs;
    use std::fs::File;
    use tempdir::TempDir;

    #[test]
    fn empty_dir_gives_empty_list() {
        let package_root = TempDir::new("fs-root").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 = TempDir::new("fs-root").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 = TempDir::new("fs-root").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 = TempDir::new("fs-root").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 = Path::new("/tmp/foo");
        let temp_dir = temp_package_directory(&p).unwrap();
        let temp_path = temp_dir.path();

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

    #[test]
    fn temp_package_directory_starts_with_prefix() {
        let p = Path::new("/tmp/foo");
        let temp_dir = temp_package_directory(&p).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 = Path::new("/tmp/foo");
        let temp_dir1 = temp_package_directory(&p).unwrap();
        let temp_dir2 = temp_package_directory(&p).unwrap();

        assert_ne!(&temp_dir1.path(), &temp_dir2.path());
    }
}
<commit_msg>Export package_list_for_origin<commit_after>// Copyright (c) 2016-2018 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;

use super::metadata::{read_metafile, MetaFile};
use super::{PackageIdent, PackageTarget};

use error::{Error, Result};

use tempdir::TempDir;

const INSTALL_TMP_PREFIX: &'static 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(Error::PackageUnpackFailed(
        "Could not determine parent directory for temporary package directory".to_owned(),
    ))?;
    fs::create_dir_all(base)?;
    let temp_install_prefix = path
        .file_name()
        .and_then(|f| f.to_str())
        .and_then(|dirname| Some(format!("{}-{}", INSTALL_TMP_PREFIX, dirname)))
        .ok_or(Error::PackageUnpackFailed(
            "Could not generate prefix for temporary package directory".to_owned(),
        ))?;
    Ok(TempDir::new_in(base, &temp_install_prefix)?)
}

/// 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: &String) -> 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.clone())
            }
        }
    }
    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: &String, 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: &String,
    name: &String,
    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: &String,
    name: &String,
    version: &String,
    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: &String,
    name: &String,
    version: &String,
    active_target: &PackageTarget,
    dir: &Path,
) -> Option<PackageIdent> {
    let release = if let Some(rel) = dir.file_name().and_then(|f| f.to_str()) {
        rel
    } else {
        return None;
    };

    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 {
        return Some(PackageIdent::new(
            origin.clone(),
            name.clone(),
            Some(version.clone()),
            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,
        );
        return None;
    }
}

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

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

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

    use fs;
    use std::fs::File;
    use tempdir::TempDir;

    #[test]
    fn empty_dir_gives_empty_list() {
        let package_root = TempDir::new("fs-root").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 = TempDir::new("fs-root").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 = TempDir::new("fs-root").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 = TempDir::new("fs-root").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 = Path::new("/tmp/foo");
        let temp_dir = temp_package_directory(&p).unwrap();
        let temp_path = temp_dir.path();

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

    #[test]
    fn temp_package_directory_starts_with_prefix() {
        let p = Path::new("/tmp/foo");
        let temp_dir = temp_package_directory(&p).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 = Path::new("/tmp/foo");
        let temp_dir1 = temp_package_directory(&p).unwrap();
        let temp_dir2 = temp_package_directory(&p).unwrap();

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

    #[test]
    fn list_for_origin_skips_non_origin_packages() {
        let fs_root = TempDir::new("fs-root").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 = TempDir::new("fs-root").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>#![allow(unused_variables)]

extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::input::keyboard::Key;
use piston::input::Button::Keyboard;
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;

struct App {
    window: RcWindow,
    graphics: Rc<RefCell<GlGraphics>>,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    virtual_cursor_pos: [f64; 2],
    bg_color: [f32; 4]
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0f64;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.virtual_cursor_pos = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs, window: RcWindow) {
        // we need to pass the window into update (and set the size here) because
        // using the update event from the window events is currently broken.
        self.translator.set_size(window.borrow().size());

        // update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        let mut gl_graphics = self.graphics.borrow_mut();

        // draw the background color
        {
            gl_graphics.draw(args.viewport(), |_, gl| clear(self.bg_color, gl));
        }

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color, square, c.transform, gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.virtual_cursor_pos[0], self.virtual_cursor_pos[1], 5.0);
            const CURSOR_DOT_COLOR: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
            gl_graphics.draw(args.viewport(), |c, gl| ellipse(CURSOR_DOT_COLOR, dot, c.transform, gl));
        }
    }
}

struct Character {
    color: [f32; 4],
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: [f32; 4], tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0f64, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("calaxite", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .unwrap_or_else(|e| panic!("Could not create window: {}", e));

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = RebindBuilder::new(WINDOW_SIZE)
        .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
        .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
        .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
        .build_translator();

    let character = Character::new([1.0, 0.0, 0.0, 1.0],
                                   [30.0, (WINDOW_SIZE.1 as f64) * 0.85],
                                   50.0);

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        translator: translator,
        character: character,
        virtual_cursor_pos: [0.0, 0.0],
        bg_color: [0.0, 0.0, 0.0, 1.0] // black background
    };

    for e in  app.window.clone().events() {
        let app_window = app.window.clone();
        match e {
            Event::Render(r) => { app.render(&r); },
            Event::Update(u) => { app.update(&u, app_window); },
            Event::Input(i) => { app.input(&i); },
            _ => { }
        }
    }
}
<commit_msg>Improve documentation<commit_after>#![allow(unused_variables)]

extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::input::keyboard::Key;
use piston::input::Button::Keyboard;
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;

struct App {
    window: RcWindow,
    graphics: Rc<RefCell<GlGraphics>>,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    virtual_cursor_pos: [f64; 2],
    bg_color: [f32; 4]
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0f64;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.virtual_cursor_pos = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs, window: RcWindow) {
        // we need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(window.borrow().size());

        // update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        let mut gl_graphics = self.graphics.borrow_mut();

        // draw the background color
        {
            gl_graphics.draw(args.viewport(), |_, gl| clear(self.bg_color, gl));
        }

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color, square, c.transform, gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.virtual_cursor_pos[0], self.virtual_cursor_pos[1], 5.0);
            const CURSOR_DOT_COLOR: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
            gl_graphics.draw(args.viewport(), |c, gl| ellipse(CURSOR_DOT_COLOR, dot, c.transform, gl));
        }
    }
}

struct Character {
    color: [f32; 4],
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: [f32; 4], tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0f64, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("calaxite", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .unwrap_or_else(|e| panic!("Could not create window: {}", e));

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = RebindBuilder::new(WINDOW_SIZE)
        .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
        .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
        .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
        .build_translator();

    let character = Character::new([1.0, 0.0, 0.0, 1.0],
                                   [30.0, (WINDOW_SIZE.1 as f64) * 0.85],
                                   50.0);

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        translator: translator,
        character: character,
        virtual_cursor_pos: [0.0, 0.0],
        bg_color: [0.0, 0.0, 0.0, 1.0] // black background
    };

    for e in  app.window.clone().events() {
        let app_window = app.window.clone();
        match e {
            Event::Render(r) => { app.render(&r); },
            Event::Update(u) => { app.update(&u, app_window); },
            Event::Input(i) => { app.input(&i); },
            _ => { }
        }
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::io::prelude::*;
use std::io;
use std::path::PathBuf;
use std::process;

pub fn create_profile_source(shim_dir: &PathBuf,
                             groonga_dir: &String,
                             install_dir: &PathBuf)
                             -> Result<(), io::Error> {
    if shim_dir.join("source-groonga.sh").exists() {
        let _ = try!(fs::remove_file(shim_dir.join("source-groonga.sh")));
    }
    let mut f = fs::File::create(shim_dir.join("source-groonga.sh").to_str().unwrap())
        .expect("Could not create a shell setting file.");
    let installed_groonga = install_dir.join(groonga_dir)
        .join("bin");
    if !installed_groonga.exists() {
        println!("Specified version is not installed.");
        process::exit(1);
    }
    let contents = format!("export PATH={}:$PATH",
                           installed_groonga.display());
    match f.write_all(&contents.as_bytes()) {
        Ok(_) => return Ok(()),
        Err(e) => println!("{}", e),
    }
    f.sync_data()
}

pub fn remove_grnenv_profile(shim_dir: &PathBuf) -> Result<(), io::Error> {
    if shim_dir.join("source-groonga.sh").exists() {
        let _ = try!(fs::remove_file(shim_dir.join("source-groonga.sh")));
    }
    // Create an empty file to prevent reading source-groonga.sh error.
    let _ = try!(fs::File::create(shim_dir.join("source-groonga.sh").to_str().unwrap()));
    Ok(())
}
<commit_msg>unix profile: Support more environment variables<commit_after>use std::fs;
use std::io::prelude::*;
use std::io;
use std::path::PathBuf;
use std::process;

pub fn create_profile_source(shim_dir: &PathBuf,
                             groonga_dir: &String,
                             install_dir: &PathBuf)
                             -> Result<(), io::Error> {
    #[cfg(not(target_os = "macos"))]
    fn dynamic_link_var_pathname() -> String {
        "LD_LIBRARY_PATH".to_string()
    }
    #[cfg(target_os = "macos")]
    fn dynamic_link_var_pathname() -> String {
        "DYLD_LIBRARY_PATH".to_string()
    }
    if shim_dir.join("source-groonga.sh").exists() {
        let _ = try!(fs::remove_file(shim_dir.join("source-groonga.sh")));
    }
    let mut f = fs::File::create(shim_dir.join("source-groonga.sh").to_str().unwrap())
        .expect("Could not create a shell setting file.");
    let installed_groonga = install_dir.join(groonga_dir)
        .join("bin");
    let libgroonga = install_dir.join(groonga_dir)
        .join("lib");
    let pkg_config = install_dir.join(groonga_dir)
        .join("lib")
        .join("pkgconfig");
    if !installed_groonga.exists() {
        println!("Specified version is not installed.");
        process::exit(1);
    }
    let contents = format!("export PATH={}:$PATH\nexport {}={}:${}\nexport PKG_CONFIG_PATH={}:$PKG_CONFIG_PATH",
                           installed_groonga.display(),
                           dynamic_link_var_pathname(),
                           libgroonga.display(),
                           dynamic_link_var_pathname(),
                           pkg_config.display());
    match f.write_all(&contents.as_bytes()) {
        Ok(_) => return Ok(()),
        Err(e) => println!("{}", e),
    }
    f.sync_data()
}

pub fn remove_grnenv_profile(shim_dir: &PathBuf) -> Result<(), io::Error> {
    if shim_dir.join("source-groonga.sh").exists() {
        let _ = try!(fs::remove_file(shim_dir.join("source-groonga.sh")));
    }
    // Create an empty file to prevent reading source-groonga.sh error.
    let _ = try!(fs::File::create(shim_dir.join("source-groonga.sh").to_str().unwrap()));
    Ok(())
}
<|endoftext|>


<commit_before>use raw::{NONE_ADDRESS, CompiledAddr};
use raw::build::BuilderNode;

#[derive(Debug)]
pub struct Registry {
    table: Vec<RegistryCell>,
    table_size: usize, // number of rows
    mru_size: usize, // number of columns
}

#[derive(Debug)]
struct RegistryCache<'a> {
    cells: &'a mut [RegistryCell],
}

#[derive(Clone, Debug)]
pub struct RegistryCell {
    addr: CompiledAddr,
    node: BuilderNode,
}

#[derive(Debug)]
pub enum RegistryEntry<'a> {
    Found(CompiledAddr),
    NotFound(&'a mut RegistryCell),
    Rejected,
}

impl Registry {
    pub fn new(table_size: usize, mru_size: usize) -> Registry {
        let empty_cell = RegistryCell::none();
        let ncells = table_size.checked_mul(mru_size).unwrap();
        Registry {
            table: vec![empty_cell; ncells],
            table_size: table_size,
            mru_size: mru_size,
        }
    }

    pub fn entry<'a>(&'a mut self, node: &BuilderNode) -> RegistryEntry<'a> {
        if self.table.is_empty() {
            return RegistryEntry::Rejected;
        }
        let bucket = self.hash(node);
        let start = self.mru_size * bucket;
        let end = start + self.mru_size;
        RegistryCache { cells: &mut self.table[start..end] }.entry(node)
    }

    fn hash(&self, node: &BuilderNode) -> usize {
        // Basic FNV-1a hash as described:
        // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
        //
        // In unscientific experiments, this provides the same compression
        // as `std::hash::SipHasher` but is much much faster.
        const FNV_PRIME: usize = 1099511628211;
        let mut h = 14695981039346656037;
        h = (h ^ (node.is_final as usize)).wrapping_mul(FNV_PRIME);
        h = (h ^ (node.final_output.value() as usize)).wrapping_mul(FNV_PRIME);
        for t in &node.trans {
            h = (h ^ (t.inp as usize)).wrapping_mul(FNV_PRIME);
            h = (h ^ (t.out.value() as usize)).wrapping_mul(FNV_PRIME);
            h = (h ^ (t.addr as usize)).wrapping_mul(FNV_PRIME);
        }
        h % self.table_size
    }
}

impl<'a> RegistryCache<'a> {
    fn entry(mut self, node: &BuilderNode) -> RegistryEntry<'a> {
        if self.cells.len() == 1 {
            let cell = &mut self.cells[0];
            if !cell.is_none() && &cell.node == node {
                RegistryEntry::Found(cell.addr)
            } else {
                cell.node.clone_from(node);
                RegistryEntry::NotFound(cell)
            }
        } else {
            let find = |c: &RegistryCell| !c.is_none() && &c.node == node;
            if let Some(i) = self.cells.iter().position(find) {
                let addr = self.cells[i].addr;
                self.promote(i); // most recently used
                RegistryEntry::Found(addr)
            } else {
                let last = self.cells.len() - 1;
                self.cells[last].node.clone_from(node); // discard LRU
                self.promote(last);
                RegistryEntry::NotFound(&mut self.cells[0])
            }
        }
    }

    fn promote(&mut self, mut i: usize) {
        assert!(i < self.cells.len());
        while i > 0 {
            self.cells.swap(i-1, i);
            i -= 1;
        }
    }
}

impl RegistryCell {
    fn none() -> RegistryCell {
        RegistryCell {
            addr: NONE_ADDRESS,
            node: BuilderNode::default(),
        }
    }

    fn is_none(&self) -> bool {
        self.addr == NONE_ADDRESS
    }

    pub fn insert(&mut self, addr: CompiledAddr) {
        self.addr = addr;
    }
}

#[cfg(test)]
mod tests {
    use raw::{Output, Transition};
    use raw::build::BuilderNode;
    use super::{
        Registry, RegistryCell, RegistryEntry, RegistryCache,
    };

    fn assert_rejected(entry: RegistryEntry) {
        match entry {
            RegistryEntry::Rejected => {}
            entry => panic!("expected rejected entry, got: {:?}", entry),
        }
    }

    fn assert_not_found(entry: RegistryEntry) {
        match entry {
            RegistryEntry::NotFound(_) => {}
            entry => panic!("expected nout found entry, got: {:?}", entry),
        }
    }

    fn assert_insert_and_found(reg: &mut Registry, bnode: &BuilderNode) {
        match reg.entry(&bnode) {
            RegistryEntry::NotFound(cell) => cell.insert(1234),
            entry => panic!("unexpected not found entry, got: {:?}", entry),
        }
        match reg.entry(&bnode) {
            RegistryEntry::Found(addr) => assert_eq!(addr, 1234),
            entry => panic!("unexpected found entry, got: {:?}", entry),
        }
    }

    #[test]
    fn empty_is_ok() {
        let mut reg = Registry::new(0, 0);
        let bnode = BuilderNode {
            is_final: false,
            final_output: Output::zero(),
            trans: vec![],
        };
        assert_rejected(reg.entry(&bnode));
    }

    #[test]
    fn one_final_is_ok() {
        let mut reg = Registry::new(1, 1);
        let bnode = BuilderNode {
            is_final: true,
            final_output: Output::zero(),
            trans: vec![],
        };
        assert_insert_and_found(&mut reg, &bnode);
    }

    #[test]
    fn one_with_trans_is_ok() {
        let mut reg = Registry::new(1, 1);
        let bnode = BuilderNode {
            is_final: false,
            final_output: Output::zero(),
            trans: vec![Transition {
                addr: 0, inp: b'a', out: Output::zero(),
            }],
        };
        assert_insert_and_found(&mut reg, &bnode);
        assert_not_found(
            reg.entry(&BuilderNode { is_final: true, .. bnode.clone() }));
        assert_not_found(reg.entry(&BuilderNode {
            trans: vec![Transition {
                addr: 0, inp: b'b', out: Output::zero(),
            }],
            .. bnode.clone()
        }));
        assert_not_found(reg.entry(&BuilderNode {
            trans: vec![Transition {
                addr: 0, inp: b'a', out: Output::new(1),
            }],
            .. bnode.clone()
        }));
    }

    #[test]
    fn cache_works() {
        let mut reg = Registry::new(1, 1);

        let bnode1 = BuilderNode { is_final: true, ..BuilderNode::default() };
        assert_insert_and_found(&mut reg, &bnode1);

        let bnode2 = BuilderNode {
            final_output: Output::new(1), ..bnode1.clone()
        };
        assert_insert_and_found(&mut reg, &bnode2);
        assert_not_found(reg.entry(&bnode1));
    }

    #[test]
    fn promote() {
        let bn = BuilderNode::default();
        let mut bnodes = vec![
            RegistryCell { addr: 1, node: bn.clone() },
            RegistryCell { addr: 2, node: bn.clone() },
            RegistryCell { addr: 3, node: bn.clone() },
            RegistryCell { addr: 4, node: bn.clone() },
        ];
        let mut cache = RegistryCache { cells: &mut bnodes };

        cache.promote(0);
        assert_eq!(cache.cells[0].addr, 1);
        assert_eq!(cache.cells[1].addr, 2);
        assert_eq!(cache.cells[2].addr, 3);
        assert_eq!(cache.cells[3].addr, 4);

        cache.promote(1);
        assert_eq!(cache.cells[0].addr, 2);
        assert_eq!(cache.cells[1].addr, 1);
        assert_eq!(cache.cells[2].addr, 3);
        assert_eq!(cache.cells[3].addr, 4);

        cache.promote(3);
        assert_eq!(cache.cells[0].addr, 4);
        assert_eq!(cache.cells[1].addr, 2);
        assert_eq!(cache.cells[2].addr, 1);
        assert_eq!(cache.cells[3].addr, 3);

        cache.promote(2);
        assert_eq!(cache.cells[0].addr, 1);
        assert_eq!(cache.cells[1].addr, 4);
        assert_eq!(cache.cells[2].addr, 2);
        assert_eq!(cache.cells[3].addr, 3);
    }
}
<commit_msg>Don't use an a huge literal for usize. Just use u64 instead.<commit_after>use raw::{NONE_ADDRESS, CompiledAddr};
use raw::build::BuilderNode;

#[derive(Debug)]
pub struct Registry {
    table: Vec<RegistryCell>,
    table_size: usize, // number of rows
    mru_size: usize, // number of columns
}

#[derive(Debug)]
struct RegistryCache<'a> {
    cells: &'a mut [RegistryCell],
}

#[derive(Clone, Debug)]
pub struct RegistryCell {
    addr: CompiledAddr,
    node: BuilderNode,
}

#[derive(Debug)]
pub enum RegistryEntry<'a> {
    Found(CompiledAddr),
    NotFound(&'a mut RegistryCell),
    Rejected,
}

impl Registry {
    pub fn new(table_size: usize, mru_size: usize) -> Registry {
        let empty_cell = RegistryCell::none();
        let ncells = table_size.checked_mul(mru_size).unwrap();
        Registry {
            table: vec![empty_cell; ncells],
            table_size: table_size,
            mru_size: mru_size,
        }
    }

    pub fn entry<'a>(&'a mut self, node: &BuilderNode) -> RegistryEntry<'a> {
        if self.table.is_empty() {
            return RegistryEntry::Rejected;
        }
        let bucket = self.hash(node);
        let start = self.mru_size * bucket;
        let end = start + self.mru_size;
        RegistryCache { cells: &mut self.table[start..end] }.entry(node)
    }

    fn hash(&self, node: &BuilderNode) -> usize {
        // Basic FNV-1a hash as described:
        // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
        //
        // In unscientific experiments, this provides the same compression
        // as `std::hash::SipHasher` but is much much faster.
        const FNV_PRIME: u64 = 1099511628211;
        let mut h = 14695981039346656037;
        h = (h ^ (node.is_final as u64)).wrapping_mul(FNV_PRIME);
        h = (h ^ node.final_output.value()).wrapping_mul(FNV_PRIME);
        for t in &node.trans {
            h = (h ^ (t.inp as u64)).wrapping_mul(FNV_PRIME);
            h = (h ^ t.out.value()).wrapping_mul(FNV_PRIME);
            h = (h ^ (t.addr as u64)).wrapping_mul(FNV_PRIME);
        }
        (h as usize) % self.table_size
    }
}

impl<'a> RegistryCache<'a> {
    fn entry(mut self, node: &BuilderNode) -> RegistryEntry<'a> {
        if self.cells.len() == 1 {
            let cell = &mut self.cells[0];
            if !cell.is_none() && &cell.node == node {
                RegistryEntry::Found(cell.addr)
            } else {
                cell.node.clone_from(node);
                RegistryEntry::NotFound(cell)
            }
        } else {
            let find = |c: &RegistryCell| !c.is_none() && &c.node == node;
            if let Some(i) = self.cells.iter().position(find) {
                let addr = self.cells[i].addr;
                self.promote(i); // most recently used
                RegistryEntry::Found(addr)
            } else {
                let last = self.cells.len() - 1;
                self.cells[last].node.clone_from(node); // discard LRU
                self.promote(last);
                RegistryEntry::NotFound(&mut self.cells[0])
            }
        }
    }

    fn promote(&mut self, mut i: usize) {
        assert!(i < self.cells.len());
        while i > 0 {
            self.cells.swap(i-1, i);
            i -= 1;
        }
    }
}

impl RegistryCell {
    fn none() -> RegistryCell {
        RegistryCell {
            addr: NONE_ADDRESS,
            node: BuilderNode::default(),
        }
    }

    fn is_none(&self) -> bool {
        self.addr == NONE_ADDRESS
    }

    pub fn insert(&mut self, addr: CompiledAddr) {
        self.addr = addr;
    }
}

#[cfg(test)]
mod tests {
    use raw::{Output, Transition};
    use raw::build::BuilderNode;
    use super::{
        Registry, RegistryCell, RegistryEntry, RegistryCache,
    };

    fn assert_rejected(entry: RegistryEntry) {
        match entry {
            RegistryEntry::Rejected => {}
            entry => panic!("expected rejected entry, got: {:?}", entry),
        }
    }

    fn assert_not_found(entry: RegistryEntry) {
        match entry {
            RegistryEntry::NotFound(_) => {}
            entry => panic!("expected nout found entry, got: {:?}", entry),
        }
    }

    fn assert_insert_and_found(reg: &mut Registry, bnode: &BuilderNode) {
        match reg.entry(&bnode) {
            RegistryEntry::NotFound(cell) => cell.insert(1234),
            entry => panic!("unexpected not found entry, got: {:?}", entry),
        }
        match reg.entry(&bnode) {
            RegistryEntry::Found(addr) => assert_eq!(addr, 1234),
            entry => panic!("unexpected found entry, got: {:?}", entry),
        }
    }

    #[test]
    fn empty_is_ok() {
        let mut reg = Registry::new(0, 0);
        let bnode = BuilderNode {
            is_final: false,
            final_output: Output::zero(),
            trans: vec![],
        };
        assert_rejected(reg.entry(&bnode));
    }

    #[test]
    fn one_final_is_ok() {
        let mut reg = Registry::new(1, 1);
        let bnode = BuilderNode {
            is_final: true,
            final_output: Output::zero(),
            trans: vec![],
        };
        assert_insert_and_found(&mut reg, &bnode);
    }

    #[test]
    fn one_with_trans_is_ok() {
        let mut reg = Registry::new(1, 1);
        let bnode = BuilderNode {
            is_final: false,
            final_output: Output::zero(),
            trans: vec![Transition {
                addr: 0, inp: b'a', out: Output::zero(),
            }],
        };
        assert_insert_and_found(&mut reg, &bnode);
        assert_not_found(
            reg.entry(&BuilderNode { is_final: true, .. bnode.clone() }));
        assert_not_found(reg.entry(&BuilderNode {
            trans: vec![Transition {
                addr: 0, inp: b'b', out: Output::zero(),
            }],
            .. bnode.clone()
        }));
        assert_not_found(reg.entry(&BuilderNode {
            trans: vec![Transition {
                addr: 0, inp: b'a', out: Output::new(1),
            }],
            .. bnode.clone()
        }));
    }

    #[test]
    fn cache_works() {
        let mut reg = Registry::new(1, 1);

        let bnode1 = BuilderNode { is_final: true, ..BuilderNode::default() };
        assert_insert_and_found(&mut reg, &bnode1);

        let bnode2 = BuilderNode {
            final_output: Output::new(1), ..bnode1.clone()
        };
        assert_insert_and_found(&mut reg, &bnode2);
        assert_not_found(reg.entry(&bnode1));
    }

    #[test]
    fn promote() {
        let bn = BuilderNode::default();
        let mut bnodes = vec![
            RegistryCell { addr: 1, node: bn.clone() },
            RegistryCell { addr: 2, node: bn.clone() },
            RegistryCell { addr: 3, node: bn.clone() },
            RegistryCell { addr: 4, node: bn.clone() },
        ];
        let mut cache = RegistryCache { cells: &mut bnodes };

        cache.promote(0);
        assert_eq!(cache.cells[0].addr, 1);
        assert_eq!(cache.cells[1].addr, 2);
        assert_eq!(cache.cells[2].addr, 3);
        assert_eq!(cache.cells[3].addr, 4);

        cache.promote(1);
        assert_eq!(cache.cells[0].addr, 2);
        assert_eq!(cache.cells[1].addr, 1);
        assert_eq!(cache.cells[2].addr, 3);
        assert_eq!(cache.cells[3].addr, 4);

        cache.promote(3);
        assert_eq!(cache.cells[0].addr, 4);
        assert_eq!(cache.cells[1].addr, 2);
        assert_eq!(cache.cells[2].addr, 1);
        assert_eq!(cache.cells[3].addr, 3);

        cache.promote(2);
        assert_eq!(cache.cells[0].addr, 1);
        assert_eq!(cache.cells[1].addr, 4);
        assert_eq!(cache.cells[2].addr, 2);
        assert_eq!(cache.cells[3].addr, 3);
    }
}
<|endoftext|>


<commit_before>use std::io;

use std::fmt::Write;

use pombase_rc_string::RcString;

use crate::web::config::*;
use crate::data_types::*;

pub const GO_ASPECT_NAMES: [&str; 3] =
    ["cellular_component", "biological_process", "molecular_function"];

fn eco_evidence_from_annotation(mapping: &GoEcoMapping,
                                annotation: &OntAnnotationDetail)
                                -> String
{
    if let Some(ref go_evidence) = annotation.evidence {
        if let Some(ref reference) = annotation.reference {
            mapping.lookup_with_go_ref(go_evidence, reference)
                .unwrap_or_else(|| mapping.lookup_default(go_evidence)
                                .unwrap_or_else(|| panic!("failed to find {} for {} in GO mapping",
                                                          go_evidence, reference)))
        } else {
            mapping.lookup_default(go_evidence)
                .unwrap_or_else(|| panic!("failed to find {} in GO mapping",
                                          go_evidence))
        }
    } else {
        String::from("")
    }
}

fn get_gpad_relation_of(term_details: &TermDetails,
                        annotation_detail: &OntAnnotationDetail) -> String {
    match term_details.cv_name.as_str() {
        "molecular_function" => {
            if annotation_detail.qualifiers.iter()
                .find(|q| q.as_str() == "contributes_to").is_some()
            {
                String::from("RO:0002326")
            } else {
                String::from("RO:0002327")
            }
        },
        "biological_process" => String::from("RO:0002331"),
        "cellular_component" => {
            if term_details.interesting_parent_details.iter()
                .any(|p| p.termid == RcString::from("GO:0032991"))
            {
                String::from("BFO:0000050")
            } else {
                String::from("RO:0001025")
            }
        },
        _ => panic!("unknown cv_name in GPAD export: {}", term_details.cv_name)
    }
}

fn make_gpad_extension_string(config: &Config, extension: &Vec<ExtPart>) -> String {
    let rel_mapping = &config.file_exports.gpad_gpi.extension_relation_mappings;
    let get_rel_termid = |ext_part: &ExtPart| {
        if let Some(map_termid) = rel_mapping.get(ext_part.rel_type_name.as_ref()) {
            map_termid.clone().unwrap()
        } else {
            ext_part.rel_type_id.clone().unwrap()
        }
    };

    let get_range = |ext_part: &ExtPart| {
        let mut range_copy = ext_part.ext_range.clone();

        if let ExtRange::Gene(ref mut gene_uniquename) = range_copy {
            if !gene_uniquename.contains(":") {
                let new_uniquename =
                    RcString::from(&format!("{}:{}", config.database_name,
                                            gene_uniquename));
                *gene_uniquename = new_uniquename;
            }
        }
        range_copy
    };

    extension.iter()
        .filter(|ext_part| {
            if let Some(map_termid) = rel_mapping.get(ext_part.rel_type_name.as_ref()) {
                map_termid.is_some()
            } else {
                ext_part.rel_type_id.is_some()
            }
        })
        .map(|ext_part| format!("{}({})", get_rel_termid(ext_part),
                                get_range(ext_part)))
        .collect::<Vec<_>>().join(",")
}

pub fn write_gene_product_annotation(gpad_writer: &mut dyn io::Write,
                                     go_eco_mappping: &GoEcoMapping, config: &Config,
                                     api_maps: &APIMaps, gene_details: &GeneDetails)
                                     -> Result<(), io::Error>
{
    let database_name = &config.database_name;
    let db_object_id = format!("{}:{}", database_name, gene_details.uniquename);

    for (cv_name, term_annotations) in &gene_details.cv_annotations {
        if !GO_ASPECT_NAMES.contains(&cv_name.as_ref()) {
            continue;
        }

        for term_annotation in term_annotations {
            let term = api_maps.terms.get(&term_annotation.term)
                .expect(&format!("failed to find term summary for {}",
                                 term_annotation.term));

            for annotation_id in &term_annotation.annotations {
                let annotation_detail = api_maps.annotation_details
                    .get(&annotation_id)
                    .expect(&format!("can't find annotation {}", annotation_id));
                let not = if term_annotation.is_not {
                    "NOT"
                } else {
                    ""
                };

                let relation = get_gpad_relation_of(term, annotation_detail);

                let ontology_class_id = &term_annotation.term;
                let reference_uniquename =
                    annotation_detail.reference.clone()
                    .unwrap_or_else(|| RcString::from(""));
                let evidence_type =
                    eco_evidence_from_annotation(go_eco_mappping, annotation_detail);
                let assigned_by = &config.database_name;

                let with_iter = annotation_detail.withs.iter();
                let from_iter = annotation_detail.froms.iter();
                let mut with_or_from_parts =
                    with_iter.chain(from_iter).map(|s| {
                        let s: RcString = s.clone().into();
                        if s.contains(":") {
                            s
                        } else {
                            RcString::from(&format!("{}:{}", assigned_by, s))
                        }
                    })
                    .collect::<Vec<RcString>>();
                with_or_from_parts.sort_unstable();
                let with_or_from = with_or_from_parts.join(",");

                let date = annotation_detail.date.clone()
                    .expect(&format!("date missing from annotation with ID {}",
                                     annotation_id));
                let annotation_extensions =
                    make_gpad_extension_string(config, &annotation_detail.extension);
                let line = format!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t{}\t{}\t{}\t\n",
                                   db_object_id,
                                   not, relation, ontology_class_id,
                                   reference_uniquename, evidence_type,
                                   with_or_from, date, assigned_by,
                                   annotation_extensions);
                gpad_writer.write_all(line.as_bytes())?;
            }
        }
    }

    Ok(())
}

fn make_gaf_extension_string(config: &Config, extension: &Vec<ExtPart>) -> String {
    let mut ret_string = String::new();

    for (idx, ext_part) in extension.iter().enumerate() {
        ret_string += &ext_part.rel_type_name;
        ret_string.push('(');

        if let ExtRange::Gene(ref gene_uniquename) = ext_part.ext_range {
            if gene_uniquename.contains(":") {
                ret_string += gene_uniquename;
            } else {
                ret_string += &config.database_name;
                ret_string.push(':');
                ret_string += &gene_uniquename;
            }
        } else {
            write!(ret_string, "{}", &ext_part.ext_range).unwrap();
        }
        ret_string.push(')');

        if idx < extension.len() - 1 {
            ret_string.push(',');
        }
    }

    ret_string
}

pub fn write_go_annotation_format(writer: &mut dyn io::Write, config: &Config,
                                  api_maps: &APIMaps, gene_details: &GeneDetails,
                                  cv_name: &str)
                                  -> Result<(), io::Error>
{
    if !GO_ASPECT_NAMES.contains(&cv_name) {
        return Err(io::Error::new(io::ErrorKind::InvalidInput,
                                  format!("unknown CV: {}", cv_name)));
    }

    let database_name = &config.database_name;
    let db_object_id = &gene_details.uniquename;
    let db_object_symbol =
        if let Some(ref gene_name) = gene_details.name {
            gene_name.as_str()
        } else {
            &db_object_id
        };
    let db_object_synonyms =
        gene_details.synonyms.iter().filter(|synonym| {
            synonym.synonym_type == "exact"
        })
        .map(|synonym| synonym.name.to_string())
        .collect::<Vec<String>>()
        .join("|");

    let single_letter_aspect =
        if cv_name.starts_with("b") {
            "P"
        } else {
            if cv_name.starts_with("c") {
                "C"
            } else {
                "F"
            }
        };

    if let Some(term_annotations) = gene_details.cv_annotations.get(cv_name) {
        for term_annotation in term_annotations {
            for annotation_id in &term_annotation.annotations {
                let annotation_detail = api_maps.annotation_details
                    .get(&annotation_id)
                    .expect(&format!("can't find annotation {}", annotation_id));

                let mut qualifier_parts = vec![];
                if term_annotation.is_not {
                    qualifier_parts.push("NOT");
                };

                qualifier_parts.extend(annotation_detail.qualifiers.iter()
                                       .map(|s| s.as_str()));

                let go_id = &term_annotation.term;
                let reference_uniquename =
                    annotation_detail.reference.clone()
                    .unwrap_or_else(|| RcString::from(""));
                let assigned_by =
                    if let Some(ref assigned_by) = annotation_detail.assigned_by {
                        assigned_by.as_str()
                    } else {
                        database_name.as_str()
                    };

                let with_iter = annotation_detail.withs.iter();
                let from_iter = annotation_detail.froms.iter();
                let mut with_or_from_parts =
                    with_iter.chain(from_iter).map(|s| {
                        let s: RcString = s.clone().into();
                        if s.contains(":") {
                            s
                        } else {
                            RcString::from(&format!("{}:{}", assigned_by, s))
                        }
                    })
                    .collect::<Vec<RcString>>();
                with_or_from_parts.sort_unstable();
                let with_or_from = with_or_from_parts.join(",");

                let evidence_code =
                    annotation_detail.evidence
                    .as_ref()
                    .map(|s| s.as_str())
                    .unwrap_or_else(|| "");

                let db_object_name =
                    if let Some(ref product) = gene_details.product {
                        product.as_str()
                    } else {
                        ""
                    };

                let transcript_type_str =
                    gene_details.transcripts[0].transcript_type.as_str();
                let db_object_type =
                    if transcript_type_str == "mRNA" {
                        "protein"
                    } else {
                        transcript_type_str
                    };
                let date = annotation_detail.date.clone()
                    .map(|d| d.replace("-", ""))
                    .expect(&format!("date missing from annotation with ID {}",
                                     annotation_id));
                let annotation_extensions =
                    make_gaf_extension_string(config, &annotation_detail.extension);

                let gene_product_form_id =
                    if let Some(ref gene_product_form_id) = annotation_detail.gene_product_form_id
                {
                    gene_product_form_id.as_str()
                } else {
                    ""
                };

                let line = format!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\ttaxon:{}\t{}\t{}\t{}\t{}\n",
                                   database_name,
                                   db_object_id,
                                   db_object_symbol,
                                   qualifier_parts.join("|"),
                                   go_id,
                                   reference_uniquename,
                                   evidence_code,
                                   with_or_from,
                                   single_letter_aspect,
                                   db_object_name,
                                   db_object_synonyms,
                                   db_object_type,
                                   gene_details.taxonid,
                                   date,
                                   assigned_by,
                                   annotation_extensions,
                                   gene_product_form_id);
                writer.write_all(line.as_bytes())?;
            }
        }
    }

    Ok(())
}

<commit_msg>Very minor optimisation<commit_after>use std::io;

use std::fmt::Write;

use pombase_rc_string::RcString;

use crate::web::config::*;
use crate::data_types::*;

pub const GO_ASPECT_NAMES: [&str; 3] =
    ["cellular_component", "biological_process", "molecular_function"];

fn eco_evidence_from_annotation(mapping: &GoEcoMapping,
                                annotation: &OntAnnotationDetail)
                                -> String
{
    if let Some(ref go_evidence) = annotation.evidence {
        if let Some(ref reference) = annotation.reference {
            mapping.lookup_with_go_ref(go_evidence, reference)
                .unwrap_or_else(|| mapping.lookup_default(go_evidence)
                                .unwrap_or_else(|| panic!("failed to find {} for {} in GO mapping",
                                                          go_evidence, reference)))
        } else {
            mapping.lookup_default(go_evidence)
                .unwrap_or_else(|| panic!("failed to find {} in GO mapping",
                                          go_evidence))
        }
    } else {
        String::from("")
    }
}

fn get_gpad_relation_of(term_details: &TermDetails,
                        annotation_detail: &OntAnnotationDetail) -> String {
    match term_details.cv_name.as_str() {
        "molecular_function" => {
            if annotation_detail.qualifiers.iter()
                .find(|q| q.as_str() == "contributes_to").is_some()
            {
                String::from("RO:0002326")
            } else {
                String::from("RO:0002327")
            }
        },
        "biological_process" => String::from("RO:0002331"),
        "cellular_component" => {
            if term_details.interesting_parent_details.iter()
                .any(|p| p.termid == RcString::from("GO:0032991"))
            {
                String::from("BFO:0000050")
            } else {
                String::from("RO:0001025")
            }
        },
        _ => panic!("unknown cv_name in GPAD export: {}", term_details.cv_name)
    }
}

fn make_gpad_extension_string(config: &Config, extension: &Vec<ExtPart>) -> String {
    let rel_mapping = &config.file_exports.gpad_gpi.extension_relation_mappings;
    let get_rel_termid = |ext_part: &ExtPart| {
        if let Some(map_termid) = rel_mapping.get(ext_part.rel_type_name.as_ref()) {
            map_termid.clone().unwrap()
        } else {
            ext_part.rel_type_id.clone().unwrap()
        }
    };

    let get_range = |ext_part: &ExtPart| {
        let mut range_copy = ext_part.ext_range.clone();

        if let ExtRange::Gene(ref mut gene_uniquename) = range_copy {
            if !gene_uniquename.contains(":") {
                let new_uniquename =
                    RcString::from(&format!("{}:{}", config.database_name,
                                            gene_uniquename));
                *gene_uniquename = new_uniquename;
            }
        }
        range_copy
    };

    extension.iter()
        .filter(|ext_part| {
            if let Some(map_termid) = rel_mapping.get(ext_part.rel_type_name.as_ref()) {
                map_termid.is_some()
            } else {
                ext_part.rel_type_id.is_some()
            }
        })
        .map(|ext_part| format!("{}({})", get_rel_termid(ext_part),
                                get_range(ext_part)))
        .collect::<Vec<_>>().join(",")
}

pub fn write_gene_product_annotation(gpad_writer: &mut dyn io::Write,
                                     go_eco_mappping: &GoEcoMapping, config: &Config,
                                     api_maps: &APIMaps, gene_details: &GeneDetails)
                                     -> Result<(), io::Error>
{
    let database_name = &config.database_name;
    let db_object_id = format!("{}:{}", database_name, gene_details.uniquename);

    for (cv_name, term_annotations) in &gene_details.cv_annotations {
        if !GO_ASPECT_NAMES.contains(&cv_name.as_ref()) {
            continue;
        }

        for term_annotation in term_annotations {
            let term = api_maps.terms.get(&term_annotation.term)
                .expect(&format!("failed to find term summary for {}",
                                 term_annotation.term));

            for annotation_id in &term_annotation.annotations {
                let annotation_detail = api_maps.annotation_details
                    .get(&annotation_id)
                    .expect(&format!("can't find annotation {}", annotation_id));
                let not = if term_annotation.is_not {
                    "NOT"
                } else {
                    ""
                };

                let relation = get_gpad_relation_of(term, annotation_detail);

                let ontology_class_id = &term_annotation.term;
                let reference_uniquename =
                    annotation_detail.reference.clone()
                    .unwrap_or_else(|| RcString::from(""));
                let evidence_type =
                    eco_evidence_from_annotation(go_eco_mappping, annotation_detail);
                let assigned_by = &config.database_name;

                let with_iter = annotation_detail.withs.iter();
                let from_iter = annotation_detail.froms.iter();
                let mut with_or_from_parts =
                    with_iter.chain(from_iter).map(|s| {
                        let s: RcString = s.clone().into();
                        if s.contains(":") {
                            s
                        } else {
                            RcString::from(&format!("{}:{}", assigned_by, s))
                        }
                    })
                    .collect::<Vec<RcString>>();
                with_or_from_parts.sort_unstable();
                let with_or_from = with_or_from_parts.join(",");

                let date = annotation_detail.date.clone()
                    .expect(&format!("date missing from annotation with ID {}",
                                     annotation_id));
                let annotation_extensions =
                    make_gpad_extension_string(config, &annotation_detail.extension);
                let line = format!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t{}\t{}\t{}\t\n",
                                   db_object_id,
                                   not, relation, ontology_class_id,
                                   reference_uniquename, evidence_type,
                                   with_or_from, date, assigned_by,
                                   annotation_extensions);
                gpad_writer.write_all(line.as_bytes())?;
            }
        }
    }

    Ok(())
}

fn make_gaf_extension_string(config: &Config, extension: &Vec<ExtPart>) -> String {
    let mut ret_string = String::new();

    for (idx, ext_part) in extension.iter().enumerate() {
        ret_string += &ext_part.rel_type_name;
        ret_string.push('(');

        if let ExtRange::Gene(ref gene_uniquename) = ext_part.ext_range {
            if gene_uniquename.contains(":") {
                ret_string += gene_uniquename;
            } else {
                ret_string += &config.database_name;
                ret_string.push(':');
                ret_string += &gene_uniquename;
            }
        } else {
            write!(ret_string, "{}", &ext_part.ext_range).unwrap();
        }
        ret_string.push(')');

        if idx < extension.len() - 1 {
            ret_string.push(',');
        }
    }

    ret_string
}

pub fn write_go_annotation_format(writer: &mut dyn io::Write, config: &Config,
                                  api_maps: &APIMaps, gene_details: &GeneDetails,
                                  cv_name: &str)
                                  -> Result<(), io::Error>
{
    if !GO_ASPECT_NAMES.contains(&cv_name) {
        return Err(io::Error::new(io::ErrorKind::InvalidInput,
                                  format!("unknown CV: {}", cv_name)));
    }

    let database_name = &config.database_name;
    let db_object_id = &gene_details.uniquename;
    let db_object_symbol =
        if let Some(ref gene_name) = gene_details.name {
            gene_name.as_str()
        } else {
            &db_object_id
        };
    let db_object_synonyms =
        gene_details.synonyms.iter().filter(|synonym| {
            synonym.synonym_type == "exact"
        })
        .map(|synonym| synonym.name.to_string())
        .collect::<Vec<String>>()
        .join("|");

    let single_letter_aspect =
        if cv_name.starts_with("b") {
            "P"
        } else {
            if cv_name.starts_with("c") {
                "C"
            } else {
                "F"
            }
        };

    if let Some(term_annotations) = gene_details.cv_annotations.get(cv_name) {
        for term_annotation in term_annotations {
            for annotation_id in &term_annotation.annotations {
                let annotation_detail = api_maps.annotation_details
                    .get(&annotation_id)
                    .expect(&format!("can't find annotation {}", annotation_id));

                let mut qualifier_parts = vec![];
                if term_annotation.is_not {
                    qualifier_parts.push("NOT");
                };

                qualifier_parts.extend(annotation_detail.qualifiers.iter()
                                       .map(|s| s.as_str()));

                let go_id = &term_annotation.term;
                let reference_uniquename =
                    annotation_detail.reference.clone()
                    .unwrap_or_else(|| RcString::from(""));
                let assigned_by =
                    if let Some(ref assigned_by) = annotation_detail.assigned_by {
                        assigned_by.as_str()
                    } else {
                        database_name.as_str()
                    };

                let with_iter = annotation_detail.withs.iter();
                let from_iter = annotation_detail.froms.iter();
                let mut with_or_from_parts =
                    with_iter.chain(from_iter).map(|s| {
                        let s: RcString = s.clone().into();
                        if s.contains(":") {
                            s
                        } else {
                            RcString::from(&format!("{}:{}", assigned_by, s))
                        }
                    })
                    .collect::<Vec<RcString>>();
                with_or_from_parts.sort_unstable();
                let with_or_from = with_or_from_parts.join(",");

                let evidence_code =
                    annotation_detail.evidence
                    .as_ref()
                    .map(|s| s.as_str())
                    .unwrap_or_else(|| "");

                let db_object_name =
                    if let Some(ref product) = gene_details.product {
                        product.as_str()
                    } else {
                        ""
                    };

                let transcript_type_str =
                    gene_details.transcripts[0].transcript_type.as_str();
                let db_object_type =
                    if transcript_type_str == "mRNA" {
                        "protein"
                    } else {
                        transcript_type_str
                    };
                let date = annotation_detail.date.as_ref()
                    .map(|ref d| d.replace("-", ""))
                    .expect(&format!("date missing from annotation with ID {}",
                                     annotation_id));
                let annotation_extensions =
                    make_gaf_extension_string(config, &annotation_detail.extension);

                let gene_product_form_id =
                    if let Some(ref gene_product_form_id) = annotation_detail.gene_product_form_id
                {
                    gene_product_form_id.as_str()
                } else {
                    ""
                };

                let line = format!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\ttaxon:{}\t{}\t{}\t{}\t{}\n",
                                   database_name,
                                   db_object_id,
                                   db_object_symbol,
                                   qualifier_parts.join("|"),
                                   go_id,
                                   reference_uniquename,
                                   evidence_code,
                                   with_or_from,
                                   single_letter_aspect,
                                   db_object_name,
                                   db_object_synonyms,
                                   db_object_type,
                                   gene_details.taxonid,
                                   date,
                                   assigned_by,
                                   annotation_extensions,
                                   gene_product_form_id);
                writer.write_all(line.as_bytes())?;
            }
        }
    }

    Ok(())
}

<|endoftext|>


<commit_before>extern crate autograd as ag;
extern crate ndarray;

struct LSTM
{
    pub vector_dim: usize,
    pub hs:         Vec<ag::Tensor>,
    pub cells:      Vec<ag::Tensor>,
    pub wx:         ag::Tensor,
    pub wh:         ag::Tensor,
    pub b:          ag::Tensor,
}

impl LSTM
{
    pub fn new(vector_dim: usize) -> LSTM
    {
        LSTM {
            vector_dim,
            hs: vec![],
            cells: vec![],
            wx: ag::variable(ag::ndarray_ext::random_normal(
                &[vector_dim, 4 * vector_dim],
                0.,
                0.01,
            )),
            wh: ag::variable(ag::ndarray_ext::random_normal(
                &[vector_dim, 4 * vector_dim],
                0.,
                0.01,
            )),
            b: ag::variable(ag::ndarray_ext::zeros(&[1, 4 * vector_dim])),
        }
    }

    pub fn list_vars(&self) -> Vec<&ag::Tensor>
    {
        vec![&self.wx, &self.wh, &self.b]
    }

    #[inline]
    /// Applies standard LSTM unit without peephole to input.
    ///
    /// # Arguments
    /// * `x` - Tensor with shape (batch_size, embedding_dim)
    ///
    /// # Returns
    /// output tensor of this unit with shape (batch_size, state_size)
    fn step(&mut self, x: &ag::Tensor) -> ag::Tensor
    {
        let (cell, h) = {
            let ref last_output = self.hs.pop().unwrap_or_else(|| ag::zeros(&x.shape()));
            let ref last_cell = self.cells.pop().unwrap_or_else(|| ag::zeros(&x.shape()));

            let ref xh = ag::matmul(x, &self.wx) + ag::matmul(last_output, &self.wh) + &self.b;

            let size = self.vector_dim as isize;
            let ref i = ag::slice(xh, &[0, 0 * size], &[-1, 1 * size]);
            let ref f = ag::slice(xh, &[0, 1 * size], &[-1, 2 * size]);
            let ref c = ag::slice(xh, &[0, 2 * size], &[-1, 3 * size]);
            let ref o = ag::slice(xh, &[0, 3 * size], &[-1, 4 * size]);

            let cell = ag::sigmoid(f) * last_cell + ag::sigmoid(i) * ag::tanh(c);
            let h = ag::sigmoid(o) * ag::tanh(&cell);
            (cell, h)
        };
        self.cells.push(cell);
        self.hs.push(h.clone());
        h
    }
}

pub fn main()
{
    let vec_dim = 4;
    let max_sent = 2;
    let vocab_size = 5;

    // === graph def
    let ref sentences = ag::placeholder(&[-1, max_sent]);
    let ref mut rnn = LSTM::new(vec_dim);

    let ref lookup_table = ag::variable(ag::ndarray_ext::random_normal(
        &[vocab_size, vec_dim],
        0.,
        0.01,
    ));
    let ref w_pred = ag::variable(ag::ndarray_ext::random_uniform(
        &[vec_dim, vocab_size],
        0.,
        0.01,
    ));
    // Compute cross entropy losses for each LSTM step
    let losses = (0..max_sent)
        .map(|i| {
            let ref cur_id = ag::slice(sentences, &[0, i], &[-1, i + 1]);
            let ref next_id = ag::slice(sentences, &[0, i + 1], &[-1, i + 2]);
            let ref x = ag::gather(lookup_table, cur_id, 0);
            let ref h = rnn.step(x);
            let ref prediction = ag::matmul(h, w_pred);
            ag::sparse_softmax_cross_entropy(prediction, next_id)
        })
        .collect::<Vec<_>>();

    // For simplicity, I use loss of the last output only.
    let loss = losses.last().unwrap();
    let mut vars = rnn.list_vars();
    vars.extend_from_slice(&[lookup_table, w_pred]);
    let ref g = ag::grad(&[loss], vars.as_slice());
    // === graph def end

    // test with toy data
    ag::test_helper::gradient_check(
        loss,
        g.as_slice(),
        vars.as_slice(),
        &[
            (
                sentences,
                &ndarray::arr2(&[[2., 3., 1.], [3., 0., 1.]]).into_dyn(),
            ),
        ],
        1e-3,
        1e-3,
    );
}
<commit_msg>Style fix<commit_after>extern crate autograd as ag;
extern crate ndarray;

struct LSTM
{
    vector_dim: usize,
    hs:         Vec<ag::Tensor>,
    cells:      Vec<ag::Tensor>,
    wx:         ag::Tensor,
    wh:         ag::Tensor,
    b:          ag::Tensor,
}

impl LSTM
{
    fn new(vector_dim: usize) -> LSTM
    {
        LSTM {
            vector_dim,
            hs: vec![],
            cells: vec![],
            wx: ag::variable(ag::ndarray_ext::random_normal(
                &[vector_dim, 4 * vector_dim],
                0.,
                0.01,
            )),
            wh: ag::variable(ag::ndarray_ext::random_normal(
                &[vector_dim, 4 * vector_dim],
                0.,
                0.01,
            )),
            b: ag::variable(ag::ndarray_ext::zeros(&[1, 4 * vector_dim])),
        }
    }

    /// Applies standard LSTM unit without peephole to `x`.
    /// `x` must be a tensor with shape `(batch_size, embedding_dim)`
    ///
    /// # Returns
    /// Output tensor of this unit with shape `(batch_size, state_size)`.
    fn step(&mut self, x: &ag::Tensor) -> &ag::Tensor
    {
        let (cell, h) = {
            let ref last_output = self.hs.pop().unwrap_or_else(|| ag::zeros(&x.shape()));
            let ref last_cell = self.cells.pop().unwrap_or_else(|| ag::zeros(&x.shape()));

            let ref xh = ag::matmul(x, &self.wx) + ag::matmul(last_output, &self.wh) + &self.b;

            let size = self.vector_dim as isize;
            let ref i = ag::slice(xh, &[0, 0 * size], &[-1, 1 * size]);
            let ref f = ag::slice(xh, &[0, 1 * size], &[-1, 2 * size]);
            let ref c = ag::slice(xh, &[0, 2 * size], &[-1, 3 * size]);
            let ref o = ag::slice(xh, &[0, 3 * size], &[-1, 4 * size]);

            let cell = ag::sigmoid(f) * last_cell + ag::sigmoid(i) * ag::tanh(c);
            let h = ag::sigmoid(o) * ag::tanh(&cell);
            (cell, h)
        };
        self.cells.push(cell);
        self.hs.push(h);
        self.hs.last().as_ref().unwrap()
    }
}

// TODO: Use real-world data
// TODO: Write in define-by-run style
pub fn main()
{
    let vec_dim = 4;
    let max_sent = 2;
    let vocab_size = 5;

    let ref sentences = ag::placeholder(&[-1, max_sent]);
    let ref mut rnn = LSTM::new(vec_dim);

    let lookup_table = &ag::variable(ag::ndarray_ext::random_normal(
        &[vocab_size, vec_dim],
        0.,
        0.01,
    ));
    let w_pred = &ag::variable(ag::ndarray_ext::random_uniform(
        &[vec_dim, vocab_size],
        0.,
        0.01,
    ));

    // Compute cross entropy losses for each LSTM step
    let losses: Vec<ag::Tensor> = (0..max_sent)
        .map(|i| {
            let cur_id = ag::slice(sentences, &[0, i], &[-1, i + 1]);
            let next_id = ag::slice(sentences, &[0, i + 1], &[-1, i + 2]);
            let x = ag::gather(lookup_table, &cur_id, 0);
            let h = rnn.step(&x);
            let prediction = ag::matmul(h, w_pred);
            ag::sparse_softmax_cross_entropy(prediction, next_id)
        })
        .collect();

    // Aggregate losses of generated words
    let ref loss = ag::add_n(losses.iter().map(|a| a).collect::<Vec<_>>().as_slice());

    // Compute gradients
    let vars = &[&rnn.wh, &rnn.wx, &rnn.b, lookup_table, w_pred];
    let grads = ag::grad(&[loss], vars);

    // test with toy data
    ag::test_helper::gradient_check(
        loss,
        &grads,
        vars,
        &[
            (
                sentences,
                &ndarray::arr2(&[[2., 3., 1.], [3., 0., 1.]]).into_dyn(),
            ),
        ],
        1e-3,
        1e-3,
    );
}
<|endoftext|>