<commit_before>use crate::errors::*;
use crate::registry::{registry_path, registry_url};
use crate::{Dependency, Manifest};
use env_proxy;
use git2::Repository;
use regex::Regex;
use reqwest;
use semver;
use std::env;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::Once;
use std::time::Duration;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use url::Url;

#[derive(Deserialize)]
struct CrateVersion {
    name: String,
    #[serde(rename = "vers")]
    version: semver::Version,
    yanked: bool,
}

/// Query latest version from a registry index
///
/// 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 crates.io.
pub fn get_latest_dependency(
    crate_name: &str,
    flag_allow_prerelease: bool,
    offline: bool,
) -> Result<Dependency> {
    static UPDATE: Once = Once::new();

    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!("{}--PRERELEASE_VERSION_TEST", crate_name)
        } else {
            format!("{}--CURRENT_VERSION_TEST", crate_name)
        };

        return Ok(Dependency::new(crate_name).set_version(&new_version));
    }
    if crate_name.is_empty() {
        return Err(ErrorKind::EmptyCrateName.into());
    }

    let registry_path = registry_path()?;
    let registry_url = registry_url()?;
    if !offline {
        UPDATE.call_once(|| {
            if let Err(error) = update_git_repo(&registry_path, &registry_url) {
                eprintln!("Querying a registry index failed due to: {}", error);
            }
        });
    }

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

    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
///
/// Assumes the version are sorted so that the first non-yanked version is the
/// latest, and thus the one we want.
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))
}

fn update_git_repo(path: impl AsRef<Path>, url: &Url) -> Result<()> {
    let path = path.as_ref();
    let repo = git2::Repository::open(path)?;
    let colorchoice = if atty::is(atty::Stream::Stdout) {
        ColorChoice::Auto
    } else {
        ColorChoice::Never
    };
    let mut output = StandardStream::stdout(colorchoice);
    output.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
    write!(output, "{:>12}", "Updating")?;
    output.reset()?;
    writeln!(output, " '{}' index", url)?;

    repo.remote_anonymous(url.as_str())?.fetch(
        &["refs/heads/master:refs/remotes/origin/master"],
        None,
        None,
    )?;

    Command::new("git").arg("gc").current_dir(path).output()?;

    Ok(())
}

#[test]
fn get_latest_stable_version_from_json() {
    let versions: Vec<CrateVersion> = serde_json::from_str(
        r#"[
        {
          "name": "foo",
          "vers": "0.6.0-alpha",
          "yanked": false
        },
        {
          "name": "foo",
          "vers": "0.5.0",
          "yanked": false
        }
      ]"#,
    )
    .expect("crate version is correctly parsed");

    assert_eq!(
        read_latest_version(&versions, false)
            .unwrap()
            .version()
            .unwrap(),
        "0.5.0"
    );
}

#[test]
fn get_latest_unstable_or_stable_version_from_json() {
    let versions: Vec<CrateVersion> = serde_json::from_str(
        r#"[
        {
          "name": "foo",
          "vers": "0.6.0-alpha",
          "yanked": false
        },
        {
          "name": "foo",
          "vers": "0.5.0",
          "yanked": false
        }
      ]"#,
    )
    .expect("crate version is correctly parsed");

    assert_eq!(
        read_latest_version(&versions, true)
            .unwrap()
            .version()
            .unwrap(),
        "0.6.0-alpha"
    );
}

#[test]
fn get_latest_version_from_json_test() {
    let versions: Vec<CrateVersion> = serde_json::from_str(
        r#"[
        {
          "name": "treexml",
          "vers": "0.3.1",
          "yanked": true
        },
        {
          "name": "treexml",
          "vers": "0.3.0",
          "yanked": false
        }
      ]"#,
    )
    .expect("crate version is correctly parsed");

    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> = serde_json::from_str(
        r#"[
        {
          "name": "treexml",
          "vers": "0.3.1",
          "yanked": true
        },
        {
          "name": "treexml",
          "vers": "0.3.0",
          "yanked": true
        }
      ]"#,
    )
    .expect("crate version is correctly parsed");

    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_path: impl AsRef<Path>,
) -> Result<Vec<CrateVersion>> {
    let crate_name = crate_name.into();
    let repo = Repository::open(registry_path)?;
    let tree = repo
        .find_reference("refs/remotes/origin/master")?
        .peel_to_tree()?;

    let mut found_crate = false;
    let mut result = vec![];

    let names = gen_fuzzy_crate_names(crate_name.clone())?;
    for the_name in names {
        let file = match tree.get_path(&PathBuf::from(summary_raw_path(&the_name))) {
            Ok(x) => x.to_object(&repo)?.peel_to_blob()?,
            Err(_) => continue,
        };
        found_crate = true;
        let content = String::from_utf8(file.content().to_vec())
            .map_err(|_| ErrorKind::InvalidSummaryJson)?;
        for line in content.lines() {
            result.push(
                serde_json::from_str::<CrateVersion>(line)
                    .map_err(|_| ErrorKind::InvalidSummaryJson)?,
            );
        }
    }
    if !found_crate {
        return Err(ErrorKind::NoCrate(crate_name).into());
    }

    Ok(result)
}

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| get_name_from_manifest(manifest))
            }
            _ => 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");
    Manifest::open(&Some(cargo_file))
        .chain_err(|| "Unable to open local Cargo.toml")
        .and_then(|ref manifest| get_name_from_manifest(manifest))
}

fn get_name_from_manifest(manifest: &Manifest) -> Result<String> {
    manifest
        .data
        .as_table()
        .get("package")
        .and_then(|m| m["name"].as_str().map(std::string::ToString::to_string))
        .ok_or_else(|| ErrorKind::ParseCargoToml.into())
}

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

fn get_with_timeout(url: &str, timeout: Duration) -> reqwest::Result<reqwest::Response> {
    let client = reqwest::ClientBuilder::new()
        .timeout(timeout)
        .proxy(reqwest::Proxy::custom(|url| {
            env_proxy::for_url(url).to_url()
        }))
        .build()?;

    client
        .get(url)
        .send()
        .and_then(reqwest::Response::error_for_status)
}

fn get_cargo_toml_from_git_url(url: &str) -> Result<String> {
    let mut res = get_with_timeout(url, get_default_timeout())
        .chain_err(|| "Failed to fetch crate from git")?;
    let mut body = String::new();
    res.read_to_string(&mut body)
        .chain_err(|| "Git response not a valid `String`")?;
    Ok(body)
}

/// 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"],
    );
}

fn summary_raw_path(crate_name: &str) -> String {
    match crate_name.len() {
        0 => unreachable!("we check that crate_name is not empty here"),
        1 => format!("1/{}", crate_name),
        2 => format!("2/{}", crate_name),
        3 => format!("3/{}/{}", &crate_name[..1], crate_name),
        _ => format!("{}/{}/{}", &crate_name[..2], &crate_name[2..4], crate_name),
    }
}

#[test]
fn test_summary_raw_path() {
    assert_eq!(summary_raw_path("a"), "1/a");
    assert_eq!(summary_raw_path("ab"), "2/ab");
    assert_eq!(summary_raw_path("abc"), "3/a/abc");
    assert_eq!(summary_raw_path("abcd"), "ab/cd/abcd");
    assert_eq!(summary_raw_path("abcdefg"), "ab/cd/abcdefg");
}
<commit_msg>remove git gc stuff<commit_after>use crate::errors::*;
use crate::registry::{registry_path, registry_url};
use crate::{Dependency, Manifest};
use env_proxy;
use git2::Repository;
use regex::Regex;
use reqwest;
use semver;
use std::env;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::Once;
use std::time::Duration;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use url::Url;

#[derive(Deserialize)]
struct CrateVersion {
    name: String,
    #[serde(rename = "vers")]
    version: semver::Version,
    yanked: bool,
}

/// Query latest version from a registry index
///
/// 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 crates.io.
pub fn get_latest_dependency(
    crate_name: &str,
    flag_allow_prerelease: bool,
    offline: bool,
) -> Result<Dependency> {
    static UPDATE: Once = Once::new();

    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!("{}--PRERELEASE_VERSION_TEST", crate_name)
        } else {
            format!("{}--CURRENT_VERSION_TEST", crate_name)
        };

        return Ok(Dependency::new(crate_name).set_version(&new_version));
    }
    if crate_name.is_empty() {
        return Err(ErrorKind::EmptyCrateName.into());
    }

    let registry_path = registry_path()?;
    let registry_url = registry_url()?;
    if !offline {
        UPDATE.call_once(|| {
            if let Err(error) = update_git_repo(&registry_path, &registry_url) {
                eprintln!("Querying a registry index failed due to: {}", error);
            }
        });
    }

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

    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
