<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(())
}

/// 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>add some test cases<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,
    )?;

    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");
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::env;

#[derive(Debug)]
pub struct Options {
    pub format: String,
    pub outpath: String,
    pub url: String,
}

impl Options {
    fn get_format_option_name() -> &'static str {
        "f"
    }

    fn get_outpath_option_name() -> &'static str {
        "o"
    }

    fn get_url_option_name() -> &'static str {
        "url"
    }

    fn has_option_with_name(option_name: &String) -> bool {
        let format_option_name = Options::get_format_option_name();
        let outpath_option_name = Options::get_outpath_option_name();
        let url_option_name = Options::get_url_option_name();

        option_name == format_option_name || option_name == outpath_option_name ||
        option_name == url_option_name
    }

    fn set_option(&mut self, option_name: String, option_value: String) {
        let format_option_name = Options::get_format_option_name();
        let outpath_option_name = Options::get_outpath_option_name();
        let url_option_name = Options::get_url_option_name();

        if option_name == format_option_name {
            self.format = option_value
        } else if option_name == outpath_option_name {
            self.outpath = option_value
        } else if option_name == url_option_name {
            self.url = option_value
        } else {
            println!("Unknown option = {}", option_name)
        }
    }
}

pub fn parse_options() -> (String, Options, Vec<String>) {
    let options = Options {
        format: String::new(),
        outpath: String::new(),
        url: String::new(),
    };
    let options = RefCell::new(options);

    let mut args = env::args();
    let app_path = args.next().unwrap();

    let mut current_option_name: Option<String> = Option::None;
    let mut remaining_arguments: Vec<String> = Vec::new();
    for argument in args {
        if let Some(option_name) = current_option_name {
            let mut options = options.borrow_mut();
            options.set_option(option_name.to_string(), argument);
            current_option_name = Option::None
        } else if argument.starts_with('-') {
            let argument_without_dashes = argument.trim_left_matches('-');
            if argument_without_dashes.contains('=') {
                let parts: Vec<&str> = argument_without_dashes.splitn(2, '=').collect();
                let option_name = parts.first().unwrap();
                let option_value = parts.last().unwrap();
                let mut options = options.borrow_mut();
                options.set_option(option_name.to_string(), option_value.to_string());
            } else {
                let option_name = argument_without_dashes.to_string();
                if Options::has_option_with_name(&option_name) {
                    current_option_name = Some(option_name);
                } else {
                    println!("Unknown option {}", argument_without_dashes)
                }
            }
        } else {
            remaining_arguments.push(argument)
        }
    }

    (app_path, options.into_inner(), remaining_arguments)
}
<commit_msg>default constructor for Options was added<commit_after>use std::cell::RefCell;
use std::env;

#[derive(Debug)]
pub struct Options {
    pub format: String,
    pub outpath: String,
    pub url: String,
}

impl Options {
    pub fn new() -> Options {
        Options { format: String::new(), outpath: String::new(),
                  url: String::new() }
    }

    fn get_format_option_name() -> &'static str {
        "f"
    }

    fn get_outpath_option_name() -> &'static str {
        "o"
    }

    fn get_url_option_name() -> &'static str {
        "url"
    }

    fn has_option_with_name(option_name: &String) -> bool {
        let format_option_name = Options::get_format_option_name();
        let outpath_option_name = Options::get_outpath_option_name();
        let url_option_name = Options::get_url_option_name();

        option_name == format_option_name || option_name == outpath_option_name ||
        option_name == url_option_name
    }

    fn set_option(&mut self, option_name: String, option_value: String) {
        let format_option_name = Options::get_format_option_name();
        let outpath_option_name = Options::get_outpath_option_name();
        let url_option_name = Options::get_url_option_name();

        if option_name == format_option_name {
            self.format = option_value
        } else if option_name == outpath_option_name {
            self.outpath = option_value
        } else if option_name == url_option_name {
            self.url = option_value
        } else {
            println!("Unknown option = {}", option_name)
        }
    }
}

pub fn parse_options() -> (String, Options, Vec<String>) {
    let options = Options::new();
    let options = RefCell::new(options);

    let mut args = env::args();
    let app_path = args.next().unwrap();

    let mut current_option_name: Option<String> = Option::None;
    let mut remaining_arguments: Vec<String> = Vec::new();
    for argument in args {
        if let Some(option_name) = current_option_name {
            let mut options = options.borrow_mut();
            options.set_option(option_name.to_string(), argument);
            current_option_name = Option::None
        } else if argument.starts_with('-') {
            let argument_without_dashes = argument.trim_left_matches('-');
            if argument_without_dashes.contains('=') {
                let parts: Vec<&str> = argument_without_dashes.splitn(2, '=').collect();
                let option_name = parts.first().unwrap();
                let option_value = parts.last().unwrap();
                let mut options = options.borrow_mut();
                options.set_option(option_name.to_string(), option_value.to_string());
            } else {
                let option_name = argument_without_dashes.to_string();
                if Options::has_option_with_name(&option_name) {
                    current_option_name = Some(option_name);
                } else {
                    println!("Unknown option {}", argument_without_dashes)
                }
            }
        } else {
            remaining_arguments.push(argument)
        }
    }

    (app_path, options.into_inner(), remaining_arguments)
}
<|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/.

// Discover or identify devices that may belong to Stratis.

