<commit_before>use crate::errors::*;
use crate::registry::registry_url;
use crate::VersionExt;
use crate::{Dependency, LocalManifest, Manifest};
use regex::Regex;
use std::env;
use std::io::Write;
use std::path::Path;
use std::time::Duration;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use url::Url;

#[derive(Debug)]
struct CrateVersion {
    name: String,
    version: semver::Version,
    yanked: bool,
}

/// Query latest version from a registry index
///
/// The registry argument must be specified for crates
/// from alternative registries.
///
/// The latest version will be returned as a `Dependency`. This will fail, when
///
/// - there is no Internet connection and offline is false.
/// - summaries in registry index with an incorrect format.
/// - a crate with the given name does not exist on the registry.
pub fn get_latest_dependency(
    crate_name: &str,
    flag_allow_prerelease: bool,
    manifest_path: &Path,
    registry: &Option<Url>,
) -> Result<Dependency> {
    if env::var("CARGO_IS_TEST").is_ok() {
        // We are in a simulated reality. Nothing is real here.
        // FIXME: Use actual test handling code.
        let new_version = if flag_allow_prerelease {
            format!("99999.0.0-alpha.1+{}", crate_name)
        } else {
            match crate_name {
                "test_breaking" => "0.2.0".to_string(),
                "test_nonbreaking" => "0.1.1".to_string(),
                other => format!("99999.0.0+{}", other),
            }
        };

        return Ok(Dependency::new(crate_name).set_version(&new_version));
    }

    if crate_name.is_empty() {
        return Err(ErrorKind::EmptyCrateName.into());
    }

    let registry = match registry {
        Some(url) => url.clone(),
        None => registry_url(manifest_path, None)?,
    };

    let crate_versions = fuzzy_query_registry_index(crate_name, &registry)?;

    let dep = read_latest_version(&crate_versions, flag_allow_prerelease)?;

    if dep.name != crate_name {
        println!("WARN: Added `{}` instead of `{}`", dep.name, crate_name);
    }

    Ok(dep)
}

// Checks whether a version object is a stable release
fn version_is_stable(version: &CrateVersion) -> bool {
    !version.version.is_prerelease()
}

/// Read latest version from Versions structure
fn read_latest_version(
    versions: &[CrateVersion],
    flag_allow_prerelease: bool,
) -> Result<Dependency> {
    let latest = versions
        .iter()
        .filter(|&v| flag_allow_prerelease || version_is_stable(v))
        .filter(|&v| !v.yanked)
        .max_by_key(|&v| v.version.clone())
        .ok_or(ErrorKind::NoVersionsAvailable)?;

    let name = &latest.name;
    let version = latest.version.to_string();
    Ok(Dependency::new(name).set_version(&version))
}

/// update registry index for given project
pub fn update_registry_index(registry: &Url, quiet: bool) -> Result<()> {
    let colorchoice = if atty::is(atty::Stream::Stdout) {
        ColorChoice::Auto
    } else {
        ColorChoice::Never
    };
    let mut output = StandardStream::stderr(colorchoice);

    let mut index = crates_index::Index::from_url(registry.as_str())?;
    if !quiet {
        output.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
        write!(output, "{:>12}", "Updating")?;
        output.reset()?;
        writeln!(output, " '{}' index", registry)?;
    }

    while need_retry(index.update())? {
        registry_blocked_message(&mut output)?;
        std::thread::sleep(REGISTRY_BACKOFF);
    }

    Ok(())
}

/// Time between retries for retrieving the registry.
const REGISTRY_BACKOFF: Duration = Duration::from_secs(1);

/// Check if we need to retry retrieving the Index.
fn need_retry(res: std::result::Result<(), crates_index::Error>) -> Result<bool> {
    match res {
        Ok(()) => Ok(false),
        Err(crates_index::Error::Git(err)) => {
            if err.class() == git2::ErrorClass::Index && err.code() == git2::ErrorCode::Locked {
                Ok(true)
            } else {
                Err(crates_index::Error::Git(err).into())
            }
        }
        Err(err) => Err(err.into()),
    }
}

/// Report to user that the Registry is locked
fn registry_blocked_message(output: &mut StandardStream) -> Result<()> {
    output.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
    write!(output, "{:>12}", "Blocking")?;
    output.reset()?;
    writeln!(output, " waiting for lock on registry index")?;
    Ok(())
}

#[test]
fn get_latest_stable_version() {
    let versions = vec![
        CrateVersion {
            name: "foo".into(),
            version: "0.6.0-alpha".parse().unwrap(),
            yanked: false,
        },
        CrateVersion {
            name: "foo".into(),
            version: "0.5.0".parse().unwrap(),
            yanked: false,
        },
    ];
    assert_eq!(
        read_latest_version(&versions, false)
            .unwrap()
            .version()
            .unwrap(),
        "0.5.0"
    );
}

#[test]
fn get_latest_unstable_or_stable_version() {
    let versions = vec![
        CrateVersion {
            name: "foo".into(),
            version: "0.6.0-alpha".parse().unwrap(),
            yanked: false,
        },
        CrateVersion {
            name: "foo".into(),
            version: "0.5.0".parse().unwrap(),
            yanked: false,
        },
    ];
    assert_eq!(
        read_latest_version(&versions, true)
            .unwrap()
            .version()
            .unwrap(),
        "0.6.0-alpha"
    );
}

#[test]
fn get_latest_version_with_yanked() {
    let versions = vec![
        CrateVersion {
            name: "treexml".into(),
            version: "0.3.1".parse().unwrap(),
            yanked: true,
        },
        CrateVersion {
            name: "true".into(),
            version: "0.3.0".parse().unwrap(),
            yanked: false,
        },
    ];
    assert_eq!(
        read_latest_version(&versions, false)
            .unwrap()
            .version()
            .unwrap(),
        "0.3.0"
    );
}

#[test]
fn get_no_latest_version_from_json_when_all_are_yanked() {
    let versions = vec![
        CrateVersion {
            name: "treexml".into(),
            version: "0.3.1".parse().unwrap(),
            yanked: true,
        },
        CrateVersion {
            name: "true".into(),
            version: "0.3.0".parse().unwrap(),
            yanked: true,
        },
    ];
    assert!(read_latest_version(&versions, false).is_err());
}

/// Fuzzy query crate from registry index
fn fuzzy_query_registry_index(
    crate_name: impl Into<String>,
    registry: &Url,
) -> Result<Vec<CrateVersion>> {
    let mut index = crates_index::Index::from_url(registry.as_str())?;
    index.update()?;

    let crate_name = crate_name.into();
    let mut names = gen_fuzzy_crate_names(crate_name.clone())?;
    if let Some(index) = names.iter().position(|x| *x == crate_name) {
        // ref: https://github.com/killercup/cargo-edit/pull/317#discussion_r307365704
        names.swap(index, 0);
    }

    for the_name in names {
        let crate_ = match index.crate_(&the_name) {
            Some(crate_) => crate_,
            None => continue,
        };
        return crate_
            .versions()
            .iter()
            .map(|v| {
                Ok(CrateVersion {
                    name: v.name().to_owned(),
                    version: v.version().parse()?,
                    yanked: v.is_yanked(),
                })
            })
            .collect();
    }
    Err(ErrorKind::NoCrate(crate_name).into())
}

fn get_crate_name_from_repository<T>(repo: &str, matcher: &Regex, url_template: T) -> Result<String>
where
    T: Fn(&str, &str) -> String,
{
    matcher
        .captures(repo)
        .ok_or_else(|| "Unable to parse git repo URL".into())
        .and_then(|cap| match (cap.get(1), cap.get(2)) {
            (Some(user), Some(repo)) => {
                let url = url_template(user.as_str(), repo.as_str());
                let data: Result<Manifest> = get_cargo_toml_from_git_url(&url)
                    .and_then(|m| m.parse().chain_err(|| ErrorKind::ParseCargoToml));
                data.and_then(|ref manifest| {
                    manifest
                        .package_name()
                        .map(std::string::ToString::to_string)
                })
            }
            _ => Err("Git repo url seems incomplete".into()),
        })
}

/// Query crate name by accessing a github repo Cargo.toml
///
/// The name will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - Cargo.toml is not present in the root of the master branch,
/// - the response from github is an error or in an incorrect format.
pub fn get_crate_name_from_github(repo: &str) -> Result<String> {
    let re =
        Regex::new(r"^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$").unwrap();
    get_crate_name_from_repository(repo, &re, |user, repo| {
        format!(
            "https://raw.githubusercontent.com/{user}/{repo}/master/Cargo.toml",
            user = user,
            repo = repo
        )
    })
}

/// Query crate name by accessing a gitlab repo Cargo.toml
///
/// The name will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - Cargo.toml is not present in the root of the master branch,
/// - the response from gitlab is an error or in an incorrect format.
pub fn get_crate_name_from_gitlab(repo: &str) -> Result<String> {
    let re =
        Regex::new(r"^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$").unwrap();
    get_crate_name_from_repository(repo, &re, |user, repo| {
        format!(
            "https://gitlab.com/{user}/{repo}/raw/master/Cargo.toml",
            user = user,
            repo = repo
        )
    })
}

/// Query crate name by accessing Cargo.toml in a local path
///
/// The name will be returned as a string. This will fail, when
/// Cargo.toml is not present in the root of the path.
pub fn get_crate_name_from_path(path: &str) -> Result<String> {
    let cargo_file = Path::new(path).join("Cargo.toml");
    LocalManifest::try_new(&cargo_file)
        .chain_err(|| "Unable to open local Cargo.toml")
        .and_then(|ref manifest| {
            manifest
                .package_name()
                .map(std::string::ToString::to_string)
        })
}

fn get_cargo_toml_from_git_url(url: &str) -> Result<String> {
    let mut req = ureq::get(url);
    req.timeout(get_default_timeout());
    if let Some(proxy) = env_proxy::for_url_str(url)
        .to_url()
        .and_then(|url| ureq::Proxy::new(url).ok())
    {
        req.set_proxy(proxy);
    }
    let res = req.call();
    if res.error() {
        return Err(format!(
            "HTTP request `{}` failed: {}",
            url,
            res.synthetic_error()
                .as_ref()
                .map(|x| x.to_string())
                .unwrap_or_else(|| res.status().to_string())
        )
        .into());
    }

    res.into_string()
        .chain_err(|| "Git response not a valid `String`")
}