///
/// Assumes the version are sorted so that the first non-yanked version is the
/// latest, and thus the one we want.
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))
}

fn update_git_repo(path: impl AsRef<Path>, url: &Url) -> Result<()> {
    let path = path.as_ref();
    let repo = git2::Repository::open(path)?;
    let colorchoice = if atty::is(atty::Stream::Stdout) {
        ColorChoice::Auto
    } else {
        ColorChoice::Never
    };
    let mut output = StandardStream::stdout(colorchoice);
    output.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
    write!(output, "{:>12}", "Updating")?;
    output.reset()?;
    writeln!(output, " '{}' index", url)?;

    repo.remote_anonymous(url.as_str())?.fetch(
        &["refs/heads/master:refs/remotes/origin/master"],
        None,
        None,
    )?;

    Ok(())
}

#[test]
fn get_latest_stable_version_from_json() {
    let versions: Vec<CrateVersion> = serde_json::from_str(
        r#"[
        {
          "name": "foo",
          "vers": "0.6.0-alpha",
          "yanked": false
        },
        {
          "name": "foo",
          "vers": "0.5.0",
          "yanked": false
        }
      ]"#,
    )
    .expect("crate version is correctly parsed");

    assert_eq!(
        read_latest_version(&versions, false)
            .unwrap()
            .version()
            .unwrap(),
        "0.5.0"
    );
}

#[test]
fn get_latest_unstable_or_stable_version_from_json() {
    let versions: Vec<CrateVersion> = serde_json::from_str(
        r#"[
        {
          "name": "foo",
          "vers": "0.6.0-alpha",
          "yanked": false
        },
        {
          "name": "foo",
          "vers": "0.5.0",
          "yanked": false
        }
      ]"#,
    )
    .expect("crate version is correctly parsed");

    assert_eq!(
        read_latest_version(&versions, true)
            .unwrap()
            .version()
            .unwrap(),
        "0.6.0-alpha"
    );
}

#[test]
fn get_latest_version_from_json_test() {
    let versions: Vec<CrateVersion> = serde_json::from_str(
        r#"[
        {
          "name": "treexml",
          "vers": "0.3.1",
          "yanked": true
        },
        {
          "name": "treexml",
          "vers": "0.3.0",
          "yanked": false
        }
      ]"#,
    )
    .expect("crate version is correctly parsed");

    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> = serde_json::from_str(
        r#"[
        {
          "name": "treexml",
          "vers": "0.3.1",
          "yanked": true
        },
        {
          "name": "treexml",
          "vers": "0.3.0",
          "yanked": true
        }
      ]"#,
    )
    .expect("crate version is correctly parsed");

    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_path: impl AsRef<Path>,
) -> Result<Vec<CrateVersion>> {
    let crate_name = crate_name.into();
    let repo = Repository::open(registry_path)?;
    let tree = repo
        .find_reference("refs/remotes/origin/master")?
        .peel_to_tree()?;

    let mut found_crate = false;
    let mut result = vec![];

    let names = gen_fuzzy_crate_names(crate_name.clone())?;
    for the_name in names {
        let file = match tree.get_path(&PathBuf::from(summary_raw_path(&the_name))) {
            Ok(x) => x.to_object(&repo)?.peel_to_blob()?,
            Err(_) => continue,
        };
        found_crate = true;
        let content = String::from_utf8(file.content().to_vec())
            .map_err(|_| ErrorKind::InvalidSummaryJson)?;
        for line in content.lines() {
            result.push(
                serde_json::from_str::<CrateVersion>(line)
                    .map_err(|_| ErrorKind::InvalidSummaryJson)?,
            );
        }
    }
    if !found_crate {
        return Err(ErrorKind::NoCrate(crate_name).into());
    }

    Ok(result)
}

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| get_name_from_manifest(manifest))
            }
            _ => 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");
    Manifest::open(&Some(cargo_file))
        .chain_err(|| "Unable to open local Cargo.toml")
        .and_then(|ref manifest| get_name_from_manifest(manifest))
}

fn get_name_from_manifest(manifest: &Manifest) -> Result<String> {
    manifest
        .data
        .as_table()
        .get("package")
        .and_then(|m| m["name"].as_str().map(std::string::ToString::to_string))
        .ok_or_else(|| ErrorKind::ParseCargoToml.into())
}

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

fn get_with_timeout(url: &str, timeout: Duration) -> reqwest::Result<reqwest::Response> {
    let client = reqwest::ClientBuilder::new()
        .timeout(timeout)
        .proxy(reqwest::Proxy::custom(|url| {
            env_proxy::for_url(url).to_url()
        }))
        .build()?;

    client
        .get(url)
        .send()
        .and_then(reqwest::Response::error_for_status)
}

fn get_cargo_toml_from_git_url(url: &str) -> Result<String> {
    let mut res = get_with_timeout(url, get_default_timeout())
        .chain_err(|| "Failed to fetch crate from git")?;
    let mut body = String::new();
    res.read_to_string(&mut body)
        .chain_err(|| "Git response not a valid `String`")?;
    Ok(body)
}

/// 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"],
    );
}

fn summary_raw_path(crate_name: &str) -> String {
    match crate_name.len() {
        0 => unreachable!("we check that crate_name is not empty here"),
        1 => format!("1/{}", crate_name),
        2 => format!("2/{}", crate_name),
        3 => format!("3/{}/{}", &crate_name[..1], crate_name),
        _ => format!("{}/{}/{}", &crate_name[..2], &crate_name[2..4], crate_name),
    }
}

#[test]
fn test_summary_raw_path() {
    assert_eq!(summary_raw_path("a"), "1/a");
    assert_eq!(summary_raw_path("ab"), "2/ab");
    assert_eq!(summary_raw_path("abc"), "3/a/abc");
    assert_eq!(summary_raw_path("abcd"), "ab/cd/abcd");
    assert_eq!(summary_raw_path("abcdefg"), "ab/cd/abcdefg");
}
<|endoftext|>


<commit_before>use std::io;
use runner::runner;
use std::io::{Read,Write};
use std::net::SocketAddr;
use context::{Context, Transfer};
use context::stack::ProtectedFixedSizeStack;
use mio::net::{TcpStream,UdpSocket,TcpListener};
use mio::event::Evented;
use mio::{Ready,Poll,PollOpt,Token};
use std::marker::PhantomData;
use wheel::Token as WToken;
use std::time::Duration;
use std::collections::VecDeque;
use native_tls::{TlsStream, TlsConnector, TlsAcceptor, MidHandshakeTlsStream};

/// Fiber execute function. It receives current fiber, parameter that was used
/// to start the fiber and returns result that will be result of fiber.
///
/// Generally you return from function once socket is closed or read/write times out.
///
/// This function will be called on an independent execution stack which is much more limited
/// so you must either be careful with how deep your stack can get, or set the stack to a high value.
///
/// You must test your code for any possible SIGBUS situations and be careful with calling external crates.
pub type FiberFn<P,R> = fn(Fiber<P,R>,P) -> Option<R>;

/// Available within the fiber execute function to configure fiber or create child fibers. Child fibers
/// return results to parent fibers instead of poller on main stack.
#[derive(PartialEq,Debug)]
pub struct Fiber<P,R> {
    pub(crate) id: usize,
    param: PhantomData<P>,
    resp: PhantomData<R>,
}

impl<P,R> Fiber<P,R> {
    pub(crate) fn new(pos: usize) -> Fiber<P,R> {
        Fiber {
            id: pos,
            param: PhantomData,
            resp: PhantomData,
        }
    }
}

impl<P,R> Fiber<P,R> {
    /// How long to wait on socket operations before timing out. You probably want to set something.
    pub fn socket_timeout(&self, t: Option<Duration>) {
        runner::<P,R>().socket_timeout(self.id, t)
    }

    /// Return intermediate result. A fiber can stream responses to poller or to its parent fiber.
    ///
    /// This function blocks fiber until response is received to poller or parent.
    pub fn resp_chunk(&self, chunk: R) {
        runner::<P,R>().resp_chunk(self.id, chunk);
    }

    /// Accept TCP socket. Works only on fibers from TcpListener.
    pub fn accept_tcp(&self) -> io::Result<(TcpStream, SocketAddr)> {
        runner::<P,R>().accept_tcp(self.id)
    }

    /// Start fiber on TCP socket.
    ///
    /// This function does not block and fiber gets executed on next poll(). There is no relationship
    /// between calling and created fiber.
    pub fn new_tcp(&self, tcp: TcpStream, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        runner().register(Some(self.id), func, FiberSock::Tcp(tcp), param, None).map(|_|{()})
    }