use std::{
    collections::HashMap,
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::Device;

use crate::engine::{
    strat_engine::{
        backstore::metadata::device_identifiers,
        udev::{block_enumerator, decide_ownership, UdevOwnership},
    },
    types::{DevUuid, PoolUuid},
};

// A wrapper for obtaining the device number as a devicemapper Device
// which interprets absence of the value as an error, which it is in this
// context.
fn device_to_devno_wrapper(device: &libudev::Device) -> Result<Device, String> {
    device
        .devnum()
        .ok_or_else(|| "udev entry did not contain a device number".into())
        .map(Device::from)
}

// A wrapper around the metadata module's device_identifers method
// which also handles failure to open a device for reading.
// Returns an error if the device could not be opened for reading.
// Returns Ok(Err(...)) if there was an error while reading the
// Stratis identifiers from the device.
// Returns Ok(Ok(None)) if the identifers did not appear to be on
// the device.
fn device_identifiers_wrapper(
    devnode: &Path,
) -> Result<Result<Option<(PoolUuid, DevUuid)>, String>, String> {
    OpenOptions::new()
        .read(true)
        .open(devnode)
        .as_mut()
        .map_err(|err| {
            format!(
                "device {} could not be opened for reading: {}",
                devnode.display(),
                err
            )
        })
        .map(|f| {
            device_identifiers(f).map_err(|err| {
                format!(
                    "encountered an error while reading Stratis header for device {}: {}",
                    devnode.display(),
                    err
                )
            })
        })
}

/// Process a device which udev information indicates is a Stratis device.
fn process_stratis_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) | (_, Ok(Err(err))) => {
                    warn!("udev identified device {} as a Stratis device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                (_, Ok(Ok(None))) => {
                    warn!("udev identified device {} as a Stratis device but there appeared to be no Stratis metadata on the device, disregarding the device",
                          devnode.display());
                    None
                }
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a Stratis device, but the udev entry for the device had no device node, disregarding device");
            None
        }
    }
}

/// Process a device which udev information indicates is unowned.
fn process_unowned_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) => {
                    warn!("udev identified device {} as a block device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                // FIXME: Refine error return in StaticHeader::setup(),
                // so it can be used to distinguish between signficant
                // and insignficant errors and then use that ability to
                // distinguish here between different levels of
                // severity.
                (_, Ok(Err(err))) => {
                    debug!("udev identified device {} as a block device but {}, disregarding the device",
                           devnode.display(),
                           err);
                    None
                }
                (_, Ok(Ok(None))) => None,
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a block device, but the udev entry for the device had no device node, disregarding the device");
            None
        }
    }
}

// Use udev to identify all block devices and return the subset of those
// that have Stratis signatures.
fn find_all_block_devices_with_stratis_signatures(
) -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;

    let pool_map = enumerator
        .scan_devices()?
        .filter_map(|dev| identify_block_device(&dev))
        .fold(
            HashMap::new(),
            |mut acc, (pool_uuid, _, device, devnode)| {
                acc.entry(pool_uuid)
                    .or_insert_with(HashMap::new)
                    .insert(device, devnode);
                acc
            },
        );

    Ok(pool_map)
}

// Find all devices identified by udev as Stratis devices.
fn find_all_stratis_devices() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;
    enumerator.match_property("ID_FS_TYPE", "stratis")?;

    let pool_map = enumerator.scan_devices()?
        .filter(|dev| {
            let initialized = dev.is_initialized();
            if !initialized {
                warn!("Found a udev entry for a device identified as a Stratis device, but udev also identified it as uninitialized, omitting the device from the set of devices to process, for safety");
            };
            initialized
        })
        .filter_map(|dev| {
            decide_ownership(&dev)
                .map_err(|err| {
                    warn!("Could not determine ownership of a block device identified as a Stratis device by udev because of an error processing udev information, omitting the device from the set of devices to process, for safety: {}",
                          err);
                })
            .map(|decision| match decision {
                UdevOwnership::Stratis => process_stratis_device(&dev),
                UdevOwnership::MultipathMember => None,
                _ => {
                    warn!("udev enumeration identified this device as a Stratis block device but on further examination it appears not to belong to Stratis");
                    None
                }
            })
            .unwrap_or(None)
        })
        .fold(HashMap::new(), |mut acc, (pool_uuid, _, device, devnode)| {
            acc.entry(pool_uuid).or_insert_with(HashMap::new).insert(device, devnode);
            acc
        });
    Ok(pool_map)
}

/// Identify a block device in the context where a udev event has been
/// captured for some block device. Return None if the device does not
/// appear to be a Stratis device. Log at an appropriate level on all errors.
pub fn identify_block_device(
    dev: &libudev::Device,
) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    let initialized = dev.is_initialized();
    if !initialized {
        debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, disregarding the device");
        return None;
    };

    match decide_ownership(dev) {
        Err(err) => {
            warn!("Could not determine ownership of a udev block device because of an error processing udev information, disregarding the device: {}",
                  err);
            None
        }
        Ok(ownership) => match ownership {
            UdevOwnership::Stratis => process_stratis_device(dev),
            UdevOwnership::Unowned => process_unowned_device(dev),
            _ => None,
        },
    }
}

/// Retrieve all block devices that should be made use of by the
/// Stratis engine. This excludes Stratis block devices that appear to be
/// multipath members.
///
/// Includes a fallback path, which is used if no Stratis block devices are
/// found using the obvious udev property- and enumerator-based approach.
/// This fallback path is more expensive, because it must search all block
/// devices via udev rather than just all Stratis block devices.
///
/// Omits any device that appears problematic in some way.
///
/// Return an error only on a failure to construct or scan with a udev
/// enumerator.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    info!("Beginning initial search for Stratis block devices");
    let pool_map = find_all_stratis_devices()?;

    if pool_map.is_empty() {
        // No Stratis devices have been found, possible reasons are:
        // 1. There are none
        // 2. There are some but libblkid version is less than 2.32, so
        // Stratis devices are not recognized by udev.
        // 3. There are many incomplete udev entries, because this code is
        // being run before the udev database is populated.
        //
        // Try again to find Stratis block devices, but this time enumerate
        // all block devices, not just all the ones that can be identified
        // as Stratis blockdevs by udev, and process only those that appear
        // unclaimed or appear to be claimed by Stratis (and not
        // multipath members).

        info!("Could not identify any Stratis devices by a udev search for devices with ID_FS_TYPE=\"stratis\"; using fallback search mechanism");

        find_all_block_devices_with_stratis_signatures()
    } else {
        Ok(pool_map)
    }
}
<commit_msg>Make a method called identify_stratis_device<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/.

