<commit_before>//! Tests for the cache file for the rustc version info.

use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::{basic_manifest, project};
use std::env;

#[cargo_test]
fn rustc_info_cache() {
    // TODO: need to gate this on nightly as soon as -Cbitcode-in-rlib lands in
    // nightly
    if true {
        return;
    }

    let p = project()
        .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
        .build();

    let miss = "[..] rustc info cache miss[..]";
    let hit = "[..]rustc info cache hit[..]";
    let update = "[..]updated rustc info cache[..]";

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .with_stderr_contains("[..]failed to read rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("CARGO_CACHE_RUSTC_INFO", "0")
        .with_stderr_contains("[..]rustc info cache disabled[..]")
        .with_stderr_does_not_contain(update)
        .run();

    let other_rustc = {
        let p = project()
            .at("compiler")
            .file("Cargo.toml", &basic_manifest("compiler", "0.1.0"))
            .file(
                "src/main.rs",
                r#"
            use std::process::Command;
            use std::env;

            fn main() {
                let mut cmd = Command::new("rustc");
                for arg in env::args_os().skip(1) {
                    cmd.arg(arg);
                }
                std::process::exit(cmd.status().unwrap().code().unwrap());
            }
        "#,
            )
            .build();
        p.cargo("build").run();

        p.root()
            .join("target/debug/compiler")
            .with_extension(env::consts::EXE_EXTENSION)
    };

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();

    other_rustc.move_into_the_future();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();
}
<commit_msg>Re-enable rustc-info-cache test again<commit_after>//! Tests for the cache file for the rustc version info.

use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::{basic_manifest, project};
use std::env;

#[cargo_test]
fn rustc_info_cache() {
    // Needs `-Cbitcode-in-rlib` to ride to stable before this can be enabled
    // everywhere.
    if !cargo_test_support::is_nightly() {
        return;
    }

    let p = project()
        .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
        .build();

    let miss = "[..] rustc info cache miss[..]";
    let hit = "[..]rustc info cache hit[..]";
    let update = "[..]updated rustc info cache[..]";

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .with_stderr_contains("[..]failed to read rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("CARGO_CACHE_RUSTC_INFO", "0")
        .with_stderr_contains("[..]rustc info cache disabled[..]")
        .with_stderr_does_not_contain(update)
        .run();

    let other_rustc = {
        let p = project()
            .at("compiler")
            .file("Cargo.toml", &basic_manifest("compiler", "0.1.0"))
            .file(
                "src/main.rs",
                r#"
            use std::process::Command;
            use std::env;

            fn main() {
                let mut cmd = Command::new("rustc");
                for arg in env::args_os().skip(1) {
                    cmd.arg(arg);
                }
                std::process::exit(cmd.status().unwrap().code().unwrap());
            }
        "#,
            )
            .build();
        p.cargo("build").run();

        p.root()
            .join("target/debug/compiler")
            .with_extension(env::consts::EXE_EXTENSION)
    };

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();

    other_rustc.move_into_the_future();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]different compiler, creating new rustc info cache[..]")
        .with_stderr_contains(miss)
        .with_stderr_does_not_contain(hit)
        .with_stderr_contains(update)
        .run();

    p.cargo("build")
        .env("CARGO_LOG", "cargo::util::rustc=debug")
        .env("RUSTC", other_rustc.display().to_string())
        .with_stderr_contains("[..]reusing existing rustc info cache[..]")
        .with_stderr_contains(hit)
        .with_stderr_does_not_contain(miss)
        .with_stderr_does_not_contain(update)
        .run();
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::{self, File};
use std::path::{self, Path, PathBuf};

use tar::{Archive, Builder};
use flate2::{GzBuilder, Compression};
use flate2::read::GzDecoder;

use core::{SourceId, Package, PackageId};
use sources::PathSource;
use util::{self, CargoResult, human, internal, ChainError, Config};
use ops;

pub fn package(manifest_path: &Path,
               config: &Config,
               verify: bool,
               list: bool,
               metadata: bool) -> CargoResult<Option<PathBuf>> {
    let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                            config));
    let pkg = try!(src.root_package());

    if metadata {
        try!(check_metadata(&pkg, config));
    }

    if list {
        let root = pkg.root();
        let mut list: Vec<_> = try!(src.list_files(&pkg)).iter().map(|file| {
            util::without_prefix(&file, &root).unwrap().to_path_buf()
        }).collect();
        list.sort();
        for file in list.iter() {
            println!("{}", file.display());
        }
        return Ok(None)
    }

    let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
    let dir = config.target_dir(&pkg).join("package");
    let dst = dir.join(&filename);
    if fs::metadata(&dst).is_ok() {
        return Ok(Some(dst))
    }

    // Package up and test a temporary tarball and only move it to the final
    // location if it actually passes all our tests. Any previously existing
    // tarball can be assumed as corrupt or invalid, so we just blow it away if
    // it exists.
    try!(config.shell().status("Packaging", pkg.package_id().to_string()));
    let tmp_dst = dir.join(format!(".{}", filename));
    let _ = fs::remove_file(&tmp_dst);
    try!(tar(&pkg, &src, config, &tmp_dst, &filename).chain_error(|| {
        human("failed to prepare local package for uploading")
    }));
    if verify {
        try!(run_verify(config, &pkg, &tmp_dst).chain_error(|| {
            human("failed to verify package tarball")
        }))
    }
    try!(fs::rename(&tmp_dst, &dst).chain_error(|| {
        human("failed to move temporary tarball into final location")
    }));
    Ok(Some(dst))
}

// check that the package has some piece of metadata that a human can
// use to tell what the package is about.
#[allow(deprecated)] // connect => join in 1.3
fn check_metadata(pkg: &Package, config: &Config) -> CargoResult<()> {
    let md = pkg.manifest().metadata();

    let mut missing = vec![];

    macro_rules! lacking {
        ($( $($field: ident)||* ),*) => {{
            $(
                if $(md.$field.as_ref().map_or(true, |s| s.is_empty()))&&* {
                    $(missing.push(stringify!($field).replace("_", "-"));)*
                }
            )*
        }}
    }
    lacking!(description, license || license_file, documentation || homepage || repository);

    if !missing.is_empty() {
        let mut things = missing[..missing.len() - 1].connect(", ");
        // things will be empty if and only if length == 1 (i.e. the only case
        // to have no `or`).
        if !things.is_empty() {
            things.push_str(" or ");
        }
        things.push_str(&missing.last().unwrap());

        try!(config.shell().warn(
            &format!("warning: manifest has no {things}. \
                    See http://doc.crates.io/manifest.html#package-metadata for more info.",
                    things = things)))
    }
    Ok(())
}

fn tar(pkg: &Package,
       src: &PathSource,
       config: &Config,
       dst: &Path,
       filename: &str) -> CargoResult<()> {
    if fs::metadata(&dst).is_ok() {
        bail!("destination already exists: {}", dst.display())
    }

    try!(fs::create_dir_all(dst.parent().unwrap()));

    let tmpfile = try!(File::create(dst));

    // Prepare the encoder and its header
    let filename = Path::new(filename);
    let encoder = GzBuilder::new().filename(try!(util::path2bytes(filename)))
                                  .write(tmpfile, Compression::Best);

    // Put all package files into a compressed archive
    let mut ar = Builder::new(encoder);
    let root = pkg.root();
    for file in try!(src.list_files(pkg)).iter() {
        if &**file == dst { continue }
        let relative = util::without_prefix(&file, &root).unwrap();
        try!(check_filename(relative));
        let relative = try!(relative.to_str().chain_error(|| {
            human(format!("non-utf8 path in source directory: {}",
                          relative.display()))
        }));
        let mut file = try!(File::open(file));
        try!(config.shell().verbose(|shell| {
            shell.status("Archiving", &relative)
        }));
        let path = format!("{}-{}{}{}", pkg.name(), pkg.version(),
                           path::MAIN_SEPARATOR, relative);
        try!(ar.append_file(&path, &mut file).chain_error(|| {
            internal(format!("could not archive source file `{}`", relative))
        }));
    }
    let encoder = try!(ar.into_inner());
    try!(encoder.finish());
    Ok(())
}

fn run_verify(config: &Config, pkg: &Package, tar: &Path)
              -> CargoResult<()> {
    try!(config.shell().status("Verifying", pkg));

    let f = try!(GzDecoder::new(try!(File::open(tar))));
    let dst = pkg.root().join(&format!("target/package/{}-{}",
                                       pkg.name(), pkg.version()));
    if fs::metadata(&dst).is_ok() {
        try!(fs::remove_dir_all(&dst));
    }
    let mut archive = Archive::new(f);
    try!(archive.unpack(dst.parent().unwrap()));
    let manifest_path = dst.join("Cargo.toml");

    // When packages are uploaded to the registry, all path dependencies are
    // implicitly converted to registry-based dependencies, so we rewrite those
    // dependencies here.
    //
    // We also make sure to point all paths at `dst` instead of the previous
    // location that the package was originally read from. In locking the
    // `SourceId` we're telling it that the corresponding `PathSource` will be
    // considered updated and we won't actually read any packages.
    let registry = try!(SourceId::for_central(config));
    let precise = Some("locked".to_string());
    let new_src = try!(SourceId::for_path(&dst)).with_precise(precise);
    let new_pkgid = try!(PackageId::new(pkg.name(), pkg.version(), &new_src));
    let new_summary = pkg.summary().clone().map_dependencies(|d| {
        if !d.source_id().is_path() { return d }
        d.clone_inner().set_source_id(registry.clone()).into_dependency()
    });
    let mut new_manifest = pkg.manifest().clone();
    new_manifest.set_summary(new_summary.override_id(new_pkgid));
    let new_pkg = Package::new(new_manifest, &manifest_path);

    // Now that we've rewritten all our path dependencies, compile it!
    try!(ops::compile_pkg(&new_pkg, None, &ops::CompileOptions {
        config: config,
        jobs: None,
        target: None,
        features: &[],
        no_default_features: false,
        spec: &[],
        filter: ops::CompileFilter::Everything,
        exec_engine: None,
        release: false,
        mode: ops::CompileMode::Build,
        target_rustdoc_args: None,
        target_rustc_args: None,
    }));

    Ok(())
}