const fn get_default_timeout() -> Duration {
    Duration::from_secs(10)
}

/// Generate all similar crate names
///
/// Examples:
///
/// | input | output |
/// | ----- | ------ |
/// | cargo | cargo  |
/// | cargo-edit | cargo-edit, cargo_edit |
/// | parking_lot_core | parking_lot_core, parking_lot-core, parking-lot_core, parking-lot-core |
fn gen_fuzzy_crate_names(crate_name: String) -> Result<Vec<String>> {
    const PATTERN: [u8; 2] = [b'-', b'_'];

    let wildcard_indexs = crate_name
        .bytes()
        .enumerate()
        .filter(|(_, item)| PATTERN.contains(item))
        .map(|(index, _)| index)
        .take(10)
        .collect::<Vec<usize>>();
    if wildcard_indexs.is_empty() {
        return Ok(vec![crate_name]);
    }

    let mut result = vec![];
    let mut bytes = crate_name.into_bytes();
    for mask in 0..2u128.pow(wildcard_indexs.len() as u32) {
        for (mask_index, wildcard_index) in wildcard_indexs.iter().enumerate() {
            let mask_value = (mask >> mask_index) & 1 == 1;
            if mask_value {
                bytes[*wildcard_index] = b'-';
            } else {
                bytes[*wildcard_index] = b'_';
            }
        }
        result.push(String::from_utf8(bytes.clone()).unwrap());
    }
    Ok(result)
}

#[test]
fn test_gen_fuzzy_crate_names() {
    fn test_helper(input: &str, expect: &[&str]) {
        let mut actual = gen_fuzzy_crate_names(input.to_string()).unwrap();
        actual.sort();

        let mut expect = expect.iter().map(|x| x.to_string()).collect::<Vec<_>>();
        expect.sort();

        assert_eq!(actual, expect);
    }

    test_helper("", &[""]);
    test_helper("-", &["_", "-"]);
    test_helper("DCjanus", &["DCjanus"]);
    test_helper("DC-janus", &["DC-janus", "DC_janus"]);
    test_helper(
        "DC-_janus",
        &["DC__janus", "DC_-janus", "DC-_janus", "DC--janus"],
    );
}
<commit_msg>Remove unnecessary index update (#533)<commit_after>use crate::errors::*;
use crate::registry::registry_url;
use crate::VersionExt;
use crate::{Dependency, LocalManifest, Manifest};
use regex::Regex;
use std::env;
use std::io::Write;
use std::path::Path;
use std::time::Duration;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use url::Url;

#[derive(Debug)]
struct CrateVersion {
    name: String,
    version: semver::Version,
    yanked: bool,
}

/// Query latest version from a registry index
///
/// The registry argument must be specified for crates
/// from alternative registries.
///
/// The latest version will be returned as a `Dependency`. This will fail, when
///
/// - there is no Internet connection and offline is false.
/// - summaries in registry index with an incorrect format.
/// - a crate with the given name does not exist on the registry.
pub fn get_latest_dependency(
    crate_name: &str,
    flag_allow_prerelease: bool,
    manifest_path: &Path,
    registry: &Option<Url>,
) -> Result<Dependency> {
    if env::var("CARGO_IS_TEST").is_ok() {
        // We are in a simulated reality. Nothing is real here.
        // FIXME: Use actual test handling code.
        let new_version = if flag_allow_prerelease {
            format!("99999.0.0-alpha.1+{}", crate_name)
        } else {
            match crate_name {
                "test_breaking" => "0.2.0".to_string(),
                "test_nonbreaking" => "0.1.1".to_string(),
                other => format!("99999.0.0+{}", other),
            }
        };

        return Ok(Dependency::new(crate_name).set_version(&new_version));
    }

    if crate_name.is_empty() {
        return Err(ErrorKind::EmptyCrateName.into());
    }

    let registry = match registry {
        Some(url) => url.clone(),
        None => registry_url(manifest_path, None)?,
    };

    let crate_versions = fuzzy_query_registry_index(crate_name, &registry)?;

    let dep = read_latest_version(&crate_versions, flag_allow_prerelease)?;

    if dep.name != crate_name {
        println!("WARN: Added `{}` instead of `{}`", dep.name, crate_name);
    }

    Ok(dep)
}

// Checks whether a version object is a stable release
fn version_is_stable(version: &CrateVersion) -> bool {
    !version.version.is_prerelease()
}

/// Read latest version from Versions structure
fn read_latest_version(
    versions: &[CrateVersion],
    flag_allow_prerelease: bool,
) -> Result<Dependency> {
    let latest = versions
        .iter()
        .filter(|&v| flag_allow_prerelease || version_is_stable(v))
        .filter(|&v| !v.yanked)
        .max_by_key(|&v| v.version.clone())
        .ok_or(ErrorKind::NoVersionsAvailable)?;

    let name = &latest.name;
    let version = latest.version.to_string();
    Ok(Dependency::new(name).set_version(&version))
}

/// update registry index for given project
pub fn update_registry_index(registry: &Url, quiet: bool) -> Result<()> {
    let colorchoice = if atty::is(atty::Stream::Stdout) {
        ColorChoice::Auto
    } else {
        ColorChoice::Never
    };
    let mut output = StandardStream::stderr(colorchoice);

    let mut index = crates_index::Index::from_url(registry.as_str())?;
    if !quiet {
        output.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
        write!(output, "{:>12}", "Updating")?;
        output.reset()?;
        writeln!(output, " '{}' index", registry)?;
    }

    while need_retry(index.update())? {
        registry_blocked_message(&mut output)?;
        std::thread::sleep(REGISTRY_BACKOFF);
    }

    Ok(())
}

/// Time between retries for retrieving the registry.
const REGISTRY_BACKOFF: Duration = Duration::from_secs(1);

/// Check if we need to retry retrieving the Index.
fn need_retry(res: std::result::Result<(), crates_index::Error>) -> Result<bool> {
    match res {
        Ok(()) => Ok(false),
        Err(crates_index::Error::Git(err)) => {
            if err.class() == git2::ErrorClass::Index && err.code() == git2::ErrorCode::Locked {
                Ok(true)
            } else {
                Err(crates_index::Error::Git(err).into())
            }
        }
        Err(err) => Err(err.into()),
    }
}

/// Report to user that the Registry is locked
fn registry_blocked_message(output: &mut StandardStream) -> Result<()> {
    output.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
    write!(output, "{:>12}", "Blocking")?;
    output.reset()?;
    writeln!(output, " waiting for lock on registry index")?;
    Ok(())
}

#[test]
fn get_latest_stable_version() {
    let versions = vec![
        CrateVersion {
            name: "foo".into(),
            version: "0.6.0-alpha".parse().unwrap(),
            yanked: false,
        },
        CrateVersion {
            name: "foo".into(),
            version: "0.5.0".parse().unwrap(),
            yanked: false,
        },
    ];
    assert_eq!(
        read_latest_version(&versions, false)
            .unwrap()
            .version()
            .unwrap(),
        "0.5.0"
    );
}

#[test]
fn get_latest_unstable_or_stable_version() {
    let versions = vec![
        CrateVersion {
            name: "foo".into(),
            version: "0.6.0-alpha".parse().unwrap(),
            yanked: false,
        },
        CrateVersion {
            name: "foo".into(),
            version: "0.5.0".parse().unwrap(),
            yanked: false,
        },
    ];
    assert_eq!(
        read_latest_version(&versions, true)
            .unwrap()
            .version()
            .unwrap(),
        "0.6.0-alpha"
    );
}

#[test]
fn get_latest_version_with_yanked() {
    let versions = vec![
        CrateVersion {
            name: "treexml".into(),
            version: "0.3.1".parse().unwrap(),
            yanked: true,
        },
        CrateVersion {
            name: "true".into(),
            version: "0.3.0".parse().unwrap(),
            yanked: false,
        },
    ];
    assert_eq!(
        read_latest_version(&versions, false)
            .unwrap()
            .version()
            .unwrap(),
        "0.3.0"
    );
}

#[test]
fn get_no_latest_version_from_json_when_all_are_yanked() {
    let versions = vec![
        CrateVersion {
            name: "treexml".into(),
            version: "0.3.1".parse().unwrap(),
            yanked: true,
        },
        CrateVersion {
            name: "true".into(),
            version: "0.3.0".parse().unwrap(),
            yanked: true,
        },
    ];
    assert!(read_latest_version(&versions, false).is_err());
}

/// Fuzzy query crate from registry index
fn fuzzy_query_registry_index(
    crate_name: impl Into<String>,
    registry: &Url,
) -> Result<Vec<CrateVersion>> {
    let index = crates_index::Index::from_url(registry.as_str())?;

    let crate_name = crate_name.into();
    let mut names = gen_fuzzy_crate_names(crate_name.clone())?;
    if let Some(index) = names.iter().position(|x| *x == crate_name) {
        // ref: https://github.com/killercup/cargo-edit/pull/317#discussion_r307365704
        names.swap(index, 0);
    }

    for the_name in names {
        let crate_ = match index.crate_(&the_name) {
            Some(crate_) => crate_,
            None => continue,
        };
        return crate_
            .versions()
            .iter()
            .map(|v| {
                Ok(CrateVersion {
                    name: v.name().to_owned(),
                    version: v.version().parse()?,
                    yanked: v.is_yanked(),
                })
            })
            .collect();
    }
    Err(ErrorKind::NoCrate(crate_name).into())
}

fn get_crate_name_from_repository<T>(repo: &str, matcher: &Regex, url_template: T) -> Result<String>
where
    T: Fn(&str, &str) -> String,
{
    matcher
        .captures(repo)
        .ok_or_else(|| "Unable to parse git repo URL".into())
        .and_then(|cap| match (cap.get(1), cap.get(2)) {
            (Some(user), Some(repo)) => {
                let url = url_template(user.as_str(), repo.as_str());
                let data: Result<Manifest> = get_cargo_toml_from_git_url(&url)
                    .and_then(|m| m.parse().chain_err(|| ErrorKind::ParseCargoToml));
                data.and_then(|ref manifest| {
                    manifest
                        .package_name()
                        .map(std::string::ToString::to_string)
                })
            }
            _ => Err("Git repo url seems incomplete".into()),
        })
}

/// Query crate name by accessing a github repo Cargo.toml
///
/// The name will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - Cargo.toml is not present in the root of the master branch,
/// - the response from github is an error or in an incorrect format.
pub fn get_crate_name_from_github(repo: &str) -> Result<String> {
    let re =
        Regex::new(r"^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$").unwrap();
    get_crate_name_from_repository(repo, &re, |user, repo| {
        format!(
            "https://raw.githubusercontent.com/{user}/{repo}/master/Cargo.toml",
            user = user,
            repo = repo
        )
    })
}

/// Query crate name by accessing a gitlab repo Cargo.toml
///
/// The name will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - Cargo.toml is not present in the root of the master branch,
/// - the response from gitlab is an error or in an incorrect format.
pub fn get_crate_name_from_gitlab(repo: &str) -> Result<String> {
    let re =
        Regex::new(r"^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$").unwrap();
    get_crate_name_from_repository(repo, &re, |user, repo| {
        format!(
            "https://gitlab.com/{user}/{repo}/raw/master/Cargo.toml",
            user = user,
            repo = repo
        )
    })
}

/// Query crate name by accessing Cargo.toml in a local path
///
/// The name will be returned as a string. This will fail, when
/// Cargo.toml is not present in the root of the path.
pub fn get_crate_name_from_path(path: &str) -> Result<String> {
    let cargo_file = Path::new(path).join("Cargo.toml");
    LocalManifest::try_new(&cargo_file)
        .chain_err(|| "Unable to open local Cargo.toml")
        .and_then(|ref manifest| {
            manifest
                .package_name()
                .map(std::string::ToString::to_string)
        })
}

fn get_cargo_toml_from_git_url(url: &str) -> Result<String> {
    let mut req = ureq::get(url);
    req.timeout(get_default_timeout());
    if let Some(proxy) = env_proxy::for_url_str(url)
        .to_url()
        .and_then(|url| ureq::Proxy::new(url).ok())
    {
        req.set_proxy(proxy);
    }
    let res = req.call();
    if res.error() {
        return Err(format!(
            "HTTP request `{}` failed: {}",
            url,
            res.synthetic_error()
                .as_ref()
                .map(|x| x.to_string())
                .unwrap_or_else(|| res.status().to_string())
        )
        .into());
    }

    res.into_string()
        .chain_err(|| "Git response not a valid `String`")
}

const fn get_default_timeout() -> Duration {
    Duration::from_secs(10)
}

/// Generate all similar crate names
///
/// Examples:
///
/// | input | output |
/// | ----- | ------ |
/// | cargo | cargo  |
/// | cargo-edit | cargo-edit, cargo_edit |
/// | parking_lot_core | parking_lot_core, parking_lot-core, parking-lot_core, parking-lot-core |
fn gen_fuzzy_crate_names(crate_name: String) -> Result<Vec<String>> {
    const PATTERN: [u8; 2] = [b'-', b'_'];

    let wildcard_indexs = crate_name
        .bytes()
        .enumerate()
        .filter(|(_, item)| PATTERN.contains(item))
        .map(|(index, _)| index)
        .take(10)
        .collect::<Vec<usize>>();
    if wildcard_indexs.is_empty() {
        return Ok(vec![crate_name]);
    }

    let mut result = vec![];
    let mut bytes = crate_name.into_bytes();
    for mask in 0..2u128.pow(wildcard_indexs.len() as u32) {
        for (mask_index, wildcard_index) in wildcard_indexs.iter().enumerate() {
            let mask_value = (mask >> mask_index) & 1 == 1;
            if mask_value {
                bytes[*wildcard_index] = b'-';
            } else {
                bytes[*wildcard_index] = b'_';
            }
        }
        result.push(String::from_utf8(bytes.clone()).unwrap());
    }
    Ok(result)
}

#[test]
fn test_gen_fuzzy_crate_names() {
    fn test_helper(input: &str, expect: &[&str]) {
        let mut actual = gen_fuzzy_crate_names(input.to_string()).unwrap();
        actual.sort();

        let mut expect = expect.iter().map(|x| x.to_string()).collect::<Vec<_>>();
        expect.sort();

        assert_eq!(actual, expect);
    }

    test_helper("", &[""]);
    test_helper("-", &["_", "-"]);
    test_helper("DCjanus", &["DCjanus"]);
    test_helper("DC-janus", &["DC-janus", "DC_janus"]);
    test_helper(
        "DC-_janus",
        &["DC__janus", "DC_-janus", "DC-_janus", "DC--janus"],
    );
}
<|endoftext|>


<commit_before>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

extern crate docopt;
extern crate lrcat;
extern crate serde;
#[macro_use]
extern crate serde_derive;

use std::collections::BTreeMap;
use std::path::PathBuf;

use docopt::Docopt;

use lrcat::{Catalog, CatalogVersion, Collection, Folders, Image, Keyword, KeywordTree,
            LibraryFile, LrId, LrObject};

const USAGE: &'static str = "
Usage:
  dumper <command> ([--all] | [--collections] [--libfiles] [--images] [--folders] [--keywords]) <path>

Options:
    --all          Select all objects
    --collections  Select only collections
    --libfiles     Select only library files
    --images       Select only images
    --folders      Select only folders
    --keywords     Select only keywords

Commands are:
    dump           Dump the objects
    audit          Audit mode: output what we ignored
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_command: Command,
    arg_path: PathBuf,
    flag_all: bool,
    flag_collections: bool,
    flag_libfiles: bool,
    flag_images: bool,
    flag_folders: bool,
    flag_keywords: bool,
}