// Discover or identify devices that may belong to Stratis.

use std::{
    collections::HashMap,
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::Device;

use crate::engine::{
    strat_engine::{
        backstore::metadata::device_identifiers,
        udev::{block_enumerator, decide_ownership, UdevOwnership},
    },
    types::{DevUuid, PoolUuid},
};

// A wrapper for obtaining the device number as a devicemapper Device
// which interprets absence of the value as an error, which it is in this
// context.
fn device_to_devno_wrapper(device: &libudev::Device) -> Result<Device, String> {
    device
        .devnum()
        .ok_or_else(|| "udev entry did not contain a device number".into())
        .map(Device::from)
}

// A wrapper around the metadata module's device_identifers method
// which also handles failure to open a device for reading.
// Returns an error if the device could not be opened for reading.
// Returns Ok(Err(...)) if there was an error while reading the
// Stratis identifiers from the device.
// Returns Ok(Ok(None)) if the identifers did not appear to be on
// the device.
fn device_identifiers_wrapper(
    devnode: &Path,
) -> Result<Result<Option<(PoolUuid, DevUuid)>, String>, String> {
    OpenOptions::new()
        .read(true)
        .open(devnode)
        .as_mut()
        .map_err(|err| {
            format!(
                "device {} could not be opened for reading: {}",
                devnode.display(),
                err
            )
        })
        .map(|f| {
            device_identifiers(f).map_err(|err| {
                format!(
                    "encountered an error while reading Stratis header for device {}: {}",
                    devnode.display(),
                    err
                )
            })
        })
}

/// Process a device which udev information indicates is a Stratis device.
fn process_stratis_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) | (_, Ok(Err(err))) => {
                    warn!("udev identified device {} as a Stratis device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                (_, Ok(Ok(None))) => {
                    warn!("udev identified device {} as a Stratis device but there appeared to be no Stratis metadata on the device, disregarding the device",
                          devnode.display());
                    None
                }
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a Stratis device, but the udev entry for the device had no device node, disregarding device");
            None
        }
    }
}

/// Process a device which udev information indicates is unowned.
fn process_unowned_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) => {
                    warn!("udev identified device {} as a block device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                // FIXME: Refine error return in StaticHeader::setup(),
                // so it can be used to distinguish between signficant
                // and insignficant errors and then use that ability to
                // distinguish here between different levels of
                // severity.
                (_, Ok(Err(err))) => {
                    debug!("udev identified device {} as a block device but {}, disregarding the device",
                           devnode.display(),
                           err);
                    None
                }
                (_, Ok(Ok(None))) => None,
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a block device, but the udev entry for the device had no device node, disregarding the device");
            None
        }
    }
}

// Use udev to identify all block devices and return the subset of those
// that have Stratis signatures.
fn find_all_block_devices_with_stratis_signatures(
) -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;

    let pool_map = enumerator
        .scan_devices()?
        .filter_map(|dev| identify_block_device(&dev))
        .fold(
            HashMap::new(),
            |mut acc, (pool_uuid, _, device, devnode)| {
                acc.entry(pool_uuid)
                    .or_insert_with(HashMap::new)
                    .insert(device, devnode);
                acc
            },
        );

    Ok(pool_map)
}

// Find all devices identified by udev as Stratis devices.
fn find_all_stratis_devices() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;
    enumerator.match_property("ID_FS_TYPE", "stratis")?;

    let pool_map = enumerator
        .scan_devices()?
        .filter_map(|dev| identify_stratis_device(&dev))
        .fold(
            HashMap::new(),
            |mut acc, (pool_uuid, _, device, devnode)| {
                acc.entry(pool_uuid)
                    .or_insert_with(HashMap::new)
                    .insert(device, devnode);
                acc
            },
        );
    Ok(pool_map)
}

// Identify a device that udev enumeration has already picked up as a Stratis
// device. Return None if the device does not, after all, appear to be a Stratis
// device. Log anything unusual at an appropriate level.
fn identify_stratis_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    let initialized = dev.is_initialized();
    if !initialized {
        warn!("Found a udev entry for a device identified as a Stratis device, but udev also identified it as uninitialized, disregarding the device");
        return None;
    };

    match decide_ownership(dev) {
        Err(err) => {
            warn!("Could not determine ownership of a block device identified as a Stratis device by udev because of an error processing udev information, disregarding the device: {}",
                  err);
            None
        }
        Ok(ownership) => match ownership {
            UdevOwnership::Stratis => process_stratis_device(dev),
            UdevOwnership::MultipathMember => None,
            _ => {
                warn!("udev enumeration identified this device as a Stratis block device but on further examination it appears not to belong to Stratis");
                None
            }
        },
    }
}

/// Identify a block device in the context where a udev event has been
/// captured for some block device. Return None if the device does not
/// appear to be a Stratis device. Log at an appropriate level on all errors.
pub fn identify_block_device(
    dev: &libudev::Device,
) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    let initialized = dev.is_initialized();
    if !initialized {
        debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, disregarding the device");
        return None;
    };

    match decide_ownership(dev) {
        Err(err) => {
            warn!("Could not determine ownership of a udev block device because of an error processing udev information, disregarding the device: {}",
                  err);
            None
        }
        Ok(ownership) => match ownership {
            UdevOwnership::Stratis => process_stratis_device(dev),
            UdevOwnership::Unowned => process_unowned_device(dev),
            _ => None,
        },
    }
}

