<commit_before>/*!
The types module provides a way of associating glob patterns on file names to
file types.
*/

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::path::Path;

use regex;

use gitignore::{Match, Pattern};
use glob::{self, MatchOptions};

const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("awk", &["*.awk"]),
    ("c", &["*.c", "*.h", "*.H"]),
    ("cbor", &["*.cbor"]),
    ("clojure", &["*.clj", "*.cljs"]),
    ("cmake", &["CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("cpp", &[
        "*.C", "*.cc", "*.cpp", "*.cxx",
        "*.h", "*.H", "*.hh", "*.hpp",
    ]),
    ("csharp", &["*.cs"]),
    ("css", &["*.css"]),
    ("cython", &["*.pyx"]),
    ("dart", &["*.dart"]),
    ("d", &["*.d"]),
    ("elisp", &["*.el"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("go", &["*.go"]),
    ("groovy", &["*.groovy"]),
    ("haskell", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html"]),
    ("java", &["*.java"]),
    ("js", &["*.js"]),
    ("json", &["*.json"]),
    ("jsonl", &["*.jsonl"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lua", &["*.lua"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk"]),
    ("markdown", &["*.md"]),
    ("matlab", &["*.m"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("py", &["*.py"]),
    ("readme", &["README*", "*README"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("rst", &["*.rst"]),
    ("ruby", &["*.rb"]),
    ("rust", &["*.rs"]),
    ("scala", &["*.scala"]),
    ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
    ("sql", &["*.sql"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("txt", &["*.txt"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vimscript", &["*.vim"]),
    ("xml", &["*.xml"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
];

/// Describes all the possible failure conditions for building a file type
/// matcher.
#[derive(Debug)]
pub enum Error {
    /// We tried to select (or negate) a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
    /// There was an error building the matcher (probably a bad glob).
    Glob(glob::Error),
    /// There was an error compiling a glob as a regex.
    Regex(regex::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
        }
    }
}

impl From<glob::Error> for Error {
    fn from(err: glob::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

/// A single file type definition.
#[derive(Clone, Debug)]
pub struct FileTypeDef {
    name: String,
    pats: Vec<String>,
}

impl FileTypeDef {
    /// Return the name of this file type.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Return the glob patterns used to recognize this file type.
    pub fn patterns(&self) -> &[String] {
        &self.pats
    }
}

/// Types is a file type matcher.
#[derive(Clone, Debug)]
pub struct Types {
    selected: Option<glob::SetYesNo>,
    negated: Option<glob::SetYesNo>,
    has_selected: bool,
    unmatched_pat: Pattern,
}

impl Types {
    /// Creates a new file type matcher from the given Gitignore matcher. If
    /// not Gitignore matcher is provided, then the file type matcher has no
    /// effect.
    ///
    /// If has_selected is true, then at least one file type was selected.
    /// Therefore, any non-matches should be ignored.
    fn new(
        selected: Option<glob::SetYesNo>,
        negated: Option<glob::SetYesNo>,
        has_selected: bool,
    ) -> Types {
        Types {
            selected: selected,
            negated: negated,
            has_selected: has_selected,
            unmatched_pat: Pattern {
                from: Path::new("<filetype>").to_path_buf(),
                original: "<N/A>".to_string(),
                pat: "<N/A>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Creates a new file type matcher that never matches.
    pub fn empty() -> Types {
        Types::new(None, None, false)
    }

    /// Returns a match for the given path against this file type matcher.
    ///
    /// The path is considered whitelisted if it matches a selected file type.
    /// The path is considered ignored if it matched a negated file type.
    /// If at least one file type is selected and path doesn't match, then
    /// the path is also considered ignored.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // If we don't have any matcher, then we can't do anything.
        if self.negated.is_none() && self.selected.is_none() {
            return Match::None;
        }
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy(),
            None if self.has_selected => {
                return Match::Ignored(&self.unmatched_pat);
            }
            None => {
                return Match::None;
            }
        };
        if self.negated.as_ref().map(|s| s.is_match(&*name)).unwrap_or(false) {
            return Match::Ignored(&self.unmatched_pat);
        }
        if self.selected.as_ref().map(|s|s.is_match(&*name)).unwrap_or(false) {
            return Match::Whitelist(&self.unmatched_pat);
        }
        if self.has_selected {
            Match::Ignored(&self.unmatched_pat)
        } else {
            Match::None
        }
    }
}

/// TypesBuilder builds a type matcher from a set of file type definitions and
/// a set of file type selections.
pub struct TypesBuilder {
    types: HashMap<String, Vec<String>>,
    selected: Vec<String>,
    negated: Vec<String>,
}

impl TypesBuilder {
    /// Create a new builder for a file type matcher.
    pub fn new() -> TypesBuilder {
        TypesBuilder {
            types: HashMap::new(),
            selected: vec![],
            negated: vec![],
        }
    }

    /// Build the current set of file type definitions *and* selections into
    /// a file type matcher.
    pub fn build(&self) -> Result<Types, Error> {
        let opts = MatchOptions {
            require_literal_separator: true, ..MatchOptions::default()
        };
        let selected_globs =
            if self.selected.is_empty() {
                None
            } else {
                let mut bset = glob::SetBuilder::new();
                for name in &self.selected {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        try!(bset.add_with(glob, &opts));
                    }
                }
                Some(try!(bset.build_yesno()))
            };
        let negated_globs =
            if self.negated.is_empty() {
                None
            } else {
                let mut bset = glob::SetBuilder::new();
                for name in &self.negated {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        try!(bset.add_with(glob, &opts));
                    }
                }
                Some(try!(bset.build_yesno()))
            };
        Ok(Types::new(
            selected_globs, negated_globs, !self.selected.is_empty()))
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> Vec<FileTypeDef> {
        let mut defs = vec![];
        for (ref name, ref pats) in &self.types {
            let mut pats = pats.to_vec();
            pats.sort();
            defs.push(FileTypeDef {
                name: name.to_string(),
                pats: pats,
            });
        }
        defs.sort_by(|def1, def2| def1.name().cmp(def2.name()));
        defs
    }

    /// Select the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are selected.
    pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.selected.push(name.to_string());
            }
        } else {
            self.selected.push(name.to_string());
        }
        self
    }

    /// Ignore the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are negated.
    pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.negated.push(name.to_string());
            }
        } else {
            self.negated.push(name.to_string());
        }
        self
    }

    /// Clear any file type definitions for the type given.
    pub fn clear(&mut self, name: &str) -> &mut TypesBuilder {
        self.types.remove(name);
        self
    }

    /// Add a new file type definition. `name` can be arbitrary and `pat`
    /// should be a glob recognizing file paths belonging to the `name` type.
    pub fn add(&mut self, name: &str, pat: &str) -> &mut TypesBuilder {
        self.types.entry(name.to_string())
            .or_insert(vec![]).push(pat.to_string());
        self
    }

    /// Add a new file type definition specified in string form. The format
    /// is `name:glob`. Names may not include a colon.
    pub fn add_def(&mut self, def: &str) -> Result<(), Error> {
        let name: String = def.chars().take_while(|&c| c != ':').collect();
        let pat: String = def.chars().skip(name.chars().count() + 1).collect();
        if name.is_empty() || pat.is_empty() {
            return Err(Error::InvalidDefinition);
        }
        self.add(&name, &pat);
        Ok(())
    }

    /// Add a set of default file type definitions.
    pub fn add_defaults(&mut self) -> &mut TypesBuilder {
        for &(name, exts) in TYPE_EXTENSIONS {
            for ext in exts {
                self.add(name, ext);
            }
        }
        self
    }
}

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

    macro_rules! matched {
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, true);
        };
        (not, $name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, false);
        };
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr, $matched:expr) => {
            #[test]
            fn $name() {
                let mut btypes = TypesBuilder::new();
                for tydef in $types {
                    btypes.add_def(tydef).unwrap();
                }
                for sel in $sel {
                    btypes.select(sel);
                }
                for selnot in $selnot {
                    btypes.negate(selnot);
                }
                let types = btypes.build().unwrap();
                let mat = types.matched($path, false);
                assert_eq!($matched, !mat.is_ignored());
            }
        };
    }

    fn types() -> Vec<&'static str> {
        vec![
            "html:*.html",
            "html:*.htm",
            "rust:*.rs",
            "js:*.js",
        ]
    }

    matched!(match1, types(), vec!["rust"], vec![], "lib.rs");
    matched!(match2, types(), vec!["html"], vec![], "index.html");
    matched!(match3, types(), vec!["html"], vec![], "index.htm");
    matched!(match4, types(), vec!["html", "rust"], vec![], "main.rs");
    matched!(match5, types(), vec![], vec![], "index.html");
    matched!(match6, types(), vec![], vec!["rust"], "index.html");

    matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
    matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
}
<commit_msg>--type js: include more extensions<commit_after>/*!
The types module provides a way of associating glob patterns on file names to
file types.
*/

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::path::Path;

use regex;

use gitignore::{Match, Pattern};
use glob::{self, MatchOptions};

const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("awk", &["*.awk"]),
    ("c", &["*.c", "*.h", "*.H"]),
    ("cbor", &["*.cbor"]),
    ("clojure", &["*.clj", "*.cljs"]),
    ("cmake", &["CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("cpp", &[
        "*.C", "*.cc", "*.cpp", "*.cxx",
        "*.h", "*.H", "*.hh", "*.hpp",
    ]),
    ("csharp", &["*.cs"]),
    ("css", &["*.css"]),
    ("cython", &["*.pyx"]),
    ("dart", &["*.dart"]),
    ("d", &["*.d"]),
    ("elisp", &["*.el"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("go", &["*.go"]),
    ("groovy", &["*.groovy"]),
    ("haskell", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html"]),
    ("java", &["*.java"]),
    ("js", &[
        "*.js", "*.jsx", "*.vue",
    ]),
    ("json", &["*.json"]),
    ("jsonl", &["*.jsonl"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lua", &["*.lua"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk"]),
    ("markdown", &["*.md"]),
    ("matlab", &["*.m"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("py", &["*.py"]),
    ("readme", &["README*", "*README"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("rst", &["*.rst"]),
    ("ruby", &["*.rb"]),
    ("rust", &["*.rs"]),
    ("scala", &["*.scala"]),
    ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
    ("sql", &["*.sql"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("txt", &["*.txt"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vimscript", &["*.vim"]),
    ("xml", &["*.xml"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
];

/// Describes all the possible failure conditions for building a file type
/// matcher.
#[derive(Debug)]
pub enum Error {
    /// We tried to select (or negate) a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
    /// There was an error building the matcher (probably a bad glob).
    Glob(glob::Error),
    /// There was an error compiling a glob as a regex.
    Regex(regex::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
        }
    }
}

impl From<glob::Error> for Error {
    fn from(err: glob::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

/// A single file type definition.
#[derive(Clone, Debug)]
pub struct FileTypeDef {
    name: String,
    pats: Vec<String>,
}

impl FileTypeDef {
    /// Return the name of this file type.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Return the glob patterns used to recognize this file type.
    pub fn patterns(&self) -> &[String] {
        &self.pats
    }
}

/// Types is a file type matcher.
#[derive(Clone, Debug)]
pub struct Types {
    selected: Option<glob::SetYesNo>,
    negated: Option<glob::SetYesNo>,
    has_selected: bool,
    unmatched_pat: Pattern,
}

impl Types {
    /// Creates a new file type matcher from the given Gitignore matcher. If
    /// not Gitignore matcher is provided, then the file type matcher has no
    /// effect.
    ///
    /// If has_selected is true, then at least one file type was selected.
    /// Therefore, any non-matches should be ignored.
    fn new(
        selected: Option<glob::SetYesNo>,
        negated: Option<glob::SetYesNo>,
        has_selected: bool,
    ) -> Types {
        Types {
            selected: selected,
            negated: negated,
            has_selected: has_selected,
            unmatched_pat: Pattern {
                from: Path::new("<filetype>").to_path_buf(),
                original: "<N/A>".to_string(),
                pat: "<N/A>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Creates a new file type matcher that never matches.
    pub fn empty() -> Types {
        Types::new(None, None, false)
    }

    /// Returns a match for the given path against this file type matcher.
    ///
    /// The path is considered whitelisted if it matches a selected file type.
    /// The path is considered ignored if it matched a negated file type.
    /// If at least one file type is selected and path doesn't match, then
    /// the path is also considered ignored.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // If we don't have any matcher, then we can't do anything.
        if self.negated.is_none() && self.selected.is_none() {
            return Match::None;
        }
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy(),
            None if self.has_selected => {
                return Match::Ignored(&self.unmatched_pat);
            }
            None => {
                return Match::None;
            }
        };
        if self.negated.as_ref().map(|s| s.is_match(&*name)).unwrap_or(false) {
            return Match::Ignored(&self.unmatched_pat);
        }
        if self.selected.as_ref().map(|s|s.is_match(&*name)).unwrap_or(false) {
            return Match::Whitelist(&self.unmatched_pat);
        }
        if self.has_selected {
            Match::Ignored(&self.unmatched_pat)
        } else {
            Match::None
        }
    }
}

/// TypesBuilder builds a type matcher from a set of file type definitions and
/// a set of file type selections.
pub struct TypesBuilder {
    types: HashMap<String, Vec<String>>,
    selected: Vec<String>,
    negated: Vec<String>,
}

impl TypesBuilder {
    /// Create a new builder for a file type matcher.
    pub fn new() -> TypesBuilder {
        TypesBuilder {
            types: HashMap::new(),
            selected: vec![],
            negated: vec![],
        }
    }

    /// Build the current set of file type definitions *and* selections into
    /// a file type matcher.
    pub fn build(&self) -> Result<Types, Error> {
        let opts = MatchOptions {
            require_literal_separator: true, ..MatchOptions::default()
        };
        let selected_globs =
            if self.selected.is_empty() {
                None
            } else {
                let mut bset = glob::SetBuilder::new();
                for name in &self.selected {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        try!(bset.add_with(glob, &opts));
                    }
                }
                Some(try!(bset.build_yesno()))
            };
        let negated_globs =
            if self.negated.is_empty() {
                None
            } else {
                let mut bset = glob::SetBuilder::new();
                for name in &self.negated {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        try!(bset.add_with(glob, &opts));
                    }
                }
                Some(try!(bset.build_yesno()))
            };
        Ok(Types::new(
            selected_globs, negated_globs, !self.selected.is_empty()))
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> Vec<FileTypeDef> {
        let mut defs = vec![];
        for (ref name, ref pats) in &self.types {
            let mut pats = pats.to_vec();
            pats.sort();
            defs.push(FileTypeDef {
                name: name.to_string(),
                pats: pats,
            });
        }
        defs.sort_by(|def1, def2| def1.name().cmp(def2.name()));
        defs
    }

    /// Select the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are selected.
    pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.selected.push(name.to_string());
            }
        } else {
            self.selected.push(name.to_string());
        }
        self
    }

    /// Ignore the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are negated.
    pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.negated.push(name.to_string());
            }
        } else {
            self.negated.push(name.to_string());
        }
        self
    }

    /// Clear any file type definitions for the type given.
    pub fn clear(&mut self, name: &str) -> &mut TypesBuilder {
        self.types.remove(name);
        self
    }

    /// Add a new file type definition. `name` can be arbitrary and `pat`
    /// should be a glob recognizing file paths belonging to the `name` type.
    pub fn add(&mut self, name: &str, pat: &str) -> &mut TypesBuilder {
        self.types.entry(name.to_string())
            .or_insert(vec![]).push(pat.to_string());
        self
    }

    /// Add a new file type definition specified in string form. The format
    /// is `name:glob`. Names may not include a colon.
    pub fn add_def(&mut self, def: &str) -> Result<(), Error> {
        let name: String = def.chars().take_while(|&c| c != ':').collect();
        let pat: String = def.chars().skip(name.chars().count() + 1).collect();
        if name.is_empty() || pat.is_empty() {
            return Err(Error::InvalidDefinition);
        }
        self.add(&name, &pat);
        Ok(())
    }

    /// Add a set of default file type definitions.
    pub fn add_defaults(&mut self) -> &mut TypesBuilder {
        for &(name, exts) in TYPE_EXTENSIONS {
            for ext in exts {
                self.add(name, ext);
            }
        }
        self
    }
}

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

    macro_rules! matched {
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, true);
        };
        (not, $name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, false);
        };
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr, $matched:expr) => {
            #[test]
            fn $name() {
                let mut btypes = TypesBuilder::new();
                for tydef in $types {
                    btypes.add_def(tydef).unwrap();
                }
                for sel in $sel {
                    btypes.select(sel);
                }
                for selnot in $selnot {
                    btypes.negate(selnot);
                }
                let types = btypes.build().unwrap();
                let mat = types.matched($path, false);
                assert_eq!($matched, !mat.is_ignored());
            }
        };
    }

    fn types() -> Vec<&'static str> {
        vec![
            "html:*.html",
            "html:*.htm",
            "rust:*.rs",
            "js:*.js",
        ]
    }

    matched!(match1, types(), vec!["rust"], vec![], "lib.rs");
    matched!(match2, types(), vec!["html"], vec![], "index.html");
    matched!(match3, types(), vec!["html"], vec![], "index.htm");
    matched!(match4, types(), vec!["html", "rust"], vec![], "main.rs");
    matched!(match5, types(), vec![], vec![], "index.html");
    matched!(match6, types(), vec![], vec!["rust"], "index.html");

    matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
    matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
}
<|endoftext|>


<commit_before>use num::bigint::{ToBigUint, BigUint};
use num::traits::{One, ToPrimitive};

use std::num::ParseIntError;
use std::mem;

pub trait Max {
    fn max(width: u32) -> Self;
}
impl Max for BigUint {
    fn max(width: u32) -> BigUint {
        (BigUint::one() << width as usize) - BigUint::one()
    }
}

pub trait ParseNum {
    fn parse_num(num: &str) -> Result<Self, ParseIntError> where Self: Sized;
}

impl ParseNum for u64 {
    fn parse_num(num: &str) -> Result<Self, ParseIntError> {
        if num.len() > 2 && &num[0..2] == "0x" {
            u64::from_str_radix(&num[0..2], 16)
        } else {
            u64::from_str_radix(num, 10)
        }
    }
}

trait AsFixed<T> {
    fn as_fixed(&self) -> T;
}
impl AsFixed<u64> for BigUint {
    #[inline]
    fn as_fixed(&self) -> u64 {
        (self & 0xffFFffFF_ffFFffFFu64.to_biguint().unwrap())
            .to_u64()
            .unwrap()
    }
}
impl AsFixed<f64> for BigUint {
    #[inline]
    fn as_fixed(&self) -> f64 {
        let imm: u64 = self.as_fixed();
        unsafe { mem::transmute(imm) }
    }
}
impl AsFixed<f32> for BigUint {
    #[inline]
    fn as_fixed(&self) -> f32 {
        let imm: u64 = self.as_fixed();
        unsafe { mem::transmute(imm as u32) }
    }
}

/// Implement this trait for Floats to get the raw representation
pub trait AsRaw<T> {
    /// Returns the raw representation of the floating point number
    fn as_raw(&self) -> T;
}

impl AsRaw<u32> for f32 {
    fn as_raw(&self) -> u32 {
        unsafe { mem::transmute(*self) }
    }
}
impl AsRaw<u64> for f64 {
    fn as_raw(&self) -> u64 {
        unsafe { mem::transmute(*self) }
    }
}

use std::collections::VecDeque;
use std::collections::vec_deque;

// Objects
pub struct LastCache<T> {
    stack: VecDeque<T>,
    max: usize
}

impl<T> LastCache<T> {
    pub fn new(max: usize) -> LastCache<T> {
        LastCache {
            stack: VecDeque::with_capacity(max),
            max: max
        }
    }

    pub fn push(&mut self, item: T) {
        if self.stack.len() >= self.max {
            self.stack.pop_back();
            self.stack.remove(self.max - 1);
        }
        self.stack.push_front(item);
    }

    pub fn iter<'a>(&'a mut self) -> vec_deque::Iter<'a, T> {
        self.stack.iter()
    }

    pub fn last<'a>(&'a self) -> &T {
        self.stack.front().unwrap()
    }
}