#[derive(Debug, Deserialize)]
enum Command {
    Dump,
    Audit,
    Unknown(String),
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.argv(std::env::args()).deserialize())
        .unwrap_or_else(|e| e.exit());
    {
        match args.arg_command {
            Command::Dump => process_dump(&args),
            Command::Audit => process_audit(&args),
            _ => (),
        };
    }
}

fn process_dump(args: &Args) {
    let mut catalog = Catalog::new(&args.arg_path);
    if catalog.open() {
        catalog.load_version();
        println!("Catalog:");
        println!(
            "\tVersion: {} ({:?})",
            catalog.version, catalog.catalog_version
        );
        println!("\tRoot keyword id: {}", catalog.root_keyword_id);

        match catalog.catalog_version {
            CatalogVersion::Lr4 | CatalogVersion::Lr6 => {}
            _ => {
                println!("Unsupported catalog version");
                return;
            }
        }

        {
            let root_keyword_id = catalog.root_keyword_id;
            let keywordtree = catalog.load_keywords_tree();
            let keywords = catalog.load_keywords();
            println!("\tKeywords count: {}", keywords.len());

            if args.flag_all || args.flag_keywords {
                dump_keywords(root_keyword_id, &keywords, &keywordtree);
            }
        }

        {
            let folders = catalog.load_folders();
            if args.flag_all || args.flag_folders {
                dump_folders(&folders);
            }
        }

        {
            let libfiles = catalog.load_library_files();
            if args.flag_all || args.flag_libfiles {
                dump_libfiles(&libfiles);
            }
        }
        {
            let images = catalog.load_images();
            if args.flag_all || args.flag_images {
                dump_images(&images);
            }
        }
        {
            let collections = catalog.load_collections();
            if args.flag_all || args.flag_collections {
                dump_collections(&collections);
            }
        }
    }
}

fn print_keyword(level: i32, id: LrId, keywords: &BTreeMap<i64, Keyword>, tree: &KeywordTree) {
    if let Some(keyword) = keywords.get(&id) {
        let mut indent = String::from("");
        if level > 0 {
            for _ in 0..level - 1 {
                indent.push(' ');
            }
            indent.push_str("+ ")
        }
        println!(
            "| {:>7} | {} | {:>7} | {}{}",
            keyword.id(),
            keyword.uuid(),
            keyword.parent,
            indent,
            keyword.name
        );
        let children = tree.children_for(id);
        for child in children {
            print_keyword(level + 1, child, keywords, tree);
        }
    }
}

fn dump_keywords(root: LrId, keywords: &BTreeMap<i64, Keyword>, tree: &KeywordTree) {
    println!("Keywords");
    println!(
        "+---------+--------------------------------------+---------+----------------------------"
    );
    println!("| id      | uuid                                 | parent  | name");
    println!(
        "+---------+--------------------------------------+---------+----------------------------"
    );
    print_keyword(0, root, keywords, tree);
    println!(
        "+---------+--------------------------------------+---------+----------------------------"
    );
}

fn dump_folders(folders: &Folders) {
    println!("Root Folders");
    println!("+---------+--------------------------------------+------------------+----------------------------");
    println!("| id      | uuid                                 | name             | absolute path");
    println!("+---------+--------------------------------------+------------------+----------------------------");
    for root in &folders.roots {
        println!(
            "| {:>7} | {} | {:<16} | {:<26}",
            root.id(),
            root.uuid(),
            root.name,
            root.absolute_path
        );
    }
    println!("+---------+--------------------------------------+------------------+----------------------------");
    println!("Folders");
    println!("+---------+--------------------------------------+--------+-----------------------------+----------");
    println!(
        "| id      | uuid                                 | root   | path                        |"
    );
    println!("+---------+--------------------------------------+--------+-----------------------------+----------");
    for folder in &folders.folders {
        println!(
            "| {:>7} | {} | {:>7} | {:<26} | {:?}",
            folder.id(),
            folder.uuid(),
            folder.root_folder,
            folder.path_from_root,
            folder.content
        );
    }
    println!("+---------+--------------------------------------+--------+-----------------------------+----------");
}