/// Retrieve all block devices that should be made use of by the
/// Stratis engine. This excludes Stratis block devices that appear to be
/// multipath members.
///
/// Includes a fallback path, which is used if no Stratis block devices are
/// found using the obvious udev property- and enumerator-based approach.
/// This fallback path is more expensive, because it must search all block
/// devices via udev rather than just all Stratis block devices.
///
/// Omits any device that appears problematic in some way.
///
/// Return an error only on a failure to construct or scan with a udev
/// enumerator.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    info!("Beginning initial search for Stratis block devices");
    let pool_map = find_all_stratis_devices()?;

    if pool_map.is_empty() {
        // No Stratis devices have been found, possible reasons are:
        // 1. There are none
        // 2. There are some but libblkid version is less than 2.32, so
        // Stratis devices are not recognized by udev.
        // 3. There are many incomplete udev entries, because this code is
        // being run before the udev database is populated.
        //
        // Try again to find Stratis block devices, but this time enumerate
        // all block devices, not just all the ones that can be identified
        // as Stratis blockdevs by udev, and process only those that appear
        // unclaimed or appear to be claimed by Stratis (and not
        // multipath members).

        info!("Could not identify any Stratis devices by a udev search for devices with ID_FS_TYPE=\"stratis\"; using fallback search mechanism");

        find_all_block_devices_with_stratis_signatures()
    } else {
        Ok(pool_map)
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate env_logger;

extern crate mig;

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

use mig::quic::threaded::{QuicConnection};

fn main() {
    env_logger::init().unwrap();

    let args = ::std::env::args;
    if args().len() != 3 || args().nth(1) == Some("--help".to_string()) {
        println!("Usage: file_client <serverip>:<port> <filename>");
        return;
    }

    let address = args().nth(1).unwrap();
    let filename = args().nth(2).unwrap();

    info!("Establishing connection...");
    let connection = match QuicConnection::new(&*address) {
        Ok(connection) => {
            connection
        },
        Err(e) => {
            error!("Cannot create a connection: {}", e);
            return;
        },
    };

    info!("Running client connected to {}", address);

    let mut stream = connection.get_stream(2);

    info!("Requesting the file");
    stream.write_all(filename.as_bytes()).unwrap();
    stream.finalize();

    let mut inc_buf = vec![];
    let amt = stream.read_to_end(&mut inc_buf).unwrap();
    let inc_buf = &inc_buf[..amt];
    let inc_string = str::from_utf8(&inc_buf).unwrap();
    println!("{}", inc_string);
}
<commit_msg>Update file_client to handle binary files<commit_after>#[macro_use]
extern crate log;
extern crate env_logger;

extern crate mig;

use std::io::Write;
use std::str;

use mig::quic::threaded::{QuicConnection};

fn main() {
    env_logger::init().unwrap();

    let args = ::std::env::args;
    if args().len() != 3 || args().nth(1) == Some("--help".to_string()) {
        println!("Usage: file_client <serverip>:<port> <filename>");
        return;
    }

    let address = args().nth(1).unwrap();
    let filename = args().nth(2).unwrap();

    info!("Establishing connection...");
    let connection = match QuicConnection::new(&*address) {
        Ok(connection) => {
            connection
        },
        Err(e) => {
            error!("Cannot create a connection: {}", e);
            return;
        },
    };

    info!("Running client connected to {}", address);

    let mut stream = connection.get_stream(2);

    info!("Requesting the file");
    stream.write_all(filename.as_bytes()).unwrap();
    stream.finalize();

    std::io::copy(&mut stream, &mut std::io::stdout()).unwrap();
}
<|endoftext|>


<commit_before>// errors1.rs
// This function refuses to generate text to be printed on a nametag if
// you pass it an empty string. It'd be nicer if it explained what the problem
// was, instead of just sometimes returning `None`. The 2nd test currently
// does not compile or pass, but it illustrates the behavior we would like
// this function to have.
// Scroll down for hints!!!

pub fn generate_nametag_text(name: String) -> Option<String> {
    if name.len() > 0 {
        Some(format!("Hi! My name is {}", name))
    } else {
        // Empty names aren't allowed.
        None
    }
}

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

    // This test passes initially if you comment out the 2nd test.
    // You'll need to update what this test expects when you change
    // the function under test!
    #[test]
    fn generates_nametag_text_for_a_nonempty_name() {
        assert_eq!(
            generate_nametag_text("Beyoncé".into()),
            Some("Hi! My name is Beyoncé".into())
        );
    }

    #[test]
    fn explains_why_generating_nametag_text_fails() {
        assert_eq!(
            generate_nametag_text("".into()),
            Err("`name` was empty; it must be nonempty.".into())
        );
    }
}

// `Err` is one of the variants of `Result`, so what the 2nd test is saying
// is that `generate_nametag_text` should return a `Result` instead of an
// `Option`.

// To make this change, you'll need to:
// - update the return type in the function signature to be a Result that
//   could be the variants `Ok(String)` and `Err(String)`
// - change the body of the function to return `Ok(stuff)` where it currently
//   returns `Some(stuff)`
// - change the body of the function to return `Err(error message)` where it
//   currently returns `None`
// - change the first test to expect `Ok(stuff)` where it currently expects
//   `Some(stuff)`.
<commit_msg>Update errors1.rs<commit_after>// errors1.rs
// This function refuses to generate text to be printed on a nametag if
// you pass it an empty string. It'd be nicer if it explained what the problem
// was, instead of just sometimes returning `None`. The 2nd test currently
// does not compile or pass, but it illustrates the behavior we would like
// this function to have.
// Scroll down for hints!!!

pub fn generate_nametag_text(name: String) -> Option<String> {
    if name.len() > 0 {
        Some(format!("Hi! My name is {}", name))
    } else {
        // Empty names aren't allowed.
        None
    }
}

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

    // This test passes initially if you comment out the 2nd test.
    // You'll need to update what this test expects when you change
    // the function under test!
    #[test]
    fn generates_nametag_text_for_a_nonempty_name() {
        assert_eq!(
            generate_nametag_text("Beyoncé".into()),
            Some("Hi! My name is Beyoncé".into())
        );
    }

    #[test]
    fn explains_why_generating_nametag_text_fails() {
        assert_eq!(
            generate_nametag_text("".into()),
            Err("`name` was empty; it must be nonempty.".into())
        );
    }
}

// `Err` is one of the variants of `Result`, so what the 2nd test is saying
// is that `generate_nametag_text` should return a `Result` instead of an
// `Option`.