impl<'a, T> IntoIterator for &'a LastCache<T> {
    type Item = &'a T;
    type IntoIter = vec_deque::Iter<'a, T>;

    fn into_iter(self) -> vec_deque::Iter<'a, T> {
        self.stack.iter()
    }
}

// Arithmetic/binary utils
/// Hamming weight
pub trait Hamming {
    fn hamming_weight(&self) -> u32;
}

impl Hamming for u64 {
    fn hamming_weight(&self) -> u32 {
        // Optimized by rust's intrinsics
        self.count_ones()
    }
}

impl Hamming for BigUint {
    fn hamming_weight(&self) -> u32 {
        let bits = self.bits();
        if bits <= 64 {
            let in64 = self.to_u64().unwrap();
            in64.hamming_weight()
        } else {
            let mut res = 0;
            for i in 0..bits {
                res += ((self >> i) & BigUint::one()).to_u32().unwrap();
            }
            res
        }
    }
}

pub fn flip_bits(num: &BigUint, width: u32) -> BigUint {
    let bits = num.bits();
    ((BigUint::one() << width as usize) - BigUint::one()) ^ num
}

// CloneWeightedChoice
use rand::Rng;
use rand::distributions::{Sample, Weighted, Range, IndependentSample};

pub struct CloneWeightedChoice<T> {
    items: Vec<Weighted<T>>,
    weight_range: Range<u32>
}

