<commit_before>//! Handle `cargo add` arguments

use cargo_edit::Dependency;
use cargo_edit::{get_latest_dependency, CrateName};
use semver;
use std::path::PathBuf;

use errors::*;

#[derive(Debug, Deserialize)]
/// Docopts input args.
pub struct Args {
    /// Crate name (usage 1)
    pub arg_crate: String,
    /// Crate names (usage 2)
    pub arg_crates: Vec<String>,
    /// dev-dependency
    pub flag_dev: bool,
    /// build-dependency
    pub flag_build: bool,
    /// Version
    pub flag_vers: Option<String>,
    /// Git repo Path
    pub flag_git: Option<String>,
    /// Crate directory path
    pub flag_path: Option<PathBuf>,
    /// Crate directory path
    pub flag_target: Option<String>,
    /// Optional dependency
    pub flag_optional: bool,
    /// `Cargo.toml` path
    pub flag_manifest_path: Option<PathBuf>,
    /// `--version`
    pub flag_version: bool,
    /// `---upgrade`
    pub flag_upgrade: Option<String>,
    /// '--fetch-prereleases'
    pub flag_allow_prerelease: bool,
    /// '--quiet'
    pub flag_quiet: bool,
}

fn parse_version_req(s: &str) -> Result<&str> {
    semver::VersionReq::parse(s).chain_err(|| "Invalid dependency version requirement")?;
    Ok(s)
}

impl Args {
    /// Get dependency section
    pub fn get_section(&self) -> Vec<String> {
        if self.flag_dev {
            vec!["dev-dependencies".to_owned()]
        } else if self.flag_build {
            vec!["build-dependencies".to_owned()]
        } else if let Some(ref target) = self.flag_target {
            if target.is_empty() {
                panic!("Target specification may not be empty");
            }
            vec![
                "target".to_owned(),
                target.clone(),
                "dependencies".to_owned(),
            ]
        } else {
            vec!["dependencies".to_owned()]
        }
    }

    /// Build dependencies from arguments
    pub fn parse_dependencies(&self) -> Result<Vec<Dependency>> {
        if !self.arg_crates.is_empty() {
            return self.arg_crates
                .iter()
                .map(|crate_name| {
                    Ok(
                        if let Some(krate) = CrateName::new(crate_name).parse_as_version()? {
                            krate
                        } else {
                            get_latest_dependency(crate_name, self.flag_allow_prerelease)?
                        }.set_optional(self.flag_optional),
                    )
                })
                .collect();
        }

        let crate_name = CrateName::new(&self.arg_crate);

        let dependency = if let Some(dependency) = crate_name.parse_as_version()? {
            if let &Some(ref url) = &self.flag_git {
                let url = url.clone();
                let version = dependency.version().unwrap().to_string();
                Err(ErrorKind::GitUrlWithVersion(url, version))?;
            }
            if let &Some(ref path) = &self.flag_path {
                dependency.set_path(path.to_str().unwrap())
            } else {
                dependency
            }
        } else if !crate_name.is_url_or_path() {
            let dependency = Dependency::new(&self.arg_crate);

            match (&self.flag_git, &self.flag_vers, &self.flag_path) {
                (&Some(ref repo), &Some(ref version), _) => {
                    let repo = repo.clone();
                    let version = version.to_string();
                    return Err(ErrorKind::GitUrlWithVersion(repo, version))?;
                }
                (&Some(ref repo), &None, &Some(ref path)) => {
                    let repo = repo.clone();
                    let path = path.to_string_lossy().to_string();
                    return Err(ErrorKind::GitUrlWithPath(repo, path))?;
                }
                (&Some(ref repo), &None, &None) => dependency.set_git(repo),
                (&None, &Some(ref version), &Some(ref path)) => dependency
                    .set_version(parse_version_req(version)?)
                    .set_path(path.to_str().unwrap()),
                (&None, &Some(ref version), &None) => {
                    dependency.set_version(parse_version_req(version)?)
                }
                (&None, &None, &Some(ref path)) => dependency.set_path(path.to_str().unwrap()),
                (&None, &None, &None) => {
                    let dep = get_latest_dependency(&self.arg_crate, self.flag_allow_prerelease)?;
                    let v = format!(
                        "{prefix}{version}",
                        prefix = self.get_upgrade_prefix().unwrap_or(""),
                        // If version is unavailable `get_latest_dependency` must have
                        // returned `Err(FetchVersionError::GetVersion)`
                        version = dep.version().unwrap_or_else(|| unreachable!())
                    );
                    dep.set_version(&v)
                }
            }
        } else {
            crate_name.parse_crate_name_from_uri()?
        }.set_optional(self.flag_optional);

        Ok(vec![dependency])
    }

    fn get_upgrade_prefix(&self) -> Option<&'static str> {
        self.flag_upgrade
            .clone()
            .and_then(|flag| match flag.to_uppercase().as_ref() {
                "NONE" => Some("="),
                "PATCH" => Some("~"),
                "MINOR" => Some("^"),
                "ALL" => Some(">="),
                _ => {
                    println!(
                        "WARN: cannot understand upgrade option \"{}\", using default",
                        flag
                    );
                    None
                }
            })
    }
}

impl Default for Args {
    fn default() -> Args {
        Args {
            arg_crate: "demo".to_owned(),
            arg_crates: vec![],
            flag_dev: false,
            flag_build: false,
            flag_vers: None,
            flag_git: None,
            flag_path: None,
            flag_target: None,
            flag_optional: false,
            flag_manifest_path: None,
            flag_version: false,
            flag_upgrade: None,
            flag_allow_prerelease: false,
            flag_quiet: false,
        }
    }
}

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

    #[test]
    fn test_dependency_parsing() {
        let args = Args {
            flag_vers: Some("0.4.2".to_owned()),
            ..Args::default()
        };

        assert_eq!(
            args.parse_dependencies().unwrap(),
            vec![Dependency::new("demo").set_version("0.4.2")]
        );
    }

    #[test]
    #[cfg(feature = "test-external-apis")]
    fn test_repo_as_arg_parsing() {
        let github_url = "https://github.com/killercup/cargo-edit/";
        let args_github = Args {
            arg_crate: github_url.to_owned(),
            ..Args::default()
        };
        assert_eq!(
            args_github.parse_dependencies().unwrap(),
            vec![Dependency::new("cargo-edit").set_git(github_url)]
        );

        let gitlab_url = "https://gitlab.com/Polly-lang/Polly.git";
        let args_gitlab = Args {
            arg_crate: gitlab_url.to_owned(),
            ..Args::default()
        };
        assert_eq!(
            args_gitlab.parse_dependencies().unwrap(),
            vec![Dependency::new("polly").set_git(gitlab_url)]
        );
    }

    #[test]
    fn test_path_as_arg_parsing() {
        let self_path = ".";
        let args_path = Args {
            arg_crate: self_path.to_owned(),
            ..Args::default()
        };
        assert_eq!(
            args_path.parse_dependencies().unwrap(),
            vec![Dependency::new("cargo-edit").set_path(self_path)]
        );
    }

}
<commit_msg>Make clippy happy.<commit_after>//! Handle `cargo add` arguments

use cargo_edit::Dependency;
use cargo_edit::{get_latest_dependency, CrateName};
use semver;
use std::path::PathBuf;

use errors::*;

#[derive(Debug, Deserialize)]
/// Docopts input args.
pub struct Args {
    /// Crate name (usage 1)
    pub arg_crate: String,
    /// Crate names (usage 2)
    pub arg_crates: Vec<String>,
    /// dev-dependency
    pub flag_dev: bool,
    /// build-dependency
    pub flag_build: bool,
    /// Version
    pub flag_vers: Option<String>,
    /// Git repo Path
    pub flag_git: Option<String>,
    /// Crate directory path
    pub flag_path: Option<PathBuf>,
    /// Crate directory path
    pub flag_target: Option<String>,
    /// Optional dependency
    pub flag_optional: bool,
    /// `Cargo.toml` path
    pub flag_manifest_path: Option<PathBuf>,
    /// `--version`
    pub flag_version: bool,
    /// `---upgrade`
    pub flag_upgrade: Option<String>,
    /// '--fetch-prereleases'
    pub flag_allow_prerelease: bool,
    /// '--quiet'
    pub flag_quiet: bool,
}

fn parse_version_req(s: &str) -> Result<&str> {
    semver::VersionReq::parse(s).chain_err(|| "Invalid dependency version requirement")?;
    Ok(s)
}

impl Args {
    /// Get dependency section
    pub fn get_section(&self) -> Vec<String> {
        if self.flag_dev {
            vec!["dev-dependencies".to_owned()]
        } else if self.flag_build {
            vec!["build-dependencies".to_owned()]
        } else if let Some(ref target) = self.flag_target {
            if target.is_empty() {
                panic!("Target specification may not be empty");
            }
            vec![
                "target".to_owned(),
                target.clone(),
                "dependencies".to_owned(),
            ]
        } else {
            vec!["dependencies".to_owned()]
        }
    }

    /// Build dependencies from arguments
    pub fn parse_dependencies(&self) -> Result<Vec<Dependency>> {
        if !self.arg_crates.is_empty() {
            return self.arg_crates
                .iter()
                .map(|crate_name| {
                    Ok(
                        if let Some(krate) = CrateName::new(crate_name).parse_as_version()? {
                            krate
                        } else {
                            get_latest_dependency(crate_name, self.flag_allow_prerelease)?
                        }.set_optional(self.flag_optional),
                    )
                })
                .collect();
        }

        let crate_name = CrateName::new(&self.arg_crate);

        let dependency = if let Some(dependency) = crate_name.parse_as_version()? {
            if let Some(ref url) = self.flag_git {
                let url = url.clone();
                let version = dependency.version().unwrap().to_string();
                Err(ErrorKind::GitUrlWithVersion(url, version))?;
            }
            if let Some(ref path) = self.flag_path {
                dependency.set_path(path.to_str().unwrap())
            } else {
                dependency
            }
        } else if !crate_name.is_url_or_path() {
            let dependency = Dependency::new(&self.arg_crate);

            match (&self.flag_git, &self.flag_vers, &self.flag_path) {
                (&Some(ref repo), &Some(ref version), _) => {
                    let repo = repo.clone();
                    let version = version.to_string();
                    return Err(ErrorKind::GitUrlWithVersion(repo, version))?;
                }
                (&Some(ref repo), &None, &Some(ref path)) => {
                    let repo = repo.clone();
                    let path = path.to_string_lossy().to_string();
                    return Err(ErrorKind::GitUrlWithPath(repo, path))?;
                }
                (&Some(ref repo), &None, &None) => dependency.set_git(repo),
                (&None, &Some(ref version), &Some(ref path)) => dependency
                    .set_version(parse_version_req(version)?)
                    .set_path(path.to_str().unwrap()),
                (&None, &Some(ref version), &None) => {
                    dependency.set_version(parse_version_req(version)?)
                }
                (&None, &None, &Some(ref path)) => dependency.set_path(path.to_str().unwrap()),
                (&None, &None, &None) => {
                    let dep = get_latest_dependency(&self.arg_crate, self.flag_allow_prerelease)?;
                    let v = format!(
                        "{prefix}{version}",
                        prefix = self.get_upgrade_prefix().unwrap_or(""),
                        // If version is unavailable `get_latest_dependency` must have
                        // returned `Err(FetchVersionError::GetVersion)`
                        version = dep.version().unwrap_or_else(|| unreachable!())
                    );
                    dep.set_version(&v)
                }
            }
        } else {
            crate_name.parse_crate_name_from_uri()?
        }.set_optional(self.flag_optional);

        Ok(vec![dependency])
    }