// To make this change, you'll need to:
// - update the return type in the function signature to be a Result<String, String> that
//   could be the variants `Ok(String)` and `Err(String)`
// - change the body of the function to return `Ok(stuff)` where it currently
//   returns `Some(stuff)`
// - change the body of the function to return `Err(error message)` where it
//   currently returns `None`
// - change the first test to expect `Ok(stuff)` where it currently expects
//   `Some(stuff)`.
<|endoftext|>


<commit_before>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#![allow(unused_variables)]
#![allow(unused_imports)]

use byteorder::{BigEndian, ByteOrder};
use core::libchain::block::Block;
use core::libchain::chain::Chain;
use libproto;
use libproto::*;
use libproto::blockchain::{Status, RichStatus};
use protobuf::{Message, RepeatedField};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering, AtomicBool};
use std::sync::mpsc::{Sender, Receiver};
use std::thread;
use std::time::Duration;
use types::ids::BlockId;
use util::Address;

const BATCH_SYNC: u64 = 120;

pub struct Synchronizer {
    pub chain: Arc<Chain>,
    pub height_marker: AtomicUsize,
}

impl Synchronizer {
    pub fn new(chain: Arc<Chain>) -> Arc<Synchronizer> {
        Arc::new(Synchronizer {
                     height_marker: AtomicUsize::new(chain.get_current_height() as usize),
                     chain: chain,
                 })
    }

    pub fn sync(&self, ctx_pub: Sender<(String, Vec<u8>)>) {
        let block_map = self.chain.block_map.read();
        if !block_map.is_empty() {
            let start_height = self.chain.get_current_height() + 1;
            if !self.chain.is_sync.load(Ordering::SeqCst) {
                self.chain.is_sync.store(true, Ordering::SeqCst);
            }
            for height in start_height..start_height + BATCH_SYNC {
                if block_map.contains_key(&height) {
                    let value = block_map[&(height)].clone();
                    let block = value.1;
                    let is_verified = value.2;
                    if is_verified {
                        self.add_block(ctx_pub.clone(), block);
                    } else {
                        trace!("chain proof not ok height: {}, wait next sync", height);
                        break;
                    }
                } else {
                    trace!("chain sync break {:?}", height);
                    break;
                }
            }
        }
        self.chain.is_sync.store(false, Ordering::SeqCst);
    }

    pub fn sync_status(&self, ctx_pub: Sender<(String, Vec<u8>)>) {
        self.chain.is_sync.store(false, Ordering::SeqCst);
        let current_hash = self.chain.get_current_hash();
        let current_height = self.chain.get_current_height();
        let max_height = self.chain.get_max_height();
        let nodes: Vec<Address> = self.chain.nodes.read().clone();

        drop(self);
        info!("sync_status {:?}, {:?}", current_hash, current_height);

        let mut rich_status = RichStatus::new();
        rich_status.set_hash(current_hash.0.to_vec());
        rich_status.set_height(current_height);
        let node_list = nodes.into_iter().map(|address| address.to_vec()).collect();
        rich_status.set_nodes(RepeatedField::from_vec(node_list));

        let msg = factory::create_msg(submodules::CHAIN, topics::RICH_STATUS, communication::MsgType::RICH_STATUS, rich_status.write_to_bytes().unwrap());
        trace!("chain after sync current height {:?}  known height{:?}", current_height, max_height);
        ctx_pub.send(("chain.richstatus".to_string(), msg.write_to_bytes().unwrap())).unwrap();

        let status: Status = rich_status.into();
        let sync_msg = factory::create_msg(submodules::CHAIN, topics::NEW_STATUS, communication::MsgType::STATUS, status.write_to_bytes().unwrap());
        trace!("add_block chain.status {:?}, {:?}", status.get_height(), status.get_hash());
        ctx_pub.send(("chain.status".to_string(), sync_msg.write_to_bytes().unwrap())).unwrap();
    }

    fn add_block(&self, ctx_pub: Sender<(String, Vec<u8>)>, blk: Block) {
        trace!("chain sync add blk {:?}", blk.number());
        let rich_status = self.chain.set_block(blk);

        if let Some(rich_status) = rich_status {
            let msg = factory::create_msg(submodules::CHAIN, topics::RICH_STATUS, communication::MsgType::RICH_STATUS, rich_status.write_to_bytes().unwrap());
            trace!("chain after sync current height {:?}  known height{:?}", self.chain.get_current_height(), self.chain.get_max_height());
            ctx_pub.send(("chain.richstatus".to_string(), msg.write_to_bytes().unwrap())).unwrap();

            let status: Status = rich_status.into();
            let sync_msg = factory::create_msg(submodules::CHAIN, topics::NEW_STATUS, communication::MsgType::STATUS, status.write_to_bytes().unwrap());
            trace!("add_block chain.status {:?}, {:?}", status.get_height(), status.get_hash());
            ctx_pub.send(("chain.status".to_string(), sync_msg.write_to_bytes().unwrap())).unwrap();
            let block_height = status.get_height();
            self.sync_block_tx_hashes(block_height, ctx_pub);
        }
    }

    pub fn sync_block_tx_hashes(&self, block_height: u64, ctx_pub: Sender<(String, Vec<u8>)>) {
        if let Some(tx_hashes) = self.chain.transaction_hashes(BlockId::Number(block_height)) {
            //prepare and send the block tx hashes to auth
            let mut block_tx_hashes = BlockTxHashes::new();
            block_tx_hashes.set_height(block_height);
            let mut tx_hashes_in_u8 = Vec::new();
            for tx_hash_in_h256 in tx_hashes.iter() {
                tx_hashes_in_u8.push(tx_hash_in_h256.to_vec());
            }
            block_tx_hashes.set_tx_hashes(RepeatedField::from_slice(&tx_hashes_in_u8[..]));

            let msg = factory::create_msg(submodules::CHAIN, topics::BLOCK_TXHASHES, communication::MsgType::BLOCK_TXHASHES, block_tx_hashes.write_to_bytes().unwrap());

            ctx_pub.send(("chain.txhashes".to_string(), msg.write_to_bytes().unwrap())).unwrap();
            trace!("sync block's tx hashes for height:{}", block_height);
        }
    }
}
<commit_msg>No need richstatus for sync_status<commit_after>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use core::libchain::block::Block;
use core::libchain::chain::Chain;
use libproto::*;
use libproto::blockchain::Status;
use protobuf::{Message, RepeatedField};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::Sender;
use types::ids::BlockId;