impl<T: Clone> CloneWeightedChoice<T> {
    /// Create a new `WeightedChoice`.
    ///
    /// Panics if:
    /// - `v` is empty
    /// - the total weight is 0
    /// - the total weight is larger than a `u32` can contain.
    pub fn new(items: &[Weighted<T>]) -> CloneWeightedChoice<T> {
        // strictly speaking, this is subsumed by the total weight == 0 case
        assert!(!items.is_empty(), "WeightedChoice::new called with no items");
        let mut items = items.to_vec();

        let mut running_total: u32 = 0;

        // we convert the list from individual weights to cumulative
        // weights so we can binary search. This *could* drop elements
        // with weight == 0 as an optimisation.
        for item in items.iter_mut() {
            running_total = match running_total.checked_add(item.weight) {
                Some(n) => n,
                None => panic!("WeightedChoice::new called with a total weight \
                               larger than a u32 can contain")
            };

            item.weight = running_total;
        }
        assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0");

        CloneWeightedChoice {
            items: items,
            // we're likely to be generating numbers in this range
            // relatively often, so might as well cache it
            weight_range: Range::new(0, running_total)
        }
    }
}

impl<T: Clone> Sample<T> for CloneWeightedChoice<T> {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> T { self.ind_sample(rng) }
}

impl<T: Clone> IndependentSample<T> for CloneWeightedChoice<T> {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> T {
        // we want to find the first element that has cumulative
        // weight > sample_weight, which we do by binary since the
        // cumulative weights of self.items are sorted.

        // choose a weight in [0, total_weight)
        let sample_weight = self.weight_range.ind_sample(rng);

        // short circuit when it's the first item
        if sample_weight < self.items[0].weight {
            return self.items[0].item.clone();
        }

        let mut idx = 0;
        let mut modifier = self.items.len();

        // now we know that every possibility has an element to the
        // left, so we can just search for the last element that has
        // cumulative weight <= sample_weight, then the next one will
        // be "it". (Note that this greatest element will never be the
        // last element of the vector, since sample_weight is chosen
        // in [0, total_weight) and the cumulative weight of the last
        // one is exactly the total weight.)
        while modifier > 1 {
            let i = idx + modifier / 2;
            if self.items[i].weight <= sample_weight {
                // we're small, so look to the right, but allow this
                // exact element still.
                idx = i;
                // we need the `/ 2` to round up otherwise we'll drop
                // the trailing elements when `modifier` is odd.
                modifier += 1;
            } else {
                // otherwise we're too big, so go left. (i.e. do
                // nothing)
            }
            modifier /= 2;
        }
        return self.items[idx + 1].item.clone();
    }
}

// Macro utils
macro_rules! for_one {
    ($x:ident) => (1)
}
macro_rules! enum_and_list{
    ($e_name:ident, $c_name:ident, $($m:ident),+) => {
        #[derive(Copy,Clone,Debug,PartialEq,Eq,Hash,PartialOrd,Ord)]
        pub enum $e_name {
            $(
                $m,
            )+
        }
        pub const $c_name: [$e_name; 0 $( + for_one!($m) )+ ] =
            [ $($e_name::$m,)+ ];
        impl Rand for $e_name {
            fn rand<R: Rng>(rng: &mut R) -> Self {
                let val = rng.gen_range(0, $c_name.len());
                $c_name[val]
            }
        }
    }
}

macro_rules! debugln{
    ($($arg:tt)*) => (
        {
            use std::io::Write;
            match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
                Ok(_) => {},
                Err(x) => panic!("Unable to write to stderr: {}", x),
            }
        }
     )
}

macro_rules! debug{
    ($($arg:tt)*) => (
        {
            use std::io::Write;
            match write!(&mut ::std::io::stderr(), $($arg)* ) {
                Ok(_) => {},
                Err(x) => panic!("Unable to write to stderr: {}", x),
            }
        }
     )
}

#[cfg(test)]
mod test_util {
    use num::bigint::{BigUint, ToBigUint};
    use num::traits::{One, Zero};
    use utils::AsRaw;
    use utils::Hamming;
    use utils::LastCache;

    #[test]
    fn test_hamming() {
        let full_128_bu = {
            let mut res = BigUint::zero();
            for i in 0..128 {
                res = res | BigUint::one() << i;
            }
            res
        };
        assert_eq!(full_128_bu.hamming_weight(), 128);

        let full_64_u64 = 0xffFF_ffFF_ffFF_ffFFu64;
        assert_eq!(full_64_u64.hamming_weight(), 64);

        let full_64_bu = 0xffFF_ffFF_ffFF_ffFFu64.to_biguint().unwrap();
        assert_eq!(full_64_bu.hamming_weight(), 64);
    }

    #[test]
    fn test_biguint() {
        assert_eq!(0xaabbccddu32.to_biguint().unwrap().to_string(),
                   "2864434397");
    }

    #[test]
    fn test_lastcache() {
        let mut lc = LastCache::new(10);
        for i in 0 .. 10 {
            lc.push(1);
        }
        assert_eq!(lc.iter().all(|i| *i == 1), true);
        lc.push(2);
        lc.push(2);
        let mut i = 0;
        for item in &lc {
            if i == 0 || i == 1 {
                assert_eq!(*item, 2);
            } else {
                assert_eq!(*item, 1);
            }
            i += 1;
        }
    }

    #[test]
    fn test_as_raw_f32() {
        use std::f32::INFINITY;
        assert_eq!(2.8411367E-29f32.as_raw(), 0x10101010);
        assert_eq!((-4.99999998E11f32).as_raw(), 0xd2e8d4a5);
        assert_eq!(100.0f32.as_raw(), 0x42c80000);
        assert_eq!(0.0f32.as_raw(), 0);
        assert_eq!((-0.0f32).as_raw(), 0x80000000);
        assert_eq!(INFINITY.as_raw(), 0x7f800000);
    }

    #[test]
    fn test_as_raw_f64() {
        use std::f64::INFINITY;
        assert_eq!(2.8411367E-29f64.as_raw(), 0x3A0202020351C16B);
        assert_eq!((-4.99999998E11f64).as_raw(), 0xC25D1A94A00C0000);
        assert_eq!(100.0f64.as_raw(), 0x4059000000000000);
        assert_eq!(0.0f64.as_raw(), 0);
        assert_eq!((-0.0f64).as_raw(), 0x8000000000000000);
        assert_eq!(INFINITY.as_raw(), 0x7ff0000000000000);
    }

}
<commit_msg>fixing ParseNum for u64<commit_after>use num::bigint::{ToBigUint, BigUint};
use num::traits::{One, ToPrimitive};

use std::num::ParseIntError;
use std::mem;

pub trait Max {
    fn max(width: u32) -> Self;
}
impl Max for BigUint {
    fn max(width: u32) -> BigUint {
        (BigUint::one() << width as usize) - BigUint::one()
    }
}

pub trait ParseNum {
    fn parse_num(num: &str) -> Result<Self, ParseIntError> where Self: Sized;
}

impl ParseNum for u64 {
    fn parse_num(num: &str) -> Result<Self, ParseIntError> {
        if num.len() > 2 && &num[0..2] == "0x" {
            u64::from_str_radix(&num[2..], 16)
        } else {
            u64::from_str_radix(num, 10)
        }
    }
}