    /// Convert fiber from TcpStream created with TcpStream::connect to TlsStream.
    ///
    /// This function does not block and fiber gets executed on next poll(). There is no relationship
    /// between calling and created fiber.
    pub fn tcp_tls_connect(&self, con: TlsConnector, domain: &str) -> io::Result<()> {
        runner::<P,R>().tcp_tls_connect(self.id, con, domain)
    }

    /// Convert fiber from TcpStream created with TcpListener::accept to TlsStream.
    ///
    /// This function does not block and fiber gets executed on next poll(). There is no relationship
    /// between calling and created fiber.
    pub fn tcp_tls_accept(&self, con: TlsAcceptor) -> io::Result<()> {
        runner::<P,R>().tcp_tls_accept(self.id, con)
    }

    /// Start fiber on TCP listener.
    ///
    /// This function does not block and fiber gets executed on next poll(). There is no relationship
    /// between calling and created fiber.
    pub fn new_listener(&self, tcp: TcpListener, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        runner().register(Some(self.id), func, FiberSock::Listener(tcp), param, None).map(|_|{()})
    }

    /// Start fiber on UDP socket.
    ///
    /// This function does not block and fiber gets executed on next poll(). There is no relationship
    /// between calling and created fiber.
    pub fn new_udp(&self, udp: UdpSocket, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        runner().register(Some(self.id), func, FiberSock::Udp(udp), param, None).map(|_|{()})
    }

    /// Get result of child. If fiber has multiple children it will return first available result.
    /// Will block current fiber if nothing available immediately.
    ///
    /// If none is returned all children have finished executing.
    pub fn get_child(&self) -> Option<R> {
        runner::<P,R>().child_iter(self.id)
    }

    /// Start a child fiber with tcp socket.
    ///
    /// This function does not block current fiber.
    pub fn join_tcp(&self, tcp: TcpStream, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        match runner::<P,R>().register(Some(self.id), func, FiberSock::Tcp(tcp), param, Some(self.id)) {
            Ok(Some(_)) => {
                Ok(())
            }
            Ok(None) => {
                Err(io::Error::new(io::ErrorKind::Other, "can not create child"))
            }
            Err(e) => {
                Err(e)
            }
        }
    }

    /// Start a child fiber with an udp socket.
    ///
    /// This function does not block current fiber.
    pub fn join_udp(&self, udp: UdpSocket, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        match runner::<P,R>().register(Some(self.id), func, FiberSock::Udp(udp), param, Some(self.id)) {
            Ok(Some(_)) => {
                Ok(())
            }
            Ok(None) => {
                Err(io::Error::new(io::ErrorKind::Other, "can not create child"))
            }
            Err(e) => {
                Err(e)
            }
        }
    }

    /// Call main stack.
    ///
    /// This function blocks until main stack produces response.
    pub fn join_main(&self, param: P) -> R {
        runner::<P,R>().join_main(self.id)
    }
}

// We use unwrap directly as we do not allow
// creating fiber without a poller.
impl<P,R> Read for Fiber<P,R> {
    /// Read data from socket. If no data is available to read, fiber will be scheduled out for another one,
    /// until there is data available.
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        runner::<P,R>().read(self.id, buf)
    }
}

impl<'a,P,R> Read for &'a Fiber<P,R> {
    /// Read data from socket. If no data is available to read, fiber will be scheduled out for another one,
    /// until there is data available.
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        runner::<P,R>().read(self.id, buf)
    }
}

impl<P,R> Write for Fiber<P,R> {
    /// Write data to socket. If data can not be written at this time,
    /// fiber will be scheduled out for another one until there is data available.
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        runner::<P,R>().write(self.id, buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        runner::<P,R>().flush(self.id)
    }
}

/// Reference to fiber on main stack.
pub struct FiberRef<P,R> {
    pub(crate) id: usize,
    p: PhantomData<P>,
    r: PhantomData<R>,
}

impl<P,R> FiberRef<P,R> {
    pub(crate) fn new(id: usize) -> FiberRef<P,R> {
        FiberRef {
            id,
            p: PhantomData,
            r: PhantomData,
        }
    }

    /// Resume fiber with response from main stack.
    ///
    /// This function does not block, fiber gets resumed on next poll().
    pub fn resume_fiber(self, resp: R) {
        runner::<P,R>().resume_fiber(self.id, resp);
    }
}

#[derive(PartialEq)]
pub(crate) enum FiberState {
    Closed,
    Stacked,
    Unstacked,
}

pub(crate) struct FiberInt<P,R> {
    pub(crate) ready: Ready,
    // pub(crate) blocking_on: Ready,
    pub(crate) param: Option<P>,
    pub(crate) state: FiberState,
    pub(crate) me: usize,
    pub(crate) func: FiberFn<P,R>,
    pub(crate) sock: FiberSock,
    // Only when executing is stack and transfer here
    pub(crate) t: Option<Transfer>,
    pub(crate) stack: Option<ProtectedFixedSizeStack>,
    // If a fiber is waiting on it
    pub(crate) parent: Option<usize>,
    pub(crate) children: VecDeque<usize>,
    // Will hold result of fiber function.
    pub(crate) result: Option<R>,
    pub(crate) wtoken: Option<WToken>,
    pub(crate) timeout: Option<Duration>,
    pub(crate) timed_out: bool,
}

impl<P,R> FiberInt<P,R> {
    pub(crate) fn evented(&self) -> &Evented {
        self.sock.evented()
    }

    pub(crate) fn register(&mut self, poll: &Poll, rdy: Ready) -> io::Result<()> {
        let res = if self.ready.is_empty() {
            poll.register(self.evented(), Token(self.me), rdy, PollOpt::edge())
        } else {
            poll.reregister(self.evented(), Token(self.me), rdy, PollOpt::edge())
        };
        self.ready = rdy;
        res
    }
}

pub(crate) enum FiberSock {
    Tcp(TcpStream),
    Udp(UdpSocket),
    Listener(TcpListener),
    // TlsStream starts as TcpStream but morphs into TlsStream
    // either through TlsAcceptor or TlsConnector
    Tls(TlsStream<TcpStream>),
    TlsTemp(MidHandshakeTlsStream<TcpStream>),
    Empty,
}

impl FiberSock {
    pub fn evented(&self) -> &Evented {
        match self {
            &FiberSock::Tcp(ref tcp) => tcp,
            &FiberSock::Udp(ref udp) => udp,
            &FiberSock::Listener(ref tcp) => tcp,
            &FiberSock::TlsTemp(ref tcp) => tcp.get_ref(),
            _ => panic!("Evented on an empty fiber"),
        }
    }

    pub fn accept_tcp(&self) -> io::Result<(TcpStream, SocketAddr)> {
        match self {
            &FiberSock::Listener(ref tcp) => {
                tcp.accept()
            }
            _ => {
                Err(io::Error::new(io::ErrorKind::InvalidInput,"can not accept on non listen socket"))
            }
        }
    }
}

impl Read for FiberSock {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            FiberSock::Tcp(ref mut tcp) => tcp.read(buf),
            FiberSock::Udp(ref mut udp) => udp.recv(buf),
            FiberSock::Listener(ref tcp) =>
                Err(io::Error::new(io::ErrorKind::InvalidInput,"can not read on listen socket")),
            FiberSock::Tls(ref mut tcp) => tcp.read(buf),
            _ => panic!("Read on an empty fiber"),
        }
    }
}

impl Write for FiberSock {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            FiberSock::Tcp(ref mut tcp) => tcp.write(buf),
            FiberSock::Udp(ref mut udp) => udp.send(buf),
            FiberSock::Listener(ref tcp) =>
                Err(io::Error::new(io::ErrorKind::InvalidInput,"can not write on listen socket")),
            FiberSock::Tls(ref mut tcp) => tcp.write(buf),
            _ => panic!("Write on an empty fiber"),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            FiberSock::Tcp(ref mut tcp) => tcp.flush(),
            FiberSock::Udp(ref mut udp) => Ok(()),
            FiberSock::Listener(ref tcp) =>
                Err(io::Error::new(io::ErrorKind::InvalidInput,"can not flush on listen socket")),
            FiberSock::Tls(ref mut tcp) => tcp.flush(),
            _ => panic!("Flush on an empty fiber"),
        }
    }
}
<commit_msg>fix doc<commit_after>use std::io;
use runner::runner;
use std::io::{Read,Write};
use std::net::SocketAddr;
use context::{Context, Transfer};
use context::stack::ProtectedFixedSizeStack;
use mio::net::{TcpStream,UdpSocket,TcpListener};
use mio::event::Evented;
use mio::{Ready,Poll,PollOpt,Token};
use std::marker::PhantomData;
use wheel::Token as WToken;
use std::time::Duration;
use std::collections::VecDeque;
use native_tls::{TlsStream, TlsConnector, TlsAcceptor, MidHandshakeTlsStream};