fn dump_libfiles(libfiles: &Vec<LibraryFile>) {
    println!("Libfiles");
    println!("+---------+--------------------------------------+---------+--------+---------------------+----------+");
    println!("| id      | uuid                                 | folder  | extens | basename            | sidecars |");
    println!("+---------+--------------------------------------+---------+--------+---------------------+----------+");
    for libfile in libfiles {
        println!(
            "| {:>7} | {} | {:>7} | {:<6} | {:<19} | {:<8} |",
            libfile.id(),
            libfile.uuid(),
            libfile.folder,
            libfile.extension,
            libfile.basename,
            libfile.sidecar_extensions
        );
    }
    println!("+---------+--------------------------------------+---------+--------+---------------------+----------+");
}

fn dump_images(images: &Vec<Image>) {
    println!("Images");
    println!("+---------+--------------------------------------+---------+--------+-------+----+");
    println!("| id      | uuid                                 | root    | format | or    | P  |");
    println!("+---------+--------------------------------------+---------+--------+-------+----+");
    for image in images {
        println!(
            "| {:>7} | {} | {:>7} | {:<6} | {:<2}({}) | {:>2} |",
            image.id(),
            image.uuid(),
            image.root_file,
            image.file_format,
            image.orientation.as_ref().unwrap_or(&String::new()),
            image.exif_orientation(),
            image.pick
        );
    }
    println!("+---------+--------------------------------------+---------+--------+-------+----+");
}

fn dump_collections(collections: &Vec<Collection>) {
    println!("Collections");
    println!("+---------+--------------------------------------+---------+------");
    println!("| id      | name                                 | parent  |");
    println!("+---------+--------------------------------------+---------+------");
    for collection in collections {
        println!(
            "| {:>7} | {:<36} | {:>7} | {}",
            collection.id(),
            collection.name,
            collection.parent,
            collection.system_only
        )
    }
    println!("+---------+--------------------------------------+---------+------");
}

fn process_audit(_: &Args) {}
<commit_msg>dumper: dump content for collections<commit_after>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

extern crate docopt;
extern crate lrcat;
extern crate serde;
#[macro_use]
extern crate serde_derive;

use std::collections::BTreeMap;
use std::path::PathBuf;

use docopt::Docopt;

use lrcat::{Catalog, CatalogVersion, Collection, Folders, Image, Keyword, KeywordTree,
            LibraryFile, LrId, LrObject};

const USAGE: &'static str = "
Usage:
  dumper <command> ([--all] | [--collections] [--libfiles] [--images] [--folders] [--keywords]) <path>

Options:
    --all          Select all objects
    --collections  Select only collections
    --libfiles     Select only library files
    --images       Select only images
    --folders      Select only folders
    --keywords     Select only keywords

Commands are:
    dump           Dump the objects
    audit          Audit mode: output what we ignored
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_command: Command,
    arg_path: PathBuf,
    flag_all: bool,
    flag_collections: bool,
    flag_libfiles: bool,
    flag_images: bool,
    flag_folders: bool,
    flag_keywords: bool,
}

#[derive(Debug, Deserialize)]
enum Command {
    Dump,
    Audit,
    Unknown(String),
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.argv(std::env::args()).deserialize())
        .unwrap_or_else(|e| e.exit());
    {
        match args.arg_command {
            Command::Dump => process_dump(&args),
            Command::Audit => process_audit(&args),
            _ => (),
        };
    }
}

fn process_dump(args: &Args) {
    let mut catalog = Catalog::new(&args.arg_path);
    if catalog.open() {
        catalog.load_version();
        println!("Catalog:");
        println!(
            "\tVersion: {} ({:?})",
            catalog.version, catalog.catalog_version
        );
        println!("\tRoot keyword id: {}", catalog.root_keyword_id);

        match catalog.catalog_version {
            CatalogVersion::Lr4 | CatalogVersion::Lr6 => {}
            _ => {
                println!("Unsupported catalog version");
                return;
            }
        }

        {
            let root_keyword_id = catalog.root_keyword_id;
            let keywordtree = catalog.load_keywords_tree();
            let keywords = catalog.load_keywords();
            println!("\tKeywords count: {}", keywords.len());

            if args.flag_all || args.flag_keywords {
                dump_keywords(root_keyword_id, &keywords, &keywordtree);
            }
        }

        {
            let folders = catalog.load_folders();
            if args.flag_all || args.flag_folders {
                dump_folders(&folders);
            }
        }

        {
            let libfiles = catalog.load_library_files();
            if args.flag_all || args.flag_libfiles {
                dump_libfiles(&libfiles);
            }
        }
        {
            let images = catalog.load_images();
            if args.flag_all || args.flag_images {
                dump_images(&images);
            }
        }
        {
            let collections = catalog.load_collections();
            if args.flag_all || args.flag_collections {
                dump_collections(&collections);
            }
        }
    }
}

fn print_keyword(level: i32, id: LrId, keywords: &BTreeMap<i64, Keyword>, tree: &KeywordTree) {
    if let Some(keyword) = keywords.get(&id) {
        let mut indent = String::from("");
        if level > 0 {
            for _ in 0..level - 1 {
                indent.push(' ');
            }
            indent.push_str("+ ")
        }
        println!(
            "| {:>7} | {} | {:>7} | {}{}",
            keyword.id(),
            keyword.uuid(),
            keyword.parent,
            indent,
            keyword.name
        );
        let children = tree.children_for(id);
        for child in children {
            print_keyword(level + 1, child, keywords, tree);
        }
    }
}

fn dump_keywords(root: LrId, keywords: &BTreeMap<i64, Keyword>, tree: &KeywordTree) {
    println!("Keywords");
    println!(
        "+---------+--------------------------------------+---------+----------------------------"
    );
    println!("| id      | uuid                                 | parent  | name");
    println!(
        "+---------+--------------------------------------+---------+----------------------------"
    );
    print_keyword(0, root, keywords, tree);
    println!(
        "+---------+--------------------------------------+---------+----------------------------"
    );
}

fn dump_folders(folders: &Folders) {
    println!("Root Folders");
    println!("+---------+--------------------------------------+------------------+----------------------------");
    println!("| id      | uuid                                 | name             | absolute path");
    println!("+---------+--------------------------------------+------------------+----------------------------");
    for root in &folders.roots {
        println!(
            "| {:>7} | {} | {:<16} | {:<26}",
            root.id(),
            root.uuid(),
            root.name,
            root.absolute_path
        );
    }
    println!("+---------+--------------------------------------+------------------+----------------------------");
    println!("Folders");
    println!("+---------+--------------------------------------+--------+-----------------------------+----------");
    println!(
        "| id      | uuid                                 | root   | path                        |"
    );
    println!("+---------+--------------------------------------+--------+-----------------------------+----------");
    for folder in &folders.folders {
        println!(
            "| {:>7} | {} | {:>7} | {:<26} | {:?}",
            folder.id(),
            folder.uuid(),
            folder.root_folder,
            folder.path_from_root,
            folder.content
        );
    }
    println!("+---------+--------------------------------------+--------+-----------------------------+----------");
}

fn dump_libfiles(libfiles: &Vec<LibraryFile>) {
    println!("Libfiles");
    println!("+---------+--------------------------------------+---------+--------+---------------------+----------+");
    println!("| id      | uuid                                 | folder  | extens | basename            | sidecars |");
    println!("+---------+--------------------------------------+---------+--------+---------------------+----------+");
    for libfile in libfiles {
        println!(
            "| {:>7} | {} | {:>7} | {:<6} | {:<19} | {:<8} |",
            libfile.id(),
            libfile.uuid(),
            libfile.folder,
            libfile.extension,
            libfile.basename,
            libfile.sidecar_extensions
        );
    }
    println!("+---------+--------------------------------------+---------+--------+---------------------+----------+");
}

fn dump_images(images: &Vec<Image>) {
    println!("Images");
    println!("+---------+--------------------------------------+---------+--------+-------+----+");
    println!("| id      | uuid                                 | root    | format | or    | P  |");
    println!("+---------+--------------------------------------+---------+--------+-------+----+");
    for image in images {
        println!(
            "| {:>7} | {} | {:>7} | {:<6} | {:<2}({}) | {:>2} |",
            image.id(),
            image.uuid(),
            image.root_file,
            image.file_format,
            image.orientation.as_ref().unwrap_or(&String::new()),
            image.exif_orientation(),
            image.pick
        );
    }
    println!("+---------+--------------------------------------+---------+--------+-------+----+");
}

fn dump_collections(collections: &Vec<Collection>) {
    println!("Collections");
    println!("+---------+--------------------------------------+---------+-------+----------------------");
    println!("| id      | name                                 | parent  | syst  | content");
    println!("+---------+--------------------------------------+---------+-------+----------------------");
    for collection in collections {
        println!(
            "| {:>7} | {:<36} | {:>7} | {:<5} | {:?}",
            collection.id(),
            collection.name,
            collection.parent,
            collection.system_only,
            collection.content
        )
    }
    println!("+---------+--------------------------------------+---------+-------+----------------------");
}

fn process_audit(_: &Args) {}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use url::Url;
use util::{CargoResult,ProcessBuilder,io_error,human_error,process};
use std::str;
use std::io::{UserDir,AllPermissions};
use std::io::fs::{mkdir_recursive,rmdir_recursive,chmod};
use serialize::{Encodable,Encoder};

macro_rules! git(
    ($config:expr, $str:expr, $($rest:expr),*) => (
        try!(git_inherit(&$config, format!($str, $($rest),*)))
    );

    ($config:expr, $str:expr) => (
        try!(git_inherit(&$config, format!($str)))
    );
)

macro_rules! git_output(
    ($config:expr, $str:expr, $($rest:expr),*) => (
        try!(git_output(&$config, format!($str, $($rest),*)))
    );

    ($config:expr, $str:expr) => (
        try!(git_output(&$config, format!($str)))
    );
)

#[deriving(Eq,Clone)]
struct GitConfig {
    path: Path,
    uri: Url,
    reference: String
}

#[deriving(Eq,Clone,Encodable)]
struct EncodableGitConfig {
    path: String,
    uri: String,
    reference: String
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitConfig {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitConfig {
            path: self.path.display().to_str(),
            uri: self.uri.to_str(),
            reference: self.reference.clone()
        }.encode(s)
    }
}

#[deriving(Eq,Clone)]
pub struct GitCommand {
    config: GitConfig
}