    fn get_upgrade_prefix(&self) -> Option<&'static str> {
        self.flag_upgrade
            .clone()
            .and_then(|flag| match flag.to_uppercase().as_ref() {
                "NONE" => Some("="),
                "PATCH" => Some("~"),
                "MINOR" => Some("^"),
                "ALL" => Some(">="),
                _ => {
                    println!(
                        "WARN: cannot understand upgrade option \"{}\", using default",
                        flag
                    );
                    None
                }
            })
    }
}

impl Default for Args {
    fn default() -> Args {
        Args {
            arg_crate: "demo".to_owned(),
            arg_crates: vec![],
            flag_dev: false,
            flag_build: false,
            flag_vers: None,
            flag_git: None,
            flag_path: None,
            flag_target: None,
            flag_optional: false,
            flag_manifest_path: None,
            flag_version: false,
            flag_upgrade: None,
            flag_allow_prerelease: false,
            flag_quiet: false,
        }
    }
}

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

    #[test]
    fn test_dependency_parsing() {
        let args = Args {
            flag_vers: Some("0.4.2".to_owned()),
            ..Args::default()
        };

        assert_eq!(
            args.parse_dependencies().unwrap(),
            vec![Dependency::new("demo").set_version("0.4.2")]
        );
    }

    #[test]
    #[cfg(feature = "test-external-apis")]
    fn test_repo_as_arg_parsing() {
        let github_url = "https://github.com/killercup/cargo-edit/";
        let args_github = Args {
            arg_crate: github_url.to_owned(),
            ..Args::default()
        };
        assert_eq!(
            args_github.parse_dependencies().unwrap(),
            vec![Dependency::new("cargo-edit").set_git(github_url)]
        );

        let gitlab_url = "https://gitlab.com/Polly-lang/Polly.git";
        let args_gitlab = Args {
            arg_crate: gitlab_url.to_owned(),
            ..Args::default()
        };
        assert_eq!(
            args_gitlab.parse_dependencies().unwrap(),
            vec![Dependency::new("polly").set_git(gitlab_url)]
        );
    }

    #[test]
    fn test_path_as_arg_parsing() {
        let self_path = ".";
        let args_path = Args {
            arg_crate: self_path.to_owned(),
            ..Args::default()
        };
        assert_eq!(
            args_path.parse_dependencies().unwrap(),
            vec![Dependency::new("cargo-edit").set_path(self_path)]
        );
    }

}
<|endoftext|>


<commit_before>extern crate arguments;
extern crate configuration;
extern crate sql;
extern crate sqlite;
extern crate term;

#[macro_use] extern crate log;
#[macro_use] extern crate streamer;

use configuration::format::TOML;
use log::LogLevel;
use streamer::{Result, platform, schedule, traffic, workload};
use streamer::system::{self, Event};

mod logger;
mod output;

use logger::Logger;
use output::Output;

type System = system::System<traffic::Fractal,
                             workload::Random,
                             platform::Thermal,
                             schedule::Impartial>;

const USAGE: &'static str = "
Usage: streamer [options]

Options:
    --config <path>          Configuration file (required).

    --verbose                Display progress information.
    --help                   Display this message.
";

#[allow(unused_must_use)]
fn main() {
    if let Err(error) = start() {
        use std::io::Write;
        if let Some(mut output) = term::stderr() {
            output.fg(term::color::RED);
            output.write_fmt(format_args!("Error: {}.\n", error));
            output.reset();
        }
        std::process::exit(1);
    }
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.get::<bool>("help").unwrap_or(false) {
        println!("{}", USAGE.trim());
        return Ok(());
    }
    if arguments.get::<bool>("verbose").unwrap_or(false) {
        Logger::install(LogLevel::Info);
    } else {
        Logger::install(LogLevel::Warn);
    }

    let config = ok!(TOML::open(some!(arguments.get::<String>("config"),
                                      "a configuration file is required")));
    macro_rules! branch(($name:expr) => (config.branch($name).as_ref().unwrap_or(&config)));
    let mut system = {
        let source = streamer::source(&config);
        let traffic = try!(traffic::Fractal::new(branch!("traffic"), &source));
        let workload = try!(workload::Random::new(branch!("workload"), &source));
        let platform = try!(platform::Thermal::new(branch!("platform")));
        let schedule = try!(schedule::Impartial::new(branch!("schedule"), &platform));
        try!(System::new(traffic, workload, platform, schedule))
    };
    let time_span = *some!(branch!("output").get::<f64>("time_span"), "a time span is required");
    let mut output = if branch!("output").get::<String>("path").is_some() {
        Some(try!(Output::new(system.platform(), branch!("output"))))
    } else {
        None
    };

    info!(target: "Streamer", "Synthesizing {} seconds...", time_span);
    while let Some((event, data)) = try!(system.next()) {
        if event.time > time_span {
            break;
        }
        display(&system, &event);
        if let Some(ref mut output) = output {
            try!(output.next(&event, &data));
        }
    }
    info!(target: "Streamer", "Well done.");

    Ok(())
}

fn display(system: &System, event: &Event) {
    use streamer::system::EventKind;

    let (job, kind) = match &event.kind {
        &EventKind::Arrived(ref job) => (job, "arrived"),
        &EventKind::Started(ref job) => (job, "started"),
        &EventKind::Finished(ref job) => (job, "finished"),
    };
    info!(target: "Streamer",
          "{:8.2} s | job #{:4} ( {:20} | {:2} units | {:6.2} s ) {:8} | {:2} queued",
          event.time, job.id, job.name, job.units, job.duration(), kind,
          system.history().arrived - system.history().started);
}
<commit_msg>Make a cosmetic adjustment<commit_after>extern crate arguments;
extern crate configuration;
extern crate sql;
extern crate sqlite;
extern crate term;

#[macro_use] extern crate log;
#[macro_use] extern crate streamer;

use configuration::format::TOML;
use log::LogLevel;
use streamer::{Result, platform, schedule, traffic, workload};
use streamer::system::{self, Event};

mod logger;
mod output;

use logger::Logger;
use output::Output;

type System = system::System<traffic::Fractal,
                             workload::Random,
                             platform::Thermal,
                             schedule::Impartial>;

const USAGE: &'static str = "
Usage: streamer [options]

Options:
    --config <path>          Configuration file (required).

    --verbose                Display progress information.
    --help                   Display this message.
";

#[allow(unused_must_use)]
fn main() {
    if let Err(error) = start() {
        use std::io::Write;
        if let Some(mut output) = term::stderr() {
            output.fg(term::color::RED);
            output.write_fmt(format_args!("Error: {}.\n", error));
            output.reset();
        }
        std::process::exit(1);
    }
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.get::<bool>("help").unwrap_or(false) {
        println!("{}", USAGE.trim());
        return Ok(());
    }
    if arguments.get::<bool>("verbose").unwrap_or(false) {
        Logger::install(LogLevel::Info);
    } else {
        Logger::install(LogLevel::Warn);
    }

    let config = ok!(TOML::open(some!(arguments.get::<String>("config"),
                                      "a configuration file is required")));
    macro_rules! branch(($name:expr) => (config.branch($name).as_ref().unwrap_or(&config)));
    let mut system = {
        let source = streamer::source(&config);
        let traffic = try!(traffic::Fractal::new(branch!("traffic"), &source));
        let workload = try!(workload::Random::new(branch!("workload"), &source));
        let platform = try!(platform::Thermal::new(branch!("platform")));
        let schedule = try!(schedule::Impartial::new(branch!("schedule"), &platform));
        try!(System::new(traffic, workload, platform, schedule))
    };
    let time_span = *some!(config.get::<f64>("output.time_span"), "a time span is required");
    let mut output = if config.get::<String>("output.path").is_some() {
        Some(try!(Output::new(system.platform(), branch!("output"))))
    } else {
        None
    };

    info!(target: "Streamer", "Synthesizing {} seconds...", time_span);
    while let Some((event, data)) = try!(system.next()) {
        if event.time > time_span {
            break;
        }
        display(&system, &event);
        if let Some(ref mut output) = output {
            try!(output.next(&event, &data));
        }
    }
    info!(target: "Streamer", "Well done.");

    Ok(())
}

fn display(system: &System, event: &Event) {
    use streamer::system::EventKind;

    let (job, kind) = match &event.kind {
        &EventKind::Arrived(ref job) => (job, "arrived"),
        &EventKind::Started(ref job) => (job, "started"),
        &EventKind::Finished(ref job) => (job, "finished"),
    };
    info!(target: "Streamer",
          "{:8.2} s | job #{:4} ( {:20} | {:2} units | {:6.2} s ) {:8} | {:2} queued",
          event.time, job.id, job.name, job.units, job.duration(), kind,
          system.history().arrived - system.history().started);
}
<|endoftext|>


<commit_before>use libzakopane::config::Config;
use libzakopane::snapshot::Snapshot;
use libzakopane::structs::CompareCliOptions;
use libzakopane::structs::ZakopaneError;

use clap::{App, Arg, ArgMatches};

const DEFAULT_POLICY_ARG_NAME: &'static str = "default-policy";
const CONFIG_FILE_ARG_NAME: &'static str = "config";
const OLD_SNAPSHOT_PATH_ARG_NAME: &'static str = "old-snapshot-path";
const NEW_SNAPSHOT_PATH_ARG_NAME: &'static str = "new-snapshot-path";

// Holds one instance of each struct necessary to operate.
struct OperationalData {
    config: Config,
    old_snapshot: Snapshot,
    new_snapshot: Snapshot,
}