// It can often be the case that files of a particular name on one platform
// can't actually be created on another platform. For example files with colons
// in the name are allowed on Unix but not on Windows.
//
// To help out in situations like this, issue about weird filenames when
// packaging as a "heads up" that something may not work on other platforms.
fn check_filename(file: &Path) -> CargoResult<()> {
    let name = match file.file_name() {
        Some(name) => name,
        None => return Ok(()),
    };
    let name = match name.to_str() {
        Some(name) => name,
        None => {
            bail!("path does not have a unicode filename which may not unpack \
                   on all platforms: {}", file.display())
        }
    };
    let bad_chars = ['/', '\\', '<', '>', ':', '"', '|', '?', '*'];
    for c in bad_chars.iter().filter(|c| name.contains(**c)) {
        bail!("cannot package a filename with a special character `{}`: {}",
              c, file.display())
    }
    Ok(())
}
<commit_msg>Auto merge of #2327 - alexcrichton:ustar-archives, r=alexcrichton<commit_after>use std::io::prelude::*;
use std::fs::{self, File};
use std::path::{self, Path, PathBuf};

use tar::{Archive, Builder, Header};
use flate2::{GzBuilder, Compression};
use flate2::read::GzDecoder;

use core::{SourceId, Package, PackageId};
use sources::PathSource;
use util::{self, CargoResult, human, internal, ChainError, Config};
use ops;

pub fn package(manifest_path: &Path,
               config: &Config,
               verify: bool,
               list: bool,
               metadata: bool) -> CargoResult<Option<PathBuf>> {
    let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                            config));
    let pkg = try!(src.root_package());

    if metadata {
        try!(check_metadata(&pkg, config));
    }

    if list {
        let root = pkg.root();
        let mut list: Vec<_> = try!(src.list_files(&pkg)).iter().map(|file| {
            util::without_prefix(&file, &root).unwrap().to_path_buf()
        }).collect();
        list.sort();
        for file in list.iter() {
            println!("{}", file.display());
        }
        return Ok(None)
    }

    let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
    let dir = config.target_dir(&pkg).join("package");
    let dst = dir.join(&filename);
    if fs::metadata(&dst).is_ok() {
        return Ok(Some(dst))
    }

    // Package up and test a temporary tarball and only move it to the final
    // location if it actually passes all our tests. Any previously existing
    // tarball can be assumed as corrupt or invalid, so we just blow it away if
    // it exists.
    try!(config.shell().status("Packaging", pkg.package_id().to_string()));
    let tmp_dst = dir.join(format!(".{}", filename));
    let _ = fs::remove_file(&tmp_dst);
    try!(tar(&pkg, &src, config, &tmp_dst, &filename).chain_error(|| {
        human("failed to prepare local package for uploading")
    }));
    if verify {
        try!(run_verify(config, &pkg, &tmp_dst).chain_error(|| {
            human("failed to verify package tarball")
        }))
    }
    try!(fs::rename(&tmp_dst, &dst).chain_error(|| {
        human("failed to move temporary tarball into final location")
    }));
    Ok(Some(dst))
}

// check that the package has some piece of metadata that a human can
// use to tell what the package is about.
#[allow(deprecated)] // connect => join in 1.3
fn check_metadata(pkg: &Package, config: &Config) -> CargoResult<()> {
    let md = pkg.manifest().metadata();

    let mut missing = vec![];

    macro_rules! lacking {
        ($( $($field: ident)||* ),*) => {{
            $(
                if $(md.$field.as_ref().map_or(true, |s| s.is_empty()))&&* {
                    $(missing.push(stringify!($field).replace("_", "-"));)*
                }
            )*
        }}
    }
    lacking!(description, license || license_file, documentation || homepage || repository);

    if !missing.is_empty() {
        let mut things = missing[..missing.len() - 1].connect(", ");
        // things will be empty if and only if length == 1 (i.e. the only case
        // to have no `or`).
        if !things.is_empty() {
            things.push_str(" or ");
        }
        things.push_str(&missing.last().unwrap());

        try!(config.shell().warn(
            &format!("warning: manifest has no {things}. \
                    See http://doc.crates.io/manifest.html#package-metadata for more info.",
                    things = things)))
    }
    Ok(())
}

fn tar(pkg: &Package,
       src: &PathSource,
       config: &Config,
       dst: &Path,
       filename: &str) -> CargoResult<()> {
    if fs::metadata(&dst).is_ok() {
        bail!("destination already exists: {}", dst.display())
    }

    try!(fs::create_dir_all(dst.parent().unwrap()));

    let tmpfile = try!(File::create(dst));

    // Prepare the encoder and its header
    let filename = Path::new(filename);
    let encoder = GzBuilder::new().filename(try!(util::path2bytes(filename)))
                                  .write(tmpfile, Compression::Best);

    // Put all package files into a compressed archive
    let mut ar = Builder::new(encoder);
    let root = pkg.root();
    for file in try!(src.list_files(pkg)).iter() {
        if &**file == dst { continue }
        let relative = util::without_prefix(&file, &root).unwrap();
        try!(check_filename(relative));
        let relative = try!(relative.to_str().chain_error(|| {
            human(format!("non-utf8 path in source directory: {}",
                          relative.display()))
        }));
        let mut file = try!(File::open(file));
        try!(config.shell().verbose(|shell| {
            shell.status("Archiving", &relative)
        }));
        let path = format!("{}-{}{}{}", pkg.name(), pkg.version(),
                           path::MAIN_SEPARATOR, relative);

        // The tar::Builder type by default will build GNU archives, but
        // unfortunately we force it here to use UStar archives instead. The
        // UStar format has more limitations on the length of path name that it
        // can encode, so it's not quite as nice to use.
        //
        // Older cargos, however, had a bug where GNU archives were interpreted
        // as UStar archives. This bug means that if we publish a GNU archive
        // which has fully filled out metadata it'll be corrupt when unpacked by
        // older cargos.
        //
        // Hopefully in the future after enough cargos have been running around
        // with the bugfixed tar-rs library we'll be able to switch this over to
        // GNU archives, but for now we'll just say that you can't encode paths
        // in archives that are *too* long.
        //
        // For an instance of this in the wild, use the tar-rs 0.3.3 library to
        // unpack the selectors 0.4.0 crate on crates.io. Either that or take a
        // look at rust-lang/cargo#2326
        let mut header = Header::new_ustar();
        let metadata = try!(file.metadata().chain_error(|| {
            human(format!("could not learn metadata for: `{}`", relative))
        }));
        try!(header.set_path(&path).chain_error(|| {
            human(format!("failed to add to archive: `{}`", relative))
        }));
        header.set_metadata(&metadata);
        header.set_cksum();

        try!(ar.append(&header, &mut file).chain_error(|| {
            internal(format!("could not archive source file `{}`", relative))
        }));
    }
    let encoder = try!(ar.into_inner());
    try!(encoder.finish());
    Ok(())
}

fn run_verify(config: &Config, pkg: &Package, tar: &Path)
              -> CargoResult<()> {
    try!(config.shell().status("Verifying", pkg));

    let f = try!(GzDecoder::new(try!(File::open(tar))));
    let dst = pkg.root().join(&format!("target/package/{}-{}",
                                       pkg.name(), pkg.version()));
    if fs::metadata(&dst).is_ok() {
        try!(fs::remove_dir_all(&dst));
    }
    let mut archive = Archive::new(f);
    try!(archive.unpack(dst.parent().unwrap()));
    let manifest_path = dst.join("Cargo.toml");

    // When packages are uploaded to the registry, all path dependencies are
    // implicitly converted to registry-based dependencies, so we rewrite those
    // dependencies here.
    //
    // We also make sure to point all paths at `dst` instead of the previous
    // location that the package was originally read from. In locking the
    // `SourceId` we're telling it that the corresponding `PathSource` will be
    // considered updated and we won't actually read any packages.
    let registry = try!(SourceId::for_central(config));
    let precise = Some("locked".to_string());
    let new_src = try!(SourceId::for_path(&dst)).with_precise(precise);
    let new_pkgid = try!(PackageId::new(pkg.name(), pkg.version(), &new_src));
    let new_summary = pkg.summary().clone().map_dependencies(|d| {
        if !d.source_id().is_path() { return d }
        d.clone_inner().set_source_id(registry.clone()).into_dependency()
    });
    let mut new_manifest = pkg.manifest().clone();
    new_manifest.set_summary(new_summary.override_id(new_pkgid));
    let new_pkg = Package::new(new_manifest, &manifest_path);

    // Now that we've rewritten all our path dependencies, compile it!
    try!(ops::compile_pkg(&new_pkg, None, &ops::CompileOptions {
        config: config,
        jobs: None,
        target: None,
        features: &[],
        no_default_features: false,
        spec: &[],
        filter: ops::CompileFilter::Everything,
        exec_engine: None,
        release: false,
        mode: ops::CompileMode::Build,
        target_rustdoc_args: None,
        target_rustc_args: None,
    }));

    Ok(())
}