trait AsFixed<T> {
    fn as_fixed(&self) -> T;
}
impl AsFixed<u64> for BigUint {
    #[inline]
    fn as_fixed(&self) -> u64 {
        (self & 0xffFFffFF_ffFFffFFu64.to_biguint().unwrap())
            .to_u64()
            .unwrap()
    }
}
impl AsFixed<f64> for BigUint {
    #[inline]
    fn as_fixed(&self) -> f64 {
        let imm: u64 = self.as_fixed();
        unsafe { mem::transmute(imm) }
    }
}
impl AsFixed<f32> for BigUint {
    #[inline]
    fn as_fixed(&self) -> f32 {
        let imm: u64 = self.as_fixed();
        unsafe { mem::transmute(imm as u32) }
    }
}

/// Implement this trait for Floats to get the raw representation
pub trait AsRaw<T> {
    /// Returns the raw representation of the floating point number
    fn as_raw(&self) -> T;
}

impl AsRaw<u32> for f32 {
    fn as_raw(&self) -> u32 {
        unsafe { mem::transmute(*self) }
    }
}
impl AsRaw<u64> for f64 {
    fn as_raw(&self) -> u64 {
        unsafe { mem::transmute(*self) }
    }
}

use std::collections::VecDeque;
use std::collections::vec_deque;

// Objects
pub struct LastCache<T> {
    stack: VecDeque<T>,
    max: usize
}

impl<T> LastCache<T> {
    pub fn new(max: usize) -> LastCache<T> {
        LastCache {
            stack: VecDeque::with_capacity(max),
            max: max
        }
    }

    pub fn push(&mut self, item: T) {
        if self.stack.len() >= self.max {
            self.stack.pop_back();
            self.stack.remove(self.max - 1);
        }
        self.stack.push_front(item);
    }

    pub fn iter<'a>(&'a mut self) -> vec_deque::Iter<'a, T> {
        self.stack.iter()
    }

    pub fn last<'a>(&'a self) -> &T {
        self.stack.front().unwrap()
    }
}

impl<'a, T> IntoIterator for &'a LastCache<T> {
    type Item = &'a T;
    type IntoIter = vec_deque::Iter<'a, T>;

    fn into_iter(self) -> vec_deque::Iter<'a, T> {
        self.stack.iter()
    }
}

// Arithmetic/binary utils
/// Hamming weight
pub trait Hamming {
    fn hamming_weight(&self) -> u32;
}

impl Hamming for u64 {
    fn hamming_weight(&self) -> u32 {
        // Optimized by rust's intrinsics
        self.count_ones()
    }
}

impl Hamming for BigUint {
    fn hamming_weight(&self) -> u32 {
        let bits = self.bits();
        if bits <= 64 {
            let in64 = self.to_u64().unwrap();
            in64.hamming_weight()
        } else {
            let mut res = 0;
            for i in 0..bits {
                res += ((self >> i) & BigUint::one()).to_u32().unwrap();
            }
            res
        }
    }
}

pub fn flip_bits(num: &BigUint, width: u32) -> BigUint {
    let bits = num.bits();
    ((BigUint::one() << width as usize) - BigUint::one()) ^ num
}

// CloneWeightedChoice
use rand::Rng;
use rand::distributions::{Sample, Weighted, Range, IndependentSample};

pub struct CloneWeightedChoice<T> {
    items: Vec<Weighted<T>>,
    weight_range: Range<u32>
}

impl<T: Clone> CloneWeightedChoice<T> {
    /// Create a new `WeightedChoice`.
    ///
    /// Panics if:
    /// - `v` is empty
    /// - the total weight is 0
    /// - the total weight is larger than a `u32` can contain.
    pub fn new(items: &[Weighted<T>]) -> CloneWeightedChoice<T> {
        // strictly speaking, this is subsumed by the total weight == 0 case
        assert!(!items.is_empty(), "WeightedChoice::new called with no items");
        let mut items = items.to_vec();

        let mut running_total: u32 = 0;

        // we convert the list from individual weights to cumulative
        // weights so we can binary search. This *could* drop elements
        // with weight == 0 as an optimisation.
        for item in items.iter_mut() {
            running_total = match running_total.checked_add(item.weight) {
                Some(n) => n,
                None => panic!("WeightedChoice::new called with a total weight \
                               larger than a u32 can contain")
            };

            item.weight = running_total;
        }
        assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0");

        CloneWeightedChoice {
            items: items,
            // we're likely to be generating numbers in this range
            // relatively often, so might as well cache it
            weight_range: Range::new(0, running_total)
        }
    }
}

impl<T: Clone> Sample<T> for CloneWeightedChoice<T> {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> T { self.ind_sample(rng) }
}

impl<T: Clone> IndependentSample<T> for CloneWeightedChoice<T> {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> T {
        // we want to find the first element that has cumulative
        // weight > sample_weight, which we do by binary since the
        // cumulative weights of self.items are sorted.

        // choose a weight in [0, total_weight)
        let sample_weight = self.weight_range.ind_sample(rng);

        // short circuit when it's the first item
        if sample_weight < self.items[0].weight {
            return self.items[0].item.clone();
        }

        let mut idx = 0;
        let mut modifier = self.items.len();

        // now we know that every possibility has an element to the
        // left, so we can just search for the last element that has
        // cumulative weight <= sample_weight, then the next one will
        // be "it". (Note that this greatest element will never be the
        // last element of the vector, since sample_weight is chosen
        // in [0, total_weight) and the cumulative weight of the last
        // one is exactly the total weight.)
        while modifier > 1 {
            let i = idx + modifier / 2;
            if self.items[i].weight <= sample_weight {
                // we're small, so look to the right, but allow this
                // exact element still.
                idx = i;
                // we need the `/ 2` to round up otherwise we'll drop
                // the trailing elements when `modifier` is odd.
                modifier += 1;
            } else {
                // otherwise we're too big, so go left. (i.e. do
                // nothing)
            }
            modifier /= 2;
        }
        return self.items[idx + 1].item.clone();
    }
}

// Macro utils
macro_rules! for_one {
    ($x:ident) => (1)
}
macro_rules! enum_and_list{
    ($e_name:ident, $c_name:ident, $($m:ident),+) => {
        #[derive(Copy,Clone,Debug,PartialEq,Eq,Hash,PartialOrd,Ord)]
        pub enum $e_name {
            $(
                $m,
            )+
        }
        pub const $c_name: [$e_name; 0 $( + for_one!($m) )+ ] =
            [ $($e_name::$m,)+ ];
        impl Rand for $e_name {
            fn rand<R: Rng>(rng: &mut R) -> Self {
                let val = rng.gen_range(0, $c_name.len());
                $c_name[val]
            }
        }
    }
}

macro_rules! debugln{
    ($($arg:tt)*) => (
        {
            use std::io::Write;
            match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
                Ok(_) => {},
                Err(x) => panic!("Unable to write to stderr: {}", x),
            }
        }
     )
}

macro_rules! debug{
    ($($arg:tt)*) => (
        {
            use std::io::Write;
            match write!(&mut ::std::io::stderr(), $($arg)* ) {
                Ok(_) => {},
                Err(x) => panic!("Unable to write to stderr: {}", x),
            }
        }
     )
}

#[cfg(test)]
mod test_util {
    use num::bigint::{BigUint, ToBigUint};
    use num::traits::{One, Zero};
    use utils::AsRaw;
    use utils::Hamming;
    use utils::LastCache;

    #[test]
    fn test_hamming() {
        let full_128_bu = {
            let mut res = BigUint::zero();
            for i in 0..128 {
                res = res | BigUint::one() << i;
            }
            res
        };
        assert_eq!(full_128_bu.hamming_weight(), 128);

        let full_64_u64 = 0xffFF_ffFF_ffFF_ffFFu64;
        assert_eq!(full_64_u64.hamming_weight(), 64);

        let full_64_bu = 0xffFF_ffFF_ffFF_ffFFu64.to_biguint().unwrap();
        assert_eq!(full_64_bu.hamming_weight(), 64);
    }

    #[test]
    fn test_biguint() {
        assert_eq!(0xaabbccddu32.to_biguint().unwrap().to_string(),
                   "2864434397");
    }

    #[test]
    fn test_lastcache() {
        let mut lc = LastCache::new(10);
        for i in 0 .. 10 {
            lc.push(1);
        }
        assert_eq!(lc.iter().all(|i| *i == 1), true);
        lc.push(2);
        lc.push(2);
        let mut i = 0;
        for item in &lc {
            if i == 0 || i == 1 {
                assert_eq!(*item, 2);
            } else {
                assert_eq!(*item, 1);
            }
            i += 1;
        }
    }

    #[test]
    fn test_as_raw_f32() {
        use std::f32::INFINITY;
        assert_eq!(2.8411367E-29f32.as_raw(), 0x10101010);
        assert_eq!((-4.99999998E11f32).as_raw(), 0xd2e8d4a5);
        assert_eq!(100.0f32.as_raw(), 0x42c80000);
        assert_eq!(0.0f32.as_raw(), 0);
        assert_eq!((-0.0f32).as_raw(), 0x80000000);
        assert_eq!(INFINITY.as_raw(), 0x7f800000);
    }

    #[test]
    fn test_as_raw_f64() {
        use std::f64::INFINITY;
        assert_eq!(2.8411367E-29f64.as_raw(), 0x3A0202020351C16B);
        assert_eq!((-4.99999998E11f64).as_raw(), 0xC25D1A94A00C0000);
        assert_eq!(100.0f64.as_raw(), 0x4059000000000000);
        assert_eq!(0.0f64.as_raw(), 0);
        assert_eq!((-0.0f64).as_raw(), 0x8000000000000000);
        assert_eq!(INFINITY.as_raw(), 0x7ff0000000000000);
    }

}
<|endoftext|>