const BATCH_SYNC: u64 = 120;

pub struct Synchronizer {
    pub chain: Arc<Chain>,
    pub height_marker: AtomicUsize,
}

impl Synchronizer {
    pub fn new(chain: Arc<Chain>) -> Arc<Synchronizer> {
        Arc::new(Synchronizer {
                     height_marker: AtomicUsize::new(chain.get_current_height() as usize),
                     chain: chain,
                 })
    }

    pub fn sync(&self, ctx_pub: Sender<(String, Vec<u8>)>) {
        let block_map = self.chain.block_map.read();
        if !block_map.is_empty() {
            let start_height = self.chain.get_current_height() + 1;
            if !self.chain.is_sync.load(Ordering::SeqCst) {
                self.chain.is_sync.store(true, Ordering::SeqCst);
            }
            for height in start_height..start_height + BATCH_SYNC {
                if block_map.contains_key(&height) {
                    let value = block_map[&(height)].clone();
                    let block = value.1;
                    let is_verified = value.2;
                    if is_verified {
                        self.add_block(ctx_pub.clone(), block);
                    } else {
                        trace!("chain proof not ok height: {}, wait next sync", height);
                        break;
                    }
                } else {
                    trace!("chain sync break {:?}", height);
                    break;
                }
            }
        }
        self.chain.is_sync.store(false, Ordering::SeqCst);
    }

    pub fn sync_status(&self, ctx_pub: Sender<(String, Vec<u8>)>) {
        self.chain.is_sync.store(false, Ordering::SeqCst);
        let current_hash = self.chain.get_current_hash();
        let current_height = self.chain.get_current_height();

        drop(self);
        info!("sync_status {:?}, {:?}", current_hash, current_height);

        let mut status = Status::new();
        status.set_hash(current_hash.0.to_vec());
        status.set_height(current_height);

        let sync_msg = factory::create_msg(submodules::CHAIN, topics::NEW_STATUS, communication::MsgType::STATUS, status.write_to_bytes().unwrap());
        trace!("add_block chain.status {:?}, {:?}", status.get_height(), status.get_hash());
        ctx_pub.send(("chain.status".to_string(), sync_msg.write_to_bytes().unwrap())).unwrap();
    }

    fn add_block(&self, ctx_pub: Sender<(String, Vec<u8>)>, blk: Block) {
        trace!("chain sync add blk {:?}", blk.number());
        let rich_status = self.chain.set_block(blk);

        if let Some(rich_status) = rich_status {
            let msg = factory::create_msg(submodules::CHAIN, topics::RICH_STATUS, communication::MsgType::RICH_STATUS, rich_status.write_to_bytes().unwrap());
            trace!("chain after sync current height {:?}  known height{:?}", self.chain.get_current_height(), self.chain.get_max_height());
            ctx_pub.send(("chain.richstatus".to_string(), msg.write_to_bytes().unwrap())).unwrap();

            let status: Status = rich_status.into();
            let sync_msg = factory::create_msg(submodules::CHAIN, topics::NEW_STATUS, communication::MsgType::STATUS, status.write_to_bytes().unwrap());
            trace!("add_block chain.status {:?}, {:?}", status.get_height(), status.get_hash());
            ctx_pub.send(("chain.status".to_string(), sync_msg.write_to_bytes().unwrap())).unwrap();
            let block_height = status.get_height();
            self.sync_block_tx_hashes(block_height, ctx_pub);
        }
    }

    pub fn sync_block_tx_hashes(&self, block_height: u64, ctx_pub: Sender<(String, Vec<u8>)>) {
        if let Some(tx_hashes) = self.chain.transaction_hashes(BlockId::Number(block_height)) {
            //prepare and send the block tx hashes to auth
            let mut block_tx_hashes = BlockTxHashes::new();
            block_tx_hashes.set_height(block_height);
            let mut tx_hashes_in_u8 = Vec::new();
            for tx_hash_in_h256 in tx_hashes.iter() {
                tx_hashes_in_u8.push(tx_hash_in_h256.to_vec());
            }
            block_tx_hashes.set_tx_hashes(RepeatedField::from_slice(&tx_hashes_in_u8[..]));

            let msg = factory::create_msg(submodules::CHAIN, topics::BLOCK_TXHASHES, communication::MsgType::BLOCK_TXHASHES, block_tx_hashes.write_to_bytes().unwrap());

            ctx_pub.send(("chain.txhashes".to_string(), msg.write_to_bytes().unwrap())).unwrap();
            trace!("sync block's tx hashes for height:{}", block_height);
        }
    }
}
<|endoftext|>


<commit_before>use slab::Slab;

use na::{self, RealField};
use crate::world::ColliderWorld;
use crate::object::{Body, DefaultBodySet, BodySet, BodyHandle, ColliderHandle, ColliderSet};
use crate::joint::{JointConstraint, JointConstraintSet};
use crate::utils::union_find::UnionFindSet;
use crate::utils::union_find;

/// Structure that monitors island-based activation/deactivation of bodies.
///
/// It is responsible for making objects sleep or wake up.
#[derive(Clone)]
pub struct ActivationManager<N: RealField, Handle: BodyHandle> {
    mix_factor: N,
    ufind: Vec<UnionFindSet>,
    can_deactivate: Vec<bool>,
    to_activate: Vec<Handle>,
    id_to_body: Vec<Handle>,
}