#[deriving(Eq,Clone,Encodable)]
pub struct GitRepo {
    config: GitConfig,
    revision: String
}

impl GitCommand {
    pub fn new(path: Path, uri: Url, reference: String) -> GitCommand {
        GitCommand { config: GitConfig { path: path, uri: uri, reference: reference } }
    }

    pub fn checkout(&self) -> CargoResult<GitRepo> {
        let config = &self.config;

        if config.path.exists() {
            git!(*config, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", config.uri);
        } else {
            let dirname = Path::new(config.path.dirname());
            let mut checkout_config = self.config.clone();
            checkout_config.path = dirname;

            try!(mkdir_recursive(&checkout_config.path, UserDir).map_err(|err|
                human_error(format!("Couldn't recursively create `{}`", checkout_config.path.display()), format!("path={}", checkout_config.path.display()), io_error(err))));

            git!(checkout_config, "clone {} {} --bare --no-hardlinks --quiet", config.uri, config.path.display());
        }

        Ok(GitRepo { config: config.clone(), revision: try!(rev_for(config)) })
    }
}

impl GitRepo {
    #[allow(unused_variable)]
    fn copy_to(destination: &Path) -> CargoResult<()> {
        Ok(())
    }

    fn clone_to(&self, destination: &Path) -> CargoResult<()> {
        try!(mkdir_recursive(&Path::new(destination.dirname()), UserDir).map_err(io_error));
        try!(rmdir_recursive(destination).map_err(io_error));
        git!(self.config, "clone --no-checkout --quiet {} {}", self.config.path.display(), destination.display());
        try!(chmod(destination, AllPermissions).map_err(io_error));

        let mut dest_config = self.config.clone();
        dest_config.path = destination.clone();

        git!(dest_config, "fetch --force --quiet --tags {}", self.config.path.display());
        git!(dest_config, "reset --hard {}", self.revision);
        git!(dest_config, "submodule update --init --recursive");

        Ok(())
    }
}

fn rev_for(config: &GitConfig) -> CargoResult<String> {
    Ok(git_output!(*config, "rev-parse {}", config.reference))
}

fn git(config: &GitConfig, str: &str) -> ProcessBuilder {
    println!("Executing git {} @ {}", str, config.path.display());
    process("git").args(str.split(' ').collect::<Vec<&str>>().as_slice()).cwd(config.path.clone())
}

fn git_inherit(config: &GitConfig, str: String) -> CargoResult<()> {
    git(config, str.as_slice()).exec().map_err(|err|
        human_error(format!("Couldn't execute `git {}`: {}", str, err), None::<&str>, err))
}

fn git_output(config: &GitConfig, str: String) -> CargoResult<String> {
    let output = try!(git(config, str.as_slice()).exec_with_output().map_err(|err|
        human_error(format!("Couldn't execute `git {}`", str), None::<&str>, err)));

    Ok(to_str(output.output.as_slice()))
}

fn to_str(vec: &[u8]) -> String {
    str::from_utf8_lossy(vec).to_str()
}
<commit_msg>Add support for updating git repos<commit_after>#![allow(dead_code)]

use url::Url;
use util::{CargoResult,ProcessBuilder,io_error,human_error,process};
use std::fmt::Show;
use std::str;
use std::io::{UserDir,AllPermissions};
use std::io::fs::{mkdir_recursive,rmdir_recursive,chmod};
use serialize::{Encodable,Encoder};

macro_rules! git(
    ($config:expr, $str:expr, $($rest:expr),*) => (
        try!(git_inherit(&$config, format!($str, $($rest),*)))
    );

    ($config:expr, $str:expr) => (
        try!(git_inherit(&$config, format!($str)))
    );
)

macro_rules! git_output(
    ($config:expr, $str:expr, $($rest:expr),*) => (
        try!(git_output(&$config, format!($str, $($rest),*)))
    );

    ($config:expr, $str:expr) => (
        try!(git_output(&$config, format!($str)))
    );
)

#[deriving(Eq,Clone)]
struct GitConfig {
    path: Path,
    uri: Url,
    reference: String
}

#[deriving(Eq,Clone,Encodable)]
struct EncodableGitConfig {
    path: String,
    uri: String,
    reference: String
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitConfig {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitConfig {
            path: self.path.display().to_str(),
            uri: self.uri.to_str(),
            reference: self.reference.clone()
        }.encode(s)
    }
}

#[deriving(Eq,Clone)]
pub struct GitCommand {
    config: GitConfig
}

#[deriving(Eq,Clone,Encodable)]
pub struct GitRepo {
    config: GitConfig,
    revision: String
}

impl GitCommand {
    pub fn new(path: Path, uri: Url, reference: String) -> GitCommand {
        GitCommand { config: GitConfig { path: path, uri: uri, reference: reference } }
    }

    pub fn get_cwd<'a>(&'a self) -> &'a Path {
        &self.config.path
    }

    pub fn checkout(&self) -> CargoResult<GitRepo> {
        if self.config.path.exists() {
            // TODO: If the revision we have is a rev, avoid unnecessarily fetching if we have the rev already
            try!(self.fetch());
        } else {
            try!(self.clone());
        }

        Ok(GitRepo { config: self.config.clone(), revision: try!(rev_for(&self.config)) })
    }

    fn fetch(&self) -> CargoResult<()> {
        Ok(git!(self.config.path, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", self.config.uri))
    }

    fn clone(&self) -> CargoResult<()> {
        let dirname = Path::new(self.config.path.dirname());

        try!(mkdir_recursive(&self.config.path, UserDir).map_err(|err|
            human_error(format!("Couldn't recursively create `{}`", dirname.display()), format!("path={}", dirname.display()), io_error(err))));

        Ok(git!(dirname, "clone {} {} --bare --no-hardlinks --quiet", self.config.uri, self.config.path.display()))
    }
}

struct GitCheckout<'a> {
    location: Path,
    repo: &'a GitRepo
}

impl<'a> GitCheckout<'a> {
    fn clone<'a>(into: Path, repo: &'a GitRepo) -> CargoResult<GitCheckout<'a>> {
        let checkout = GitCheckout { location: into, repo: repo };

        // If the git checkout already exists, we don't need to clone it again
        if !checkout.location.join(".git").exists() {
            try!(checkout.clone_repo());
        }

        Ok(checkout)
    }

    fn get_source<'a>(&'a self) -> &'a Path {
        self.repo.get_path()
    }

    fn clone_repo(&self) -> CargoResult<()> {
        try!(mkdir_recursive(&Path::new(self.location.dirname()), UserDir).map_err(io_error));
        try!(rmdir_recursive(&self.location).map_err(io_error));

        git!(self.location, "clone --no-checkout --quiet {} {}", self.get_source().display(), self.location.display());
        try!(chmod(&self.location, AllPermissions).map_err(io_error));

        Ok(())
    }

    fn fetch(&self) -> CargoResult<()> {
        Ok(git!(self.location, "fetch --force --quiet --tags {}", self.get_source().display()))
    }

    fn reset<T: Show>(&self, revision: T) -> CargoResult<()> {
        Ok(git!(self.location, "reset --hard {}", revision))
    }

    fn update_submodules(&self) -> CargoResult<()> {
        Ok(git!(self.location, "submodule update --init --recursive"))
    }
}

impl GitRepo {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.config.path
    }

    #[allow(unused_variable)]
    fn copy_to<'a>(&'a self, dest: Path) -> CargoResult<GitCheckout<'a>> {
        let checkout = try!(GitCheckout::clone(dest, self));

        try!(checkout.fetch());
        try!(checkout.reset(self.revision.as_slice()));
        try!(checkout.update_submodules());

        Ok(checkout)
    }

    fn clone_to(&self, destination: &Path) -> CargoResult<()> {
        try!(mkdir_recursive(&Path::new(destination.dirname()), UserDir).map_err(io_error));
        try!(rmdir_recursive(destination).map_err(io_error));
        git!(self.config.path, "clone --no-checkout --quiet {} {}", self.config.path.display(), destination.display());
        try!(chmod(destination, AllPermissions).map_err(io_error));

        git!(*destination, "fetch --force --quiet --tags {}", self.config.path.display());
        git!(*destination, "reset --hard {}", self.revision);
        git!(*destination, "submodule update --init --recursive");

        Ok(())
    }
}

fn rev_for(config: &GitConfig) -> CargoResult<String> {
    Ok(git_output!(config.path, "rev-parse {}", config.reference))
}

#[allow(dead_code)]
fn has_rev<T: Show>(path: &Path, rev: T) -> bool {
    git_output(path, format!("cat-file -e {}", rev)).is_ok()
}

fn git(path: &Path, str: &str) -> ProcessBuilder {
    println!("Executing git {} @ {}", str, path.display());
    process("git").args(str.split(' ').collect::<Vec<&str>>().as_slice()).cwd(path.clone())
}

fn git_inherit(path: &Path, str: String) -> CargoResult<()> {
    git(path, str.as_slice()).exec().map_err(|err|
        human_error(format!("Couldn't execute `git {}`: {}", str, err), None::<&str>, err))
}

fn git_output(path: &Path, str: String) -> CargoResult<String> {
    let output = try!(git(path, str.as_slice()).exec_with_output().map_err(|err|
        human_error(format!("Couldn't execute `git {}`", str), None::<&str>, err)));

    Ok(to_str(output.output.as_slice()))
}

fn to_str(vec: &[u8]) -> String {
    str::from_utf8_lossy(vec).to_str()
}
<|endoftext|>


<commit_before>use item_state::ItemState;
use bar_builder::UpdateStream;
use std::rc::Rc;
use xcb::{Connection, Rectangle, Window};
use futures::stream::{Merge, MergedItem};
use futures::{Future, Stream};
use xcb_event_stream::XcbEventStream;
use error::*;
use std::error::Error;
use xcb;
use component::Slot;

type UpdateAndEventStream = Merge<UpdateStream, XcbEventStream>;