// It can often be the case that files of a particular name on one platform
// can't actually be created on another platform. For example files with colons
// in the name are allowed on Unix but not on Windows.
//
// To help out in situations like this, issue about weird filenames when
// packaging as a "heads up" that something may not work on other platforms.
fn check_filename(file: &Path) -> CargoResult<()> {
    let name = match file.file_name() {
        Some(name) => name,
        None => return Ok(()),
    };
    let name = match name.to_str() {
        Some(name) => name,
        None => {
            bail!("path does not have a unicode filename which may not unpack \
                   on all platforms: {}", file.display())
        }
    };
    let bad_chars = ['/', '\\', '<', '>', ':', '"', '|', '?', '*'];
    for c in bad_chars.iter().filter(|c| name.contains(**c)) {
        bail!("cannot package a filename with a special character `{}`: {}",
              c, file.display())
    }
    Ok(())
}
<|endoftext|>


<commit_before>use regex::Regex;
use time::Timespec;

use crate::timefmt::parse_time_str;

pub struct FileNameInfo<'a> {
    pub file_name: &'a str,
    pub info: Info,
}

#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Info {
    pub tp: Type,
    pub compressed: bool,
    pub encrypted: bool,
}

#[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Type {
    Full {
        time: Timespec,
        volume_number: usize,
    },
    FullManifest {
        time: Timespec,
        partial: bool,
    },
    Inc {
        start_time: Timespec,
        end_time: Timespec,
        volume_number: usize,
    },
    IncManifest {
        start_time: Timespec,
        end_time: Timespec,
        partial: bool,
    },
    FullSig {
        time: Timespec,
        partial: bool,
    },
    NewSig {
        start_time: Timespec,
        end_time: Timespec,
        partial: bool,
    },
}

pub struct FileNameParser {
    full_vol_re: Regex,
    full_manifest_re: Regex,
    inc_vol_re: Regex,
    inc_manifest_re: Regex,
    full_sig_re: Regex,
    new_sig_re: Regex,
}

impl<'a> FileNameInfo<'a> {
    pub fn new(name: &'a str, info: Info) -> Self {
        FileNameInfo {
            file_name: &name,
            info: info,
        }
    }

    pub fn start_time(&self) -> Timespec {
        self.info.tp.time_range().0
    }
}

impl Type {
    pub fn time_range(&self) -> (Timespec, Timespec) {
        match *self {
            Type::Full { time, .. }
            | Type::FullSig { time, .. }
            | Type::FullManifest { time, .. } => (time, time),
            Type::Inc {
                start_time,
                end_time,
                ..
            }
            | Type::IncManifest {
                start_time,
                end_time,
                ..
            }
            | Type::NewSig {
                start_time,
                end_time,
                ..
            } => (start_time, end_time),
        }
    }
}

impl FileNameParser {
    pub fn new() -> Self {
        FileNameParser {
            full_vol_re: Regex::new(r"^duplicity-full\.(?P<time>.*?)\.vol(?P<num>[0-9]+)\.difftar(?P<partial>(\.part))?($|\.)").unwrap(),
            full_manifest_re: Regex::new(r"^duplicity-full\.(?P<time>.*?)\.manifest(?P<partial>(\.part))?($|\.)").unwrap(),
            inc_vol_re: Regex::new(r"^duplicity-inc\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.vol(?P<num>[0-9]+)\.difftar($|\.)").unwrap(),
            inc_manifest_re: Regex::new(r"^duplicity-inc\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.manifest(?P<partial>(\.part))?(\.|$)").unwrap(),
            full_sig_re: Regex::new(r"^duplicity-full-signatures\.(?P<time>.*?)\.sigtar(?P<partial>(\.part))?(\.|$)").unwrap(),
            new_sig_re: Regex::new(r"^duplicity-new-signatures\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.sigtar(?P<partial>(\.part))?(\.|$)").unwrap(),
        }
    }

    pub fn parse(&self, filename: &str) -> Option<Info> {
        let lower_fname = filename.to_ascii_lowercase();
        let opt_type = self
            .check_full(&lower_fname)
            .or(self.check_inc(&lower_fname))
            .or(self.check_sig(&lower_fname));
        opt_type.map(|t| Info {
            tp: t,
            compressed: is_compressed(lower_fname.as_ref()),
            encrypted: is_encrypted(lower_fname.as_ref()),
        })
    }