<commit_before>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Small system utility modules for usage by other modules.

extern crate data_model;
extern crate libc;
extern crate syscall_defines;
#[allow(unused_imports)]
#[macro_use]
extern crate poll_token_derive;

#[macro_use]
pub mod handle_eintr;
#[macro_use]
pub mod ioctl;
#[macro_use]
pub mod syslog;
mod mmap;
mod shm;
mod eventfd;
mod errno;
mod guest_address;
mod guest_memory;
mod poll;
mod struct_util;
mod tempdir;
mod terminal;
pub mod signal;
mod fork;
mod signalfd;
mod sock_ctrl_msg;
mod passwd;
mod file_flags;
mod timerfd;

pub use mmap::*;
pub use shm::*;
pub use eventfd::*;
pub use errno::{Error, Result};
use errno::errno_result;
pub use guest_address::*;
pub use guest_memory::*;
pub use poll::*;
pub use struct_util::*;
pub use tempdir::*;
pub use terminal::*;
pub use signal::*;
pub use fork::*;
pub use signalfd::*;
pub use ioctl::*;
pub use sock_ctrl_msg::*;
pub use passwd::*;
pub use poll_token_derive::*;
pub use file_flags::*;
pub use timerfd::*;

pub use mmap::Error as MmapError;
pub use guest_memory::Error as GuestMemoryError;
pub use signalfd::Error as SignalFdError;

use std::ffi::CStr;
use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::ptr;

use libc::{kill, syscall, sysconf, waitpid, pipe2, c_long, pid_t, uid_t, gid_t, _SC_PAGESIZE,
           SIGKILL, WNOHANG, O_CLOEXEC};

use syscall_defines::linux::LinuxSyscall::SYS_getpid;

/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
#[inline(always)]
pub fn pagesize() -> usize {
    // Trivially safe
    unsafe { sysconf(_SC_PAGESIZE) as usize }
}

/// Uses the system's page size in bytes to round the given value up to the nearest page boundary.
#[inline(always)]
pub fn round_up_to_page_size(v: usize) -> usize {
    let page_mask = pagesize() - 1;
    (v + page_mask) & !page_mask
}

/// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used
/// elsewhere.
#[inline(always)]
pub fn getpid() -> pid_t {
    // Safe because this syscall can never fail and we give it a valid syscall number.
    unsafe { syscall(SYS_getpid as c_long) as pid_t }
}

/// Safe wrapper for `geteuid(2)`.
#[inline(always)]
pub fn geteuid() -> uid_t {
    // trivially safe
    unsafe { libc::geteuid() }
}

/// Safe wrapper for `getegid(2)`.
#[inline(always)]
pub fn getegid() -> gid_t {
    // trivially safe
    unsafe { libc::getegid() }
}

/// Safe wrapper for chown(2).
#[inline(always)]
pub fn chown(path: &CStr, uid: uid_t, gid: gid_t) -> Result<()> {
    // Safe since we pass in a valid string pointer and check the return value.
    let ret = unsafe { libc::chown(path.as_ptr(), uid, gid) };

    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// The operation to perform with `flock`.
pub enum FlockOperation {
    LockShared,
    LockExclusive,
    Unlock,
}

/// Safe wrapper for flock(2) with the operation `op` and optionally `nonblocking`. The lock will be
/// dropped automatically when `file` is dropped.
#[inline(always)]
pub fn flock(file: &AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()> {
    let mut operation = match op {
        FlockOperation::LockShared => libc::LOCK_SH,
        FlockOperation::LockExclusive => libc::LOCK_EX,
        FlockOperation::Unlock => libc::LOCK_UN,
    };

    if nonblocking {
        operation |= libc::LOCK_NB;
    }

    // Safe since we pass in a valid fd and flock operation, and check the return value.
    let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };

    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// Reaps a child process that has terminated.
///
/// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children
/// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap.
///
/// # Examples
///
/// Reaps all child processes until there are no terminated children to reap.
///
/// ```
/// # extern crate libc;
/// # extern crate sys_util;
/// fn reap_children() {
///     loop {
///         match sys_util::reap_child() {
///             Ok(0) => println!("no children ready to reap"),
///             Ok(pid) => {
///                 println!("reaped {}", pid);
///                 continue
///             },
///             Err(e) if e.errno() == libc::ECHILD => println!("no children left"),
///             Err(e) => println!("error reaping children: {:?}", e),
///         }
///         break
///     }
/// }
/// ```
pub fn reap_child() -> Result<pid_t> {
    // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
    let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
    if ret == -1 {
        errno_result()
    } else {
        Ok(ret)
    }
}

/// Kill all processes in the current process group.
///
/// On success, this kills all processes in the current process group, including the current
/// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
pub fn kill_process_group() -> Result<()> {
    let ret = unsafe { kill(0, SIGKILL) };
    if ret == -1 {
        errno_result()
    } else {
        // Kill succeeded, so this process never reaches here.
        unreachable!();
    }
}

/// Spawns a pipe pair where the first pipe is the read end and the second pipe is the write end.
///
/// If `close_on_exec` is true, the `O_CLOEXEC` flag will be set during pipe creation.
pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
    let flags = if close_on_exec { O_CLOEXEC } else { 0 };
    let mut pipe_fds = [-1; 2];
    // Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
    // for error.
    let ret = unsafe { pipe2(&mut pipe_fds[0], flags) };
    if ret == -1 {
        errno_result()
    } else {
        // Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
        // exclusive ownership of them.
        Ok(unsafe {
            (
                File::from_raw_fd(pipe_fds[0]),
                File::from_raw_fd(pipe_fds[1])
            )
        })
    }
}
<commit_msg>sys_util: add safe wrapper for fallocate()<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Small system utility modules for usage by other modules.

extern crate data_model;
extern crate libc;
extern crate syscall_defines;
#[allow(unused_imports)]
#[macro_use]
extern crate poll_token_derive;

#[macro_use]
pub mod handle_eintr;
#[macro_use]
pub mod ioctl;
#[macro_use]
pub mod syslog;
mod mmap;
mod shm;
mod eventfd;
mod errno;
mod guest_address;
mod guest_memory;
mod poll;
mod struct_util;
mod tempdir;
mod terminal;
pub mod signal;
mod fork;
mod signalfd;
mod sock_ctrl_msg;
mod passwd;
mod file_flags;
mod timerfd;

pub use mmap::*;
pub use shm::*;
pub use eventfd::*;
pub use errno::{Error, Result};
use errno::errno_result;
pub use guest_address::*;
pub use guest_memory::*;
pub use poll::*;
pub use struct_util::*;
pub use tempdir::*;
pub use terminal::*;
pub use signal::*;
pub use fork::*;
pub use signalfd::*;
pub use ioctl::*;
pub use sock_ctrl_msg::*;
pub use passwd::*;
pub use poll_token_derive::*;
pub use file_flags::*;
pub use timerfd::*;

pub use mmap::Error as MmapError;
pub use guest_memory::Error as GuestMemoryError;
pub use signalfd::Error as SignalFdError;

use std::ffi::CStr;
use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::ptr;

use libc::{kill, syscall, sysconf, waitpid, pipe2, c_long, pid_t, uid_t, gid_t, _SC_PAGESIZE,
           SIGKILL, WNOHANG, O_CLOEXEC};

use syscall_defines::linux::LinuxSyscall::SYS_getpid;

/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
#[inline(always)]
pub fn pagesize() -> usize {
    // Trivially safe
    unsafe { sysconf(_SC_PAGESIZE) as usize }
}

/// Uses the system's page size in bytes to round the given value up to the nearest page boundary.
#[inline(always)]
pub fn round_up_to_page_size(v: usize) -> usize {
    let page_mask = pagesize() - 1;
    (v + page_mask) & !page_mask
}

/// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used
/// elsewhere.
#[inline(always)]
pub fn getpid() -> pid_t {
    // Safe because this syscall can never fail and we give it a valid syscall number.
    unsafe { syscall(SYS_getpid as c_long) as pid_t }
}

/// Safe wrapper for `geteuid(2)`.
#[inline(always)]
pub fn geteuid() -> uid_t {
    // trivially safe
    unsafe { libc::geteuid() }
}

/// Safe wrapper for `getegid(2)`.
#[inline(always)]
pub fn getegid() -> gid_t {
    // trivially safe
    unsafe { libc::getegid() }
}

/// Safe wrapper for chown(2).
#[inline(always)]
pub fn chown(path: &CStr, uid: uid_t, gid: gid_t) -> Result<()> {
    // Safe since we pass in a valid string pointer and check the return value.
    let ret = unsafe { libc::chown(path.as_ptr(), uid, gid) };

    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// The operation to perform with `flock`.
pub enum FlockOperation {
    LockShared,
    LockExclusive,
    Unlock,
}

/// Safe wrapper for flock(2) with the operation `op` and optionally `nonblocking`. The lock will be
/// dropped automatically when `file` is dropped.
#[inline(always)]
pub fn flock(file: &AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()> {
    let mut operation = match op {
        FlockOperation::LockShared => libc::LOCK_SH,
        FlockOperation::LockExclusive => libc::LOCK_EX,
        FlockOperation::Unlock => libc::LOCK_UN,
    };

    if nonblocking {
        operation |= libc::LOCK_NB;
    }

    // Safe since we pass in a valid fd and flock operation, and check the return value.
    let ret = unsafe { libc::flock(file.as_raw_fd(), operation) };

    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// The operation to perform with `fallocate`.
pub enum FallocateMode {
    PunchHole,
    ZeroRange,
}

/// Safe wrapper for `fallocate()`.
pub fn fallocate(
    file: &AsRawFd,
    mode: FallocateMode,
    keep_size: bool,
    offset: u64,
    len: u64
) -> Result<()> {
    let offset = if offset > libc::off_t::max_value() as u64 {
        return Err(Error::new(libc::EINVAL));
    } else {
        offset as libc::off_t
    };

    let len = if len > libc::off_t::max_value() as u64 {
        return Err(Error::new(libc::EINVAL));
    } else {
        len as libc::off_t
    };

    let mut mode = match mode {
        FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE,
        FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE,
    };

    if keep_size {
        mode |= libc::FALLOC_FL_KEEP_SIZE;
    }

    // Safe since we pass in a valid fd and fallocate mode, validate offset and len,
    // and check the return value.
    let ret = unsafe { libc::fallocate(file.as_raw_fd(), mode, offset, len) };
    if ret < 0 {
        errno_result()
    } else {
        Ok(())
    }
}

/// Reaps a child process that has terminated.
///
/// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children
/// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap.
///
/// # Examples
///
/// Reaps all child processes until there are no terminated children to reap.
///
/// ```
/// # extern crate libc;
/// # extern crate sys_util;
/// fn reap_children() {
///     loop {
///         match sys_util::reap_child() {
///             Ok(0) => println!("no children ready to reap"),
///             Ok(pid) => {
///                 println!("reaped {}", pid);
///                 continue
///             },
///             Err(e) if e.errno() == libc::ECHILD => println!("no children left"),
///             Err(e) => println!("error reaping children: {:?}", e),
///         }
///         break
///     }
/// }
/// ```
pub fn reap_child() -> Result<pid_t> {
    // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
    let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
    if ret == -1 {
        errno_result()
    } else {
        Ok(ret)
    }
}

/// Kill all processes in the current process group.
///
/// On success, this kills all processes in the current process group, including the current
/// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
pub fn kill_process_group() -> Result<()> {
    let ret = unsafe { kill(0, SIGKILL) };
    if ret == -1 {
        errno_result()
    } else {
        // Kill succeeded, so this process never reaches here.
        unreachable!();
    }
}

/// Spawns a pipe pair where the first pipe is the read end and the second pipe is the write end.
///
/// If `close_on_exec` is true, the `O_CLOEXEC` flag will be set during pipe creation.
pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
    let flags = if close_on_exec { O_CLOEXEC } else { 0 };
    let mut pipe_fds = [-1; 2];
    // Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
    // for error.
    let ret = unsafe { pipe2(&mut pipe_fds[0], flags) };
    if ret == -1 {
        errno_result()
    } else {
        // Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
        // exclusive ownership of them.
        Ok(unsafe {
            (
                File::from_raw_fd(pipe_fds[0]),
                File::from_raw_fd(pipe_fds[1])
            )
        })
    }
}
<|endoftext|>


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

use std::collections::HashMap;
use std::iter::IntoIterator;
use std::slice::{Iter, IterMut};

use uuid::Uuid;

use super::engine::{HasName, HasUuid};

/// Map UUID and name to T items.
#[derive(Debug)]
pub struct Table<T: HasName + HasUuid> {
    items: Vec<T>,
    name_map: HashMap<String, usize>,
    uuid_map: HashMap<Uuid, usize>,
}

impl<T: HasName + HasUuid> Default for Table<T> {
    fn default() -> Table<T> {
        Table {
            items: Vec::default(),
            name_map: HashMap::default(),
            uuid_map: HashMap::default(),
        }
    }
}

impl<'a, T: HasName + HasUuid> IntoIterator for &'a mut Table<T> {
    type Item = &'a mut T;
    type IntoIter = IterMut<'a, T>;

    fn into_iter(self) -> IterMut<'a, T> {
        self.items.iter_mut()
    }
}

impl<'a, T: HasName + HasUuid> IntoIterator for &'a Table<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.items.iter()
    }
}