// Reads parsed command-line arguments and returns the appropriate
// operational data. Can abort the program on error.
fn complete_initialization(matches: &ArgMatches) -> Result<OperationalData, ZakopaneError> {
    // The two snapshot paths are required, so these are safe to unwrap.
    let old_snapshot_path = matches.value_of(OLD_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let new_snapshot_path = matches.value_of(NEW_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let old_contents = libzakopane::helpers::ingest_file(old_snapshot_path)?;
    let new_contents = libzakopane::helpers::ingest_file(new_snapshot_path)?;

    let options = CompareCliOptions {
        config_path: matches.value_of(CONFIG_FILE_ARG_NAME),
        default_policy: matches.value_of(DEFAULT_POLICY_ARG_NAME),
    };

    Ok(OperationalData {
        config: Config::new(&options)?,
        old_snapshot: Snapshot::new(&old_contents)?,
        new_snapshot: Snapshot::new(&new_contents)?,
    })
}

// Begins parsing command-line arguments. Can abort the program on
// error.
fn initialize() -> Result<OperationalData, ZakopaneError> {
    let matches = App::new("zakopane")
        .version("0.2.0")
        .author("j39m")
        .about("checksums directories")
        .subcommand(
            App::new("compare")
                .about("compares two zakopane snapshots")
                .arg(
                    Arg::with_name(CONFIG_FILE_ARG_NAME)
                        .short("c")
                        .long("config")
                        .value_name("FILE")
                        .help("specifies a zakopane config")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(DEFAULT_POLICY_ARG_NAME)
                        .short("d")
                        .long("default-policy")
                        .value_name("POLICY_TOKENS")
                        .help("specifies an explicit default policy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(OLD_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to older snapshot")
                        .index(1)
                        .required(true),
                )
                .arg(
                    Arg::with_name(NEW_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to newer snapshot")
                        .index(2)
                        .required(true),
                ),
        )
        .get_matches();

    if let Some(ref matches) = matches.subcommand_matches("compare") {
        return complete_initialization(&matches);
    }
    Err(ZakopaneError::Unknown("not implemented".to_string()))
}

fn main() {
    let operational_data = match initialize() {
        Ok(data) => data,
        Err(error) => {
            eprintln!("{}", error.to_string());
            std::process::exit(1);
        }
    };

    let OperationalData {
        config,
        new_snapshot,
        old_snapshot,
    } = operational_data;
    assert!(config.rules() > 0);
    let violations = libzakopane::compare(&config, &old_snapshot, &new_snapshot);
    println!("{}", violations);
}
<commit_msg>zakopane: Refactor main routine<commit_after>use libzakopane::config::Config;
use libzakopane::snapshot::Snapshot;
use libzakopane::structs::CompareCliOptions;
use libzakopane::structs::ZakopaneError;

use clap::{App, Arg, ArgMatches};

const DEFAULT_POLICY_ARG_NAME: &'static str = "default-policy";
const CONFIG_FILE_ARG_NAME: &'static str = "config";
const OLD_SNAPSHOT_PATH_ARG_NAME: &'static str = "old-snapshot-path";
const NEW_SNAPSHOT_PATH_ARG_NAME: &'static str = "new-snapshot-path";

// Holds one instance of each struct necessary to execute the `compare`
// subcommand.
struct CompareData {
    config: Config,
    old_snapshot: Snapshot,
    new_snapshot: Snapshot,
}

enum SubcommandData {
    Compare(CompareData),
    Checksum(std::path::PathBuf),
}

// Reads parsed command-line arguments and returns the appropriate
// operational data. Can abort the program on error.
fn compare_data_from(matches: &ArgMatches) -> Result<SubcommandData, ZakopaneError> {
    // The two snapshot paths are required, so these are safe to unwrap.
    let old_snapshot_path = matches.value_of(OLD_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let new_snapshot_path = matches.value_of(NEW_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let old_contents = libzakopane::helpers::ingest_file(old_snapshot_path)?;
    let new_contents = libzakopane::helpers::ingest_file(new_snapshot_path)?;

    let options = CompareCliOptions {
        config_path: matches.value_of(CONFIG_FILE_ARG_NAME),
        default_policy: matches.value_of(DEFAULT_POLICY_ARG_NAME),
    };

    Ok(SubcommandData::Compare(CompareData {
        config: Config::new(&options)?,
        old_snapshot: Snapshot::new(&old_contents)?,
        new_snapshot: Snapshot::new(&new_contents)?,
    }))
}

// Begins parsing command-line arguments. Can abort the program on
// error.
fn initialize() -> Result<SubcommandData, ZakopaneError> {
    let matches = App::new("zakopane")
        .version("0.2.0")
        .author("j39m")
        .about("checksums directories")
        .subcommand(
            App::new("compare")
                .about("compares two zakopane snapshots")
                .arg(
                    Arg::with_name(CONFIG_FILE_ARG_NAME)
                        .short("c")
                        .long("config")
                        .value_name("FILE")
                        .help("specifies a zakopane config")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(DEFAULT_POLICY_ARG_NAME)
                        .short("d")
                        .long("default-policy")
                        .value_name("POLICY_TOKENS")
                        .help("specifies an explicit default policy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(OLD_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to older snapshot")
                        .index(1)
                        .required(true),
                )
                .arg(
                    Arg::with_name(NEW_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to newer snapshot")
                        .index(2)
                        .required(true),
                ),
        )
        .get_matches();

    if let Some(ref matches) = matches.subcommand_matches("compare") {
        return compare_data_from(&matches);
    }
    Err(ZakopaneError::Unknown("not implemented".to_string()))
}

fn do_compare(data: CompareData) {
    let CompareData {
        config,
        new_snapshot,
        old_snapshot,
    } = data;
    assert!(config.rules() > 0);
    let violations = libzakopane::compare(&config, &old_snapshot, &new_snapshot);
    println!("{}", violations);
}

fn main() {
    let subcommand = match initialize() {
        Ok(data) => data,
        Err(error) => {
            eprintln!("{}", error.to_string());
            std::process::exit(1);
        }
    };
    match subcommand {
        SubcommandData::Compare(compare_data) => return do_compare(compare_data),
        _ => eprintln!("not implemented"),
    }
}
<|endoftext|>


<commit_before>use bridges::{GOOGLE_IDP_ORIGIN, Provider, ProviderKind};
use crypto;
use email_address::EmailAddress;
use error::BrokerError;
use futures::{Future, future};
use http::{ContextHandle, HandlerResult};
use hyper::{Response, StatusCode};
use hyper::header::Location;
use std::error::Error;
use std::rc::Rc;
use store_cache::{CacheKey, fetch_json_url};
use time::now_utc;
use url::Url;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// Macro used to extract a typed field from a JSON Value.
///
/// Will return from the caller with a `BrokerError` if the field is missing or its value is an
/// incompatible type. `descr` is used to format the error message.
///
/// ```
/// let foo = try_get_json_field!(value, "foo", "example document");
/// ```
macro_rules! try_get_json_field {
    ( $input:expr, $key:tt, $conv:expr, $descr:expr ) => {
        match $input.get($key).and_then($conv) {
            Some(v) => v,
            None => return future::err(BrokerError::Provider(
                format!("{} missing from {}", $key, $descr))),
        }
    };
    ( $input:expr, $key:tt, $descr:expr ) => {
        try_get_json_field!($input, $key,
            |v| v.as_str().map(|s| s.to_owned()), $descr)
    };
}

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `Config`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(ctx_handle: &ContextHandle, email_addr: &Rc<EmailAddress>, provider: &Rc<Provider>)
    -> HandlerResult {

    let mut ctx = ctx_handle.borrow_mut();

    // Determine the parameters to use with provider.
    let (origin, client_id) = match **provider {
        Provider::Portier { ref origin } => {
            (origin.clone(), ctx.app.public_url.clone())
        },
        Provider::Google { ref client_id } => {
            (GOOGLE_IDP_ORIGIN.to_owned(), client_id.clone())
        },
    };

    // Generate a nonce for the provider.
    let provider_nonce = crypto::nonce();

    // Store the nonce in the session for use in the verify handler,
    // and set the session type.
    ctx.session.set("type", "oidc".to_owned());
    ctx.session.set("provider_kind", provider.kind().as_str().to_owned());
    ctx.session.set("provider_origin", origin);
    ctx.session.set("provider_client_id", client_id);
    ctx.session.set("provider_nonce", provider_nonce.clone());

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    let email_addr = email_addr.clone();
    let email_addr2 = email_addr.clone();
    let email_addr3 = email_addr.clone();
    let f = {
        let origin = &ctx.session["provider_origin"];
        let config_url = build_config_url(origin);
        fetch_json_url(&ctx.app, &config_url, &CacheKey::OidcConfig { origin })
    }
        .map_err(move |e| {
            BrokerError::Provider(format!("could not fetch {}'s discovery document: {}",
                                            email_addr3.domain(), e.description()))
        })
        .and_then(move |config_obj| {
            let descr = format!("{}'s discovery document", email_addr2.domain());
            let authz_base = try_get_json_field!(config_obj, "authorization_endpoint", descr);
            future::ok(authz_base)
        });

    // Create the URL to redirect to, properly escaping all parameters.
    let ctx_handle2 = ctx_handle.clone();
    let f = f.and_then(move |authz_base| {
        let ctx = ctx_handle2.borrow();

        let result = Url::parse(&vec![
            authz_base.as_str(),
            "?",
            "client_id=",
            &utf8_percent_encode(&ctx.session["provider_client_id"], QUERY_ENCODE_SET).to_string(),
            "&response_type=id_token",
            "&scope=",
            &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
            "&redirect_uri=",
            &utf8_percent_encode(&format!("{}/callback", &ctx.app.public_url),
                                 QUERY_ENCODE_SET).to_string(),
            "&state=",
            &utf8_percent_encode(&ctx.session.id, QUERY_ENCODE_SET).to_string(),
            "&nonce=",
            &utf8_percent_encode(&provider_nonce, QUERY_ENCODE_SET).to_string(),
            "&login_hint=",
            &utf8_percent_encode(email_addr.as_str(), QUERY_ENCODE_SET).to_string(),
        ].join("")).map_err(|_| {
            let domain = email_addr.domain();
            BrokerError::Provider(format!("failed to build valid authorization URL from {}'s 'authorization_endpoint'", domain))
        });
        future::result(result)
    });

    let ctx_handle = ctx_handle.clone();
    let f = f.and_then(move |auth_url| {
        let ctx = ctx_handle.borrow();
        if let Err(err) = ctx.save_session() {
            return future::err(err);
        }

        let res = Response::new()
            .with_status(StatusCode::SeeOther)
            .with_header(Location::new(auth_url.into_string()));
        future::ok(res)
    });

    Box::new(f)
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(ctx_handle: &ContextHandle, id_token: String)
              -> Box<Future<Item=String, Error=BrokerError>> {

    let ctx = ctx_handle.borrow();
    let email_addr = Rc::new(EmailAddress::from_trusted(&ctx.session["email"]));

    // Request the provider's Discovery document to get the `jwks_uri` values from it.
    let email_addr2 = email_addr.clone();
    let email_addr3 = email_addr.clone();
    let f = {
        let origin = &ctx.session["provider_origin"];
        let config_url = build_config_url(origin);
        fetch_json_url(&ctx.app, &config_url, &CacheKey::OidcConfig { origin })
    }
        .map_err(move |e| {
            BrokerError::Provider(format!("could not fetch {}'s discovery document: {}",
                                          email_addr3.domain(), e.description()))
        })
        .and_then(move |config_obj| {
            let descr = format!("{}'s discovery document", email_addr2.domain());
            let jwks_url = try_get_json_field!(config_obj, "jwks_uri", descr);
            match jwks_url.parse::<Url>() {
                Ok(url) => future::ok(url),
                Err(e) => future::err(BrokerError::Provider(
                    format!("could not parse {}'s JWKs URI: {}", email_addr2.domain(), e.description()))),
            }
        });

    // Grab the keys from the provider, then verify the signature.
    let ctx_handle2 = ctx_handle.clone();
    let email_addr2 = email_addr.clone();
    let email_addr3 = email_addr.clone();
    let f = f.and_then(move |jwks_url| {
        let ctx = ctx_handle2.borrow();
        {
            let origin = &ctx.session["provider_origin"];
            fetch_json_url(&ctx.app, &jwks_url, &CacheKey::OidcKeySet { origin })
        }
            .map_err(move |e| {
                BrokerError::Provider(format!("could not fetch {}'s keys: {}",
                                              email_addr3.domain(), e.description()))
            })
            .and_then(move |keys_obj| {
                match crypto::verify_jws(&id_token, &keys_obj) {
                    Err(_) => future::err(BrokerError::Provider(
                        format!("could not verify the token received from {}", email_addr2.domain()))),
                    Ok(jwt_payload) => future::ok(jwt_payload),
                }
            })
    });

    let ctx_handle = ctx_handle.clone();
    let f = f.and_then(move |jwt_payload| {
        let ctx = ctx_handle.borrow();
        let domain = email_addr.domain();
        let redirect_uri = ctx.redirect_uri.as_ref()
            .expect("email::verify called without redirect_uri set");

        // Verify the claims contained in the token.
        let descr = format!("{}'s token payload", domain);
        let iss = try_get_json_field!(jwt_payload, "iss", descr);
        let aud = try_get_json_field!(jwt_payload, "aud", descr);
        let token_addr = try_get_json_field!(jwt_payload, "email", descr);
        let exp = try_get_json_field!(jwt_payload, "exp", |v| v.as_i64(), descr);
        let nonce = try_get_json_field!(jwt_payload, "nonce", descr);
        // TODO: Turn these into provider errors.
        let token_addr: EmailAddress = token_addr.parse()
            .expect("failed to parse provider token email address");
        assert_eq!(iss, ctx.session["provider_origin"]);
        assert_eq!(aud, ctx.session["provider_client_id"]);

        // Apply normalization for Google.
        match ctx.session["provider_kind"].parse().expect("invalid provider kind in session") {
            ProviderKind::Portier => assert_eq!(token_addr, email_addr),
            ProviderKind::Google => assert_eq!(token_addr.normalize_google(), email_addr.normalize_google()),
        }

        let now = now_utc().to_timespec().sec;
        assert!(now < exp);
        assert_eq!(nonce, ctx.session["provider_nonce"]);

        // If everything is okay, build a new identity token and send it
        // to the relying party.
        let aud = redirect_uri.origin().ascii_serialization();
        future::ok(crypto::create_jwt(&ctx.app, &*email_addr, &aud, &ctx.session["nonce"]))
    });

    Box::new(f)
}

/// Build the URL to the OpenID Connect configuration for an origin.
fn build_config_url(origin: &str) -> Url {
    format!("{}/.well-known/openid-configuration", origin).parse()
        .expect("could not build the OpenID Connect configuration URL")
}
<commit_msg>Handle invalid tokens from providers.<commit_after>use bridges::{GOOGLE_IDP_ORIGIN, Provider, ProviderKind};
use crypto;
use email_address::EmailAddress;
use error::BrokerError;
use futures::{Future, future};
use http::{ContextHandle, HandlerResult};
use hyper::{Response, StatusCode};
use hyper::header::Location;
use std::error::Error;
use std::rc::Rc;
use store_cache::{CacheKey, fetch_json_url};
use time::now_utc;
use url::Url;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// Macro used to extract a typed field from a JSON Value.
///
/// Will return from the caller with a `BrokerError` if the field is missing or its value is an
/// incompatible type. `descr` is used to format the error message.
///
/// ```
/// let foo = try_get_json_field!(value, "foo", "example document");
/// ```
macro_rules! try_get_json_field {
    ( $input:expr, $key:tt, $conv:expr, $descr:expr ) => {
        match $input.get($key).and_then($conv) {
            Some(v) => v,
            None => return future::err(BrokerError::Provider(
                format!("{} missing from {}", $key, $descr))),
        }
    };
    ( $input:expr, $key:tt, $descr:expr ) => {
        try_get_json_field!($input, $key,
            |v| v.as_str().map(|s| s.to_owned()), $descr)
    };
}

/// Macro used to verify a token payload field.
///
/// Will return from the caller with a `BrokerError` if the check fails. The `$key` and `$descr`
/// parameters are used in the error description.
///
/// ```
/// check_field!(foo == "bar", "foo", "example document");
/// ```
macro_rules! check_field {
    ( $check:expr, $key:expr, $descr:expr ) => {
        if !$check {
            return future::err(BrokerError::Provider(
                format!("{} has incorrect value in {}", $key, $descr)));
        }
    }
}

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `Config`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(ctx_handle: &ContextHandle, email_addr: &Rc<EmailAddress>, provider: &Rc<Provider>)
    -> HandlerResult {

    let mut ctx = ctx_handle.borrow_mut();

    // Determine the parameters to use with provider.
    let (origin, client_id) = match **provider {
        Provider::Portier { ref origin } => {
            (origin.clone(), ctx.app.public_url.clone())
        },
        Provider::Google { ref client_id } => {
            (GOOGLE_IDP_ORIGIN.to_owned(), client_id.clone())
        },
    };

    // Generate a nonce for the provider.
    let provider_nonce = crypto::nonce();

    // Store the nonce in the session for use in the verify handler,
    // and set the session type.
    ctx.session.set("type", "oidc".to_owned());
    ctx.session.set("provider_kind", provider.kind().as_str().to_owned());
    ctx.session.set("provider_origin", origin);
    ctx.session.set("provider_client_id", client_id);
    ctx.session.set("provider_nonce", provider_nonce.clone());

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    let email_addr = email_addr.clone();
    let email_addr2 = email_addr.clone();
    let email_addr3 = email_addr.clone();
    let f = {
        let origin = &ctx.session["provider_origin"];
        let config_url = build_config_url(origin);
        fetch_json_url(&ctx.app, &config_url, &CacheKey::OidcConfig { origin })
    }
        .map_err(move |e| {
            BrokerError::Provider(format!("could not fetch {}'s discovery document: {}",
                                            email_addr3.domain(), e.description()))
        })
        .and_then(move |config_obj| {
            let descr = format!("{}'s discovery document", email_addr2.domain());
            let authz_base = try_get_json_field!(config_obj, "authorization_endpoint", descr);
            future::ok(authz_base)
        });

    // Create the URL to redirect to, properly escaping all parameters.
    let ctx_handle2 = ctx_handle.clone();
    let f = f.and_then(move |authz_base| {
        let ctx = ctx_handle2.borrow();

        let result = Url::parse(&vec![
            authz_base.as_str(),
            "?",
            "client_id=",
            &utf8_percent_encode(&ctx.session["provider_client_id"], QUERY_ENCODE_SET).to_string(),
            "&response_type=id_token",
            "&scope=",
            &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
            "&redirect_uri=",
            &utf8_percent_encode(&format!("{}/callback", &ctx.app.public_url),
                                 QUERY_ENCODE_SET).to_string(),
            "&state=",
            &utf8_percent_encode(&ctx.session.id, QUERY_ENCODE_SET).to_string(),
            "&nonce=",
            &utf8_percent_encode(&provider_nonce, QUERY_ENCODE_SET).to_string(),
            "&login_hint=",
            &utf8_percent_encode(email_addr.as_str(), QUERY_ENCODE_SET).to_string(),
        ].join("")).map_err(|_| {
            let domain = email_addr.domain();
            BrokerError::Provider(format!("failed to build valid authorization URL from {}'s 'authorization_endpoint'", domain))
        });
        future::result(result)
    });

    let ctx_handle = ctx_handle.clone();
    let f = f.and_then(move |auth_url| {
        let ctx = ctx_handle.borrow();
        if let Err(err) = ctx.save_session() {
            return future::err(err);
        }

        let res = Response::new()
            .with_status(StatusCode::SeeOther)
            .with_header(Location::new(auth_url.into_string()));
        future::ok(res)
    });

    Box::new(f)
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(ctx_handle: &ContextHandle, id_token: String)
              -> Box<Future<Item=String, Error=BrokerError>> {

    let ctx = ctx_handle.borrow();
    let email_addr = Rc::new(EmailAddress::from_trusted(&ctx.session["email"]));

    // Request the provider's Discovery document to get the `jwks_uri` values from it.
    let email_addr2 = email_addr.clone();
    let email_addr3 = email_addr.clone();
    let f = {
        let origin = &ctx.session["provider_origin"];
        let config_url = build_config_url(origin);
        fetch_json_url(&ctx.app, &config_url, &CacheKey::OidcConfig { origin })
    }
        .map_err(move |e| {
            BrokerError::Provider(format!("could not fetch {}'s discovery document: {}",
                                          email_addr3.domain(), e.description()))
        })
        .and_then(move |config_obj| {
            let descr = format!("{}'s discovery document", email_addr2.domain());
            let jwks_url = try_get_json_field!(config_obj, "jwks_uri", descr);
            match jwks_url.parse::<Url>() {
                Ok(url) => future::ok(url),
                Err(e) => future::err(BrokerError::Provider(
                    format!("could not parse {}'s JWKs URI: {}", email_addr2.domain(), e.description()))),
            }
        });

    // Grab the keys from the provider, then verify the signature.
    let ctx_handle2 = ctx_handle.clone();
    let email_addr2 = email_addr.clone();
    let email_addr3 = email_addr.clone();
    let f = f.and_then(move |jwks_url| {
        let ctx = ctx_handle2.borrow();
        {
            let origin = &ctx.session["provider_origin"];
            fetch_json_url(&ctx.app, &jwks_url, &CacheKey::OidcKeySet { origin })
        }
            .map_err(move |e| {
                BrokerError::Provider(format!("could not fetch {}'s keys: {}",
                                              email_addr3.domain(), e.description()))
            })
            .and_then(move |keys_obj| {
                match crypto::verify_jws(&id_token, &keys_obj) {
                    Err(_) => future::err(BrokerError::Provider(
                        format!("could not verify the token received from {}", email_addr2.domain()))),
                    Ok(jwt_payload) => future::ok(jwt_payload),
                }
            })
    });

    let ctx_handle = ctx_handle.clone();
    let f = f.and_then(move |jwt_payload| {
        let ctx = ctx_handle.borrow();
        let domain = email_addr.domain();
        let redirect_uri = ctx.redirect_uri.as_ref()
            .expect("email::verify called without redirect_uri set");

        // Verify the claims contained in the token.
        let descr = format!("{}'s token payload", domain);
        let iss = try_get_json_field!(jwt_payload, "iss", descr);
        let aud = try_get_json_field!(jwt_payload, "aud", descr);
        let token_addr = try_get_json_field!(jwt_payload, "email", descr);
        let exp = try_get_json_field!(jwt_payload, "exp", |v| v.as_i64(), descr);
        let nonce = try_get_json_field!(jwt_payload, "nonce", descr);

        let token_addr: EmailAddress = match token_addr.parse() {
            Ok(addr) => addr,
            Err(_) => return future::err(BrokerError::Provider(format!(
                    "failed to parse email from {}", descr))),
        };

        check_field!(iss == ctx.session["provider_origin"], "iss", descr);
        check_field!(aud == ctx.session["provider_client_id"], "aud", descr);
        check_field!(nonce == ctx.session["provider_nonce"], "nonce", descr);

        // Apply normalization for Google.
        let kind = ctx.session["provider_kind"].parse().expect("invalid provider kind in session");
        check_field!(match kind {
            ProviderKind::Portier => token_addr == *email_addr,
            ProviderKind::Google => token_addr.normalize_google() == email_addr.normalize_google(),
        }, "email", descr);

        let now = now_utc().to_timespec().sec;
        check_field!(now < exp, "exp", descr);

        // If everything is okay, build a new identity token and send it
        // to the relying party.
        let aud = redirect_uri.origin().ascii_serialization();
        future::ok(crypto::create_jwt(&ctx.app, &*email_addr, &aud, &ctx.session["nonce"]))
    });

    Box::new(f)
}

/// Build the URL to the OpenID Connect configuration for an origin.
fn build_config_url(origin: &str) -> Url {
    format!("{}/.well-known/openid-configuration", origin).parse()
        .expect("could not build the OpenID Connect configuration URL")
}
<|endoftext|>


<commit_before>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Little-Endian utilities
//!
//! Little-Endian order has been chosen for internal usage; this makes some
//! useful functions available.

use core::convert::TryInto;

/// Reads unsigned 32 bit integers from `src` into `dst`.
#[inline]
pub fn read_u32_into(src: &[u8], dst: &mut [u32]) {
    for (out, chunk) in dst.iter_mut().zip(src.chunks(4)) {
        *out = u32::from_le_bytes(chunk.try_into().unwrap());
    }
}

/// Reads unsigned 64 bit integers from `src` into `dst`.
#[inline]
pub fn read_u64_into(src: &[u8], dst: &mut [u64]) {
    for (out, chunk) in dst.iter_mut().zip(src.chunks(8)) {
        *out = u64::from_le_bytes(chunk.try_into().unwrap());
    }
}

#[test]
fn test_read() {
    let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];

    let mut buf = [0u32; 4];
    read_u32_into(&bytes, &mut buf);
    assert_eq!(buf[0], 0x04030201);
    assert_eq!(buf[3], 0x100F0E0D);

    let mut buf = [0u32; 3];
    read_u32_into(&bytes[1..13], &mut buf); // unaligned
    assert_eq!(buf[0], 0x05040302);
    assert_eq!(buf[2], 0x0D0C0B0A);

    let mut buf = [0u64; 2];
    read_u64_into(&bytes, &mut buf);
    assert_eq!(buf[0], 0x0807060504030201);
    assert_eq!(buf[1], 0x100F0E0D0C0B0A09);

    let mut buf = [0u64; 1];
    read_u64_into(&bytes[7..15], &mut buf); // unaligned
    assert_eq!(buf[0], 0x0F0E0D0C0B0A0908);
}
<commit_msg>Check source size and prefer `chunks_exact`<commit_after>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Little-Endian utilities
//!
//! Little-Endian order has been chosen for internal usage; this makes some
//! useful functions available.

use core::convert::TryInto;

/// Reads unsigned 32 bit integers from `src` into `dst`.
#[inline]
pub fn read_u32_into(src: &[u8], dst: &mut [u32]) {
    assert!(4 * src.len() >= dst.len());
    for (out, chunk) in dst.iter_mut().zip(src.chunks_exact(4)) {
        *out = u32::from_le_bytes(chunk.try_into().unwrap());
    }
}

/// Reads unsigned 64 bit integers from `src` into `dst`.
#[inline]
pub fn read_u64_into(src: &[u8], dst: &mut [u64]) {
    assert!(8 * src.len() >= dst.len());
    for (out, chunk) in dst.iter_mut().zip(src.chunks_exact(8)) {
        *out = u64::from_le_bytes(chunk.try_into().unwrap());
    }
}

#[test]
fn test_read() {
    let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];

    let mut buf = [0u32; 4];
    read_u32_into(&bytes, &mut buf);
    assert_eq!(buf[0], 0x04030201);
    assert_eq!(buf[3], 0x100F0E0D);

    let mut buf = [0u32; 3];
    read_u32_into(&bytes[1..13], &mut buf); // unaligned
    assert_eq!(buf[0], 0x05040302);
    assert_eq!(buf[2], 0x0D0C0B0A);

    let mut buf = [0u64; 2];
    read_u64_into(&bytes, &mut buf);
    assert_eq!(buf[0], 0x0807060504030201);
    assert_eq!(buf[1], 0x100F0E0D0C0B0A09);

    let mut buf = [0u64; 1];
    read_u64_into(&bytes[7..15], &mut buf); // unaligned
    assert_eq!(buf[0], 0x0F0E0D0C0B0A0908);
}
<|endoftext|>


<commit_before>extern crate validate_isin;

use validate_isin::luhn_test;

fn main() {
    assert_eq!(validate_isin("US0378331005"), true);
    assert_eq!(validate_isin("US0373831005"), false);
    assert_eq!(validate_isin("U50378331005"), false);
    assert_eq!(validate_isin("US03378331005"), false);
    assert_eq!(validate_isin("AU0000XVGZA3"), true);
    assert_eq!(validate_isin("AU0000VXGZA3"), true);
    assert_eq!(validate_isin("FR0000988040"), true);
}

fn validate_isin(isin: &str) -> bool {
    if !isin.chars().all(|x| x.is_alphanumeric()) || isin.len() != 12 {
        return false;
    }
    if !isin[..2].chars().all(|x| x.is_alphabetic())
        || !isin[2..12].chars().all(|x| x.is_alphanumeric())
        || !isin.chars().last().unwrap().is_numeric()
    {
        return false;
    }

    let bytes = isin.as_bytes();

    let s2 = bytes
        .iter()
        .flat_map(|&c| {
            if c.is_ascii_digit() {
                vec![c]
            } else {
                (c + 10 - ('A' as u8)).to_string().into_bytes()
            }
        })
        .collect::<Vec<u8>>();

    let string = std::str::from_utf8(&s2).unwrap();
    let number = string.parse::<usize>().unwrap();

    return luhn_test(number as u64);
}
<commit_msg>Moved tests in a test module<commit_after>extern crate validate_isin;

use validate_isin::luhn_test;

fn validate_isin(isin: &str) -> bool {
    if !isin.chars().all(|x| x.is_alphanumeric()) || isin.len() != 12 {
        return false;
    }
    if !isin[..2].chars().all(|x| x.is_alphabetic())
        || !isin[2..12].chars().all(|x| x.is_alphanumeric())
        || !isin.chars().last().unwrap().is_numeric()
    {
        return false;
    }

    let bytes = isin.as_bytes();

    let s2 = bytes
        .iter()
        .flat_map(|&c| {
            if c.is_ascii_digit() {
                vec![c]
            } else {
                (c + 10 - ('A' as u8)).to_string().into_bytes()
            }
        })
        .collect::<Vec<u8>>();

    let string = std::str::from_utf8(&s2).unwrap();
    let number = string.parse::<usize>().unwrap();

    return luhn_test(number as u64);
}

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

    #[test]
    fn test_validate_isin() {
        assert_eq!(validate_isin("US0378331005"), true);
        assert_eq!(validate_isin("US0373831005"), false);
        assert_eq!(validate_isin("U50378331005"), false);
        assert_eq!(validate_isin("US03378331005"), false);
        assert_eq!(validate_isin("AU0000XVGZA3"), true);
        assert_eq!(validate_isin("AU0000VXGZA3"), true);
        assert_eq!(validate_isin("FR0000988040"), true);
    }
}
<|endoftext|>


<commit_before>pub struct Solution;

impl Solution {
    pub fn merge(intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
        if intervals.len() == 0 {
            return Vec::new();
        }
        let mut sorted_intervals = intervals.clone();
        sorted_intervals.sort_by(|a, b| a[0].cmp(&b[0]));
        let mut result = Vec::new();
        let mut curr_intv = sorted_intervals[0].clone();
        for intv in sorted_intervals.iter().skip(1) {
            if intv[0] > curr_intv[1] {
                result.push(curr_intv);
                curr_intv = intv.clone();
            } else {
                curr_intv[1] = curr_intv[1].max(intv[1]);
            }
        }
        result.push(curr_intv);
        result
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works_for_sample_test_1() {
        assert_eq!(
            Solution::merge(vec![
                vec![1,3],
                vec![2,6],
                vec![8,10],
                vec![15,18]
            ]),
            vec![
                vec![1,6],
                vec![8,10],
                vec![15,18]
            ]
        );
    }

    #[test]
    fn it_works_for_sample_test_2() {
        assert_eq!(
            Solution::merge(vec![
                vec![1,4],
                vec![1,5]
            ]),
            vec![
                vec![1,5]
            ]
        );
    }
}
<commit_msg>Comments<commit_after>pub struct Solution;

impl Solution {
    pub fn merge(mut intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
        // 由于需要使用intervals中第一个区间作为基准，因此对空做特殊判断。
        if intervals.len() == 0 {
            return Vec::new();
        }

        // 以左值为基准进行排序。
        intervals.sort_by(|a, b| a[0].cmp(&b[0]));

        // 结果集
        let mut result = Vec::new();

        // 取出第0个区间。
        let mut curr_intv = intervals[0].clone();

        // 遍历剩下的区间
        for intv in intervals.iter().skip(1) {
            if intv[0] > curr_intv[1] {  // 不重合，
                // 则把之前合并的结果存入结果集，
                result.push(curr_intv);
                // 然后以当前区间为基准继续。
                curr_intv = intv.clone();
            } else {
                // 如果重合，如：[1, 4] 和 [2, 5]，
                // 则取 (4) 和 (5) 中更大的作为合并结果的右值：
                // [1, 5]。
                curr_intv[1] = curr_intv[1].max(intv[1]);
            }
        }
        // 最后一个结果还没有存入结果集。
        result.push(curr_intv);
        // 返回最终结果
        result
    }
}

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

    #[test]
    fn it_works_for_sample_test_1() {
        assert_eq!(
            Solution::merge(vec![
                vec![1,3],
                vec![2,6],
                vec![8,10],
                vec![15,18]
            ]),
            vec![
                vec![1,6],
                vec![8,10],
                vec![15,18]
            ]
        );
    }

    #[test]
    fn it_works_for_sample_test_2() {
        assert_eq!(
            Solution::merge(vec![
                vec![1,4],
                vec![1,5]
            ]),
            vec![
                vec![1,5]
            ]
        );
    }
}
<|endoftext|>


<commit_before>use std::mem;

use pin_project::pin_project;
use tokio::stream::Stream;
use tokio::sync::{mpsc, watch};

use super::{task, Future, Never, Pin, Poll};

pub fn channel() -> (Signal, Watch) {
    let (tx, rx) = watch::channel(());
    let (drained_tx, drained_rx) = mpsc::channel(1);
    (
        Signal {
            drained_rx,
            _tx: tx,
        },
        Watch { drained_tx, rx },
    )
}

pub struct Signal {
    drained_rx: mpsc::Receiver<Never>,
    _tx: watch::Sender<()>,
}

#[pin_project::pin_project]
pub struct Draining {
    #[pin]
    drained_rx: mpsc::Receiver<Never>,
}

#[derive(Clone)]
pub struct Watch {
    drained_tx: mpsc::Sender<Never>,
    rx: watch::Receiver<()>,
}

#[allow(missing_debug_implementations)]
#[pin_project]
pub struct Watching<F, FN> {
    #[pin]
    future: F,
    state: State<FN>,
    watch: Pin<Box<dyn Future<Output = ()> + Send + Sync>>,
    _drained_tx: mpsc::Sender<Never>,
}

enum State<F> {
    Watch(F),
    Draining,
}

impl Signal {
    pub fn drain(self) -> Draining {
        // Simply dropping `self.tx` will signal the watchers
        Draining {
            drained_rx: self.drained_rx,
        }
    }
}

impl Future for Draining {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        match ready!(self.project().drained_rx.poll_next(cx)) {
            Some(never) => match never {},
            None => Poll::Ready(()),
        }
    }
}

impl Watch {
    pub fn watch<F, FN>(self, future: F, on_drain: FN) -> Watching<F, FN>
    where
        F: Future,
        FN: FnOnce(Pin<&mut F>),
    {
        let Self { drained_tx, mut rx } = self;
        Watching {
            future,
            state: State::Watch(on_drain),
            watch: Box::pin(async move {
                let _ = rx.changed().await;
            }),
            _drained_tx: drained_tx,
        }
    }
}

impl<F, FN> Future for Watching<F, FN>
where
    F: Future,
    FN: FnOnce(Pin<&mut F>),
{
    type Output = F::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        let mut me = self.project();
        loop {
            match mem::replace(me.state, State::Draining) {
                State::Watch(on_drain) => {
                    match Pin::new(&mut me.watch).poll(cx) {
                        Poll::Ready(()) => {
                            // Drain has been triggered!
                            on_drain(me.future.as_mut());
                        }
                        Poll::Pending => {
                            *me.state = State::Watch(on_drain);
                            return me.future.poll(cx);
                        }
                    }
                }
                State::Draining => return me.future.poll(cx),
            }
        }
    }
}

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

    struct TestMe {
        draining: bool,
        finished: bool,
        poll_cnt: usize,
    }

    impl Future for TestMe {
        type Output = ();

        fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> Poll<Self::Output> {
            self.poll_cnt += 1;
            if self.finished {
                Poll::Ready(())
            } else {
                Poll::Pending
            }
        }
    }

    #[test]
    fn watch() {
        let mut mock = tokio_test::task::spawn(());
        mock.enter(|cx, _| {
            let (tx, rx) = channel();
            let fut = TestMe {
                draining: false,
                finished: false,
                poll_cnt: 0,
            };

            let mut watch = rx.watch(fut, |mut fut| {
                fut.draining = true;
            });

            assert_eq!(watch.future.poll_cnt, 0);

            // First poll should poll the inner future
            assert!(Pin::new(&mut watch).poll(cx).is_pending());
            assert_eq!(watch.future.poll_cnt, 1);

            // Second poll should poll the inner future again
            assert!(Pin::new(&mut watch).poll(cx).is_pending());
            assert_eq!(watch.future.poll_cnt, 2);

            let mut draining = tx.drain();
            // Drain signaled, but needs another poll to be noticed.
            assert!(!watch.future.draining);
            assert_eq!(watch.future.poll_cnt, 2);

            // Now, poll after drain has been signaled.
            assert!(Pin::new(&mut watch).poll(cx).is_pending());
            assert_eq!(watch.future.poll_cnt, 3);
            assert!(watch.future.draining);

            // Draining is not ready until watcher completes
            assert!(Pin::new(&mut draining).poll(cx).is_pending());

            // Finishing up the watch future
            watch.future.finished = true;
            assert!(Pin::new(&mut watch).poll(cx).is_ready());
            assert_eq!(watch.future.poll_cnt, 4);
            drop(watch);

            assert!(Pin::new(&mut draining).poll(cx).is_ready());
        })
    }

    #[test]
    fn watch_clones() {
        let mut mock = tokio_test::task::spawn(());
        mock.enter(|cx, _| {
            let (tx, rx) = channel();

            let fut1 = TestMe {
                draining: false,
                finished: false,
                poll_cnt: 0,
            };
            let fut2 = TestMe {
                draining: false,
                finished: false,
                poll_cnt: 0,
            };

            let watch1 = rx.clone().watch(fut1, |mut fut| {
                fut.draining = true;
            });
            let watch2 = rx.watch(fut2, |mut fut| {
                fut.draining = true;
            });

            let mut draining = tx.drain();

            // Still 2 outstanding watchers
            assert!(Pin::new(&mut draining).poll(cx).is_pending());

            // drop 1 for whatever reason
            drop(watch1);

            // Still not ready, 1 other watcher still pending
            assert!(Pin::new(&mut draining).poll(cx).is_pending());

            drop(watch2);

            // Now all watchers are gone, draining is complete
            assert!(Pin::new(&mut draining).poll(cx).is_ready());
        });
    }
}
<commit_msg>refactor(server): simplify drain (#2328)<commit_after>use std::mem;

use pin_project::pin_project;
use tokio::sync::watch;

use super::{task, Future, Pin, Poll};

pub fn channel() -> (Signal, Watch) {
    let (tx, rx) = watch::channel(());
    (Signal { tx }, Watch { rx })
}

pub struct Signal {
    tx: watch::Sender<()>,
}

pub struct Draining(Pin<Box<dyn Future<Output = ()> + Send + Sync>>);

#[derive(Clone)]
pub struct Watch {
    rx: watch::Receiver<()>,
}

#[allow(missing_debug_implementations)]
#[pin_project]
pub struct Watching<F, FN> {
    #[pin]
    future: F,
    state: State<FN>,
    watch: Pin<Box<dyn Future<Output = ()> + Send + Sync>>,
    _rx: watch::Receiver<()>,
}

enum State<F> {
    Watch(F),
    Draining,
}

impl Signal {
    pub fn drain(self) -> Draining {
        let _ = self.tx.send(());
        Draining(Box::pin(async move { self.tx.closed().await }))
    }
}

impl Future for Draining {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        Pin::new(&mut self.as_mut().0).poll(cx)
    }
}

impl Watch {
    pub fn watch<F, FN>(self, future: F, on_drain: FN) -> Watching<F, FN>
    where
        F: Future,
        FN: FnOnce(Pin<&mut F>),
    {
        let Self { mut rx } = self;
        let _rx = rx.clone();
        Watching {
            future,
            state: State::Watch(on_drain),
            watch: Box::pin(async move {
                let _ = rx.changed().await;
            }),
            // Keep the receiver alive until the future completes, so that
            // dropping it can signal that draining has completed.
            _rx,
        }
    }
}

impl<F, FN> Future for Watching<F, FN>
where
    F: Future,
    FN: FnOnce(Pin<&mut F>),
{
    type Output = F::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        let mut me = self.project();
        loop {
            match mem::replace(me.state, State::Draining) {
                State::Watch(on_drain) => {
                    match Pin::new(&mut me.watch).poll(cx) {
                        Poll::Ready(()) => {
                            // Drain has been triggered!
                            on_drain(me.future.as_mut());
                        }
                        Poll::Pending => {
                            *me.state = State::Watch(on_drain);
                            return me.future.poll(cx);
                        }
                    }
                }
                State::Draining => return me.future.poll(cx),
            }
        }
    }
}

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

    struct TestMe {
        draining: bool,
        finished: bool,
        poll_cnt: usize,
    }

    impl Future for TestMe {
        type Output = ();

        fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> Poll<Self::Output> {
            self.poll_cnt += 1;
            if self.finished {
                Poll::Ready(())
            } else {
                Poll::Pending
            }
        }
    }

    #[test]
    fn watch() {
        let mut mock = tokio_test::task::spawn(());
        mock.enter(|cx, _| {
            let (tx, rx) = channel();
            let fut = TestMe {
                draining: false,
                finished: false,
                poll_cnt: 0,
            };

            let mut watch = rx.watch(fut, |mut fut| {
                fut.draining = true;
            });

            assert_eq!(watch.future.poll_cnt, 0);

            // First poll should poll the inner future
            assert!(Pin::new(&mut watch).poll(cx).is_pending());
            assert_eq!(watch.future.poll_cnt, 1);

            // Second poll should poll the inner future again
            assert!(Pin::new(&mut watch).poll(cx).is_pending());
            assert_eq!(watch.future.poll_cnt, 2);

            let mut draining = tx.drain();
            // Drain signaled, but needs another poll to be noticed.
            assert!(!watch.future.draining);
            assert_eq!(watch.future.poll_cnt, 2);

            // Now, poll after drain has been signaled.
            assert!(Pin::new(&mut watch).poll(cx).is_pending());
            assert_eq!(watch.future.poll_cnt, 3);
            assert!(watch.future.draining);

            // Draining is not ready until watcher completes
            assert!(Pin::new(&mut draining).poll(cx).is_pending());

            // Finishing up the watch future
            watch.future.finished = true;
            assert!(Pin::new(&mut watch).poll(cx).is_ready());
            assert_eq!(watch.future.poll_cnt, 4);
            drop(watch);

            assert!(Pin::new(&mut draining).poll(cx).is_ready());
        })
    }

    #[test]
    fn watch_clones() {
        let mut mock = tokio_test::task::spawn(());
        mock.enter(|cx, _| {
            let (tx, rx) = channel();

            let fut1 = TestMe {
                draining: false,
                finished: false,
                poll_cnt: 0,
            };
            let fut2 = TestMe {
                draining: false,
                finished: false,
                poll_cnt: 0,
            };

            let watch1 = rx.clone().watch(fut1, |mut fut| {
                fut.draining = true;
            });
            let watch2 = rx.watch(fut2, |mut fut| {
                fut.draining = true;
            });

            let mut draining = tx.drain();

            // Still 2 outstanding watchers
            assert!(Pin::new(&mut draining).poll(cx).is_pending());

            // drop 1 for whatever reason
            drop(watch1);

            // Still not ready, 1 other watcher still pending
            assert!(Pin::new(&mut draining).poll(cx).is_pending());

            drop(watch2);

            // Now all watchers are gone, draining is complete
            assert!(Pin::new(&mut draining).poll(cx).is_ready());
        });
    }
}
<|endoftext|>


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