impl<N: RealField, Handle: BodyHandle> ActivationManager<N, Handle> {
    /// Creates a new `ActivationManager2`.
    ///
    /// # Arguments:
    /// * `thresold`   - the minimum energy required to keep an object awake.
    /// * `mix_factor` - the ratio of energy to keep between two frames.
    pub fn new(mix_factor: N) -> ActivationManager<N, Handle> {
        assert!(
            mix_factor >= na::zero(),
            "The energy mixing factor must be between 0.0 and 1.0."
        );

        ActivationManager {
            mix_factor,
            ufind: Vec::new(),
            can_deactivate: Vec::new(),
            to_activate: Vec::new(),
            id_to_body: Vec::new(),
        }
    }

    /// Notify the `ActivationManager2` that is has to activate an object at the next update.
    // FIXME: this is not a very good name
    pub fn deferred_activate(&mut self, handle: Handle) {
        self.to_activate.push(handle);
    }

    fn update_energy(&self, body: &mut (impl Body<N> + ?Sized)) {
        // FIXME: avoid the Copy when NLL lands ?
        let status = *body.activation_status();

        if let Some(threshold) = status.deactivation_threshold() {
            // FIXME: take the time in account (to make a true RWA)
            let new_energy = (N::one() - self.mix_factor) * status.energy()
                + self.mix_factor * (body.generalized_velocity().norm_squared());

            body.activate_with_energy(new_energy.min(threshold * na::convert(4.0f64)));
        }
    }

    /// Update the activation manager, activating and deactivating objects when needed.
    pub fn update<Bodies, Colliders, Constraints>(
        &mut self,
        bodies: &mut Bodies,
        colliders: &Colliders,
        cworld: &ColliderWorld<N, Handle, Colliders::Handle>,
        constraints: &Constraints,
        active_bodies: &mut Vec<Handle>,
    )
        where Bodies: BodySet<N, Handle = Handle>,
              Colliders: ColliderSet<N, Handle>,
              Constraints: JointConstraintSet<N, Bodies> {
        /*
         *
         * Update bodies energy
         *
         */
        self.id_to_body.clear();

        bodies.foreach_mut(|handle, body| {
            if body.status_dependent_ndofs() != 0 {
                if body.is_active() {
                    self.update_energy(body);
                }

                body.set_companion_id(self.id_to_body.len());
                self.id_to_body.push(handle);
            }

            if body.is_kinematic() {
                body.set_companion_id(self.id_to_body.len());
                self.id_to_body.push(handle);
            }
        });

        /*
         *
         * Activate bodies that need it.
         *
         */
        for handle in self.to_activate.iter() {
            let body = try_continue!(bodies.get_mut(*handle));

            if body.activation_status().deactivation_threshold().is_some() {
                body.activate()
            }
        }

        self.to_activate.clear();

        /*
         *
         * Build islands.
         *
         */
        // Resize buffers.
        self.ufind
            .resize(self.id_to_body.len(), UnionFindSet::new(0));
        self.can_deactivate.resize(self.id_to_body.len(), true);

        // Init the union find.
        // FIXME: are there more efficient ways of doing those?
        for (i, u) in self.ufind.iter_mut().enumerate() {
            u.reinit(i)
        }

        for d in self.can_deactivate.iter_mut() {
            *d = true
        }

        // Run the union-find.
        // FIXME: use the union-find from petgraph?
        #[inline(always)]
        fn make_union<N: RealField, Bodies: BodySet<N>>(
            bodies: &Bodies,
            b1: Bodies::Handle,
            b2: Bodies::Handle,
            ufs: &mut [UnionFindSet],
        ) {
            let b1 = try_ret!(bodies.get(b1));
            let b2 = try_ret!(bodies.get(b2));
            if (b1.status_dependent_ndofs() != 0 || b1.is_kinematic())
                && (b2.status_dependent_ndofs() != 0 || b2.is_kinematic())
                {
                    union_find::union(b1.companion_id(), b2.companion_id(), ufs)
                }
        }

        /*
        for (_, c1, _, c2, _, manifold) in cworld.contact_pairs(colliders, false) {
            if manifold.len() > 0 {
                make_union(bodies, c1.body(), c2.body(), &mut self.ufind)
            }
        }*/

        constraints.foreach(|_, c| {
            let (b1, b2) = c.anchors();
            make_union(bodies, b1.0, b2.0, &mut self.ufind);
        });

        /*
         * Body activation/deactivation.
         */
        // Find deactivable islands.
        for i in 0usize..self.ufind.len() {
            let root = union_find::find(i, &mut self.ufind[..]);
            let handle = self.id_to_body[i];
            let body = try_continue!(bodies.get(handle));
            // FIXME: avoid the Copy when NLL lands ?
            let status = *body.activation_status();

            self.can_deactivate[root] = match status.deactivation_threshold() {
                Some(threshold) => self.can_deactivate[root] && status.energy() < threshold,
                None => false,
            };
        }

        // Activate/deactivate islands.
        for i in 0usize..self.ufind.len() {
            let root = union_find::find(i, &mut self.ufind[..]);
            let handle = self.id_to_body[i];
            let body = try_continue!(bodies.get_mut(handle));

            if self.can_deactivate[root] {
                // Everybody in this set can be deactivacted.
                if body.is_active() {
                    body.deactivate();
                }
            } else if !body.is_kinematic() {
                // Everybody in this set must be reactivated.
                active_bodies.push(handle);

                // FIXME: avoid the Copy when NLL lands ?
                let status = *body.activation_status();

                if !status.is_active() && status.deactivation_threshold().is_some() {
                    body.activate()
                }
            }
        }
    }
}
<commit_msg>Fix activation manager.<commit_after>use slab::Slab;

use na::{self, RealField};
use crate::world::ColliderWorld;
use crate::object::{Body, DefaultBodySet, BodySet, BodyHandle, ColliderHandle, ColliderSet};
use crate::joint::{JointConstraint, JointConstraintSet};
use crate::utils::union_find::UnionFindSet;
use crate::utils::union_find;