/// All operations are O(1).
/// The implementation does not privilege the name key over the UUID key
/// in any way. They are both treated as constants once the item has been
/// inserted. In order to rename a T item, it must be removed, renamed, and
/// reinserted under the new name.
impl<T: HasName + HasUuid> Table<T> {
    /// Empty this table of all its items, returning them in a vector.
    pub fn empty(self) -> Vec<T> {
        self.items
    }

    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

    pub fn len(&self) -> usize {
        self.items.len()
    }

    /// Returns true if map has an item corresponding to this name, else false.
    pub fn contains_name(&self, name: &str) -> bool {
        self.name_map.contains_key(name)
    }

    /// Returns true if map has an item corresponding to this uuid, else false.
    pub fn contains_uuid(&self, uuid: Uuid) -> bool {
        self.uuid_map.contains_key(&uuid)
    }

    /// Get item by name.
    pub fn get_by_name(&self, name: &str) -> Option<&T> {
        self.name_map.get(name).map(|index| &self.items[*index])
    }

    /// Get item by uuid.
    pub fn get_by_uuid(&self, uuid: Uuid) -> Option<&T> {
        self.uuid_map
            .get(&uuid)
            .map(|index| &self.items[*index])
    }

    /// Get mutable item by name.
    pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
        if let Some(index) = self.name_map.get(name) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Get mutable item by uuid.
    pub fn get_mut_by_uuid(&mut self, uuid: Uuid) -> Option<&mut T> {
        if let Some(index) = self.uuid_map.get(&uuid) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Removes the item corresponding to name if there is one.
    pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
        if let Some(index) = self.name_map.remove(name) {
            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items
                                     .last()
                                     .expect("name_map is non-empty <-> items is non-empty");
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.uuid_map.remove(&item.uuid());

            // Remove the name again, in case there is only one item.
            self.name_map.remove(name);

            Some(item)
        } else {
            None
        }
    }

    /// Removes the item corresponding to the uuid if there is one.
    pub fn remove_by_uuid(&mut self, uuid: Uuid) -> Option<T> {
        if let Some(index) = self.uuid_map.remove(&uuid) {
            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items
                                     .last()
                                     .expect("uuid_map is non-empty <-> items is non-empty");
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.name_map.remove(item.name());

            // Remove the uuid again, in case there is only one item.
            self.uuid_map.remove(&uuid);

            Some(item)
        } else {
            None
        }
    }

    /// Inserts an item for given uuid and name.
    /// Returns a list of the items displaced, which may be empty if no items
    /// are displaced, have one entry if the uuid and the name map to the same
    /// item, and may have two entries if the uuid and the name map to
    /// different items.
    pub fn insert(&mut self, item: T) -> Vec<T> {
        let name_item = self.remove_by_name(item.name());
        let uuid_item = self.remove_by_uuid(item.uuid());

        let future_last_index = self.items.len();
        self.name_map
            .insert(item.name().into(), future_last_index);
        self.uuid_map.insert(item.uuid(), future_last_index);

        self.items.push(item);

        match (name_item, uuid_item) {
            (None, None) => vec![],
            (None, Some(uuid_item)) => vec![uuid_item],
            (Some(name_item), None) => vec![name_item],
            (Some(name_item), Some(uuid_item)) => vec![name_item, uuid_item],
        }
    }
}

#[cfg(test)]
mod tests {

    use rand;
    use uuid::Uuid;

    use super::super::engine::{HasName, HasUuid};

    use super::Table;

    #[derive(Debug)]
    struct TestThing {
        name: String,
        uuid: Uuid,
        stuff: u32,
    }

    // A global invariant checker for the table.
    // Verifies proper relationship between internal data structures.
    fn table_invariant<T>(table: &Table<T>) -> ()
        where T: HasName + HasUuid
    {
        let ref items = table.items;
        let ref name_map = table.name_map;
        let ref uuid_map = table.uuid_map;
        for i in 0..items.len() {
            let name = items[i].name();
            let uuid = items[i].uuid();
            assert_eq!(name_map.get(name).unwrap(), &i);
            assert_eq!(uuid_map.get(&uuid).unwrap(), &i);
        }

        for name in name_map.keys() {
            let index = name_map.get(name).unwrap();
            assert_eq!(items[*index].name(), name);
        }

        for &uuid in uuid_map.keys() {
            let index = uuid_map.get(&uuid).unwrap();
            assert_eq!(items[*index].uuid(), uuid);
        }

    }

    impl TestThing {
        pub fn new(name: &str, uuid: Uuid) -> TestThing {
            TestThing {
                name: name.to_owned(),
                uuid: uuid.clone(),
                stuff: rand::random::<u32>(),
            }
        }
    }

    impl HasUuid for TestThing {
        fn uuid(&self) -> Uuid {
            self.uuid
        }
    }

    impl HasName for TestThing {
        fn name(&self) -> &str {
            &self.name
        }
    }

    #[test]
    /// Remove a test object by its uuid.
    /// Mutate the removed test object.
    /// Verify that the table is now empty and that removing by name yields
    /// no result.
    fn remove_existing_item() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        t.insert(TestThing::new(&name, uuid));
        table_invariant(&t);

        assert!(t.get_by_name(&name).is_some());
        assert!(t.get_by_uuid(uuid).is_some());
        let thing = t.remove_by_uuid(uuid);
        table_invariant(&t);
        assert!(thing.is_some());
        let mut thing = thing.unwrap();
        thing.stuff = 0;
        assert!(t.is_empty());
        assert!(t.remove_by_name(&name).is_none());
        table_invariant(&t);