use super::*;
use crate::util::expect_err;
use alloc::{borrow::ToOwned, vec};
use core::cmp::Ordering;

#[test]
fn test_label_encode() {
    let tests = vec![
        (Label::Int(2), "02"),
        (Label::Int(-1), "20"),
        (Label::Text("abc".to_owned()), "63616263"),
    ];

    for (i, (label, label_data)) in tests.iter().enumerate() {
        let got = label.clone().to_vec().unwrap();
        assert_eq!(*label_data, hex::encode(&got), "case {}", i);

        let got = Label::from_slice(&got).unwrap();
        assert_eq!(*label, got);
    }
}

#[test]
fn test_label_sort() {
    // Pairs of `Label`s with the "smaller" first.
    let pairs = vec![
        (Label::Int(0x1234), Label::Text("a".to_owned())),
        (Label::Int(0x1234), Label::Text("ab".to_owned())),
        (Label::Int(0), Label::Text("ab".to_owned())),
        (Label::Int(-1), Label::Text("ab".to_owned())),
        (Label::Int(0), Label::Int(10)),
        (Label::Int(0), Label::Int(-10)),
        (Label::Int(10), Label::Int(-1)),
        (Label::Int(-1), Label::Int(-2)),
        (Label::Int(0x12), Label::Int(0x1234)),
        (Label::Int(0x99), Label::Int(0x1234)),
        (Label::Int(0x1234), Label::Int(0x1235)),
        (Label::Text("a".to_owned()), Label::Text("ab".to_owned())),
        (Label::Text("aa".to_owned()), Label::Text("ab".to_owned())),
    ];
    for (left, right) in pairs.into_iter() {
        let value_cmp = left.cmp(&right);
        let value_partial_cmp = left.partial_cmp(&right);
        let left_data = left.clone().to_vec().unwrap();
        let right_data = right.clone().to_vec().unwrap();
        let data_cmp = left_data.cmp(&right_data);
        let reverse_cmp = right.cmp(&left);
        let equal_cmp = left.cmp(&left);

        assert_eq!(value_cmp, Ordering::Less, "{:?} < {:?}", left, right);
        assert_eq!(
            value_partial_cmp,
            Some(Ordering::Less),
            "{:?} < {:?}",
            left,
            right
        );
        assert_eq!(
            data_cmp,
            Ordering::Less,
            "{:?}={} < {:?}={}",
            left,
            hex::encode(&left_data),
            right,
            hex::encode(&right_data)
        );
        assert_eq!(reverse_cmp, Ordering::Greater, "{:?} > {:?}", right, left);
        assert_eq!(equal_cmp, Ordering::Equal, "{:?} = {:?}", left, left);
    }
}