/// Fiber execute function. It receives current fiber, parameter that was used
/// to start the fiber and returns result that will be result of fiber.
///
/// Generally you return from function once socket is closed or read/write times out.
///
/// This function will be called on an independent execution stack which is much more limited
/// so you must either be careful with how deep your stack can get, or set the stack to a high value.
///
/// You must test your code for any possible SIGBUS situations and be careful with calling external crates.
pub type FiberFn<P,R> = fn(Fiber<P,R>,P) -> Option<R>;

/// Available within the fiber execute function to configure fiber or create child fibers. Child fibers
/// return results to parent fibers instead of poller on main stack.
#[derive(PartialEq,Debug)]
pub struct Fiber<P,R> {
    pub(crate) id: usize,
    param: PhantomData<P>,
    resp: PhantomData<R>,
}

impl<P,R> Fiber<P,R> {
    pub(crate) fn new(pos: usize) -> Fiber<P,R> {
        Fiber {
            id: pos,
            param: PhantomData,
            resp: PhantomData,
        }
    }
}

impl<P,R> Fiber<P,R> {
    /// How long to wait on socket operations before timing out. You probably want to set something.
    pub fn socket_timeout(&self, t: Option<Duration>) {
        runner::<P,R>().socket_timeout(self.id, t)
    }

    /// Return intermediate result. A fiber can stream responses to poller or to its parent fiber.
    ///
    /// This function blocks fiber until response is received to poller or parent.
    pub fn resp_chunk(&self, chunk: R) {
        runner::<P,R>().resp_chunk(self.id, chunk);
    }

    /// Accept TCP socket. Works only on fibers from TcpListener.
    pub fn accept_tcp(&self) -> io::Result<(TcpStream, SocketAddr)> {
        runner::<P,R>().accept_tcp(self.id)
    }

    /// Convert fiber from TcpStream created with TcpStream::connect to TlsStream.
    pub fn tcp_tls_connect(&self, con: TlsConnector, domain: &str) -> io::Result<()> {
        runner::<P,R>().tcp_tls_connect(self.id, con, domain)
    }

    /// Convert fiber from TcpStream created with TcpListener::accept to TlsStream.
    pub fn tcp_tls_accept(&self, con: TlsAcceptor) -> io::Result<()> {
        runner::<P,R>().tcp_tls_accept(self.id, con)
    }

    /// Start fiber on TCP socket.
    ///
    /// This function does not block and fiber gets executed on next poll(). There is no relationship
    /// between calling and created fiber.
    pub fn new_tcp(&self, tcp: TcpStream, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        runner().register(Some(self.id), func, FiberSock::Tcp(tcp), param, None).map(|_|{()})
    }

    /// Start fiber on TCP listener.
    ///
    /// This function does not block and fiber gets executed on next poll(). There is no relationship
    /// between calling and created fiber.
    pub fn new_listener(&self, tcp: TcpListener, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        runner().register(Some(self.id), func, FiberSock::Listener(tcp), param, None).map(|_|{()})
    }

    /// Start fiber on UDP socket.
    ///
    /// This function does not block and fiber gets executed on next poll(). There is no relationship
    /// between calling and created fiber.
    pub fn new_udp(&self, udp: UdpSocket, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        runner().register(Some(self.id), func, FiberSock::Udp(udp), param, None).map(|_|{()})
    }

    /// Start a child fiber with tcp socket.
    ///
    /// This function does not block current fiber.
    pub fn join_tcp(&self, tcp: TcpStream, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        match runner::<P,R>().register(Some(self.id), func, FiberSock::Tcp(tcp), param, Some(self.id)) {
            Ok(Some(_)) => {
                Ok(())
            }
            Ok(None) => {
                Err(io::Error::new(io::ErrorKind::Other, "can not create child"))
            }
            Err(e) => {
                Err(e)
            }
        }
    }

    /// Start a child fiber with an udp socket.
    ///
    /// This function does not block current fiber.
    pub fn join_udp(&self, udp: UdpSocket, func: FiberFn<P,R>, param: P) -> io::Result<()> {
        match runner::<P,R>().register(Some(self.id), func, FiberSock::Udp(udp), param, Some(self.id)) {
            Ok(Some(_)) => {
                Ok(())
            }
            Ok(None) => {
                Err(io::Error::new(io::ErrorKind::Other, "can not create child"))
            }
            Err(e) => {
                Err(e)
            }
        }
    }

    /// Call main stack.
    ///
    /// This function blocks until main stack produces response.
    pub fn join_main(&self, param: P) -> R {
        runner::<P,R>().join_main(self.id)
    }

    /// Get result of child. If fiber has multiple children it will return first available result.
    /// Will block current fiber if nothing available immediately.
    ///
    /// If none is returned all children have finished executing.
    pub fn get_child(&self) -> Option<R> {
        runner::<P,R>().child_iter(self.id)
    }
}

// We use unwrap directly as we do not allow
// creating fiber without a poller.
impl<P,R> Read for Fiber<P,R> {
    /// Read data from socket. If no data is available to read, fiber will be scheduled out for another one,
    /// until there is data available.
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        runner::<P,R>().read(self.id, buf)
    }
}

impl<'a,P,R> Read for &'a Fiber<P,R> {
    /// Read data from socket. If no data is available to read, fiber will be scheduled out for another one,
    /// until there is data available.
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        runner::<P,R>().read(self.id, buf)
    }
}

impl<P,R> Write for Fiber<P,R> {
    /// Write data to socket. If data can not be written at this time,
    /// fiber will be scheduled out for another one until there is data available.
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        runner::<P,R>().write(self.id, buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        runner::<P,R>().flush(self.id)
    }
}

/// Reference to fiber on main stack.
pub struct FiberRef<P,R> {
    pub(crate) id: usize,
    p: PhantomData<P>,
    r: PhantomData<R>,
}

impl<P,R> FiberRef<P,R> {
    pub(crate) fn new(id: usize) -> FiberRef<P,R> {
        FiberRef {
            id,
            p: PhantomData,
            r: PhantomData,
        }
    }

    /// Resume fiber with response from main stack.
    ///
    /// This function does not block, fiber gets resumed on next poll().
    pub fn resume_fiber(self, resp: R) {
        runner::<P,R>().resume_fiber(self.id, resp);
    }
}

#[derive(PartialEq)]
pub(crate) enum FiberState {
    Closed,
    Stacked,
    Unstacked,
}

pub(crate) struct FiberInt<P,R> {
    pub(crate) ready: Ready,
    // pub(crate) blocking_on: Ready,
    pub(crate) param: Option<P>,
    pub(crate) state: FiberState,
    pub(crate) me: usize,
    pub(crate) func: FiberFn<P,R>,
    pub(crate) sock: FiberSock,
    // Only when executing is stack and transfer here
    pub(crate) t: Option<Transfer>,
    pub(crate) stack: Option<ProtectedFixedSizeStack>,
    // If a fiber is waiting on it
    pub(crate) parent: Option<usize>,
    pub(crate) children: VecDeque<usize>,
    // Will hold result of fiber function.
    pub(crate) result: Option<R>,
    pub(crate) wtoken: Option<WToken>,
    pub(crate) timeout: Option<Duration>,
    pub(crate) timed_out: bool,
}

impl<P,R> FiberInt<P,R> {
    pub(crate) fn evented(&self) -> &Evented {
        self.sock.evented()
    }

    pub(crate) fn register(&mut self, poll: &Poll, rdy: Ready) -> io::Result<()> {
        let res = if self.ready.is_empty() {
            poll.register(self.evented(), Token(self.me), rdy, PollOpt::edge())
        } else {
            poll.reregister(self.evented(), Token(self.me), rdy, PollOpt::edge())
        };
        self.ready = rdy;
        res
    }
}

pub(crate) enum FiberSock {
    Tcp(TcpStream),
    Udp(UdpSocket),
    Listener(TcpListener),
    // TlsStream starts as TcpStream but morphs into TlsStream
    // either through TlsAcceptor or TlsConnector
    Tls(TlsStream<TcpStream>),
    TlsTemp(MidHandshakeTlsStream<TcpStream>),
    Empty,
}

impl FiberSock {
    pub fn evented(&self) -> &Evented {
        match self {
            &FiberSock::Tcp(ref tcp) => tcp,
            &FiberSock::Udp(ref udp) => udp,
            &FiberSock::Listener(ref tcp) => tcp,
            &FiberSock::TlsTemp(ref tcp) => tcp.get_ref(),
            _ => panic!("Evented on an empty fiber"),
        }
    }

    pub fn accept_tcp(&self) -> io::Result<(TcpStream, SocketAddr)> {
        match self {
            &FiberSock::Listener(ref tcp) => {
                tcp.accept()
            }
            _ => {
                Err(io::Error::new(io::ErrorKind::InvalidInput,"can not accept on non listen socket"))
            }
        }
    }
}