        assert!(t.get_by_name(&name).is_none());
        assert!(t.get_by_uuid(uuid).is_none());
    }

    #[test]
    /// Insert a thing and then insert another thing with same keys.
    /// The previously inserted thing should be returned.
    /// You can't insert the identical thing, because that would be a move.
    /// This is good, because then you can't have a thing that is both in
    /// the table and not in the table.
    fn insert_same_keys() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;
        let displaced = t.insert(thing);
        table_invariant(&t);

        // There was nothing previously, so displaced must be empty.
        assert!(displaced.is_empty());

        // t now contains the inserted thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key);

        // Add another thing with the same keys.
        let thing2 = TestThing::new(&name, uuid);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // It has displaced the old thing.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);

        // But it contains a thing with the same keys.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }

    #[test]
    /// Insert a thing and then insert another thing with the same name.
    /// The previously inserted thing should be returned.
    fn insert_same_name() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;

        // There was nothing in the table before, so displaced is empty.
        let displaced = t.insert(thing);
        table_invariant(&t);
        assert!(displaced.is_empty());

        // t now contains thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));

        // Insert new item with different UUID.
        let uuid2 = Uuid::new_v4();
        let thing2 = TestThing::new(&name, uuid2);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // The items displaced consist exactly of the first item.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);
        assert_eq!(displaced_item.stuff, thing_key);

        // The table contains the new item and has no memory of the old.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid2));
        assert!(!t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid2).unwrap().stuff, thing_key2);
        assert_eq!(t.get_by_name(&name).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }
}
<commit_msg>Test table for duplicate uuid insertions<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashMap;
use std::iter::IntoIterator;
use std::slice::{Iter, IterMut};

use uuid::Uuid;

use super::engine::{HasName, HasUuid};

/// Map UUID and name to T items.
#[derive(Debug)]
pub struct Table<T: HasName + HasUuid> {
    items: Vec<T>,
    name_map: HashMap<String, usize>,
    uuid_map: HashMap<Uuid, usize>,
}

impl<T: HasName + HasUuid> Default for Table<T> {
    fn default() -> Table<T> {
        Table {
            items: Vec::default(),
            name_map: HashMap::default(),
            uuid_map: HashMap::default(),
        }
    }
}

impl<'a, T: HasName + HasUuid> IntoIterator for &'a mut Table<T> {
    type Item = &'a mut T;
    type IntoIter = IterMut<'a, T>;

    fn into_iter(self) -> IterMut<'a, T> {
        self.items.iter_mut()
    }
}

impl<'a, T: HasName + HasUuid> IntoIterator for &'a Table<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.items.iter()
    }
}

/// All operations are O(1).
/// The implementation does not privilege the name key over the UUID key
/// in any way. They are both treated as constants once the item has been
/// inserted. In order to rename a T item, it must be removed, renamed, and
/// reinserted under the new name.
impl<T: HasName + HasUuid> Table<T> {
    /// Empty this table of all its items, returning them in a vector.
    pub fn empty(self) -> Vec<T> {
        self.items
    }

    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

    pub fn len(&self) -> usize {
        self.items.len()
    }

    /// Returns true if map has an item corresponding to this name, else false.
    pub fn contains_name(&self, name: &str) -> bool {
        self.name_map.contains_key(name)
    }

    /// Returns true if map has an item corresponding to this uuid, else false.
    pub fn contains_uuid(&self, uuid: Uuid) -> bool {
        self.uuid_map.contains_key(&uuid)
    }

    /// Get item by name.
    pub fn get_by_name(&self, name: &str) -> Option<&T> {
        self.name_map.get(name).map(|index| &self.items[*index])
    }

    /// Get item by uuid.
    pub fn get_by_uuid(&self, uuid: Uuid) -> Option<&T> {
        self.uuid_map
            .get(&uuid)
            .map(|index| &self.items[*index])
    }

    /// Get mutable item by name.
    pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
        if let Some(index) = self.name_map.get(name) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Get mutable item by uuid.
    pub fn get_mut_by_uuid(&mut self, uuid: Uuid) -> Option<&mut T> {
        if let Some(index) = self.uuid_map.get(&uuid) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Removes the item corresponding to name if there is one.
    pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
        if let Some(index) = self.name_map.remove(name) {
            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items
                                     .last()
                                     .expect("name_map is non-empty <-> items is non-empty");
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.uuid_map.remove(&item.uuid());

            // Remove the name again, in case there is only one item.
            self.name_map.remove(name);

            Some(item)
        } else {
            None
        }
    }

    /// Removes the item corresponding to the uuid if there is one.
    pub fn remove_by_uuid(&mut self, uuid: Uuid) -> Option<T> {
        if let Some(index) = self.uuid_map.remove(&uuid) {
            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items
                                     .last()
                                     .expect("uuid_map is non-empty <-> items is non-empty");
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.name_map.remove(item.name());

            // Remove the uuid again, in case there is only one item.
            self.uuid_map.remove(&uuid);

            Some(item)
        } else {
            None
        }
    }

    /// Inserts an item for given uuid and name.
    /// Returns a list of the items displaced, which may be empty if no items
    /// are displaced, have one entry if the uuid and the name map to the same
    /// item, and may have two entries if the uuid and the name map to
    /// different items.
    pub fn insert(&mut self, item: T) -> Vec<T> {
        let name_item = self.remove_by_name(item.name());
        let uuid_item = self.remove_by_uuid(item.uuid());

        let future_last_index = self.items.len();
        self.name_map
            .insert(item.name().into(), future_last_index);
        self.uuid_map.insert(item.uuid(), future_last_index);

        self.items.push(item);

        match (name_item, uuid_item) {
            (None, None) => vec![],
            (None, Some(uuid_item)) => vec![uuid_item],
            (Some(name_item), None) => vec![name_item],
            (Some(name_item), Some(uuid_item)) => vec![name_item, uuid_item],
        }
    }
}

#[cfg(test)]
mod tests {

    use rand;
    use uuid::Uuid;

    use super::super::engine::{HasName, HasUuid};

    use super::Table;

    #[derive(Debug)]
    struct TestThing {
        name: String,
        uuid: Uuid,
        stuff: u32,
    }

    // A global invariant checker for the table.
    // Verifies proper relationship between internal data structures.
    fn table_invariant<T>(table: &Table<T>) -> ()
        where T: HasName + HasUuid
    {
        let ref items = table.items;
        let ref name_map = table.name_map;
        let ref uuid_map = table.uuid_map;
        for i in 0..items.len() {
            let name = items[i].name();
            let uuid = items[i].uuid();
            assert_eq!(name_map.get(name).unwrap(), &i);
            assert_eq!(uuid_map.get(&uuid).unwrap(), &i);
        }

        for name in name_map.keys() {
            let index = name_map.get(name).unwrap();
            assert_eq!(items[*index].name(), name);
        }

        for &uuid in uuid_map.keys() {
            let index = uuid_map.get(&uuid).unwrap();
            assert_eq!(items[*index].uuid(), uuid);
        }

    }

    impl TestThing {
        pub fn new(name: &str, uuid: Uuid) -> TestThing {
            TestThing {
                name: name.to_owned(),
                uuid: uuid.clone(),
                stuff: rand::random::<u32>(),
            }
        }
    }

    impl HasUuid for TestThing {
        fn uuid(&self) -> Uuid {
            self.uuid
        }
    }

    impl HasName for TestThing {
        fn name(&self) -> &str {
            &self.name
        }
    }

    #[test]
    /// Remove a test object by its uuid.
    /// Mutate the removed test object.
    /// Verify that the table is now empty and that removing by name yields
    /// no result.
    fn remove_existing_item() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        t.insert(TestThing::new(&name, uuid));
        table_invariant(&t);

        assert!(t.get_by_name(&name).is_some());
        assert!(t.get_by_uuid(uuid).is_some());
        let thing = t.remove_by_uuid(uuid);
        table_invariant(&t);
        assert!(thing.is_some());
        let mut thing = thing.unwrap();
        thing.stuff = 0;
        assert!(t.is_empty());
        assert!(t.remove_by_name(&name).is_none());
        table_invariant(&t);

        assert!(t.get_by_name(&name).is_none());
        assert!(t.get_by_uuid(uuid).is_none());
    }

    #[test]
    /// Insert a thing and then insert another thing with same keys.
    /// The previously inserted thing should be returned.
    /// You can't insert the identical thing, because that would be a move.
    /// This is good, because then you can't have a thing that is both in
    /// the table and not in the table.
    fn insert_same_keys() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;
        let displaced = t.insert(thing);
        table_invariant(&t);

        // There was nothing previously, so displaced must be empty.
        assert!(displaced.is_empty());

        // t now contains the inserted thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key);

        // Add another thing with the same keys.
        let thing2 = TestThing::new(&name, uuid);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // It has displaced the old thing.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);

        // But it contains a thing with the same keys.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }

    #[test]
    /// Insert a thing and then insert another thing with the same name.
    /// The previously inserted thing should be returned.
    fn insert_same_name() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;

        // There was nothing in the table before, so displaced is empty.
        let displaced = t.insert(thing);
        table_invariant(&t);
        assert!(displaced.is_empty());

        // t now contains thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));

        // Insert new item with different UUID.
        let uuid2 = Uuid::new_v4();
        let thing2 = TestThing::new(&name, uuid2);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // The items displaced consist exactly of the first item.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);
        assert_eq!(displaced_item.stuff, thing_key);

        // The table contains the new item and has no memory of the old.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid2));
        assert!(!t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid2).unwrap().stuff, thing_key2);
        assert_eq!(t.get_by_name(&name).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }

    #[test]
    /// Insert a thing and then insert another thing with the same uuid.
    /// The previously inserted thing should be returned.
    fn insert_same_uuid() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;

        // There was nothing in the table before, so displaced is empty.
        let displaced = t.insert(thing);
        table_invariant(&t);
        assert!(displaced.is_empty());

        // t now contains thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));

        // Insert new item with different UUID.
        let name2 = "name2";
        let thing2 = TestThing::new(&name2, uuid);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // The items displaced consist exactly of the first item.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);
        assert_eq!(displaced_item.stuff, thing_key);

        // The table contains the new item and has no memory of the old.
        assert!(t.contains_uuid(uuid));
        assert!(t.contains_name(name2));
        assert!(!t.contains_name(name));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key2);
        assert_eq!(t.get_by_name(&name2).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }
}
<|endoftext|>