#[test]
fn test_label_decode_fail() {
    let tests = vec![
        ("43010203", "expected int/tstr"),
        ("", "decode CBOR failure"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = Label::from_slice(&data);
        expect_err(result, err_msg);
    }
}

#[test]
fn test_registered_label_encode() {
    let tests = vec![
        (RegisteredLabel::Assigned(iana::Algorithm::A192GCM), "02"),
        (RegisteredLabel::Assigned(iana::Algorithm::EdDSA), "27"),
        (RegisteredLabel::Text("abc".to_owned()), "63616263"),
    ];

    for (i, (label, label_data)) in tests.iter().enumerate() {
        let got = label.clone().to_vec().unwrap();
        assert_eq!(*label_data, hex::encode(&got), "case {}", i);

        let got = RegisteredLabel::from_slice(&got).unwrap();
        assert_eq!(*label, got);
    }
}

#[test]
fn test_registered_label_sort() {
    use RegisteredLabel::{Assigned, Text};
    // Pairs of `RegisteredLabel`s with the "smaller" first.
    let pairs = vec![
        (Assigned(iana::Algorithm::A192GCM), Text("a".to_owned())),
        (Assigned(iana::Algorithm::WalnutDSA), Text("ab".to_owned())),
        (Text("ab".to_owned()), Text("cd".to_owned())),
        (Text("ab".to_owned()), Text("abcd".to_owned())),
        (
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
            Assigned(iana::Algorithm::A128KW),
        ),
        (
            Assigned(iana::Algorithm::A192GCM),
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
        ),
    ];
    for (left, right) in pairs.into_iter() {
        let value_cmp = left.cmp(&right);
        let value_partial_cmp = left.partial_cmp(&right);
        let left_data = left.clone().to_vec().unwrap();
        let right_data = right.clone().to_vec().unwrap();
        let data_cmp = left_data.cmp(&right_data);
        let reverse_cmp = right.cmp(&left);
        let equal_cmp = left.cmp(&left);

        assert_eq!(value_cmp, Ordering::Less, "{:?} < {:?}", left, right);
        assert_eq!(
            value_partial_cmp,
            Some(Ordering::Less),
            "{:?} < {:?}",
            left,
            right
        );
        assert_eq!(
            data_cmp,
            Ordering::Less,
            "{:?}={} < {:?}={}",
            left,
            hex::encode(&left_data),
            right,
            hex::encode(&right_data)
        );
        assert_eq!(reverse_cmp, Ordering::Greater, "{:?} > {:?}", right, left);
        assert_eq!(equal_cmp, Ordering::Equal, "{:?} = {:?}", left, left);
    }
}

#[test]
fn test_registered_label_decode_fail() {
    let tests = vec![
        ("43010203", "expected int/tstr"),
        ("", "decode CBOR failure"),
        ("09", "expected recognized IANA value"),
        ("394e1f", "expected recognized IANA value"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = RegisteredLabel::<iana::EllipticCurve>::from_slice(&data);
        expect_err(result, err_msg);
    }
}

iana_registry! {
    TestPrivateLabel {
        Reserved: 0,
        Something: 1,
    }
}

impl WithPrivateRange for TestPrivateLabel {
    fn is_private(i: i64) -> bool {
        i > 10 || i < 1000
    }
}

#[test]
fn test_registered_label_with_private_encode() {
    let tests = vec![
        (
            RegisteredLabelWithPrivate::Assigned(TestPrivateLabel::Something),
            "01",
        ),
        (
            RegisteredLabelWithPrivate::Text("abc".to_owned()),
            "63616263",
        ),
        (
            RegisteredLabelWithPrivate::PrivateUse(-70_000),
            "3a0001116f",
        ),
        (RegisteredLabelWithPrivate::PrivateUse(11), "0b"),
    ];

    for (i, (label, label_data)) in tests.iter().enumerate() {
        let got = label.clone().to_vec().unwrap();
        assert_eq!(*label_data, hex::encode(&got), "case {}", i);

        let got = RegisteredLabelWithPrivate::from_slice(&got).unwrap();
        assert_eq!(*label, got);
    }
}

#[test]
fn test_registered_label_with_private_sort() {
    use RegisteredLabelWithPrivate::{Assigned, PrivateUse, Text};
    // Pairs of `RegisteredLabelWithPrivate`s with the "smaller" first.
    let pairs = vec![
        (Assigned(iana::Algorithm::A192GCM), Text("a".to_owned())),
        (Assigned(iana::Algorithm::WalnutDSA), Text("ab".to_owned())),
        (Text("ab".to_owned()), Text("cd".to_owned())),
        (Text("ab".to_owned()), Text("abcd".to_owned())),
        (
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
            Assigned(iana::Algorithm::A128KW),
        ),
        (
            Assigned(iana::Algorithm::A192GCM),
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
        ),
        (
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
            PrivateUse(-70_000),
        ),
        (PrivateUse(-70_000), PrivateUse(-70_001)),
        (PrivateUse(-70_000), Text("a".to_owned())),
    ];
    for (left, right) in pairs.into_iter() {
        let value_cmp = left.cmp(&right);
        let value_partial_cmp = left.partial_cmp(&right);
        let left_data = left.clone().to_vec().unwrap();
        let right_data = right.clone().to_vec().unwrap();
        let data_cmp = left_data.cmp(&right_data);
        let reverse_cmp = right.cmp(&left);
        let equal_cmp = left.cmp(&left);

        assert_eq!(value_cmp, Ordering::Less, "{:?} < {:?}", left, right);
        assert_eq!(
            value_partial_cmp,
            Some(Ordering::Less),
            "{:?} < {:?}",
            left,
            right
        );
        assert_eq!(
            data_cmp,
            Ordering::Less,
            "{:?}={} < {:?}={}",
            left,
            hex::encode(&left_data),
            right,
            hex::encode(&right_data)
        );
        assert_eq!(reverse_cmp, Ordering::Greater, "{:?} > {:?}", right, left);
        assert_eq!(equal_cmp, Ordering::Equal, "{:?} = {:?}", left, left);
    }
}

#[test]
fn test_registered_label_with_private_decode_fail() {
    let tests = vec![
        ("43010203", "expected int/tstr"),
        ("", "decode CBOR failure"),
        ("09", "expected value in IANA or private use range"),
        ("394e1f", "expected value in IANA or private use range"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = RegisteredLabelWithPrivate::<iana::Algorithm>::from_slice(&data);
        expect_err(result, err_msg);
    }
}

// FIXME
// The most negative integer value that can be encoded in CBOR is:
//    0x3B (0b001_11011) 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
// which is -18_446_744_073_709_551_616 (-1 - 18_446_744_073_709_551_615).
//
// However, the underlying sk-cbor crate encodes negative integers as
// `Value::Negative(i64)`, which cannot hold this value
const CBOR_NINT_MIN_HEX: &str = "3bffffffffffffffff";

// FIXME
// The largest positive integer value that can be encoded in CBOR is:
//    0x1B (0b000_11011) 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
// which is 18_446_744_073_709_551_615.
//
// This is supported by the underlying sk-cbor crate as `Value::Unsigned(u64)`
// but the current crate uses `i64` everywhere for convenience, and so cannot
// hold this value.
const CBOR_INT_MAX_HEX: &str = "1bffffffffffffffff";

#[test]
fn test_large_label_decode_fail() {
    let tests = vec![
        (CBOR_NINT_MIN_HEX, "OutOfRangeIntegerValue"),
        (CBOR_INT_MAX_HEX, "expected u63"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = Label::from_slice(&data);
        expect_err(result, err_msg);
    }
}

#[test]
fn test_large_registered_label_decode_fail() {
    let tests = vec![
        (CBOR_NINT_MIN_HEX, "OutOfRangeIntegerValue"),
        (CBOR_INT_MAX_HEX, "expected u63"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = RegisteredLabel::<crate::iana::HeaderParameter>::from_slice(&data);
        expect_err(result, err_msg);
    }
}
<commit_msg>Update integer range test<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

use super::*;
use crate::util::expect_err;
use alloc::{borrow::ToOwned, vec};
use core::cmp::Ordering;

#[test]
fn test_label_encode() {
    let tests = vec![
        (Label::Int(2), "02"),
        (Label::Int(-1), "20"),
        (Label::Text("abc".to_owned()), "63616263"),
    ];

    for (i, (label, label_data)) in tests.iter().enumerate() {
        let got = label.clone().to_vec().unwrap();
        assert_eq!(*label_data, hex::encode(&got), "case {}", i);

        let got = Label::from_slice(&got).unwrap();
        assert_eq!(*label, got);
    }
}

#[test]
fn test_label_sort() {
    // Pairs of `Label`s with the "smaller" first.
    let pairs = vec![
        (Label::Int(0x1234), Label::Text("a".to_owned())),
        (Label::Int(0x1234), Label::Text("ab".to_owned())),
        (Label::Int(0), Label::Text("ab".to_owned())),
        (Label::Int(-1), Label::Text("ab".to_owned())),
        (Label::Int(0), Label::Int(10)),
        (Label::Int(0), Label::Int(-10)),
        (Label::Int(10), Label::Int(-1)),
        (Label::Int(-1), Label::Int(-2)),
        (Label::Int(0x12), Label::Int(0x1234)),
        (Label::Int(0x99), Label::Int(0x1234)),
        (Label::Int(0x1234), Label::Int(0x1235)),
        (Label::Text("a".to_owned()), Label::Text("ab".to_owned())),
        (Label::Text("aa".to_owned()), Label::Text("ab".to_owned())),
    ];
    for (left, right) in pairs.into_iter() {
        let value_cmp = left.cmp(&right);
        let value_partial_cmp = left.partial_cmp(&right);
        let left_data = left.clone().to_vec().unwrap();
        let right_data = right.clone().to_vec().unwrap();
        let data_cmp = left_data.cmp(&right_data);
        let reverse_cmp = right.cmp(&left);
        let equal_cmp = left.cmp(&left);

        assert_eq!(value_cmp, Ordering::Less, "{:?} < {:?}", left, right);
        assert_eq!(
            value_partial_cmp,
            Some(Ordering::Less),
            "{:?} < {:?}",
            left,
            right
        );
        assert_eq!(
            data_cmp,
            Ordering::Less,
            "{:?}={} < {:?}={}",
            left,
            hex::encode(&left_data),
            right,
            hex::encode(&right_data)
        );
        assert_eq!(reverse_cmp, Ordering::Greater, "{:?} > {:?}", right, left);
        assert_eq!(equal_cmp, Ordering::Equal, "{:?} = {:?}", left, left);
    }
}

#[test]
fn test_label_decode_fail() {
    let tests = vec![
        ("43010203", "expected int/tstr"),
        ("", "decode CBOR failure"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = Label::from_slice(&data);
        expect_err(result, err_msg);
    }
}

#[test]
fn test_registered_label_encode() {
    let tests = vec![
        (RegisteredLabel::Assigned(iana::Algorithm::A192GCM), "02"),
        (RegisteredLabel::Assigned(iana::Algorithm::EdDSA), "27"),
        (RegisteredLabel::Text("abc".to_owned()), "63616263"),
    ];

    for (i, (label, label_data)) in tests.iter().enumerate() {
        let got = label.clone().to_vec().unwrap();
        assert_eq!(*label_data, hex::encode(&got), "case {}", i);

        let got = RegisteredLabel::from_slice(&got).unwrap();
        assert_eq!(*label, got);
    }
}

#[test]
fn test_registered_label_sort() {
    use RegisteredLabel::{Assigned, Text};
    // Pairs of `RegisteredLabel`s with the "smaller" first.
    let pairs = vec![
        (Assigned(iana::Algorithm::A192GCM), Text("a".to_owned())),
        (Assigned(iana::Algorithm::WalnutDSA), Text("ab".to_owned())),
        (Text("ab".to_owned()), Text("cd".to_owned())),
        (Text("ab".to_owned()), Text("abcd".to_owned())),
        (
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
            Assigned(iana::Algorithm::A128KW),
        ),
        (
            Assigned(iana::Algorithm::A192GCM),
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
        ),
    ];
    for (left, right) in pairs.into_iter() {
        let value_cmp = left.cmp(&right);
        let value_partial_cmp = left.partial_cmp(&right);
        let left_data = left.clone().to_vec().unwrap();
        let right_data = right.clone().to_vec().unwrap();
        let data_cmp = left_data.cmp(&right_data);
        let reverse_cmp = right.cmp(&left);
        let equal_cmp = left.cmp(&left);

        assert_eq!(value_cmp, Ordering::Less, "{:?} < {:?}", left, right);
        assert_eq!(
            value_partial_cmp,
            Some(Ordering::Less),
            "{:?} < {:?}",
            left,
            right
        );
        assert_eq!(
            data_cmp,
            Ordering::Less,
            "{:?}={} < {:?}={}",
            left,
            hex::encode(&left_data),
            right,
            hex::encode(&right_data)
        );
        assert_eq!(reverse_cmp, Ordering::Greater, "{:?} > {:?}", right, left);
        assert_eq!(equal_cmp, Ordering::Equal, "{:?} = {:?}", left, left);
    }
}

#[test]
fn test_registered_label_decode_fail() {
    let tests = vec![
        ("43010203", "expected int/tstr"),
        ("", "decode CBOR failure"),
        ("09", "expected recognized IANA value"),
        ("394e1f", "expected recognized IANA value"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = RegisteredLabel::<iana::EllipticCurve>::from_slice(&data);
        expect_err(result, err_msg);
    }
}

iana_registry! {
    TestPrivateLabel {
        Reserved: 0,
        Something: 1,
    }
}

impl WithPrivateRange for TestPrivateLabel {
    fn is_private(i: i64) -> bool {
        i > 10 || i < 1000
    }
}

#[test]
fn test_registered_label_with_private_encode() {
    let tests = vec![
        (
            RegisteredLabelWithPrivate::Assigned(TestPrivateLabel::Something),
            "01",
        ),
        (
            RegisteredLabelWithPrivate::Text("abc".to_owned()),
            "63616263",
        ),
        (
            RegisteredLabelWithPrivate::PrivateUse(-70_000),
            "3a0001116f",
        ),
        (RegisteredLabelWithPrivate::PrivateUse(11), "0b"),
    ];

    for (i, (label, label_data)) in tests.iter().enumerate() {
        let got = label.clone().to_vec().unwrap();
        assert_eq!(*label_data, hex::encode(&got), "case {}", i);

        let got = RegisteredLabelWithPrivate::from_slice(&got).unwrap();
        assert_eq!(*label, got);
    }
}

#[test]
fn test_registered_label_with_private_sort() {
    use RegisteredLabelWithPrivate::{Assigned, PrivateUse, Text};
    // Pairs of `RegisteredLabelWithPrivate`s with the "smaller" first.
    let pairs = vec![
        (Assigned(iana::Algorithm::A192GCM), Text("a".to_owned())),
        (Assigned(iana::Algorithm::WalnutDSA), Text("ab".to_owned())),
        (Text("ab".to_owned()), Text("cd".to_owned())),
        (Text("ab".to_owned()), Text("abcd".to_owned())),
        (
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
            Assigned(iana::Algorithm::A128KW),
        ),
        (
            Assigned(iana::Algorithm::A192GCM),
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
        ),
        (
            Assigned(iana::Algorithm::AES_CCM_16_64_128),
            PrivateUse(-70_000),
        ),
        (PrivateUse(-70_000), PrivateUse(-70_001)),
        (PrivateUse(-70_000), Text("a".to_owned())),
    ];
    for (left, right) in pairs.into_iter() {
        let value_cmp = left.cmp(&right);
        let value_partial_cmp = left.partial_cmp(&right);
        let left_data = left.clone().to_vec().unwrap();
        let right_data = right.clone().to_vec().unwrap();
        let data_cmp = left_data.cmp(&right_data);
        let reverse_cmp = right.cmp(&left);
        let equal_cmp = left.cmp(&left);

        assert_eq!(value_cmp, Ordering::Less, "{:?} < {:?}", left, right);
        assert_eq!(
            value_partial_cmp,
            Some(Ordering::Less),
            "{:?} < {:?}",
            left,
            right
        );
        assert_eq!(
            data_cmp,
            Ordering::Less,
            "{:?}={} < {:?}={}",
            left,
            hex::encode(&left_data),
            right,
            hex::encode(&right_data)
        );
        assert_eq!(reverse_cmp, Ordering::Greater, "{:?} > {:?}", right, left);
        assert_eq!(equal_cmp, Ordering::Equal, "{:?} = {:?}", left, left);
    }
}

#[test]
fn test_registered_label_with_private_decode_fail() {
    let tests = vec![
        ("43010203", "expected int/tstr"),
        ("", "decode CBOR failure"),
        ("09", "expected value in IANA or private use range"),
        ("394e1f", "expected value in IANA or private use range"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = RegisteredLabelWithPrivate::<iana::Algorithm>::from_slice(&data);
        expect_err(result, err_msg);
    }
}

// The most negative integer value that can be encoded in CBOR is:
//    0x3B (0b001_11011) 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
// which is -18_446_744_073_709_551_616 (-1 - 18_446_744_073_709_551_615).
//
// However, this crate uses `i64` for all integers, which cannot hold
// negative values below `i64::MIN` (=-2^63 = 0x8000000000000000).
const CBOR_NINT_MIN_HEX: &str = "3b7fffffffffffffff";
const CBOR_NINT_OUT_OF_RANGE_HEX: &str = "3b8000000000000000";

// The largest positive integer value that can be encoded in CBOR is:
//    0x1B (0b000_11011) 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
// which is 18_446_744_073_709_551_615.
//
// However, this crate uses `i64` for all integers, which cannot hold
// positive values above `i64::MAX` (=-2^63 - 1 = 0x7fffffffffffffff).
const CBOR_INT_MAX_HEX: &str = "1b7fffffffffffffff";
const CBOR_INT_OUT_OF_RANGE_HEX: &str = "1b8000000000000000";

#[test]
fn test_large_label_decode() {
    let tests = vec![(CBOR_NINT_MIN_HEX, i64::MIN), (CBOR_INT_MAX_HEX, i64::MAX)];
    for (label_data, want) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let got = Label::from_slice(&data).unwrap();
        assert_eq!(got, Label::Int(*want))
    }
}

#[test]
fn test_large_label_decode_fail() {
    let tests = vec![
        (CBOR_NINT_OUT_OF_RANGE_HEX, "got u64, expected u63"),
        (CBOR_INT_OUT_OF_RANGE_HEX, "expected u63"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = Label::from_slice(&data);
        expect_err(result, err_msg);
    }
}

#[test]
fn test_large_registered_label_decode_fail() {
    let tests = vec![
        (CBOR_NINT_OUT_OF_RANGE_HEX, "got u64, expected u63"),
        (CBOR_INT_OUT_OF_RANGE_HEX, "expected u63"),
    ];
    for (label_data, err_msg) in tests.iter() {
        let data = hex::decode(label_data).unwrap();
        let result = RegisteredLabel::<crate::iana::HeaderParameter>::from_slice(&data);
        expect_err(result, err_msg);
    }
}
<|endoftext|>


<commit_before>#![feature(custom_attribute)]
#![feature(raw_identifiers)]
#![feature(extern_types)]
#![allow(invalid_type_param_default)]
#![allow(unused_attributes)]

use r#foo as r#alias_foo;

fn main() {
    #[r#attr]
    r#foo::r#bar();

    let r#local = 3;
    let r#async = r#foo(r#local);
    r#macro!();

    if let r#sub_pat @ r#Foo(_) = r#Foo(3) {}

    match r#async {
        r#Foo | r#Bar => r#foo(),
    }
}

fn r#bar<'a, r#T>(r#x: &'a r#T) {}

mod r#foo {
    pub fn r#bar() {}
}

enum r#Foo {
    r#Bar {},
}

trait r#Trait {
    type r#Type;
}

impl r#Trait for r#Impl {
    type r#Type = r#u32;
    fn r#xxx(r#fjio: r#u32) {}
}

extern "C" {
    type r#ccc;
    static r#static_val: u32;
}

macro_rules! r#macro {
    () => {};
}
<commit_msg>Add more tests<commit_after>#![feature(custom_attribute)]
#![feature(raw_identifiers)]
#![feature(extern_types)]
#![allow(invalid_type_param_default)]
#![allow(unused_attributes)]

use r#foo as r#alias_foo;

fn main() {
    #[r#attr]
    r#foo::r#bar();

    let r#local = 3;
    let r#async = r#foo(r#local);
    r#macro!();

    if let r#sub_pat @ r#Foo(_) = r#Foo(3) {}

    match r#async {
        r#Foo | r#Bar => r#foo(),
    }
}

fn r#bar<'a, r#T>(r#x: &'a r#T) {}

mod r#foo {
    pub fn r#bar() {}
}

enum r#Foo {
    r#Bar {},
}

struct r#Struct {
    r#field: r#FieldType,
}

trait r#Trait {
    type r#Type;
}

impl r#Trait for r#Impl {
    type r#Type = r#u32;
    fn r#xxx(r#fjio: r#u32) {}
}

extern "C" {
    type r#ccc;
    static r#static_val: u32;
}

macro_rules! r#macro {
    () => {};
}

macro_rules! foo {
    ($x:expr) => {
        let r#catch = $x + 1;
        println!("{}", r#catch);
    };
}
<|endoftext|>