impl Read for FiberSock {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            FiberSock::Tcp(ref mut tcp) => tcp.read(buf),
            FiberSock::Udp(ref mut udp) => udp.recv(buf),
            FiberSock::Listener(ref tcp) =>
                Err(io::Error::new(io::ErrorKind::InvalidInput,"can not read on listen socket")),
            FiberSock::Tls(ref mut tcp) => tcp.read(buf),
            _ => panic!("Read on an empty fiber"),
        }
    }
}

impl Write for FiberSock {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            FiberSock::Tcp(ref mut tcp) => tcp.write(buf),
            FiberSock::Udp(ref mut udp) => udp.send(buf),
            FiberSock::Listener(ref tcp) =>
                Err(io::Error::new(io::ErrorKind::InvalidInput,"can not write on listen socket")),
            FiberSock::Tls(ref mut tcp) => tcp.write(buf),
            _ => panic!("Write on an empty fiber"),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            FiberSock::Tcp(ref mut tcp) => tcp.flush(),
            FiberSock::Udp(ref mut udp) => Ok(()),
            FiberSock::Listener(ref tcp) =>
                Err(io::Error::new(io::ErrorKind::InvalidInput,"can not flush on listen socket")),
            FiberSock::Tls(ref mut tcp) => tcp.flush(),
            _ => panic!("Flush on an empty fiber"),
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;

pub fn single_night(file: &str) -> i64 {
    let input = File::open(file).expect("File open fail.");
    let reader = BufReader::new(input);

    let mut nodes = Vec::new();
    let mut edges = Vec::new();
    for line in reader.lines() {
        let line = line.unwrap();
        // println!("{}", s);

        let words: Vec<&str> = line.split_whitespace().collect();
        let from = Node(String::from(words[0]), i64::max_value());
        let to = Node(String::from(words[2]), i64::max_value());
        let distance = words[4]
            .parse::<i64>()
            .expect("parse error");

        nodes.push(from.clone());
        nodes.push(to.clone());
        edges.push((from.clone(), to.clone(), distance));
        edges.push((to, from, distance)); // bi-directed
    }
    // remove duplicate nodes
    nodes.sort_by(|a, b| a.0.cmp(&b.0));
    nodes.dedup();

    println!("nodes");
    for node in nodes.clone() {
        println!("{:?}", node);
    }

    let node_list = nodes.clone();
    let mut results = Vec::new();
    for n in node_list {
        // make n node source
        let mut nodes = nodes.clone();
        if let Ok(n_index) = nodes.binary_search(&n) {
            let mut node = nodes.get_mut(n_index).unwrap();
            *node = Node(n.0.clone(), 0);
        }
        println!("calculating next...");
        results.push(dijkstra(nodes, edges.clone()));
    }

    for result in results.clone() {
        let (route, _) = result;
        for node in route {
            println!("{:?}->", node);
        }
    }
    // sort reversed and pop min
    results.sort_by(|a, b| b.1.cmp(&a.1));
    let (_, min) = results.pop().unwrap();
    min
}

fn dijkstra(mut nodes: Vec<Node>, edges: Vec<(Node, Node, i64)>) -> (Vec<Node>, i64) {
    let mut out: Vec<Node> = Vec::new();
    loop {
        if nodes.is_empty() {
            let mut sum = 0;
            for node in out.clone() {
                sum += node.1;
                println!("{:?}", node);
            }
            return (out, sum);
        }
        println!("sorted nodes");
        // sort by value
        nodes.sort_by(|a, b| b.1.cmp(&a.1));
        for node in nodes.clone() {
            println!("{:?}", node);
        }

        let node = nodes.pop().unwrap();
        println!("pop min {:?}", node);
        println!("edges length {:?}", edges.len());

        // sort by name
        nodes.sort_by(|a, b| a.0.cmp(&b.0));

        // for each neighbor of n
        for edge in edges.clone().iter() {
            let (ref from, ref to, ref dist) = *edge;
            if from == &node {
                if let Ok(neighbor_index) = nodes.binary_search_by(|n| n.0.cmp(&to.0)) {
                    println!("edge {:?} {:?} {:?}", from.0, to.0, dist);
                    let mut neighbor = nodes.get_mut(neighbor_index).unwrap();
                    let alt = node.1 + *dist;
                    // assign new weight to neighbor
                    if alt < neighbor.1 {
                        println!("{:?} <- {:?}", neighbor.0, alt);
                        *neighbor = Node(to.0.clone(), alt);
                    }
                }
            }
        }
        out.push(node);
    }
}

#[derive(Debug, Eq, Ord, PartialOrd)]
struct Node(String, i64);

impl PartialEq for Node {
    fn eq(&self, other: &Node) -> bool {
        self.0.eq(&other.0)
    }
    fn ne(&self, other: &Node) -> bool {
        !self.0.eq(&other.0)
    }
}

impl Clone for Node {
    fn clone(&self) -> Self {
        Node(self.0.clone(), self.1)
    }
}

<commit_msg>Debug stuff.<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;

pub fn single_night(file: &str) -> i64 {
    let input = File::open(file).expect("File open fail.");
    let reader = BufReader::new(input);

    let mut nodes = Vec::new();
    let mut edges = Vec::new();
    for line in reader.lines() {
        let line = line.unwrap();
        // println!("{}", s);

        let words: Vec<&str> = line.split_whitespace().collect();
        let from = Node(String::from(words[0]), i64::max_value());
        let to = Node(String::from(words[2]), i64::max_value());
        let distance = words[4]
            .parse::<i64>()
            .expect("parse error");

        nodes.push(from.clone());
        nodes.push(to.clone());
        edges.push((from.clone(), to.clone(), distance));
        edges.push((to, from, distance)); // bi-directed
    }
    // remove duplicate nodes
    nodes.sort_by(|a, b| a.0.cmp(&b.0));
    nodes.dedup();

    println!("nodes");
    for node in nodes.clone() {
        println!("{:?}", node);
    }

    let node_list = nodes.clone();
    let mut results = Vec::new();
    for n in node_list {
        // make n node source
        let mut nodes = nodes.clone();
        if let Ok(n_index) = nodes.binary_search(&n) {
            let mut node = nodes.get_mut(n_index).unwrap();
            *node = Node(n.0.clone(), 0);
        }
        println!("calculating next...");
        results.push(dijkstra(nodes, edges.clone()));
    }

    for result in results.clone() {
        let (route, _) = result;
        for node in route {
            println!("{:?}->", node);
        }
    }
    // sort reversed and pop min
    results.sort_by(|a, b| b.1.cmp(&a.1));
    let (_, min) = results.pop().unwrap();
    min
}

fn dijkstra(mut nodes: Vec<Node>, edges: Vec<(Node, Node, i64)>) -> (Vec<Node>, i64) {
    let mut out: Vec<Node> = Vec::new();
    loop {
        if nodes.is_empty() {
            let mut sum = 0;
            for node in out.clone() {
                sum += node.1;
                println!("{:?}", node);
            }
            return (out, sum);
        }
        println!("\nsorted nodes");
        // sort by value
        nodes.sort_by(|a, b| b.1.cmp(&a.1));
        for node in nodes.clone() {
            println!("{:?}", node);
        }

        let node = nodes.pop().unwrap();
        println!("pop min {:?}", node);

        // sort by name
        nodes.sort_by(|a, b| a.0.cmp(&b.0));

        // for each neighbor of n
        for edge in edges.clone().iter() {
            let (ref from, ref to, ref dist) = *edge;
            if from == &node {
                if let Ok(neighbor_index) = nodes.binary_search_by(|n| n.0.cmp(&to.0)) {
                    println!("edge {:?} {:?} {:?}", from.0, to.0, dist);
                    let mut neighbor = nodes.get_mut(neighbor_index).unwrap();
                    let alt = node.1 + *dist;
                    // assign new weight to neighbor
                    if alt < neighbor.1 {
                        println!("{:?} <- {:?}", neighbor.0, alt);
                        *neighbor = Node(to.0.clone(), alt);
                    } else {
                        println!("{:?} - {:?}", neighbor.0, neighbor.1);
                    }
                }
            }
        }
        out.push(node);
        print!("Out: ");
        for node in out.clone() {
            print!("{:?},", node);
        }
    }
}

#[derive(Debug, Eq, Ord, PartialOrd)]
struct Node(String, i64);

impl PartialEq for Node {
    fn eq(&self, other: &Node) -> bool {
        self.0.eq(&other.0)
    }
    fn ne(&self, other: &Node) -> bool {
        !self.0.eq(&other.0)
    }
}

impl Clone for Node {
    fn clone(&self) -> Self {
        Node(self.0.clone(), self.1)
    }
}

<|endoftext|>


<commit_before>use std::cmp::max;

const DATA: &'static str = include_str!("../data/input_21.txt");

pub fn main() -> Vec<String> {
    let boss = Character::boss(DATA);
    vec![least_expensive_win(&boss).unwrap().to_string(),
         most_expensive_loss(&boss).unwrap().to_string()]
}

#[derive(Debug, PartialEq)]
struct Item<'a> {
    name: &'a str,
    cost: usize,
    damage: isize,
    armor: isize,
}