/// Struct that contains everything needed to run the bar.
pub struct Bar {
    pub center_items: Vec<ItemState>,
    pub conn: Rc<Connection>,
    pub foreground: u32,
    pub geometry: Rectangle,
    pub item_positions: Vec<(u16, u16)>,
    pub left_items: Vec<ItemState>,
    pub right_items: Vec<ItemState>,
    pub stream: Option<UpdateAndEventStream>,
    pub window: Window,
    pub inner_padding: u16,
}

impl Bar {
    /// Returns `self.stream` without borrowing or consuming `self`.
    /// Panics if called twice.
    fn get_stream(&mut self) -> UpdateAndEventStream {
        ::std::mem::replace(&mut self.stream, None).unwrap()
    }

    /// Launch and run the bar.
    pub fn run(mut self) -> Box<Future<Item = (), Error = ()>> {
        let future = self.get_stream()
            .map_err(|e| ::error::Error::with_chain(e, ErrorKind::ItemError))
            .for_each(move |item| -> Result<()> {
                let (_, update) = match item {
                    MergedItem::First(update) => (None, Some(update)),
                    MergedItem::Second(event) => (Some(event), None),
                    MergedItem::Both(update, event) => (Some(event), Some(update)),
                };

                if let Some(update) = update {
                    let size_changed;

                    // Figure out if the component has chanaged
                    // it's size since the last update.
                    // TODO: Move to a function
                    {
                        let slot_items = match update.slot {
                            Slot::Left => &mut self.left_items,
                            Slot::Center => &mut self.center_items,
                            Slot::Right => &mut self.right_items,
                        };
                        slot_items[update.index].update(update.value)?;

                        let width = slot_items[update.index].get_content_width();
                        size_changed = self.item_positions[update.id].1 != width;
                    }

                    // Redraw only neccessary stuff
                    match update.slot {
                        Slot::Center => self.redraw_center()?,
                        Slot::Left => self.redraw_left(size_changed, update.index)?,
                        Slot::Right => self.redraw_right(size_changed, update.index)?,
                    }
                }
                Ok(())
            })
            .map_err(|err| {
                println!("Error occurred: {}", err);
                let mut cause = err.cause();
                while let Some(err) = cause {
                    println!("  Caused by: {}", err);
                    cause = err.cause();
                }
            });

        Box::new(future)
    }

    /// Redraws components in the center slot.
    /// Unforunately centering means that all components
    /// must be redrawn if even one of them changes size.
    fn redraw_center(&mut self) -> Result<()> {
        // Draw blank background to prevent leftovers after shrinkage
        if let Some(first) = self.center_items.first() {
            let old_start = self.item_positions[first.get_id()].0;
            let old_width_all: u16 = self.center_items
                .iter()
                .map(|item| self.item_positions[item.get_id()].1)
                .sum();
            self.paint_bg(old_start, old_start + old_width_all)?;
        }

        let width_all: u16 = self.center_items
            .iter()
            .map(|item| item.get_content_width())
            .sum();

        let mut pos = (self.geometry.width()) / 2 - width_all / 2;

        for item in self.center_items.iter() {
            self.item_positions[item.get_id()].0 = pos;
            self.draw_item(item, pos)?;
            self.item_positions[item.get_id()].1 = item.get_content_width();
            pos += item.get_content_width();
        }

        Ok(())
    }

    /// Pretty much the same as `self.redraw_left` but with `left` replaced with `right`.
    /// The order in which the items are gone through is reversed.
    fn redraw_right(&mut self, size_changed: bool, index: usize) -> Result<()> {
        let mut pos = self.geometry.width() - self.inner_padding;

        for n in 0..self.right_items.len() {
            let item = &self.right_items[self.right_items.len() - n - 1];

            pos -= item.get_content_width();

            if n < self.right_items.len() - index - 1 {
                continue;
            }

            if size_changed {
                let mut bg_start = pos;
                let bg_end = pos + item.get_content_width();

                if n == self.right_items.len() - 1 {
                    let old_start = self.item_positions[item.get_id()].0 as u16;
                    if old_start < bg_start && old_start > 0 {
                        bg_start = old_start;
                    }
                }

                self.paint_bg(bg_start, bg_end)?;
            }

            self.item_positions[item.get_id()].0 = pos;
            self.item_positions[item.get_id()].1 = item.get_content_width();
            self.draw_item(item, pos)?;

            if !size_changed {
                break;
            }
        }

        Ok(())
    }

    /// Redraw only needed items in the right slot.
    /// Symmetric to `self.redraw_left`.
    ///
    /// If the component hasn't changed it's size, it doesn't affect
    /// any other components and we can get away with just painting
    /// the one component.
    ///
    /// However if the component has changed it's size, we must also
    /// redraw every component on the right of it. If the item has shrunk
    /// we must also repaint the exposed background.
    fn redraw_left(&mut self, size_changed: bool, index: usize) -> Result<()> {
        let mut pos = self.inner_padding;

        for n in 0..self.left_items.len() {
            let item = &self.left_items[n];

            if n < index {
                pos += item.get_content_width();
                continue;
            }

            if size_changed {
                let bg_start = pos;
                let mut bg_end = pos + item.get_content_width();

                if n == self.left_items.len() - 1 {
                    let old_end = self.item_positions[item.get_id()].0 +
                        self.item_positions[item.get_id()].1;
                    if bg_end < old_end {
                        bg_end = old_end;
                    }
                }

                self.paint_bg(bg_start, bg_end)?;
            }

            self.item_positions[item.get_id()].0 = pos;
            self.item_positions[item.get_id()].1 = item.get_content_width();
            self.draw_item(item, pos)?;

            if !size_changed {
                break;
            }

            pos += item.get_content_width();
        }

        Ok(())
    }

    /// Copies the item's pixmap to the window.
    fn draw_item(&self, item: &ItemState, pos: u16) -> Result<()> {
        if !item.is_ready() {
            return Ok(());
        }

        try_xcb!(
            xcb::copy_area_checked,
            "failed to copy pixmap",
            &self.conn,
            item.get_pixmap(),
            self.window,
            self.foreground,
            0,
            0,
            pos as i16,
            0,
            item.get_content_width() as u16,
            self.geometry.height()
        );

        Ok(())
    }

    /// Draws the background starting at point a on the x-axis until point b.
    fn paint_bg(&self, a: u16, b: u16) -> Result<()> {
        try_xcb!(
            xcb::poly_fill_rectangle,
            "failed to draw background",
            &self.conn,
            self.window,
            self.foreground,
            &[Rectangle::new(a as i16, 0, b - a, self.geometry.height())]
        );

        Ok(())
    }
}
<commit_msg>Optimize center component redraw<commit_after>use item_state::ItemState;
use bar_builder::UpdateStream;
use std::rc::Rc;
use xcb::{Connection, Rectangle, Window};
use futures::stream::{Merge, MergedItem};
use futures::{Future, Stream};
use xcb_event_stream::XcbEventStream;
use error::*;
use std::error::Error;
use xcb;
use component::Slot;

type UpdateAndEventStream = Merge<UpdateStream, XcbEventStream>;

/// Struct that contains everything needed to run the bar.
pub struct Bar {
    pub center_items: Vec<ItemState>,
    pub conn: Rc<Connection>,
    pub foreground: u32,
    pub geometry: Rectangle,
    pub item_positions: Vec<(u16, u16)>,
    pub left_items: Vec<ItemState>,
    pub right_items: Vec<ItemState>,
    pub stream: Option<UpdateAndEventStream>,
    pub window: Window,
    pub inner_padding: u16,
}

impl Bar {
    /// Returns `self.stream` without borrowing or consuming `self`.
    /// Panics if called twice.
    fn get_stream(&mut self) -> UpdateAndEventStream {
        ::std::mem::replace(&mut self.stream, None).unwrap()
    }

    /// Launch and run the bar.
    pub fn run(mut self) -> Box<Future<Item = (), Error = ()>> {
        let future = self.get_stream()
            .map_err(|e| ::error::Error::with_chain(e, ErrorKind::ItemError))
            .for_each(move |item| -> Result<()> {
                let (_, update) = match item {
                    MergedItem::First(update) => (None, Some(update)),
                    MergedItem::Second(event) => (Some(event), None),
                    MergedItem::Both(update, event) => (Some(event), Some(update)),
                };

                if let Some(update) = update {
                    let size_changed;

                    // Figure out if the component has chanaged
                    // it's size since the last update.
                    // TODO: Move to a function
                    {
                        let slot_items = match update.slot {
                            Slot::Left => &mut self.left_items,
                            Slot::Center => &mut self.center_items,
                            Slot::Right => &mut self.right_items,
                        };
                        slot_items[update.index].update(update.value)?;

                        let width = slot_items[update.index].get_content_width();
                        size_changed = self.item_positions[update.id].1 != width;
                    }

                    // Redraw only neccessary stuff
                    match update.slot {
                        Slot::Center => self.redraw_center()?,
                        Slot::Left => self.redraw_left(size_changed, update.index)?,
                        Slot::Right => self.redraw_right(size_changed, update.index)?,
                    }
                }
                Ok(())
            })
            .map_err(|err| {
                println!("Error occurred: {}", err);
                let mut cause = err.cause();
                while let Some(err) = cause {
                    println!("  Caused by: {}", err);
                    cause = err.cause();
                }
            });

        Box::new(future)
    }

    /// Redraws components in the center slot.
    /// Unforunately centering means that all components
    /// must be redrawn if even one of them changes size.
    fn redraw_center(&mut self) -> Result<()> {
        // Get future width of all center components
        let width_all: u16 = self.center_items
            .iter()
            .map(|item| item.get_content_width())
            .sum();

        // Draw blank background to prevent leftovers after shrinkage
        // Only does this when component width has shrunk
        let old_width_all: u16 = self.center_items
            .iter()
            .map(|item| self.item_positions[item.get_id()].1)
            .sum();
        if width_all < old_width_all {
            if let Some(first) = self.center_items.first() {
                let old_start = self.item_positions[first.get_id()].0;
                self.paint_bg(old_start, old_start + old_width_all)?;
            }
        }

        let mut pos = (self.geometry.width()) / 2 - width_all / 2;

        for item in self.center_items.iter() {
            self.item_positions[item.get_id()].0 = pos;
            self.draw_item(item, pos)?;
            self.item_positions[item.get_id()].1 = item.get_content_width();
            pos += item.get_content_width();
        }

        Ok(())
    }