    fn check_full(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.full_vol_re.captures(filename) {
            let time = parse_time_str(captures.name("time").unwrap())?;
            let vol_num = get_vol_num(captures.name("num").unwrap())?;
            Some(Type::Full {
                time: time,
                volume_number: vol_num,
            })
        } else if let Some(captures) = self.full_manifest_re.captures(filename) {
            let time = parse_time_str(captures.name("time").unwrap())?;
            Some(Type::FullManifest {
                time: time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }

    fn check_inc(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.inc_vol_re.captures(filename) {
            let start_time = parse_time_str(captures.name("start_time").unwrap())?;
            let end_time = parse_time_str(captures.name("end_time").unwrap())?;
            let vol_num = get_vol_num(captures.name("num").unwrap())?;
            Some(Type::Inc {
                start_time: start_time,
                end_time: end_time,
                volume_number: vol_num,
            })
        } else if let Some(captures) = self.inc_manifest_re.captures(filename) {
            let start_time = parse_time_str(captures.name("start_time").unwrap())?;
            let end_time = parse_time_str(captures.name("end_time").unwrap())?;
            Some(Type::IncManifest {
                start_time: start_time,
                end_time: end_time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }

    fn check_sig(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.full_sig_re.captures(filename) {
            let time = parse_time_str(captures.name("time").unwrap())?;
            Some(Type::FullSig {
                time: time,
                partial: captures.name("partial").is_some(),
            })
        } else if let Some(captures) = self.new_sig_re.captures(filename) {
            let start_time = parse_time_str(captures.name("start_time").unwrap())?;
            let end_time = parse_time_str(captures.name("end_time").unwrap())?;
            Some(Type::NewSig {
                start_time: start_time,
                end_time: end_time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }
}

fn get_vol_num(s: &str) -> Option<usize> {
    s.parse::<usize>().ok()
}

fn is_encrypted(s: &str) -> bool {
    s.ends_with(".gpg") || s.ends_with(".g")
}

fn is_compressed(s: &str) -> bool {
    s.ends_with(".gz") || s.ends_with(".z")
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::timefmt::parse_time_str;

    #[test]
    fn parser_test() {
        let parser = FileNameParser::new();
        // invalid
        assert_eq!(parser.parse("invalid"), None);
        // full
        assert_eq!(
            parser.parse("duplicity-full.20150617T182545Z.vol1.difftar.gz"),
            Some(Info {
                tp: Type::Full {
                    time: parse_time_str("20150617t182545z").unwrap(),
                    volume_number: 1,
                },
                compressed: true,
                encrypted: false,
            })
        );
        // full manifest
        assert_eq!(
            parser.parse("duplicity-full.20150617T182545Z.manifest"),
            Some(Info {
                tp: Type::FullManifest {
                    time: parse_time_str("20150617t182545z").unwrap(),
                    partial: false,
                },
                compressed: false,
                encrypted: false,
            })
        );
        // inc
        assert_eq!(
            parser.parse("duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz"),
            Some(Info {
                tp: Type::Inc {
                    start_time: parse_time_str("20150617t182629z").unwrap(),
                    end_time: parse_time_str("20150617t182650z").unwrap(),
                    volume_number: 1
                },
                compressed: true,
                encrypted: false
            })
        );
        // inc manifest
        assert_eq!(
            parser.parse("duplicity-inc.20150617T182545Z.to.20150617T182629Z.manifest"),
            Some(Info {
                tp: Type::IncManifest {
                    start_time: parse_time_str("20150617t182545z").unwrap(),
                    end_time: parse_time_str("20150617t182629z").unwrap(),
                    partial: false,
                },
                compressed: false,
                encrypted: false,
            })
        );
        // new sig
        assert_eq!(
            parser.parse("duplicity-new-signatures.20150617T182545Z.to.20150617T182629Z.sigtar.gz"),
            Some(Info {
                tp: Type::NewSig {
                    start_time: parse_time_str("20150617t182545z").unwrap(),
                    end_time: parse_time_str("20150617t182629z").unwrap(),
                    partial: false
                },
                compressed: true,
                encrypted: false
            })
        );
        // full sig
        assert_eq!(
            parser.parse("duplicity-full-signatures.20150617T182545Z.sigtar.gz"),
            Some(Info {
                tp: Type::FullSig {
                    time: parse_time_str("20150617t182545z").unwrap(),
                    partial: false,
                },
                compressed: true,
                encrypted: false,
            })
        );
    }
}
<commit_msg>Update after regex breaking changes.<commit_after>use regex::Regex;
use time::Timespec;

use crate::timefmt::parse_time_str;

pub struct FileNameInfo<'a> {
    pub file_name: &'a str,
    pub info: Info,
}

#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Info {
    pub tp: Type,
    pub compressed: bool,
    pub encrypted: bool,
}

#[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Type {
    Full {
        time: Timespec,
        volume_number: usize,
    },
    FullManifest {
        time: Timespec,
        partial: bool,
    },
    Inc {
        start_time: Timespec,
        end_time: Timespec,
        volume_number: usize,
    },
    IncManifest {
        start_time: Timespec,
        end_time: Timespec,
        partial: bool,
    },
    FullSig {
        time: Timespec,
        partial: bool,
    },
    NewSig {
        start_time: Timespec,
        end_time: Timespec,
        partial: bool,
    },
}

pub struct FileNameParser {
    full_vol_re: Regex,
    full_manifest_re: Regex,
    inc_vol_re: Regex,
    inc_manifest_re: Regex,
    full_sig_re: Regex,
    new_sig_re: Regex,
}

impl<'a> FileNameInfo<'a> {
    pub fn new(name: &'a str, info: Info) -> Self {
        FileNameInfo {
            file_name: &name,
            info: info,
        }
    }

    pub fn start_time(&self) -> Timespec {
        self.info.tp.time_range().0
    }
}

impl Type {
    pub fn time_range(&self) -> (Timespec, Timespec) {
        match *self {
            Type::Full { time, .. }
            | Type::FullSig { time, .. }
            | Type::FullManifest { time, .. } => (time, time),
            Type::Inc {
                start_time,
                end_time,
                ..
            }
            | Type::IncManifest {
                start_time,
                end_time,
                ..
            }
            | Type::NewSig {
                start_time,
                end_time,
                ..
            } => (start_time, end_time),
        }
    }
}

impl FileNameParser {
    pub fn new() -> Self {
        FileNameParser {
            full_vol_re: Regex::new(r"^duplicity-full\.(?P<time>.*?)\.vol(?P<num>[0-9]+)\.difftar(?P<partial>(\.part))?($|\.)").unwrap(),
            full_manifest_re: Regex::new(r"^duplicity-full\.(?P<time>.*?)\.manifest(?P<partial>(\.part))?($|\.)").unwrap(),
            inc_vol_re: Regex::new(r"^duplicity-inc\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.vol(?P<num>[0-9]+)\.difftar($|\.)").unwrap(),
            inc_manifest_re: Regex::new(r"^duplicity-inc\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.manifest(?P<partial>(\.part))?(\.|$)").unwrap(),
            full_sig_re: Regex::new(r"^duplicity-full-signatures\.(?P<time>.*?)\.sigtar(?P<partial>(\.part))?(\.|$)").unwrap(),
            new_sig_re: Regex::new(r"^duplicity-new-signatures\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.sigtar(?P<partial>(\.part))?(\.|$)").unwrap(),
        }
    }

    pub fn parse(&self, filename: &str) -> Option<Info> {
        let lower_fname = filename.to_ascii_lowercase();
        let opt_type = self
            .check_full(&lower_fname)
            .or(self.check_inc(&lower_fname))
            .or(self.check_sig(&lower_fname));
        opt_type.map(|t| Info {
            tp: t,
            compressed: is_compressed(lower_fname.as_ref()),
            encrypted: is_encrypted(lower_fname.as_ref()),
        })
    }

    fn check_full(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.full_vol_re.captures(filename) {
            let time = parse_time_str(captures.name("time").unwrap().as_str())?;
            let vol_num = get_vol_num(captures.name("num").unwrap().as_str())?;
            Some(Type::Full {
                time: time,
                volume_number: vol_num,
            })
        } else if let Some(captures) = self.full_manifest_re.captures(filename) {
            let time = parse_time_str(captures.name("time").unwrap().as_str())?;
            Some(Type::FullManifest {
                time: time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }

    fn check_inc(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.inc_vol_re.captures(filename) {
            let start_time = parse_time_str(captures.name("start_time").unwrap().as_str())?;
            let end_time = parse_time_str(captures.name("end_time").unwrap().as_str())?;
            let vol_num = get_vol_num(captures.name("num").unwrap().as_str())?;
            Some(Type::Inc {
                start_time: start_time,
                end_time: end_time,
                volume_number: vol_num,
            })
        } else if let Some(captures) = self.inc_manifest_re.captures(filename) {
            let start_time = parse_time_str(captures.name("start_time").unwrap().as_str())?;
            let end_time = parse_time_str(captures.name("end_time").unwrap().as_str())?;
            Some(Type::IncManifest {
                start_time: start_time,
                end_time: end_time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }

    fn check_sig(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.full_sig_re.captures(filename) {
            let time = parse_time_str(captures.name("time").unwrap().as_str())?;
            Some(Type::FullSig {
                time: time,
                partial: captures.name("partial").is_some(),
            })
        } else if let Some(captures) = self.new_sig_re.captures(filename) {
            let start_time = parse_time_str(captures.name("start_time").unwrap().as_str())?;
            let end_time = parse_time_str(captures.name("end_time").unwrap().as_str())?;
            Some(Type::NewSig {
                start_time: start_time,
                end_time: end_time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }
}

fn get_vol_num(s: &str) -> Option<usize> {
    s.parse::<usize>().ok()
}

fn is_encrypted(s: &str) -> bool {
    s.ends_with(".gpg") || s.ends_with(".g")
}

fn is_compressed(s: &str) -> bool {
    s.ends_with(".gz") || s.ends_with(".z")
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::timefmt::parse_time_str;

    #[test]
    fn parser_test() {
        let parser = FileNameParser::new();
        // invalid
        assert_eq!(parser.parse("invalid"), None);
        // full
        assert_eq!(
            parser.parse("duplicity-full.20150617T182545Z.vol1.difftar.gz"),
            Some(Info {
                tp: Type::Full {
                    time: parse_time_str("20150617t182545z").unwrap(),
                    volume_number: 1,
                },
                compressed: true,
                encrypted: false,
            })
        );
        // full manifest
        assert_eq!(
            parser.parse("duplicity-full.20150617T182545Z.manifest"),
            Some(Info {
                tp: Type::FullManifest {
                    time: parse_time_str("20150617t182545z").unwrap(),
                    partial: false,
                },
                compressed: false,
                encrypted: false,
            })
        );
        // inc
        assert_eq!(
            parser.parse("duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz"),
            Some(Info {
                tp: Type::Inc {
                    start_time: parse_time_str("20150617t182629z").unwrap(),
                    end_time: parse_time_str("20150617t182650z").unwrap(),
                    volume_number: 1
                },
                compressed: true,
                encrypted: false
            })
        );
        // inc manifest
        assert_eq!(
            parser.parse("duplicity-inc.20150617T182545Z.to.20150617T182629Z.manifest"),
            Some(Info {
                tp: Type::IncManifest {
                    start_time: parse_time_str("20150617t182545z").unwrap(),
                    end_time: parse_time_str("20150617t182629z").unwrap(),
                    partial: false,
                },
                compressed: false,
                encrypted: false,
            })
        );
        // new sig
        assert_eq!(
            parser.parse("duplicity-new-signatures.20150617T182545Z.to.20150617T182629Z.sigtar.gz"),
            Some(Info {
                tp: Type::NewSig {
                    start_time: parse_time_str("20150617t182545z").unwrap(),
                    end_time: parse_time_str("20150617t182629z").unwrap(),
                    partial: false
                },
                compressed: true,
                encrypted: false
            })
        );
        // full sig
        assert_eq!(
            parser.parse("duplicity-full-signatures.20150617T182545Z.sigtar.gz"),
            Some(Info {
                tp: Type::FullSig {
                    time: parse_time_str("20150617t182545z").unwrap(),
                    partial: false,
                },
                compressed: true,
                encrypted: false,
            })
        );
    }
}
<|endoftext|>


<commit_before>//! Handling of `preserveAspectRatio` values
//!
//! This module handles `preserveAspectRatio` values [per the SVG specification][spec].
//! We have an [`AspectRatio`] struct which encapsulates such a value.
//!
//! ```ignore
//! assert_eq!(
//!     AspectRatio::parse("xMidYMid", ()),
//!     Ok(AspectRatio {
//!         defer: false,
//!         align: Some(Align {
//!             x: Align1D::Mid,
//!             y: Align1D::Mid,
//!             fit: FitMode::Meet,
//!         }),
//!     })
//! );
//! ```
//!
//! [`AspectRatio`]: struct.AspectRatio.html
//! [spec]: https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

use cssparser::{Parser, ParserInput};
use error::*;
use parsers::Parse;
use parsers::ParseError;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct AspectRatio {
    defer: bool,
    align: Option<Align>,
}

impl Default for AspectRatio {
    fn default() -> AspectRatio {
        AspectRatio {
            defer: false,
            align: Some(Align::default()),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum FitMode {
    Meet,
    Slice,
}

impl Default for FitMode {
    fn default() -> FitMode {
        FitMode::Meet
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Align {
    x: Align1D,
    y: Align1D,
    fit: FitMode,
}

impl Default for Align {
    fn default() -> Align {
        Align {
            x: Align1D::Mid,
            y: Align1D::Mid,
            fit: FitMode::default(),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Align1D {
    Min,
    Mid,
    Max,
}

impl Align1D {
    fn compute(self, dest_pos: f64, dest_size: f64, obj_size: f64) -> f64 {
        match self {
            Align1D::Min => dest_pos,
            Align1D::Mid => dest_pos + (dest_size - obj_size) / 2.0,
            Align1D::Max => dest_pos + dest_size - obj_size,
        }
    }
}

impl AspectRatio {
    pub fn is_slice(&self) -> bool {
        match self.align {
            Some(Align {
                fit: FitMode::Slice,
                ..
            }) => true,

            _ => false,
        }
    }

    pub fn compute(
        &self,
        object_width: f64,
        object_height: f64,
        dest_x: f64,
        dest_y: f64,
        dest_width: f64,
        dest_height: f64,
    ) -> (f64, f64, f64, f64) {
        match self.align {
            None => (dest_x, dest_y, dest_width, dest_height),

            Some(Align { x, y, fit }) => {
                let w_factor = dest_width / object_width;
                let h_factor = dest_height / object_height;
                let factor = match fit {
                    FitMode::Meet => w_factor.min(h_factor),
                    FitMode::Slice => w_factor.max(h_factor),
                };

                let w = object_width * factor;
                let h = object_height * factor;

                let xpos = x.compute(dest_x, dest_width, w);
                let ypos = y.compute(dest_y, dest_height, h);

                (xpos, ypos, w, h)
            }
        }
    }

    fn parse_input<'i, 't>(p: &mut Parser<'i, 't>) -> Result<AspectRatio, ()> {
        let defer = p.try(|p| p.expect_ident_matching("defer")).is_ok();

        let align_xy = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| Align::parse_xy(ident))
        })?;

        let fit = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| FitMode::parse(ident))
        }).unwrap_or(FitMode::default());

        p.expect_exhausted().map_err(|_| ())?;

        let align = align_xy.map(|(x, y)| Align { x, y, fit });

        Ok(AspectRatio { defer, align })
    }
}

impl Align {
    fn parse_xy(s: &str) -> Result<Option<(Align1D, Align1D)>, ()> {
        use self::Align1D::*;

        match s {
            "none" => Ok(None),

            "xMinYMin" => Ok(Some((Min, Min))),
            "xMidYMin" => Ok(Some((Mid, Min))),
            "xMaxYMin" => Ok(Some((Max, Min))),

            "xMinYMid" => Ok(Some((Min, Mid))),
            "xMidYMid" => Ok(Some((Mid, Mid))),
            "xMaxYMid" => Ok(Some((Max, Mid))),

            "xMinYMax" => Ok(Some((Min, Max))),
            "xMidYMax" => Ok(Some((Mid, Max))),
            "xMaxYMax" => Ok(Some((Max, Max))),

            _ => Err(()),
        }
    }
}

impl FitMode {
    fn parse(s: &str) -> Result<FitMode, ()> {
        match s {
            "meet" => Ok(FitMode::Meet),
            "slice" => Ok(FitMode::Slice),
            _ => Err(()),
        }
    }
}

impl Parse for AspectRatio {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<AspectRatio, AttributeError> {
        let mut input = ParserInput::new(s);
        AspectRatio::parse_input(&mut Parser::new(&mut input)).map_err(|_| {
            AttributeError::Parse(ParseError::new(
                "expected \"[defer] <align> [meet | slice]\"",
            ))
        })
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error() {
        assert!(AspectRatio::parse("", ()).is_err());
        assert!(AspectRatio::parse("defer", ()).is_err());
        assert!(AspectRatio::parse("defer foo", ()).is_err());
        assert!(AspectRatio::parse("defer xmidymid", ()).is_err());
        assert!(AspectRatio::parse("defer xMidYMid foo", ()).is_err());
        assert!(AspectRatio::parse("xmidymid", ()).is_err());
        assert!(AspectRatio::parse("xMidYMid foo", ()).is_err());
        assert!(AspectRatio::parse("defer xMidYMid meet foo", ()).is_err());
    }

    #[test]
    fn parses_valid_strings() {
        assert_eq!(
            AspectRatio::parse("defer none", ()),
            Ok(AspectRatio {
                defer: true,
                align: None,
            },)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMid", ()),
            Ok(AspectRatio {
                defer: false,
                align: Some(Align {
                    x: Align1D::Mid,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMidYMid", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Mid,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Min,
                    y: Align1D::Max,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMaxYMid meet", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Max,
                    y: Align1D::Mid,
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax slice", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: Align1D::Min,
                    y: Align1D::Max,
                    fit: FitMode::Slice,
                },),
            },)
        );
    }

    #[test]
    fn aligns() {
        let foo = AspectRatio::parse("xMinYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMinYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);

        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMinYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));
    }
}
<commit_msg>aspect_ratio: Use seperate types for Aling X and Align Y.<commit_after>//! Handling of `preserveAspectRatio` values
//!
//! This module handles `preserveAspectRatio` values [per the SVG specification][spec].
//! We have an [`AspectRatio`] struct which encapsulates such a value.
//!
//! ```ignore
//! assert_eq!(
//!     AspectRatio::parse("xMidYMid", ()),
//!     Ok(AspectRatio {
//!         defer: false,
//!         align: Some(Align {
//!             x: X(Align1D::Mid),
//!             y: Y(Align1D::Mid),
//!             fit: FitMode::Meet,
//!         }),
//!     })
//! );
//! ```
//!
//! [`AspectRatio`]: struct.AspectRatio.html
//! [spec]: https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

use cssparser::{Parser, ParserInput};
use error::*;
use parsers::Parse;
use parsers::ParseError;
use std::ops::Deref;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct AspectRatio {
    defer: bool,
    align: Option<Align>,
}

impl Default for AspectRatio {
    fn default() -> AspectRatio {
        AspectRatio {
            defer: false,
            align: Some(Align::default()),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum FitMode {
    Meet,
    Slice,
}

impl Default for FitMode {
    fn default() -> FitMode {
        FitMode::Meet
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Align {
    x: X,
    y: Y,
    fit: FitMode,
}

impl Default for Align {
    fn default() -> Align {
        Align {
            x: X(Align1D::Mid),
            y: Y(Align1D::Mid),
            fit: FitMode::default(),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Align1D {
    Min,
    Mid,
    Max,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct X(Align1D);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Y(Align1D);

impl Deref for X {
    type Target = Align1D;

    fn deref(&self) -> &Align1D {
        &self.0
    }
}

impl Deref for Y {
    type Target = Align1D;

    fn deref(&self) -> &Align1D {
        &self.0
    }
}

impl Align1D {
    fn compute(self, dest_pos: f64, dest_size: f64, obj_size: f64) -> f64 {
        match self {
            Align1D::Min => dest_pos,
            Align1D::Mid => dest_pos + (dest_size - obj_size) / 2.0,
            Align1D::Max => dest_pos + dest_size - obj_size,
        }
    }
}

impl AspectRatio {
    pub fn is_slice(&self) -> bool {
        match self.align {
            Some(Align {
                fit: FitMode::Slice,
                ..
            }) => true,

            _ => false,
        }
    }

    pub fn compute(
        &self,
        object_width: f64,
        object_height: f64,
        dest_x: f64,
        dest_y: f64,
        dest_width: f64,
        dest_height: f64,
    ) -> (f64, f64, f64, f64) {
        match self.align {
            None => (dest_x, dest_y, dest_width, dest_height),

            Some(Align { x, y, fit }) => {
                let w_factor = dest_width / object_width;
                let h_factor = dest_height / object_height;
                let factor = match fit {
                    FitMode::Meet => w_factor.min(h_factor),
                    FitMode::Slice => w_factor.max(h_factor),
                };

                let w = object_width * factor;
                let h = object_height * factor;

                let xpos = x.compute(dest_x, dest_width, w);
                let ypos = y.compute(dest_y, dest_height, h);

                (xpos, ypos, w, h)
            }
        }
    }

    fn parse_input<'i, 't>(p: &mut Parser<'i, 't>) -> Result<AspectRatio, ()> {
        let defer = p.try(|p| p.expect_ident_matching("defer")).is_ok();

        let align_xy = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| Align::parse_xy(ident))
        })?;

        let fit = p.try(|p| {
            p.expect_ident()
                .map_err(|_| ())
                .and_then(|ident| FitMode::parse(ident))
        }).unwrap_or(FitMode::default());

        p.expect_exhausted().map_err(|_| ())?;

        let align = align_xy.map(|(x, y)| Align { x, y, fit });

        Ok(AspectRatio { defer, align })
    }
}

impl Align {
    fn parse_xy(s: &str) -> Result<Option<(X, Y)>, ()> {
        use self::Align1D::*;

        match s {
            "none" => Ok(None),

            "xMinYMin" => Ok(Some((X(Min), Y(Min)))),
            "xMidYMin" => Ok(Some((X(Mid), Y(Min)))),
            "xMaxYMin" => Ok(Some((X(Max), Y(Min)))),

            "xMinYMid" => Ok(Some((X(Min), Y(Mid)))),
            "xMidYMid" => Ok(Some((X(Mid), Y(Mid)))),
            "xMaxYMid" => Ok(Some((X(Max), Y(Mid)))),

            "xMinYMax" => Ok(Some((X(Min), Y(Max)))),
            "xMidYMax" => Ok(Some((X(Mid), Y(Max)))),
            "xMaxYMax" => Ok(Some((X(Max), Y(Max)))),

            _ => Err(()),
        }
    }
}

impl FitMode {
    fn parse(s: &str) -> Result<FitMode, ()> {
        match s {
            "meet" => Ok(FitMode::Meet),
            "slice" => Ok(FitMode::Slice),
            _ => Err(()),
        }
    }
}

impl Parse for AspectRatio {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _: ()) -> Result<AspectRatio, AttributeError> {
        let mut input = ParserInput::new(s);
        AspectRatio::parse_input(&mut Parser::new(&mut input)).map_err(|_| {
            AttributeError::Parse(ParseError::new(
                "expected \"[defer] <align> [meet | slice]\"",
            ))
        })
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error() {
        assert!(AspectRatio::parse("", ()).is_err());
        assert!(AspectRatio::parse("defer", ()).is_err());
        assert!(AspectRatio::parse("defer foo", ()).is_err());
        assert!(AspectRatio::parse("defer xmidymid", ()).is_err());
        assert!(AspectRatio::parse("defer xMidYMid foo", ()).is_err());
        assert!(AspectRatio::parse("xmidymid", ()).is_err());
        assert!(AspectRatio::parse("xMidYMid foo", ()).is_err());
        assert!(AspectRatio::parse("defer xMidYMid meet foo", ()).is_err());
    }

    #[test]
    fn parses_valid_strings() {
        assert_eq!(
            AspectRatio::parse("defer none", ()),
            Ok(AspectRatio {
                defer: true,
                align: None,
            },)
        );

        assert_eq!(
            AspectRatio::parse("xMidYMid", ()),
            Ok(AspectRatio {
                defer: false,
                align: Some(Align {
                    x: X(Align1D::Mid),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMidYMid", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Mid),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Min),
                    y: Y(Align1D::Max),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMaxYMid meet", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Max),
                    y: Y(Align1D::Mid),
                    fit: FitMode::Meet,
                },),
            },)
        );

        assert_eq!(
            AspectRatio::parse("defer xMinYMax slice", ()),
            Ok(AspectRatio {
                defer: true,
                align: Some(Align {
                    x: X(Align1D::Min),
                    y: Y(Align1D::Max),
                    fit: FitMode::Slice,
                },),
            },)
        );
    }

    #[test]
    fn aligns() {
        let foo = AspectRatio::parse("xMinYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMinYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);

        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMinYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMinYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMidYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (4.95, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMidYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMin meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMin slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, 0.0, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMid meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMid slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -49.5, 10.0, 100.0));

        let foo = AspectRatio::parse("xMaxYMax meet", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (9.9, 0.0, 0.1, 1.0));

        let foo = AspectRatio::parse("xMaxYMax slice", ()).unwrap();
        let foo = foo.compute(1.0, 10.0, 0.0, 0.0, 10.0, 1.0);
        assert_eq!(foo, (0.0, -99.0, 10.0, 100.0));
    }
}
<|endoftext|>


<commit_before>//! Types for the *m.room.encryption* event.

use js_int::UInt;
use ruma_events_macros::StateEventContent;
use serde::{Deserialize, Serialize};

use crate::{Algorithm, StateEvent};

/// Defines how messages sent in this room should be encrypted.
pub type EncryptionEvent = StateEvent<EncryptionEventContent>;

/// The payload for `EncryptionEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, StateEventContent)]
#[ruma_event(type = "m.room.encryption")]
pub struct EncryptionEventContent {
    /// The encryption algorithm to be used to encrypt messages sent in this room.
    ///
    /// Must be `m.megolm.v1.aes-sha2`.
    pub algorithm: Algorithm,

    /// How long the session should be used before changing it.
    ///
    /// `uint!(604800000)` (a week) is the recommended default.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rotation_period_ms: Option<UInt>,

    /// How many messages should be sent before changing the session.
    ///
    /// `uint!(100)` is the recommended default.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rotation_period_msgs: Option<UInt>,
}
<commit_msg>Mark EncryptionEventContent as non_exhaustive<commit_after>//! Types for the *m.room.encryption* event.

use js_int::UInt;
use ruma_events_macros::StateEventContent;
use serde::{Deserialize, Serialize};

use crate::{Algorithm, StateEvent};

/// Defines how messages sent in this room should be encrypted.
pub type EncryptionEvent = StateEvent<EncryptionEventContent>;

/// The payload for `EncryptionEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, StateEventContent)]
#[non_exhaustive]
#[ruma_event(type = "m.room.encryption")]
pub struct EncryptionEventContent {
    /// The encryption algorithm to be used to encrypt messages sent in this room.
    ///
    /// Must be `m.megolm.v1.aes-sha2`.
    pub algorithm: Algorithm,

    /// How long the session should be used before changing it.
    ///
    /// `uint!(604800000)` (a week) is the recommended default.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rotation_period_ms: Option<UInt>,

    /// How many messages should be sent before changing the session.
    ///
    /// `uint!(100)` is the recommended default.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rotation_period_msgs: Option<UInt>,
}

impl EncryptionEventContent {
    /// Creates a new `EncryptionEventContent` with the given algorithm.
    pub fn new(algorithm: Algorithm) -> Self {
        Self { algorithm, rotation_period_ms: None, rotation_period_msgs: None }
    }
}
<|endoftext|>


<commit_before>extern crate rust_ml;
use rust_ml::junk::minamoto::entropy;

#[derive(Clone)]
pub enum EvenOddClass {
    Even,
    Odd
}

impl entropy::Classified<EvenOddClass> for i32 {
    fn class(&self) -> EvenOddClass {
        match self % 2 {
            0 => EvenOddClass::Even,
            _ => EvenOddClass::Odd
        }
    }
}

impl PartialEq for EvenOddClass {
    fn eq (&self, rhs:&EvenOddClass) -> bool {
        match (self, rhs) {
            (&EvenOddClass::Even, &EvenOddClass::Even) => true,
            (&EvenOddClass::Odd, &EvenOddClass::Odd) => true,
            (_, _) => false
        }
    }

    fn ne(&self, rhs:&EvenOddClass) -> bool {
        !self.eq(rhs)
    }
}
<commit_msg>examples/even_odd_class.rs: derive Debug for fmt needs<commit_after>extern crate rust_ml;
use rust_ml::junk::minamoto::entropy;

#[derive(Clone,Debug)]
pub enum EvenOddClass {
    Even,
    Odd
}

impl entropy::Classified<EvenOddClass> for i32 {
    fn class(&self) -> EvenOddClass {
        match self % 2 {
            0 => EvenOddClass::Even,
            _ => EvenOddClass::Odd
        }
    }
}

impl PartialEq for EvenOddClass {
    fn eq (&self, rhs:&EvenOddClass) -> bool {
        match (self, rhs) {
            (&EvenOddClass::Even, &EvenOddClass::Even) => true,
            (&EvenOddClass::Odd, &EvenOddClass::Odd) => true,
            (_, _) => false
        }
    }

    fn ne(&self, rhs:&EvenOddClass) -> bool {
        !self.eq(rhs)
    }
}
<|endoftext|>


<commit_before>extern crate devices;
extern crate sys_util;
extern crate x86_64;

use std::result;

use cpuid::{c3_template, filter_cpuid, t2_template};
use kvm::*;
use memory_model::{GuestAddress, GuestMemory, GuestMemoryError};
use sys_util::EventFd;
use vmm_config::machine_config::{CpuFeaturesTemplate, VmConfig};
use x86_64::{interrupts, regs};

pub const KVM_TSS_ADDRESS: usize = 0xfffbd000;

#[derive(Debug)]
pub enum Error {
    AlreadyRunning,
    CpuId(cpuid::Error),
    GuestMemory(GuestMemoryError),
    Kvm(sys_util::Error),
    VmFd(sys_util::Error),
    VcpuFd(sys_util::Error),
    VmSetup(sys_util::Error),
    VcpuRun(sys_util::Error),
    GetSupportedCpusFailed(sys_util::Error),
    SetSupportedCpusFailed(sys_util::Error),
    NotEnoughMemory,
    NoMemoryEntry,
    LocalIntConfiguration(interrupts::Error),
    SetUserMemoryRegion(sys_util::Error),
    /// The kernel extends past the end of RAM
    KernelOffsetPastEnd,
    /// Error configuring the MSR registers
    MSRSConfiguration(regs::Error),
    /// Error configuring the general purpose registers
    REGSConfiguration(regs::Error),
    /// Error configuring the special registers
    SREGSConfiguration(regs::Error),
    /// Error configuring the floating point related registers
    FPUConfiguration(regs::Error),
    EventFd(sys_util::Error),
    Irq(sys_util::Error),
}
pub type Result<T> = result::Result<T, Error>;

impl ::std::convert::From<sys_util::Error> for Error {
    fn from(e: sys_util::Error) -> Error {
        Error::SetUserMemoryRegion(e)
    }
}

/// A wrapper around creating and using a VM.
pub struct Vm {
    fd: VmFd,
    guest_mem: Option<GuestMemory>,
}

impl Vm {
    /// Constructs a new `Vm` using the given `Kvm` instance.
    pub fn new(kvm: &Kvm) -> Result<Self> {
        //create fd for interacting with kvm-vm specific functions
        let vm_fd = VmFd::new(&kvm).map_err(Error::VmFd)?;

        Ok(Vm {
            fd: vm_fd,
            guest_mem: None,
        })
    }

    /// Currently this is x86 specific (because of the TSS address setup)
    pub fn memory_init(&mut self, guest_mem: GuestMemory) -> Result<()> {
        guest_mem.with_regions(|index, guest_addr, size, host_addr| {
            info!("Guest memory starts at {:x?}", host_addr);
            // Safe because the guest regions are guaranteed not to overlap.
            self.fd.set_user_memory_region(
                index as u32,
                guest_addr.offset() as u64,
                size as u64,
                host_addr as u64,
                0,
            )
        })?;
        self.guest_mem = Some(guest_mem);

        let tss_addr = GuestAddress(KVM_TSS_ADDRESS);
        self.fd
            .set_tss_address(tss_addr.offset())
            .map_err(Error::VmSetup)?;

        Ok(())
    }

    /// This function creates the irq chip and adds 2 interrupt events to the IRQ
    pub fn setup_irqchip(&self, com_evt_1_3: &EventFd, com_evt_2_4: &EventFd) -> Result<()> {
        self.fd.create_irq_chip().map_err(Error::VmSetup)?;

        self.fd.register_irqfd(com_evt_1_3, 4).map_err(Error::Irq)?;
        self.fd.register_irqfd(com_evt_2_4, 3).map_err(Error::Irq)?;

        Ok(())
    }

    pub fn create_pit(&self) -> Result<()> {
        self.fd.create_pit2().map_err(Error::VmSetup)?;
        Ok(())
    }

    /// Gets a reference to the guest memory owned by this VM.
    ///
    /// Note that `GuestMemory` does not include any device memory that may have been added after
    /// this VM was constructed.
    pub fn get_memory(&self) -> Option<&GuestMemory> {
        self.guest_mem.as_ref()
    }

    /// Gets a reference to the kvm file descriptor owned by this VM.
    ///
    pub fn get_fd(&self) -> &VmFd {
        &self.fd
    }
}

/// A wrapper around creating and using a kvm-based VCPU
pub struct Vcpu {
    cpuid: CpuId,
    fd: VcpuFd,
    id: u8,
}

impl Vcpu {
    /// Constructs a new VCPU for `vm`.
    ///
    /// The `id` argument is the CPU number between [0, max vcpus).
    pub fn new(id: u8, vm: &Vm) -> Result<Self> {
        let kvm_vcpu = VcpuFd::new(id, &vm.fd).map_err(Error::VcpuFd)?;
        // Initially the cpuid per vCPU is the one supported by this VM
        Ok(Vcpu {
            fd: kvm_vcpu,
            cpuid: vm.fd.get_supported_cpuid(),
            id,
        })
    }

    /// /// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
    ///
    /// # Arguments
    ///
    /// * `kernel_load_offset` - Offset from `guest_mem` at which the kernel starts.
    /// nr cpus is required for checking populating the kvm_cpuid2 entry for ebx and edx registers
    pub fn configure(
        &mut self,
        machine_config: &VmConfig,
        kernel_start_addr: GuestAddress,
        vm: &Vm,
    ) -> Result<()> {
        // the MachineConfiguration has defaults for ht_enabled and vcpu_count hence it is safe to unwrap
        filter_cpuid(
            self.id,
            machine_config.vcpu_count.unwrap(),
            machine_config.ht_enabled.unwrap(),
            &mut self.cpuid,
        ).map_err(|e| Error::CpuId(e))?;
        match machine_config.cpu_template {
            Some(template) => match template {
                CpuFeaturesTemplate::T2 => {
                    t2_template::set_cpuid_entries(self.cpuid.mut_entries_slice())
                }
                CpuFeaturesTemplate::C3 => {
                    c3_template::set_cpuid_entries(self.cpuid.mut_entries_slice())
                }
            },
            None => (),
        }

        self.fd
            .set_cpuid2(&self.cpuid)
            .map_err(Error::SetSupportedCpusFailed)?;

        regs::setup_msrs(&self.fd).map_err(Error::MSRSConfiguration)?;
        // Safe to unwrap because this method is called after the VM is configured
        let vm_memory = vm.get_memory().unwrap();
        regs::setup_regs(
            &self.fd,
            kernel_start_addr.offset() as u64,
            x86_64::layout::BOOT_STACK_POINTER as u64,
            x86_64::layout::ZERO_PAGE_START as u64,
        ).map_err(Error::REGSConfiguration)?;
        regs::setup_fpu(&self.fd).map_err(Error::FPUConfiguration)?;
        regs::setup_sregs(vm_memory, &self.fd).map_err(Error::SREGSConfiguration)?;
        interrupts::set_lint(&self.fd).map_err(Error::LocalIntConfiguration)?;
        Ok(())
    }

    /// Runs the VCPU until it exits, returning the reason.
    ///
    /// Note that the state of the VCPU and associated VM must be setup first for this to do
    /// anything useful.
    pub fn run(&self) -> Result<VcpuExit> {
        match self.fd.run() {
            Ok(v) => Ok(v),
            Err(e) => return Err(Error::VcpuRun(<sys_util::Error>::new(e.errno()))),
        }
    }
}

#[cfg(test)]
mod tests {

    impl Vcpu {
        /// Returns a clone of the CPUID entries of this vCPU
        /// For now this function is only used for testing; the cfg(test) should be removed when
        /// this function will be used for configuring the cpu features
        pub fn get_cpuid(&self) -> CpuId {
            return self.cpuid.clone();
        }
    }

    use super::*;

    #[test]
    fn create_vm() {
        let kvm = Kvm::new().unwrap();
        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
        let mut vm = Vm::new(&kvm).expect("new vm failed");
        assert!(vm.memory_init(gm).is_ok());
    }

    #[test]
    fn get_memory() {
        let kvm = Kvm::new().unwrap();
        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
        let mut vm = Vm::new(&kvm).expect("new vm failed");
        assert!(vm.memory_init(gm).is_ok());
        let obj_addr = GuestAddress(0xf0);
        vm.get_memory()
            .unwrap()
            .write_obj_at_addr(67u8, obj_addr)
            .unwrap();
        let read_val: u8 = vm
            .get_memory()
            .unwrap()
            .read_obj_from_addr(obj_addr)
            .unwrap();
        assert_eq!(read_val, 67u8);
    }

    #[test]
    fn create_vcpu() {
        let kvm = Kvm::new().unwrap();
        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
        let mut vm = Vm::new(&kvm).expect("new vm failed");
        assert!(vm.memory_init(gm).is_ok());
        Vcpu::new(0, &mut vm).unwrap();
    }

    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    #[test]
    fn test_cpuid() {
        let kvm = Kvm::new().unwrap();
        let mut vm = Vm::new(&kvm).unwrap();
        let mut vcpu = Vcpu::new(0, &mut vm).unwrap();
        assert_eq!(vcpu.get_cpuid(), vm.fd.get_supported_cpuid());
        assert!(filter_cpuid(0, 1, true, &mut vcpu.cpuid).is_ok());
        // Test using the T2 template
        t2_template::set_cpuid_entries(vcpu.cpuid.mut_entries_slice());
        assert!(vcpu.fd.set_cpuid2(&vcpu.cpuid).is_ok());
        // Test using the C3 template
        c3_template::set_cpuid_entries(vcpu.cpuid.mut_entries_slice());
        assert!(vcpu.fd.set_cpuid2(&vcpu.cpuid).is_ok());
    }

    #[test]
    fn run_code() {
        use std::io::{self, Write};
        // This example based on https://lwn.net/Articles/658511/
        let code = [
            0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
            0x00, 0xd8, /* add %bl, %al */
            0x04, '0' as u8, /* add $'0', %al */
            0xee,      /* out %al, (%dx) */
            0xb0, '\n' as u8, /* mov $'\n', %al */
            0xee,       /* out %al, (%dx) */
            0xf4,       /* hlt */
        ];

        let mem_size = 0x1000;
        let load_addr = GuestAddress(0x1000);
        let mem = GuestMemory::new(&vec![(load_addr, mem_size)]).unwrap();

        let kvm = Kvm::new().expect("new kvm failed");
        let mut vm = Vm::new(&kvm).expect("new vm failed");
        assert!(vm.memory_init(mem).is_ok());
        vm.get_memory()
            .unwrap()
            .write_slice_at_addr(&code, load_addr)
            .expect("Writing code to memory failed.");

        let vcpu = Vcpu::new(0, &mut vm).expect("new vcpu failed");

        let mut vcpu_sregs = vcpu.fd.get_sregs().expect("get sregs failed");
        assert_ne!(vcpu_sregs.cs.base, 0);
        assert_ne!(vcpu_sregs.cs.selector, 0);
        vcpu_sregs.cs.base = 0;
        vcpu_sregs.cs.selector = 0;
        vcpu.fd.set_sregs(&vcpu_sregs).expect("set sregs failed");

        let mut vcpu_regs = vcpu.fd.get_regs().expect("get regs failed");
        vcpu_regs.rip = 0x1000;
        vcpu_regs.rax = 2;
        vcpu_regs.rbx = 3;
        vcpu_regs.rflags = 2;
        vcpu.fd.set_regs(&vcpu_regs).expect("set regs failed");

        loop {
            match vcpu.run().expect("run failed") {
                VcpuExit::IoOut(0x3f8, data) => {
                    assert_eq!(data.len(), 1);
                    io::stdout().write(data).unwrap();
                }
                VcpuExit::Hlt => {
                    io::stdout().write(b"KVM_EXIT_HLT\n").unwrap();
                    break;
                }
                r => panic!("unexpected exit reason: {:?}", r),
            }
        }
    }
}
<commit_msg>vmm: added unit tests in vstate.rs<commit_after>extern crate devices;
extern crate sys_util;
extern crate x86_64;

use std::result;

use cpuid::{c3_template, filter_cpuid, t2_template};
use kvm::*;
use memory_model::{GuestAddress, GuestMemory, GuestMemoryError};
use sys_util::EventFd;
use vmm_config::machine_config::{CpuFeaturesTemplate, VmConfig};
use x86_64::{interrupts, regs};

pub const KVM_TSS_ADDRESS: usize = 0xfffbd000;

#[derive(Debug)]
pub enum Error {
    AlreadyRunning,
    CpuId(cpuid::Error),
    GuestMemory(GuestMemoryError),
    Kvm(sys_util::Error),
    VmFd(sys_util::Error),
    VcpuFd(sys_util::Error),
    VmSetup(sys_util::Error),
    VcpuRun(sys_util::Error),
    GetSupportedCpusFailed(sys_util::Error),
    SetSupportedCpusFailed(sys_util::Error),
    NotEnoughMemory,
    NoMemoryEntry,
    LocalIntConfiguration(interrupts::Error),
    SetUserMemoryRegion(sys_util::Error),
    /// The kernel extends past the end of RAM
    KernelOffsetPastEnd,
    /// Error configuring the MSR registers
    MSRSConfiguration(regs::Error),
    /// Error configuring the general purpose registers
    REGSConfiguration(regs::Error),
    /// Error configuring the special registers
    SREGSConfiguration(regs::Error),
    /// Error configuring the floating point related registers
    FPUConfiguration(regs::Error),
    EventFd(sys_util::Error),
    Irq(sys_util::Error),
}
pub type Result<T> = result::Result<T, Error>;

impl ::std::convert::From<sys_util::Error> for Error {
    fn from(e: sys_util::Error) -> Error {
        Error::SetUserMemoryRegion(e)
    }
}

/// A wrapper around creating and using a VM.
pub struct Vm {
    fd: VmFd,
    guest_mem: Option<GuestMemory>,
}

impl Vm {
    /// Constructs a new `Vm` using the given `Kvm` instance.
    pub fn new(kvm: &Kvm) -> Result<Self> {
        //create fd for interacting with kvm-vm specific functions
        let vm_fd = VmFd::new(&kvm).map_err(Error::VmFd)?;

        Ok(Vm {
            fd: vm_fd,
            guest_mem: None,
        })
    }

    /// Currently this is x86 specific (because of the TSS address setup)
    pub fn memory_init(&mut self, guest_mem: GuestMemory) -> Result<()> {
        guest_mem.with_regions(|index, guest_addr, size, host_addr| {
            info!("Guest memory starts at {:x?}", host_addr);
            // Safe because the guest regions are guaranteed not to overlap.
            self.fd.set_user_memory_region(
                index as u32,
                guest_addr.offset() as u64,
                size as u64,
                host_addr as u64,
                0,
            )
        })?;
        self.guest_mem = Some(guest_mem);

        let tss_addr = GuestAddress(KVM_TSS_ADDRESS);
        self.fd
            .set_tss_address(tss_addr.offset())
            .map_err(Error::VmSetup)?;

        Ok(())
    }

    /// This function creates the irq chip and adds 2 interrupt events to the IRQ
    pub fn setup_irqchip(&self, com_evt_1_3: &EventFd, com_evt_2_4: &EventFd) -> Result<()> {
        self.fd.create_irq_chip().map_err(Error::VmSetup)?;

        self.fd.register_irqfd(com_evt_1_3, 4).map_err(Error::Irq)?;
        self.fd.register_irqfd(com_evt_2_4, 3).map_err(Error::Irq)?;

        Ok(())
    }

    pub fn create_pit(&self) -> Result<()> {
        self.fd.create_pit2().map_err(Error::VmSetup)?;
        Ok(())
    }

    /// Gets a reference to the guest memory owned by this VM.
    ///
    /// Note that `GuestMemory` does not include any device memory that may have been added after
    /// this VM was constructed.
    pub fn get_memory(&self) -> Option<&GuestMemory> {
        self.guest_mem.as_ref()
    }

    /// Gets a reference to the kvm file descriptor owned by this VM.
    ///
    pub fn get_fd(&self) -> &VmFd {
        &self.fd
    }
}

/// A wrapper around creating and using a kvm-based VCPU
pub struct Vcpu {
    cpuid: CpuId,
    fd: VcpuFd,
    id: u8,
}

impl Vcpu {
    /// Constructs a new VCPU for `vm`.
    ///
    /// The `id` argument is the CPU number between [0, max vcpus).
    pub fn new(id: u8, vm: &Vm) -> Result<Self> {
        let kvm_vcpu = VcpuFd::new(id, &vm.fd).map_err(Error::VcpuFd)?;
        // Initially the cpuid per vCPU is the one supported by this VM
        Ok(Vcpu {
            fd: kvm_vcpu,
            cpuid: vm.fd.get_supported_cpuid(),
            id,
        })
    }

    /// /// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
    ///
    /// # Arguments
    ///
    /// * `kernel_load_offset` - Offset from `guest_mem` at which the kernel starts.
    /// nr cpus is required for checking populating the kvm_cpuid2 entry for ebx and edx registers
    pub fn configure(
        &mut self,
        machine_config: &VmConfig,
        kernel_start_addr: GuestAddress,
        vm: &Vm,
    ) -> Result<()> {
        // the MachineConfiguration has defaults for ht_enabled and vcpu_count hence it is safe to unwrap
        filter_cpuid(
            self.id,
            machine_config.vcpu_count.unwrap(),
            machine_config.ht_enabled.unwrap(),
            &mut self.cpuid,
        ).map_err(|e| Error::CpuId(e))?;
        match machine_config.cpu_template {
            Some(template) => match template {
                CpuFeaturesTemplate::T2 => {
                    t2_template::set_cpuid_entries(self.cpuid.mut_entries_slice())
                }
                CpuFeaturesTemplate::C3 => {
                    c3_template::set_cpuid_entries(self.cpuid.mut_entries_slice())
                }
            },
            None => (),
        }

        self.fd
            .set_cpuid2(&self.cpuid)
            .map_err(Error::SetSupportedCpusFailed)?;

        regs::setup_msrs(&self.fd).map_err(Error::MSRSConfiguration)?;
        // Safe to unwrap because this method is called after the VM is configured
        let vm_memory = vm.get_memory().unwrap();
        regs::setup_regs(
            &self.fd,
            kernel_start_addr.offset() as u64,
            x86_64::layout::BOOT_STACK_POINTER as u64,
            x86_64::layout::ZERO_PAGE_START as u64,
        ).map_err(Error::REGSConfiguration)?;
        regs::setup_fpu(&self.fd).map_err(Error::FPUConfiguration)?;
        regs::setup_sregs(vm_memory, &self.fd).map_err(Error::SREGSConfiguration)?;
        interrupts::set_lint(&self.fd).map_err(Error::LocalIntConfiguration)?;
        Ok(())
    }

    /// Runs the VCPU until it exits, returning the reason.
    ///
    /// Note that the state of the VCPU and associated VM must be setup first for this to do
    /// anything useful.
    pub fn run(&self) -> Result<VcpuExit> {
        match self.fd.run() {
            Ok(v) => Ok(v),
            Err(e) => return Err(Error::VcpuRun(<sys_util::Error>::new(e.errno()))),
        }
    }
}

#[cfg(test)]
mod tests {

    impl Vcpu {
        /// Returns a clone of the CPUID entries of this vCPU
        /// For now this function is only used for testing; the cfg(test) should be removed when
        /// this function will be used for configuring the cpu features
        pub fn get_cpuid(&self) -> CpuId {
            return self.cpuid.clone();
        }
    }

    use super::*;

    #[test]
    fn create_vm() {
        let kvm = Kvm::new().unwrap();
        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
        let mut vm = Vm::new(&kvm).expect("new vm failed");
        assert!(vm.memory_init(gm).is_ok());
    }

    #[test]
    fn get_memory() {
        let kvm = Kvm::new().unwrap();
        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
        let mut vm = Vm::new(&kvm).expect("new vm failed");
        assert!(vm.memory_init(gm).is_ok());
        let obj_addr = GuestAddress(0xf0);
        vm.get_memory()
            .unwrap()
            .write_obj_at_addr(67u8, obj_addr)
            .unwrap();
        let read_val: u8 = vm
            .get_memory()
            .unwrap()
            .read_obj_from_addr(obj_addr)
            .unwrap();
        assert_eq!(read_val, 67u8);
    }

    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    #[test]
    fn test_configure_vcpu() {
        let kvm = Kvm::new().unwrap();
        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
        let mut vm = Vm::new(&kvm).expect("new vm failed");
        assert!(vm.memory_init(gm).is_ok());
        let dummy_eventfd_1 = EventFd::new().unwrap();
        let dummy_eventfd_2 = EventFd::new().unwrap();

        vm.setup_irqchip(&dummy_eventfd_1, &dummy_eventfd_2)
            .unwrap();
        vm.create_pit().unwrap();

        let mut vcpu = Vcpu::new(1, &vm).unwrap();
        let vm_config = VmConfig::default();
        assert!(vcpu.configure(&vm_config, GuestAddress(0), &vm).is_ok());

        // Test configure while using the T2 template.
        let mut vm_config = VmConfig::default();
        vm_config.cpu_template = Some(CpuFeaturesTemplate::T2);
        assert!(vcpu.configure(&vm_config, GuestAddress(0), &vm).is_ok());

        // Test configure while using the C3 template.
        let mut vm_config = VmConfig::default();
        vm_config.cpu_template = Some(CpuFeaturesTemplate::C3);
        assert!(vcpu.configure(&vm_config, GuestAddress(0), &vm).is_ok());
    }

    #[test]
    fn run_code() {
        use std::io::{self, Write};
        // This example based on https://lwn.net/Articles/658511/
        let code = [
            0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
            0x00, 0xd8, /* add %bl, %al */
            0x04, '0' as u8, /* add $'0', %al */
            0xee,      /* out %al, (%dx) */
            0xb0, '\n' as u8, /* mov $'\n', %al */
            0xee,       /* out %al, (%dx) */
            0xf4,       /* hlt */
        ];

        let mem_size = 0x1000;
        let load_addr = GuestAddress(0x1000);
        let mem = GuestMemory::new(&vec![(load_addr, mem_size)]).unwrap();

        let kvm = Kvm::new().expect("new kvm failed");
        let mut vm = Vm::new(&kvm).expect("new vm failed");
        assert!(vm.memory_init(mem).is_ok());
        vm.get_memory()
            .unwrap()
            .write_slice_at_addr(&code, load_addr)
            .expect("Writing code to memory failed.");

        let vcpu = Vcpu::new(0, &mut vm).expect("new vcpu failed");

        let mut vcpu_sregs = vcpu.fd.get_sregs().expect("get sregs failed");
        assert_ne!(vcpu_sregs.cs.base, 0);
        assert_ne!(vcpu_sregs.cs.selector, 0);
        vcpu_sregs.cs.base = 0;
        vcpu_sregs.cs.selector = 0;
        vcpu.fd.set_sregs(&vcpu_sregs).expect("set sregs failed");

        let mut vcpu_regs = vcpu.fd.get_regs().expect("get regs failed");
        vcpu_regs.rip = 0x1000;
        vcpu_regs.rax = 2;
        vcpu_regs.rbx = 3;
        vcpu_regs.rflags = 2;
        vcpu.fd.set_regs(&vcpu_regs).expect("set regs failed");

        loop {
            match vcpu.run().expect("run failed") {
                VcpuExit::IoOut(0x3f8, data) => {
                    assert_eq!(data.len(), 1);
                    io::stdout().write(data).unwrap();
                }
                VcpuExit::Hlt => {
                    io::stdout().write(b"KVM_EXIT_HLT\n").unwrap();
                    break;
                }
                r => panic!("unexpected exit reason: {:?}", r),
            }
        }
    }
}
<|endoftext|>