<commit_before>#![stable(feature = "futures_api", since = "1.36.0")]

use crate::ops::Try;
use crate::result::Result;

/// Indicates whether a value is available or if the current task has been
/// scheduled to receive a wakeup instead.
#[must_use = "this `Poll` may be a `Pending` variant, which should be handled"]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[stable(feature = "futures_api", since = "1.36.0")]
pub enum Poll<T> {
    /// Represents that a value is immediately ready.
    #[stable(feature = "futures_api", since = "1.36.0")]
    Ready(
        #[stable(feature = "futures_api", since = "1.36.0")]
        T
    ),

    /// Represents that a value is not ready yet.
    ///
    /// When a function returns `Pending`, the function *must* also
    /// ensure that the current task is scheduled to be awoken when
    /// progress can be made.
    #[stable(feature = "futures_api", since = "1.36.0")]
    Pending,
}

impl<T> Poll<T> {
    /// Changes the ready value of this `Poll` with the closure provided.
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn map<U, F>(self, f: F) -> Poll<U>
        where F: FnOnce(T) -> U
    {
        match self {
            Poll::Ready(t) => Poll::Ready(f(t)),
            Poll::Pending => Poll::Pending,
        }
    }

    /// Returns `true` if this is `Poll::Ready`
    #[inline]
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn is_ready(&self) -> bool {
        match *self {
            Poll::Ready(_) => true,
            Poll::Pending => false,
        }
    }

    /// Returns `true` if this is `Poll::Pending`
    #[inline]
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn is_pending(&self) -> bool {
        !self.is_ready()
    }
}

impl<T, E> Poll<Result<T, E>> {
    /// Changes the success value of this `Poll` with the closure provided.
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn map_ok<U, F>(self, f: F) -> Poll<Result<U, E>>
        where F: FnOnce(T) -> U
    {
        match self {
            Poll::Ready(Ok(t)) => Poll::Ready(Ok(f(t))),
            Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
            Poll::Pending => Poll::Pending,
        }
    }

    /// Changes the error value of this `Poll` with the closure provided.
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn map_err<U, F>(self, f: F) -> Poll<Result<T, U>>
        where F: FnOnce(E) -> U
    {
        match self {
            Poll::Ready(Ok(t)) => Poll::Ready(Ok(t)),
            Poll::Ready(Err(e)) => Poll::Ready(Err(f(e))),
            Poll::Pending => Poll::Pending,
        }
    }
}

#[stable(feature = "futures_api", since = "1.36.0")]
impl<T> From<T> for Poll<T> {
    fn from(t: T) -> Poll<T> {
        Poll::Ready(t)
    }
}

#[stable(feature = "futures_api", since = "1.36.0")]
impl<T, E> Try for Poll<Result<T, E>> {
    type Ok = Poll<T>;
    type Error = E;

    #[inline]
    fn into_result(self) -> Result<Self::Ok, Self::Error> {
        match self {
            Poll::Ready(Ok(x)) => Ok(Poll::Ready(x)),
            Poll::Ready(Err(e)) => Err(e),
            Poll::Pending => Ok(Poll::Pending),
        }
    }

    #[inline]
    fn from_error(e: Self::Error) -> Self {
        Poll::Ready(Err(e))
    }

    #[inline]
    fn from_ok(x: Self::Ok) -> Self {
        x.map(Ok)
    }
}

#[stable(feature = "futures_api", since = "1.36.0")]
impl<T, E> Try for Poll<Option<Result<T, E>>> {
    type Ok = Poll<Option<T>>;
    type Error = E;

    #[inline]
    fn into_result(self) -> Result<Self::Ok, Self::Error> {
        match self {
            Poll::Ready(Some(Ok(x))) => Ok(Poll::Ready(Some(x))),
            Poll::Ready(Some(Err(e))) => Err(e),
            Poll::Ready(None) => Ok(Poll::Ready(None)),
            Poll::Pending => Ok(Poll::Pending),
        }
    }

    #[inline]
    fn from_error(e: Self::Error) -> Self {
        Poll::Ready(Some(Err(e)))
    }

    #[inline]
    fn from_ok(x: Self::Ok) -> Self {
        x.map(|x| x.map(Ok))
    }
}
<commit_msg>Provide map_ok and map_err method for Poll<Option<Result<T, E>>><commit_after>#![stable(feature = "futures_api", since = "1.36.0")]

use crate::ops::Try;
use crate::result::Result;

/// Indicates whether a value is available or if the current task has been
/// scheduled to receive a wakeup instead.
#[must_use = "this `Poll` may be a `Pending` variant, which should be handled"]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[stable(feature = "futures_api", since = "1.36.0")]
pub enum Poll<T> {
    /// Represents that a value is immediately ready.
    #[stable(feature = "futures_api", since = "1.36.0")]
    Ready(
        #[stable(feature = "futures_api", since = "1.36.0")]
        T
    ),

    /// Represents that a value is not ready yet.
    ///
    /// When a function returns `Pending`, the function *must* also
    /// ensure that the current task is scheduled to be awoken when
    /// progress can be made.
    #[stable(feature = "futures_api", since = "1.36.0")]
    Pending,
}

impl<T> Poll<T> {
    /// Changes the ready value of this `Poll` with the closure provided.
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn map<U, F>(self, f: F) -> Poll<U>
        where F: FnOnce(T) -> U
    {
        match self {
            Poll::Ready(t) => Poll::Ready(f(t)),
            Poll::Pending => Poll::Pending,
        }
    }

    /// Returns `true` if this is `Poll::Ready`
    #[inline]
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn is_ready(&self) -> bool {
        match *self {
            Poll::Ready(_) => true,
            Poll::Pending => false,
        }
    }

    /// Returns `true` if this is `Poll::Pending`
    #[inline]
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn is_pending(&self) -> bool {
        !self.is_ready()
    }
}

impl<T, E> Poll<Result<T, E>> {
    /// Changes the success value of this `Poll` with the closure provided.
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn map_ok<U, F>(self, f: F) -> Poll<Result<U, E>>
        where F: FnOnce(T) -> U
    {
        match self {
            Poll::Ready(Ok(t)) => Poll::Ready(Ok(f(t))),
            Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
            Poll::Pending => Poll::Pending,
        }
    }

    /// Changes the error value of this `Poll` with the closure provided.
    #[stable(feature = "futures_api", since = "1.36.0")]
    pub fn map_err<U, F>(self, f: F) -> Poll<Result<T, U>>
        where F: FnOnce(E) -> U
    {
        match self {
            Poll::Ready(Ok(t)) => Poll::Ready(Ok(t)),
            Poll::Ready(Err(e)) => Poll::Ready(Err(f(e))),
            Poll::Pending => Poll::Pending,
        }
    }
}

impl<T, E> Poll<Option<Result<T, E>>> {
    /// Changes the success value of this `Poll` with the closure provided.
    #[unstable(feature = "poll_map", issue = "63514")]
    pub fn map_ok<U, F>(self, f: F) -> Poll<Option<Result<U, E>>>
        where F: FnOnce(T) -> U
    {
        match self {
            Poll::Ready(Some(Ok(t))) => Poll::Ready(Some(Ok(f(t)))),
            Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
            Poll::Ready(None) => Poll::Ready(None),
            Poll::Pending => Poll::Pending,
        }
    }

    /// Changes the error value of this `Poll` with the closure provided.
    #[unstable(feature = "poll_map", issue = "63514")]
    pub fn map_err<U, F>(self, f: F) -> Poll<Option<Result<T, U>>>
        where F: FnOnce(E) -> U
    {
        match self {
            Poll::Ready(Some(Ok(t))) => Poll::Ready(Some(Ok(t))),
            Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(f(e)))),
            Poll::Ready(None) => Poll::Ready(None),
            Poll::Pending => Poll::Pending,
        }
    }
}

#[stable(feature = "futures_api", since = "1.36.0")]
impl<T> From<T> for Poll<T> {
    fn from(t: T) -> Poll<T> {
        Poll::Ready(t)
    }
}

#[stable(feature = "futures_api", since = "1.36.0")]
impl<T, E> Try for Poll<Result<T, E>> {
    type Ok = Poll<T>;
    type Error = E;

    #[inline]
    fn into_result(self) -> Result<Self::Ok, Self::Error> {
        match self {
            Poll::Ready(Ok(x)) => Ok(Poll::Ready(x)),
            Poll::Ready(Err(e)) => Err(e),
            Poll::Pending => Ok(Poll::Pending),
        }
    }

    #[inline]
    fn from_error(e: Self::Error) -> Self {
        Poll::Ready(Err(e))
    }

    #[inline]
    fn from_ok(x: Self::Ok) -> Self {
        x.map(Ok)
    }
}

#[stable(feature = "futures_api", since = "1.36.0")]
impl<T, E> Try for Poll<Option<Result<T, E>>> {
    type Ok = Poll<Option<T>>;
    type Error = E;

    #[inline]
    fn into_result(self) -> Result<Self::Ok, Self::Error> {
        match self {
            Poll::Ready(Some(Ok(x))) => Ok(Poll::Ready(Some(x))),
            Poll::Ready(Some(Err(e))) => Err(e),
            Poll::Ready(None) => Ok(Poll::Ready(None)),
            Poll::Pending => Ok(Poll::Pending),
        }
    }

    #[inline]
    fn from_error(e: Self::Error) -> Self {
        Poll::Ready(Some(Err(e)))
    }

    #[inline]
    fn from_ok(x: Self::Ok) -> Self {
        x.map(|x| x.map(Ok))
    }
}
<|endoftext|>