    /// Pretty much the same as `self.redraw_left` but with `left` replaced with `right`.
    /// The order in which the items are gone through is reversed.
    fn redraw_right(&mut self, size_changed: bool, index: usize) -> Result<()> {
        let mut pos = self.geometry.width() - self.inner_padding;

        for n in 0..self.right_items.len() {
            let item = &self.right_items[self.right_items.len() - n - 1];

            pos -= item.get_content_width();

            if n < self.right_items.len() - index - 1 {
                continue;
            }

            if size_changed {
                let mut bg_start = pos;
                let bg_end = pos + item.get_content_width();

                if n == self.right_items.len() - 1 {
                    let old_start = self.item_positions[item.get_id()].0 as u16;
                    if old_start < bg_start && old_start > 0 {
                        bg_start = old_start;
                    }
                }

                self.paint_bg(bg_start, bg_end)?;
            }

            self.item_positions[item.get_id()].0 = pos;
            self.item_positions[item.get_id()].1 = item.get_content_width();
            self.draw_item(item, pos)?;

            if !size_changed {
                break;
            }
        }

        Ok(())
    }

    /// Redraw only needed items in the right slot.
    /// Symmetric to `self.redraw_left`.
    ///
    /// If the component hasn't changed it's size, it doesn't affect
    /// any other components and we can get away with just painting
    /// the one component.
    ///
    /// However if the component has changed it's size, we must also
    /// redraw every component on the right of it. If the item has shrunk
    /// we must also repaint the exposed background.
    fn redraw_left(&mut self, size_changed: bool, index: usize) -> Result<()> {
        let mut pos = self.inner_padding;

        for n in 0..self.left_items.len() {
            let item = &self.left_items[n];

            if n < index {
                pos += item.get_content_width();
                continue;
            }

            if size_changed {
                let bg_start = pos;
                let mut bg_end = pos + item.get_content_width();

                if n == self.left_items.len() - 1 {
                    let old_end = self.item_positions[item.get_id()].0 +
                        self.item_positions[item.get_id()].1;
                    if bg_end < old_end {
                        bg_end = old_end;
                    }
                }

                self.paint_bg(bg_start, bg_end)?;
            }

            self.item_positions[item.get_id()].0 = pos;
            self.item_positions[item.get_id()].1 = item.get_content_width();
            self.draw_item(item, pos)?;

            if !size_changed {
                break;
            }

            pos += item.get_content_width();
        }

        Ok(())
    }

    /// Copies the item's pixmap to the window.
    fn draw_item(&self, item: &ItemState, pos: u16) -> Result<()> {
        if !item.is_ready() {
            return Ok(());
        }

        try_xcb!(
            xcb::copy_area_checked,
            "failed to copy pixmap",
            &self.conn,
            item.get_pixmap(),
            self.window,
            self.foreground,
            0,
            0,
            pos as i16,
            0,
            item.get_content_width() as u16,
            self.geometry.height()
        );

        Ok(())
    }

    /// Draws the background starting at point a on the x-axis until point b.
    fn paint_bg(&self, a: u16, b: u16) -> Result<()> {
        try_xcb!(
            xcb::poly_fill_rectangle,
            "failed to draw background",
            &self.conn,
            self.window,
            self.foreground,
            &[Rectangle::new(a as i16, 0, b - a, self.geometry.height())]
        );

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::ops::Not;
use std::ops::BitAnd;
use std::ops::BitOr;

#[derive(PartialEq, Debug)]
pub enum Bit { Zero, One }

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

    #[test]
    fn eq_zero_zero_test() {
        assert!(Bit::Zero == Bit::Zero);
    }

    #[test]
    #[should_panic]
    fn eq_zero_one_test() {
        assert!(Bit::Zero == Bit::One);
    }

    #[test]
    #[should_panic]
    fn eq_one_zero_test() {
        assert!(Bit::One == Bit::Zero);
    }

    #[test]
    fn eq_one_one_test() {
        assert!(Bit::One == Bit::One);
    }

    #[test]
    #[should_panic]
    fn neq_zero_zero_test() {
        assert!(Bit::Zero != Bit::Zero);
    }

    #[test]
    fn neq_zero_one_test() {
        assert!(Bit::Zero != Bit::One);
    }

    #[test]
    fn neq_one_zero_test() {
        assert!(Bit::One != Bit::Zero);
    }

    #[test]
    #[should_panic]
    fn neq_one_one_test() {
        assert!(Bit::One != Bit::One);
    }
}

impl Bit {
    pub fn to_i32(&self) -> i32 {
        match *self {
            Bit::Zero => 0,
            Bit::One => 1
        }
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(Bit::Zero.to_i32(), 0);
    }

    #[test]
    fn one_test() {
        assert_eq!(Bit::One.to_i32(), 1);
    }
}

impl Bit {
    pub fn to_u32(&self) -> u32 {
        match *self {
            Bit::Zero => 0u32,
            Bit::One => 1u32
        }
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(Bit::Zero.to_u32(), 0);
    }

    #[test]
    fn one_test() {
        assert_eq!(Bit::One.to_u32(), 1);
    }
}

impl Not for Bit {
    type Output = Bit;

    fn not(self) -> Bit {
        not_impl(&self)
    }
}

fn not_impl(x: &Bit) -> Bit
{
    match *x {
        Bit::Zero => Bit::One,
        Bit::One => Bit::Zero
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(!Bit::Zero, Bit::One);
    }

    #[test]
    fn one_test() {
        assert_eq!(!Bit::One, Bit::Zero);
    }
}

impl<'a> Not for &'a Bit {
    type Output = <Bit as Not>::Output;

    fn not(self) -> <Bit as Not>::Output {
        not_impl(self)
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(!&Bit::Zero, Bit::One);
    }

    #[test]
    fn one_test() {
        assert_eq!(!&Bit::One, Bit::Zero);
    }
}

impl BitAnd<Bit> for Bit {
    type Output = Bit;

    fn bitand(self, rhs: Bit) -> Bit {
        bitand_impl(&self, &rhs)
    }
}

fn bitand_impl(x: &Bit, y: &Bit) -> Bit {
    match (x, y) {
        (&Bit::Zero, &Bit::Zero) => Bit::Zero,
        (&Bit::Zero, &Bit::One) => Bit::Zero,
        (&Bit::One, &Bit::Zero) => Bit::Zero,
        (&Bit::One, &Bit::One) => Bit::One
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One & Bit::One, Bit::One);
    }
}

impl<'a> BitAnd<Bit> for &'a Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(self, &rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One & Bit::One, Bit::One);
    }
}

impl<'a> BitAnd<&'a Bit> for Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: &'a Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(&self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero & &Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One & &Bit::One, Bit::One);
    }
}

impl<'a, 'b> BitAnd<&'a Bit> for &'b Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: &'a Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero & &Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One & &Bit::One, Bit::One);
    }
}

impl BitOr for Bit {
    type Output = Bit;

    fn bitor(self, rhs: Bit) -> Bit {
        bitor_impl(&self, &rhs)
    }
}

fn bitor_impl(x: &Bit, y: &Bit) -> Bit
{
    match (x, y) {
        (&Bit::Zero, &Bit::Zero) => Bit::Zero,
        (&Bit::Zero, &Bit::One) => Bit::One,
        (&Bit::One, &Bit::Zero) => Bit::One,
        (&Bit::One, &Bit::One) => Bit::One
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero | Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero | Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One | Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One | Bit::One, Bit::One);
    }
}

impl<'a> BitOr<Bit> for &'a Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(self, &rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero | Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero | Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One | Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One | Bit::One, Bit::One);
    }
}

impl<'a> BitOr<&'a Bit> for Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: &'a Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(&self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero | &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero | &Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One | &Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One | &Bit::One, Bit::One);
    }
}

impl<'a, 'b> BitOr<&'a Bit> for &'b Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: &'a Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero | &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero | &Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One | &Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One | &Bit::One, Bit::One);
    }
}
<commit_msg>small test rename<commit_after>use std::ops::Not;
use std::ops::BitAnd;
use std::ops::BitOr;

#[derive(PartialEq, Debug)]
pub enum Bit { Zero, One }

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

    #[test]
    fn eq_zero_zero_test() {
        assert!(Bit::Zero == Bit::Zero);
    }

    #[test]
    #[should_panic]
    fn eq_zero_one_test() {
        assert!(Bit::Zero == Bit::One);
    }

    #[test]
    #[should_panic]
    fn eq_one_zero_test() {
        assert!(Bit::One == Bit::Zero);
    }

    #[test]
    fn eq_one_one_test() {
        assert!(Bit::One == Bit::One);
    }

    #[test]
    #[should_panic]
    fn ne_zero_zero_test() {
        assert!(Bit::Zero != Bit::Zero);
    }

    #[test]
    fn ne_zero_one_test() {
        assert!(Bit::Zero != Bit::One);
    }

    #[test]
    fn ne_one_zero_test() {
        assert!(Bit::One != Bit::Zero);
    }

    #[test]
    #[should_panic]
    fn ne_one_one_test() {
        assert!(Bit::One != Bit::One);
    }
}

impl Bit {
    pub fn to_i32(&self) -> i32 {
        match *self {
            Bit::Zero => 0,
            Bit::One => 1
        }
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(Bit::Zero.to_i32(), 0);
    }

    #[test]
    fn one_test() {
        assert_eq!(Bit::One.to_i32(), 1);
    }
}

impl Bit {
    pub fn to_u32(&self) -> u32 {
        match *self {
            Bit::Zero => 0u32,
            Bit::One => 1u32
        }
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(Bit::Zero.to_u32(), 0);
    }

    #[test]
    fn one_test() {
        assert_eq!(Bit::One.to_u32(), 1);
    }
}

impl Not for Bit {
    type Output = Bit;

    fn not(self) -> Bit {
        not_impl(&self)
    }
}

fn not_impl(x: &Bit) -> Bit
{
    match *x {
        Bit::Zero => Bit::One,
        Bit::One => Bit::Zero
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(!Bit::Zero, Bit::One);
    }

    #[test]
    fn one_test() {
        assert_eq!(!Bit::One, Bit::Zero);
    }
}