macro_rules! item {
    ($n:expr, $c:expr, $d:expr, $a:expr) =>
        (Item { name: $n, cost: $c, damage: $d, armor: $a })
}

static WEAPONS: [Item<'static>; 5] = [item!("Dagger", 8, 4, 0),
                                      item!("Shortsword", 10, 5, 0),
                                      item!("Warhammer", 25, 6, 0),
                                      item!("Longsword", 40, 7, 0),
                                      item!("Greataxe", 74, 8, 0)];

static ARMOR: [Item<'static>; 6] = [item!("NONE", 0, 0, 0),
                                    item!("Leather", 13, 0, 1),
                                    item!("Chainmail", 31, 0, 2),
                                    item!("Splintmail", 53, 0, 3),
                                    item!("Bandedmail", 75, 0, 4),
                                    item!("Platemail", 102, 0, 5)];

static RINGS: [Item<'static>; 8] = [item!("NONE 1", 0, 0, 0),
                                    item!("NONE 2", 0, 0, 0),
                                    item!("Damage +1", 25, 1, 0),
                                    item!("Damage +2", 50, 2, 0),
                                    item!("Damage +3", 100, 3, 0),
                                    item!("Defense +1", 20, 0, 1),
                                    item!("Defense +2", 40, 0, 2),
                                    item!("Defense +3", 80, 0, 3)];

struct Character {
    hp: isize,
    damage: isize,
    armor: isize,
}

impl Character {
    fn new() -> Character {
        Character {
            hp: 100,
            damage: 0,
            armor: 0,
        }
    }

    fn boss(data: &str) -> Character {
        let mut lines = data.lines();
        let hp = scan_fmt!(lines.next().unwrap(), "Hit Points: {}", isize).unwrap();
        let damage = scan_fmt!(lines.next().unwrap(), "Damage: {}", isize).unwrap();
        let armor = scan_fmt!(lines.next().unwrap(), "Armor: {}", isize).unwrap();

        Character {
            hp: hp,
            damage: damage,
            armor: armor,
        }
    }

    fn reset(&mut self) {
        self.damage = 0;
        self.armor = 0;
    }

    fn equip(&mut self, items: &[&Item]) {
        self.reset();
        for item in items {
            self.damage += item.damage;
            self.armor += item.armor;
        }
    }

    fn attack(&self, boss: &Character) -> bool {
        let damage = max(1, self.damage - boss.armor);
        let boss_damage = max(1, boss.damage - self.armor);
        let mut hp = self.hp;
        let mut boss_hp = boss.hp;
        loop {
            boss_hp -= damage;
            if boss_hp < 1 {
                return true;
            }
            hp -= boss_damage;
            if hp < 1 {
                return false;
            }
        }
    }
}

fn equipment_builds() -> Box<Iterator<Item = [&'static Item<'static>; 4]>> {
    box WEAPONS.iter()
               .flat_map(move |w| {
                   ARMOR.iter()
                        .flat_map(move |a| {
                            RINGS.iter()
                                 .flat_map(move |r1| {
                                     RINGS.iter()
                                          .filter(move |&r2| r2 != r1)
                                          .map(move |r2| [w, a, r1, r2])
                                 })
                        })
               })
}

fn least_expensive_win(boss: &Character) -> Option<usize> {
    let mut hero = Character::new();

    equipment_builds()
        .filter(|e| {
            hero.equip(e);
            hero.attack(&boss)
        })
        .map(|e| e.iter().fold(0, |cost, &i| cost + i.cost))
        .min()
}

fn most_expensive_loss(boss: &Character) -> Option<usize> {
    let mut hero = Character::new();

    equipment_builds()
        .filter(|e| {
            hero.equip(e);
            !hero.attack(&boss)
        })
        .map(|e| e.iter().fold(0, |cost, &i| cost + i.cost))
        .max()
}
<commit_msg>day 21 rearrange loadout generator to make filter most effective<commit_after>use std::cmp::max;

const DATA: &'static str = include_str!("../data/input_21.txt");

pub fn main() -> Vec<String> {
    let boss = Character::boss(DATA);
    vec![least_expensive_win(&boss).unwrap().to_string(),
         most_expensive_loss(&boss).unwrap().to_string()]
}

#[derive(Debug, PartialEq)]
struct Item<'a> {
    name: &'a str,
    cost: usize,
    damage: isize,
    armor: isize,
}

macro_rules! item {
    ($n:expr, $c:expr, $d:expr, $a:expr) =>
        (Item { name: $n, cost: $c, damage: $d, armor: $a })
}

static WEAPONS: [Item<'static>; 5] = [item!("Dagger", 8, 4, 0),
                                      item!("Shortsword", 10, 5, 0),
                                      item!("Warhammer", 25, 6, 0),
                                      item!("Longsword", 40, 7, 0),
                                      item!("Greataxe", 74, 8, 0)];

static ARMOR: [Item<'static>; 6] = [item!("NONE", 0, 0, 0),
                                    item!("Leather", 13, 0, 1),
                                    item!("Chainmail", 31, 0, 2),
                                    item!("Splintmail", 53, 0, 3),
                                    item!("Bandedmail", 75, 0, 4),
                                    item!("Platemail", 102, 0, 5)];

static RINGS: [Item<'static>; 8] = [item!("NONE 1", 0, 0, 0),
                                    item!("NONE 2", 0, 0, 0),
                                    item!("Damage +1", 25, 1, 0),
                                    item!("Damage +2", 50, 2, 0),
                                    item!("Damage +3", 100, 3, 0),
                                    item!("Defense +1", 20, 0, 1),
                                    item!("Defense +2", 40, 0, 2),
                                    item!("Defense +3", 80, 0, 3)];

struct Character {
    hp: isize,
    damage: isize,
    armor: isize,
}

impl Character {
    fn new() -> Character {
        Character {
            hp: 100,
            damage: 0,
            armor: 0,
        }
    }

    fn boss(data: &str) -> Character {
        let mut lines = data.lines();
        let hp = scan_fmt!(lines.next().unwrap(), "Hit Points: {}", isize).unwrap();
        let damage = scan_fmt!(lines.next().unwrap(), "Damage: {}", isize).unwrap();
        let armor = scan_fmt!(lines.next().unwrap(), "Armor: {}", isize).unwrap();

        Character {
            hp: hp,
            damage: damage,
            armor: armor,
        }
    }

    fn reset(&mut self) {
        self.damage = 0;
        self.armor = 0;
    }

    fn equip(&mut self, items: &[&Item]) {
        self.reset();
        for item in items {
            self.damage += item.damage;
            self.armor += item.armor;
        }
    }

    fn attack(&self, boss: &Character) -> bool {
        let damage = max(1, self.damage - boss.armor);
        let boss_damage = max(1, boss.damage - self.armor);
        let mut hp = self.hp;
        let mut boss_hp = boss.hp;
        loop {
            boss_hp -= damage;
            if boss_hp < 1 {
                return true;
            }
            hp -= boss_damage;
            if hp < 1 {
                return false;
            }
        }
    }
}

fn equipment_builds() -> Box<Iterator<Item = [&'static Item<'static>; 4]>> {
    box RINGS.iter()
             .flat_map(move |r1| {
                 RINGS.iter()
                      .filter(move |&r2| r2 != r1)
                      .flat_map(move |r2| {
                          WEAPONS.iter()
                                 .flat_map(move |w| {
                                     ARMOR.iter()
                                          .map(move |a| [w, a, r1, r2])
                                 })
                      })
             })
}

fn least_expensive_win(boss: &Character) -> Option<usize> {
    let mut hero = Character::new();

    equipment_builds()
        .filter(|e| {
            hero.equip(e);
            hero.attack(&boss)
        })
        .map(|e| e.iter().fold(0, |cost, &i| cost + i.cost))
        .min()
}

fn most_expensive_loss(boss: &Character) -> Option<usize> {
    let mut hero = Character::new();

    equipment_builds()
        .filter(|e| {
            hero.equip(e);
            !hero.attack(&boss)
        })
        .map(|e| e.iter().fold(0, |cost, &i| cost + i.cost))
        .max()
}
<|endoftext|>