/// Structure that monitors island-based activation/deactivation of bodies.
///
/// It is responsible for making objects sleep or wake up.
#[derive(Clone)]
pub struct ActivationManager<N: RealField, Handle: BodyHandle> {
    mix_factor: N,
    ufind: Vec<UnionFindSet>,
    can_deactivate: Vec<bool>,
    to_activate: Vec<Handle>,
    id_to_body: Vec<Handle>,
}

impl<N: RealField, Handle: BodyHandle> ActivationManager<N, Handle> {
    /// Creates a new `ActivationManager2`.
    ///
    /// # Arguments:
    /// * `thresold`   - the minimum energy required to keep an object awake.
    /// * `mix_factor` - the ratio of energy to keep between two frames.
    pub fn new(mix_factor: N) -> ActivationManager<N, Handle> {
        assert!(
            mix_factor >= na::zero(),
            "The energy mixing factor must be between 0.0 and 1.0."
        );

        ActivationManager {
            mix_factor,
            ufind: Vec::new(),
            can_deactivate: Vec::new(),
            to_activate: Vec::new(),
            id_to_body: Vec::new(),
        }
    }

    /// Notify the `ActivationManager2` that is has to activate an object at the next update.
    // FIXME: this is not a very good name
    pub fn deferred_activate(&mut self, handle: Handle) {
        self.to_activate.push(handle);
    }

    fn update_energy(&self, body: &mut (impl Body<N> + ?Sized)) {
        // FIXME: avoid the Copy when NLL lands ?
        let status = *body.activation_status();

        if let Some(threshold) = status.deactivation_threshold() {
            // FIXME: take the time in account (to make a true RWA)
            let new_energy = (N::one() - self.mix_factor) * status.energy()
                + self.mix_factor * (body.generalized_velocity().norm_squared());

            body.activate_with_energy(new_energy.min(threshold * na::convert(4.0f64)));
        }
    }

    /// Update the activation manager, activating and deactivating objects when needed.
    pub fn update<Bodies, Colliders, Constraints>(
        &mut self,
        bodies: &mut Bodies,
        colliders: &Colliders,
        cworld: &ColliderWorld<N, Handle, Colliders::Handle>,
        constraints: &Constraints,
        active_bodies: &mut Vec<Handle>,
    )
        where Bodies: BodySet<N, Handle = Handle>,
              Colliders: ColliderSet<N, Handle>,
              Constraints: JointConstraintSet<N, Bodies> {
        /*
         *
         * Update bodies energy
         *
         */
        self.id_to_body.clear();

        bodies.foreach_mut(|handle, body| {
            if body.status_dependent_ndofs() != 0 {
                if body.is_active() {
                    self.update_energy(body);
                }

                body.set_companion_id(self.id_to_body.len());
                self.id_to_body.push(handle);
            }

            if body.is_kinematic() {
                body.set_companion_id(self.id_to_body.len());
                self.id_to_body.push(handle);
            }
        });

        /*
         *
         * Activate bodies that need it.
         *
         */
        for handle in self.to_activate.iter() {
            let body = try_continue!(bodies.get_mut(*handle));

            if body.activation_status().deactivation_threshold().is_some() {
                body.activate()
            }
        }

        self.to_activate.clear();

        /*
         *
         * Build islands.
         *
         */
        // Resize buffers.
        self.ufind
            .resize(self.id_to_body.len(), UnionFindSet::new(0));
        self.can_deactivate.resize(self.id_to_body.len(), true);

        // Init the union find.
        // FIXME: are there more efficient ways of doing those?
        for (i, u) in self.ufind.iter_mut().enumerate() {
            u.reinit(i)
        }

        for d in self.can_deactivate.iter_mut() {
            *d = true
        }

        // Run the union-find.
        // FIXME: use the union-find from petgraph?
        #[inline(always)]
        fn make_union<N: RealField, Bodies: BodySet<N>>(
            bodies: &Bodies,
            b1: Bodies::Handle,
            b2: Bodies::Handle,
            ufs: &mut [UnionFindSet],
        ) {
            let b1 = try_ret!(bodies.get(b1));
            let b2 = try_ret!(bodies.get(b2));
            if (b1.status_dependent_ndofs() != 0 || b1.is_kinematic())
                && (b2.status_dependent_ndofs() != 0 || b2.is_kinematic())
                {
                    union_find::union(b1.companion_id(), b2.companion_id(), ufs)
                }
        }

        for (_, c1, _, c2, _, manifold) in cworld.contact_pairs(colliders, false) {
            if manifold.len() > 0 {
                make_union(bodies, c1.body(), c2.body(), &mut self.ufind)
            }
        }

        constraints.foreach(|_, c| {
            let (b1, b2) = c.anchors();
            make_union(bodies, b1.0, b2.0, &mut self.ufind);
        });

        /*
         * Body activation/deactivation.
         */
        // Find deactivable islands.
        for i in 0usize..self.ufind.len() {
            let root = union_find::find(i, &mut self.ufind[..]);
            let handle = self.id_to_body[i];
            let body = try_continue!(bodies.get(handle));
            // FIXME: avoid the Copy when NLL lands ?
            let status = *body.activation_status();

            self.can_deactivate[root] = match status.deactivation_threshold() {
                Some(threshold) => self.can_deactivate[root] && status.energy() < threshold,
                None => false,
            };
        }

        // Activate/deactivate islands.
        for i in 0usize..self.ufind.len() {
            let root = union_find::find(i, &mut self.ufind[..]);
            let handle = self.id_to_body[i];
            let body = try_continue!(bodies.get_mut(handle));

            if self.can_deactivate[root] {
                // Everybody in this set can be deactivacted.
                if body.is_active() {
                    body.deactivate();
                }
            } else if !body.is_kinematic() {
                // Everybody in this set must be reactivated.
                active_bodies.push(handle);

                // FIXME: avoid the Copy when NLL lands ?
                let status = *body.activation_status();

                if !status.is_active() && status.deactivation_threshold().is_some() {
                    body.activate()
                }
            }
        }
    }
}
<|endoftext|>