impl<'a> Not for &'a Bit {
    type Output = <Bit as Not>::Output;

    fn not(self) -> <Bit as Not>::Output {
        not_impl(self)
    }
}

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

    #[test]
    fn zero_test() {
        assert_eq!(!&Bit::Zero, Bit::One);
    }

    #[test]
    fn one_test() {
        assert_eq!(!&Bit::One, Bit::Zero);
    }
}

impl BitAnd<Bit> for Bit {
    type Output = Bit;

    fn bitand(self, rhs: Bit) -> Bit {
        bitand_impl(&self, &rhs)
    }
}

fn bitand_impl(x: &Bit, y: &Bit) -> Bit {
    match (x, y) {
        (&Bit::Zero, &Bit::Zero) => Bit::Zero,
        (&Bit::Zero, &Bit::One) => Bit::Zero,
        (&Bit::One, &Bit::Zero) => Bit::Zero,
        (&Bit::One, &Bit::One) => Bit::One
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One & Bit::One, Bit::One);
    }
}

impl<'a> BitAnd<Bit> for &'a Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(self, &rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One & Bit::One, Bit::One);
    }
}

impl<'a> BitAnd<&'a Bit> for Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: &'a Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(&self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero & &Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One & &Bit::One, Bit::One);
    }
}

impl<'a, 'b> BitAnd<&'a Bit> for &'b Bit {
    type Output = <Bit as BitAnd<Bit>>::Output;

    fn bitand(self, rhs: &'a Bit) -> <Bit as BitAnd<Bit>>::Output {
        bitand_impl(self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero & &Bit::One, Bit::Zero);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One & &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One & &Bit::One, Bit::One);
    }
}

impl BitOr for Bit {
    type Output = Bit;

    fn bitor(self, rhs: Bit) -> Bit {
        bitor_impl(&self, &rhs)
    }
}

fn bitor_impl(x: &Bit, y: &Bit) -> Bit
{
    match (x, y) {
        (&Bit::Zero, &Bit::Zero) => Bit::Zero,
        (&Bit::Zero, &Bit::One) => Bit::One,
        (&Bit::One, &Bit::Zero) => Bit::One,
        (&Bit::One, &Bit::One) => Bit::One
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero | Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero | Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One | Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One | Bit::One, Bit::One);
    }
}

impl<'a> BitOr<Bit> for &'a Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(self, &rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero | Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero | Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One | Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One | Bit::One, Bit::One);
    }
}

impl<'a> BitOr<&'a Bit> for Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: &'a Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(&self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(Bit::Zero | &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(Bit::Zero | &Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(Bit::One | &Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(Bit::One | &Bit::One, Bit::One);
    }
}

impl<'a, 'b> BitOr<&'a Bit> for &'b Bit {
    type Output = <Bit as BitOr<Bit>>::Output;

    fn bitor(self, rhs: &'a Bit) -> <Bit as BitOr<Bit>>::Output {
        bitor_impl(self, rhs)
    }
}

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

    #[test]
    fn zero_zero_test() {
        assert_eq!(&Bit::Zero | &Bit::Zero, Bit::Zero);
    }

    #[test]
    fn zero_one_test() {
        assert_eq!(&Bit::Zero | &Bit::One, Bit::One);
    }

    #[test]
    fn one_zero_test() {
        assert_eq!(&Bit::One | &Bit::Zero, Bit::One);
    }

    #[test]
    fn one_one_test() {
        assert_eq!(&Bit::One | &Bit::One, Bit::One);
    }
}
<|endoftext|>


<commit_before>use super::instance::*;
use super::mem::*;
use super::module::*;
use super::register::*;
use super::signal::*;

use typed_arena::Arena;

use std::cell::RefCell;
use std::collections::BTreeMap;

/// A top-level container/owner object for a [`Module`] graph.
///
/// A `Context` owns all parts of a module graph, and provides an API for creating [`Module`] objects.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("MyModule");
/// m.output("out", m.input("in", 1));
/// ```
///
/// [`Module`]: ./struct.Module.html
#[must_use]
pub struct Context<'a> {
    module_arena: Arena<Module<'a>>,
    pub(super) signal_arena: Arena<Signal<'a>>,
    pub(super) register_data_arena: Arena<RegisterData<'a>>,
    pub(super) register_arena: Arena<Register<'a>>,
    pub(super) instance_arena: Arena<Instance<'a>>,
    pub(super) mem_arena: Arena<Mem<'a>>,

    pub(super) modules: RefCell<BTreeMap<String, &'a Module<'a>>>,
}

impl<'a> Context<'a> {
    /// Creates a new, empty `Context`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    /// ```
    pub fn new() -> Context<'a> {
        Context {
            module_arena: Arena::new(),
            signal_arena: Arena::new(),
            register_data_arena: Arena::new(),
            register_arena: Arena::new(),
            instance_arena: Arena::new(),
            mem_arena: Arena::new(),

            modules: RefCell::new(BTreeMap::new()),
        }
    }

    /// Creates a new [`Module`] called `name` in this `Context`.
    ///
    /// Conventionally, `name` should be `CamelCase`, though this is not enforced.
    ///
    /// # Panics
    ///
    /// Panics if a [`Module`] with the same `name` already exists in this `Context`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let my_module = c.module("MyModule");
    /// let another_mod = c.module("AnotherMod");
    /// ```
    ///
    /// The following example panics by creating a `Module` with the same `name` as a previously-created `Module` in the same `Context`:
    ///
    /// ```should_panic
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let _ = c.module("A"); // Unique name, OK
    /// let _ = c.module("B"); // Unique name, OK
    ///
    /// let _ = c.module("A"); // Non-unique name, panic!
    /// ```
    ///
    /// [`Module`]: ./struct.Module.html
    pub fn module<S: Into<String>>(&'a self, name: S) -> &Module {
        let name = name.into();
        let mut modules = self.modules.borrow_mut();
        if modules.contains_key(&name) {
            panic!(
                "A module with the name \"{}\" already exists in this context.",
                name
            );
        }
        let module = self.module_arena.alloc(Module::new(self, name.clone()));
        modules.insert(name, module);
        module
    }
}

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

    #[test]
    #[should_panic(expected = "A module with the name \"A\" already exists in this context.")]
    fn unique_module_names() {
        let c = Context::new();

        let _ = c.module("A"); // Unique name, OK
        let _ = c.module("B"); // Unique name, OK

        // Panic
        let _ = c.module("A");
    }
}
<commit_msg>Add/document/test Context::modules<commit_after>use super::instance::*;
use super::mem::*;
use super::module::*;
use super::register::*;
use super::signal::*;

use typed_arena::Arena;

use std::cell::{Ref, RefCell};
use std::collections::BTreeMap;

/// A top-level container/owner object for a [`Module`] graph.
///
/// A `Context` owns all parts of a module graph, and provides an API for creating [`Module`] objects.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("MyModule");
/// m.output("out", m.input("in", 1));
/// ```
///
/// [`Module`]: ./struct.Module.html
#[must_use]
pub struct Context<'a> {
    module_arena: Arena<Module<'a>>,
    pub(super) signal_arena: Arena<Signal<'a>>,
    pub(super) register_data_arena: Arena<RegisterData<'a>>,
    pub(super) register_arena: Arena<Register<'a>>,
    pub(super) instance_arena: Arena<Instance<'a>>,
    pub(super) mem_arena: Arena<Mem<'a>>,

    pub(super) modules: RefCell<BTreeMap<String, &'a Module<'a>>>,
}

impl<'a> Context<'a> {
    /// Creates a new, empty `Context`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    /// ```
    pub fn new() -> Context<'a> {
        Context {
            module_arena: Arena::new(),
            signal_arena: Arena::new(),
            register_data_arena: Arena::new(),
            register_arena: Arena::new(),
            instance_arena: Arena::new(),
            mem_arena: Arena::new(),

            modules: RefCell::new(BTreeMap::new()),
        }
    }

    /// Creates a new [`Module`] called `name` in this `Context`.
    ///
    /// Conventionally, `name` should be `CamelCase`, though this is not enforced.
    ///
    /// # Panics
    ///
    /// Panics if a [`Module`] with the same `name` already exists in this `Context`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let my_module = c.module("MyModule");
    /// let another_mod = c.module("AnotherMod");
    /// ```
    ///
    /// The following example panics by creating a `Module` with the same `name` as a previously-created `Module` in the same `Context`:
    ///
    /// ```should_panic
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let _ = c.module("A"); // Unique name, OK
    /// let _ = c.module("B"); // Unique name, OK
    ///
    /// let _ = c.module("A"); // Non-unique name, panic!
    /// ```
    ///
    /// [`Module`]: ./struct.Module.html
    pub fn module<S: Into<String>>(&'a self, name: S) -> &Module {
        let name = name.into();
        let mut modules = self.modules.borrow_mut();
        if modules.contains_key(&name) {
            panic!(
                "A module with the name \"{}\" already exists in this context.",
                name
            );
        }
        let module = self.module_arena.alloc(Module::new(self, name.clone()));
        modules.insert(name, module);
        module
    }

    /// Immutably borrows this `Context`'s [`Module`]s.
    ///
    /// This is primarily useful for iterating over every [`Module`] in this `Context` when generating code.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let my_module = c.module("MyModule");
    /// let another_mod = c.module("AnotherMod");
    ///
    /// assert_eq!(c.modules().len(), 2);
    /// ```
    ///
    /// [`Module`]: ./struct.Module.html
    pub fn modules(&'a self) -> Ref<BTreeMap<String, &'a Module<'a>>> {
        self.modules.borrow()
    }
}

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

    #[test]
    #[should_panic(expected = "A module with the name \"A\" already exists in this context.")]
    fn unique_module_names() {
        let c = Context::new();

        let _ = c.module("A"); // Unique name, OK
        let _ = c.module("B"); // Unique name, OK

        // Panic
        let _ = c.module("A");
    }

    #[test]
    fn new_context_has_no_modules() {
        let c = Context::new();

        assert!(c.modules().is_empty());
    }
}
<|endoftext|>