<commit_before>extern crate zwreec;
extern crate getopts;
extern crate libc;
#[macro_use] extern crate log;
extern crate time;
extern crate term;

use std::env;
use std::vec::Vec;
use std::error::Error;
use std::fs::File;
use std::io::{Read,Write};
use std::path::Path;
use std::process::exit;

use zwreec::config;
use zwreec::config::Config;

mod logger;

// found in:
// http://stackoverflow.com/a/27590832
macro_rules! print_stderr(
    ($($arg:tt)*) => (
        match write!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

/// Returns the primary options for zwreec. It is e.g. used to generate the
/// usage information.
fn short_options() -> getopts::Options {
    let mut opts = getopts::Options::new();
    opts.optflagmulti("v", "verbose", "Be more verbose. Can be used multiple times.");
    opts.optflag("q", "quiet", "Be quiet");
    opts.optflag("w", "overwrite", "Overwrite output file if necessary.");
    opts.optflagopt("l", "logfile", "Specify log file (default standard error)", "LOGFILE");
    opts.optopt("o", "", "Name of the output file", "FILE");
    opts.optflag("h", "help", "Display this help and exit");
    opts.optflag("V", "version", "Display version");

    opts
}

/// Prints usage information
/// The verbose flag signals if all options should be shown.
/// NOTE: This is similar to librustc_driver's usage function.
fn usage(verbose: bool) {
    let options = if verbose {
        config::zwreec_options(short_options())
    } else {
        short_options()
    };

    let brief = format!("Usage: zwreec [-hV] [-vqwf] [-l [LOGFILE]] [-o OUTPUT] INPUT");

    println!("{}", config::zwreec_usage(verbose, options, &brief));
}

/// Parse command line arguments
///
/// Parses command line arguments to set up the logger and extract input and
/// output parameters. Will display the usage and exit depending on arguments.
///
/// Returns `getopts::Matches` and a `std::fs::File` for the input and output
/// file. The `getopts::Matches` can be used to
///
/// # Examples
///
/// ```
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "display this help and exit");
///
/// let (matches, mut input, mut output) = parse_arguments(
///     env::args().collect(),
///     opts
/// );
/// ```
///
/// # Failures
///
/// Depending on encountered arguments or parsing errors this function will
/// print the usage and/or call `exit(1)`.
///
/// NOTE: This is similar to librustc_driver's handle_options function.
fn parse_arguments(args: Vec<String>, opts: getopts::Options) -> (getopts::Matches, config::Config) {
    let mut loggers: Vec<Box<logger::SharedLogger>> = vec![];

    if args.is_empty() {
        // No options provided, print usage and exit
        usage(false);
        exit(1);
    }

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(f) => {
            // parsing error
            // display usage and return
            print_stderr!("{}\n", f.to_string());
            exit(1);
        }
    };

    if matches.opt_present("help") {
        usage(matches.opt_present("verbose"));
        exit(1);
    }

    if matches.opt_present("version") {
        println!("zwreec {}", match option_env!("CFG_VERSION") {
            Some(s) => s.to_string(),
            None => format!("{}.{}.{}{}",
                            env!("CARGO_PKG_VERSION_MAJOR"),
                            env!("CARGO_PKG_VERSION_MINOR"),
                            env!("CARGO_PKG_VERSION_PATCH"),
                            option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
        });
        exit(0);
    }

    if matches.opt_present("verbose") {
        // set log level to verbose
        loggers.push(logger::TermLogger::new(
                match matches.opt_count("v") {
                    1 => logger::LogLevelFilter::Info,
                    2 => logger::LogLevelFilter::Debug,
                    _ => logger::LogLevelFilter::Trace,
                }));
    } else if matches.opt_present("quiet") {
        // set log level to error
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Error));
    } else {
        // set log level to warn
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Warn));
    }

    if matches.opt_present("logfile") {
        // sets a logger to output to logfile
        let name = if let Some(n) = matches.opt_str("logfile") {
            n
        } else {
            "zwreec.log".to_string()
        };
        loggers.push(logger::FileLogger::new(
                        logger::LogLevelFilter::Trace,
                        File::create(name).unwrap())
            );
    }

    // TODO: This might not belong in a function called parse_arguments
    // activate logger
    let _ = logger::CombinedLogger::init(loggers);

    let cfg = Config::from_matches(&matches);
    (matches, cfg)
}

fn parse_input(matches: &getopts::Matches) -> Option<Box<Read>> {
    if matches.free.len() == 1 {
        let path = Path::new(&matches.free[0]);
        match File::open(path) {
            Err(why) => {
                error!("Couldn't open {}: {}",
                    path.display(), Error::description(&why));
                None
            },
            Ok(file) => {
                info!("Opened input: {}", path.display());
                Some(Box::new(file))
            }
        }
    } else if unsafe { libc::isatty(libc::STDIN_FILENO as i32) } == 0 {
        // Not connected to a terminal, assuming safe to read from stdin
        info!("Reading input from stdin");
        Some(Box::new(std::io::stdin()))
    } else {
        None
    }
}

fn parse_output(matches: &getopts::Matches) -> Option<Box<Write>> {
    let name = matches.opt_str("o").unwrap_or("a.z8".to_string());

    if name == "-" {
        // tty requested
        if unsafe { libc::isatty(libc::STDOUT_FILENO as i32)  } == 0 {
            // Not connected to a terminal, assuming safe to write to stdin
            // NOTE: this should be considered unsafe, as the library is *not*
            // guaranteed to only print to stderr
            warn!("Writing to stdout can lead to unusable output!");
            warn!("You should specify an output name using -o 'FILE'");
            info!("Writing output to stdout");
            Some(Box::new(std::io::stdout()))
        } else {
            error!("stdout is connected to a terminal.");
            error!("Zcode is a binary format and should not be printed to a tty.");
            None
        }
    } else {
        // opening file
        let path = Path::new(&name);

        if name == "a.z8" {
            debug!("No output file specified, using {}", path.display());
        }

        // Check if FILE exists and issue warning.
        if File::open(path).is_ok() {
            if !matches.opt_present("w") {
                error!("Output file {} already exists. Use '-o NAME' to use a different name or '-w' to overwrite!",
                       path.display());
                return None;
            } else {
                warn!("Output file {} already exists", path.display());
            }
        }

        match File::create(path) {
            Err(why) => {
                error!("Couldn't open {}: {}",
                       path.display(), Error::description(&why));
                None
            },
            Ok(file) => {
                info!("Opened output: {}", path.display());
                Some(Box::new(file))
            }
        }
    }
}

fn main() {
    // handle command line parameters
    let (matches, cfg) = parse_arguments(
        env::args().collect(),
        config::zwreec_options(short_options())
    );

    let mut input = parse_input(&matches);
    let mut output = parse_output(&matches);

    debug!("Parsed command line options");
    info!("Main started");

    // call library
    if !cfg.test_cases.is_empty() {
        zwreec::test_library(cfg, &mut input, &mut output);
    } else {
        // unwrap input and output
        let mut _input = match input {
            Some(i) => i,
            None => panic!("Missing input file! Compile aborted")
        };
        let mut _output = match output {
            Some(o) => o,
            None => panic!("Missing output file! Compile aborted")
        };
        zwreec::compile(cfg, &mut _input, &mut _output);
    }

    info!("Main finished");
}
<commit_msg>Exit with 0 when explicitly using --help. Other tools do this too.<commit_after>extern crate zwreec;
extern crate getopts;
extern crate libc;
#[macro_use] extern crate log;
extern crate time;
extern crate term;

use std::env;
use std::vec::Vec;
use std::error::Error;
use std::fs::File;
use std::io::{Read,Write};
use std::path::Path;
use std::process::exit;

use zwreec::config;
use zwreec::config::Config;

mod logger;

// found in:
// http://stackoverflow.com/a/27590832
macro_rules! print_stderr(
    ($($arg:tt)*) => (
        match write!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

/// Returns the primary options for zwreec. It is e.g. used to generate the
/// usage information.
fn short_options() -> getopts::Options {
    let mut opts = getopts::Options::new();
    opts.optflagmulti("v", "verbose", "Be more verbose. Can be used multiple times.");
    opts.optflag("q", "quiet", "Be quiet");
    opts.optflag("w", "overwrite", "Overwrite output file if necessary.");
    opts.optflagopt("l", "logfile", "Specify log file (default standard error)", "LOGFILE");
    opts.optopt("o", "", "Name of the output file", "FILE");
    opts.optflag("h", "help", "Display this help and exit");
    opts.optflag("V", "version", "Display version");

    opts
}

/// Prints usage information
/// The verbose flag signals if all options should be shown.
/// NOTE: This is similar to librustc_driver's usage function.
fn usage(verbose: bool) {
    let options = if verbose {
        config::zwreec_options(short_options())
    } else {
        short_options()
    };

    let brief = format!("Usage: zwreec [-hV] [-vqwf] [-l [LOGFILE]] [-o OUTPUT] INPUT");

    println!("{}", config::zwreec_usage(verbose, options, &brief));
}

/// Parse command line arguments
///
/// Parses command line arguments to set up the logger and extract input and
/// output parameters. Will display the usage and exit depending on arguments.
///
/// Returns `getopts::Matches` and a `std::fs::File` for the input and output
/// file. The `getopts::Matches` can be used to
///
/// # Examples
///
/// ```
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "display this help and exit");
///
/// let (matches, mut input, mut output) = parse_arguments(
///     env::args().collect(),
///     opts
/// );
/// ```
///
/// # Failures
///
/// Depending on encountered arguments or parsing errors this function will
/// print the usage and/or call `exit(1)`.
///
/// NOTE: This is similar to librustc_driver's handle_options function.
fn parse_arguments(args: Vec<String>, opts: getopts::Options) -> (getopts::Matches, config::Config) {
    let mut loggers: Vec<Box<logger::SharedLogger>> = vec![];

    if args.is_empty() {
        // No options provided, print usage and exit
        usage(false);
        exit(1);
    }

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(f) => {
            // parsing error
            // display usage and return
            print_stderr!("{}\n", f.to_string());
            exit(1);
        }
    };

    if matches.opt_present("help") {
        usage(matches.opt_present("verbose"));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("zwreec {}", match option_env!("CFG_VERSION") {
            Some(s) => s.to_string(),
            None => format!("{}.{}.{}{}",
                            env!("CARGO_PKG_VERSION_MAJOR"),
                            env!("CARGO_PKG_VERSION_MINOR"),
                            env!("CARGO_PKG_VERSION_PATCH"),
                            option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
        });
        exit(0);
    }

    if matches.opt_present("verbose") {
        // set log level to verbose
        loggers.push(logger::TermLogger::new(
                match matches.opt_count("v") {
                    1 => logger::LogLevelFilter::Info,
                    2 => logger::LogLevelFilter::Debug,
                    _ => logger::LogLevelFilter::Trace,
                }));
    } else if matches.opt_present("quiet") {
        // set log level to error
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Error));
    } else {
        // set log level to warn
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Warn));
    }

    if matches.opt_present("logfile") {
        // sets a logger to output to logfile
        let name = if let Some(n) = matches.opt_str("logfile") {
            n
        } else {
            "zwreec.log".to_string()
        };
        loggers.push(logger::FileLogger::new(
                        logger::LogLevelFilter::Trace,
                        File::create(name).unwrap())
            );
    }

    // TODO: This might not belong in a function called parse_arguments
    // activate logger
    let _ = logger::CombinedLogger::init(loggers);

    let cfg = Config::from_matches(&matches);
    (matches, cfg)
}

fn parse_input(matches: &getopts::Matches) -> Option<Box<Read>> {
    if matches.free.len() == 1 {
        let path = Path::new(&matches.free[0]);
        match File::open(path) {
            Err(why) => {
                error!("Couldn't open {}: {}",
                    path.display(), Error::description(&why));
                None
            },
            Ok(file) => {
                info!("Opened input: {}", path.display());
                Some(Box::new(file))
            }
        }
    } else if unsafe { libc::isatty(libc::STDIN_FILENO as i32) } == 0 {
        // Not connected to a terminal, assuming safe to read from stdin
        info!("Reading input from stdin");
        Some(Box::new(std::io::stdin()))
    } else {
        None
    }
}

fn parse_output(matches: &getopts::Matches) -> Option<Box<Write>> {
    let name = matches.opt_str("o").unwrap_or("a.z8".to_string());

    if name == "-" {
        // tty requested
        if unsafe { libc::isatty(libc::STDOUT_FILENO as i32)  } == 0 {
            // Not connected to a terminal, assuming safe to write to stdin
            // NOTE: this should be considered unsafe, as the library is *not*
            // guaranteed to only print to stderr
            warn!("Writing to stdout can lead to unusable output!");
            warn!("You should specify an output name using -o 'FILE'");
            info!("Writing output to stdout");
            Some(Box::new(std::io::stdout()))
        } else {
            error!("stdout is connected to a terminal.");
            error!("Zcode is a binary format and should not be printed to a tty.");
            None
        }
    } else {
        // opening file
        let path = Path::new(&name);

        if name == "a.z8" {
            debug!("No output file specified, using {}", path.display());
        }

        // Check if FILE exists and issue warning.
        if File::open(path).is_ok() {
            if !matches.opt_present("w") {
                error!("Output file {} already exists. Use '-o NAME' to use a different name or '-w' to overwrite!",
                       path.display());
                return None;
            } else {
                warn!("Output file {} already exists", path.display());
            }
        }

        match File::create(path) {
            Err(why) => {
                error!("Couldn't open {}: {}",
                       path.display(), Error::description(&why));
                None
            },
            Ok(file) => {
                info!("Opened output: {}", path.display());
                Some(Box::new(file))
            }
        }
    }
}

fn main() {
    // handle command line parameters
    let (matches, cfg) = parse_arguments(
        env::args().collect(),
        config::zwreec_options(short_options())
    );

    let mut input = parse_input(&matches);
    let mut output = parse_output(&matches);

    debug!("Parsed command line options");
    info!("Main started");

    // call library
    if !cfg.test_cases.is_empty() {
        zwreec::test_library(cfg, &mut input, &mut output);
    } else {
        // unwrap input and output
        let mut _input = match input {
            Some(i) => i,
            None => panic!("Missing input file! Compile aborted")
        };
        let mut _output = match output {
            Some(o) => o,
            None => panic!("Missing output file! Compile aborted")
        };
        zwreec::compile(cfg, &mut _input, &mut _output);
    }

    info!("Main finished");
}
<|endoftext|>


<commit_before>/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Sahid Orentino Ferdjaoui <sahid.ferdjaoui@redhat.com>
 */

extern crate libc;

use std::ffi::CStr;

#[allow(non_camel_case_types)]
#[repr(C)]
struct virError {
    code: libc::c_int,
    domain: libc::c_int,
    message: *const libc::c_char,
    level: libc::c_int,
}

#[link(name = "virt")]
extern "C" {
    //    fn virGetLastErrorMessage() -> *const libc::c_char;
    fn virGetLastError() -> *const virError;
}

/// Error handling
///
/// See: http://libvirt.org/html/libvirt-virterror.html
#[derive(Debug, PartialEq)]
pub struct Error {
    pub code: i32,
    pub domain: i32,
    pub message: String,
    pub level: i32,
}

impl Error {
    pub fn new() -> Error {
        unsafe {
            let ptr: *const virError = virGetLastError();
            Error {
                code: (*ptr).code,
                domain: (*ptr).domain,
                message: CStr::from_ptr((*ptr).message)
                    .to_string_lossy()
                    .into_owned(),
                level: (*ptr).level,
            }
        }
    }
}
/*
pub fn last_error_message() -> String {
    unsafe {
        CStr::from_ptr(
            virGetLastErrorMessage()).to_string_lossy().into_owned()
    }
}
*/
<commit_msg>error: just cleanup<commit_after>/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Sahid Orentino Ferdjaoui <sahid.ferdjaoui@redhat.com>
 */

extern crate libc;

pub mod sys {
    extern crate libc;

    #[allow(non_camel_case_types)]
    #[repr(C)]
    pub struct virError {
        pub code: libc::c_int,
        pub domain: libc::c_int,
        pub message: *mut libc::c_char,
        pub level: libc::c_uint,
    }

    #[allow(non_camel_case_types)]
    pub type virErrorPtr = *mut virError;
}

#[link(name = "virt")]
extern "C" {
    fn virGetLastError() -> sys::virErrorPtr;
}

pub type ErrorLevel = self::libc::c_uint;
pub const VIR_ERR_NONE: ErrorLevel = 0;
pub const VIR_ERR_WARNING: ErrorLevel = 1;
pub const VIR_ERR_ERROR: ErrorLevel = 2;

/// Error handling
///
/// See: http://libvirt.org/html/libvirt-virterror.html
#[derive(Debug, PartialEq)]
pub struct Error {
    pub code: i32,
    pub domain: i32,
    pub message: String,
    pub level: ErrorLevel,
}

impl Error {
    pub fn new() -> Error {
        unsafe {
            let ptr: sys::virErrorPtr = virGetLastError();
            Error {
                code: (*ptr).code,
                domain: (*ptr).domain,
                message: c_chars_to_string!((*ptr).message, nofree),
                level: (*ptr).level,
            }
        }
    }
}
<|endoftext|>