<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", "*.cljc", "*.cljs", "*.cljx"]),
    ("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",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("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"]),
    ("nim", &["*.nim"]),
    ("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"]),
    ("swift", &["*.swift"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("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>Add SystemVerilog (SV) type<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", "*.cljc", "*.cljs", "*.cljx"]),
    ("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",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("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"]),
    ("nim", &["*.nim"]),
    ("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"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("swift", &["*.swift"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("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 ::Client;
use ql2::types;
use ql2::proto::{Term, Datum, Term_TermType as TermType,
    //Term_AssocPair as TermPair,
    Datum_DatumType as DatumType,
    //Datum_AssocPair as DatumPair,
};
//use protobuf::repeated::RepeatedField;

pub trait IntoArray {
    fn into_array(self) -> types::Array;
}

pub trait IntoBool {
    fn into_bool(self) -> types::Bool;
}

pub trait IntoDb {
    fn into_db(self) -> types::Db;
}

pub trait IntoFunction {
    fn into_function(self) -> types::Function;
}

pub trait IntoGroupedData {
    fn into_grouped_data(self) -> types::GroupedData;
}

pub trait IntoGroupedStream {
    fn into_grouped_stream(self) -> types::GroupedStream;
}

pub trait IntoMaxVal {
    fn into_max_val(self) -> types::MaxVal;
}

pub trait IntoMinVal {
    fn into_min_val(self) -> types::MinVal;
}

pub trait IntoNull {
    fn into_null(self) -> types::Null;
}

pub trait IntoNumber {
    fn into_number(self) -> types::Number;
}

pub trait IntoObject {
    fn into_object(self) -> types::Object;
}

pub trait IntoBinary {
    fn into_binary(self) -> types::Binary;
}

pub trait IntoGeometry {
    fn into_geometry(self) -> types::Geometry;
}

pub trait IntoTime {
    fn into_time(self) -> types::Time;
}

pub trait IntoStream {
    fn into_stream(self) -> types::Stream;
}

pub trait IntoString {
    fn into_string(self) -> types::String;
}

impl IntoString for String {
    fn into_string(self) -> types::String {
        let mut datum = Datum::new();
        datum.set_field_type(DatumType::R_STR);
        datum.set_r_str(self);
        let mut term = Term::new();
        term.set_field_type(TermType::DATUM);
        term.set_datum(datum);
        term.into()
    }
}

impl<'a> IntoString for &'a String {
    fn into_string(self) -> types::String {
        let string = self.to_string();
        string.into_string()
    }
}

impl<'a> IntoString for &'a str {
    fn into_string(self) -> types::String {
        let string = self.to_string();
        string.into_string()
    }
}

impl<O> IntoString for Client<types::String, O>
{
    fn into_string(self) -> types::String {
        self.cmd.0
    }
}

pub trait IntoTable {
    fn into_table(self) -> types::Table;
}

pub trait IntoTableSlice {
    fn into_table_slice(self) -> types::TableSlice;
}

pub trait IntoObjectSelection {
    fn into_object_selection(self) -> types::ObjectSelection;
}

pub trait IntoArraySelection {
    fn into_array_selection(self) -> types::ArraySelection;
}

pub trait IntoStreamSelection {
    fn into_stream_selection(self) -> types::StreamSelection;
}

pub trait PrimaryKey {
    fn into_term(self) -> Term;
}

pub trait SecondaryKey {
    fn into_term(self) -> Term;
}
<commit_msg>Add sequence trait<commit_after>use ::Client;
use ql2::types;
use ql2::proto::{Term, Datum, Term_TermType as TermType,
    //Term_AssocPair as TermPair,
    Datum_DatumType as DatumType,
    //Datum_AssocPair as DatumPair,
};
//use protobuf::repeated::RepeatedField;

pub trait IntoArray {
    fn into_array(self) -> types::Array;
}

pub trait IntoBool {
    fn into_bool(self) -> types::Bool;
}

pub trait IntoDb {
    fn into_db(self) -> types::Db;
}

pub trait IntoFunction {
    fn into_function(self) -> types::Function;
}

pub trait IntoGroupedData {
    fn into_grouped_data(self) -> types::GroupedData;
}

pub trait IntoGroupedStream {
    fn into_grouped_stream(self) -> types::GroupedStream;
}

pub trait IntoMaxVal {
    fn into_max_val(self) -> types::MaxVal;
}

pub trait IntoMinVal {
    fn into_min_val(self) -> types::MinVal;
}

pub trait IntoNull {
    fn into_null(self) -> types::Null;
}

pub trait IntoNumber {
    fn into_number(self) -> types::Number;
}

pub trait IntoObject {
    fn into_object(self) -> types::Object;
}

pub trait IntoBinary {
    fn into_binary(self) -> types::Binary;
}

pub trait IntoGeometry {
    fn into_geometry(self) -> types::Geometry;
}

pub trait IntoTime {
    fn into_time(self) -> types::Time;
}

pub trait IntoStream {
    fn into_stream(self) -> types::Stream;
}

pub trait IntoString {
    fn into_string(self) -> types::String;
}

impl IntoString for String {
    fn into_string(self) -> types::String {
        let mut datum = Datum::new();
        datum.set_field_type(DatumType::R_STR);
        datum.set_r_str(self);
        let mut term = Term::new();
        term.set_field_type(TermType::DATUM);
        term.set_datum(datum);
        term.into()
    }
}

impl<'a> IntoString for &'a String {
    fn into_string(self) -> types::String {
        let string = self.to_string();
        string.into_string()
    }
}

impl<'a> IntoString for &'a str {
    fn into_string(self) -> types::String {
        let string = self.to_string();
        string.into_string()
    }
}

impl<O> IntoString for Client<types::String, O>
{
    fn into_string(self) -> types::String {
        self.cmd.0
    }
}

pub trait IntoTable {
    fn into_table(self) -> types::Table;
}

pub trait IntoTableSlice {
    fn into_table_slice(self) -> types::TableSlice;
}

pub trait IntoObjectSelection {
    fn into_object_selection(self) -> types::ObjectSelection;
}

pub trait IntoArraySelection {
    fn into_array_selection(self) -> types::ArraySelection;
}

pub trait IntoStreamSelection {
    fn into_stream_selection(self) -> types::StreamSelection;
}

pub trait PrimaryKey {
    fn into_term(self) -> Term;
}

pub trait SecondaryKey {
    fn into_term(self) -> Term;
}

pub trait Sequence {
    fn into_term(self) -> Term;
}
<|endoftext|>


<commit_before>use c_types;
use c_ares_sys;

use types::AddressFamily;
use std::ffi::CStr;
use std::mem;
use std::os::raw::c_int;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use std::str;

// Convert an address family into a more strongly typed AddressFamily.
pub fn address_family(family: c_int) -> Option<AddressFamily> {
    match family {
        c_types::AF_INET => Some(AddressFamily::INET),
        c_types::AF_INET6 => Some(AddressFamily::INET6),
        c_types::AF_UNSPEC => Some(AddressFamily::UNSPEC),
        _ => None,
    }
}

// Get an in_addr from an Ipv4Addr.
#[cfg(unix)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> c_types::in_addr {
    c_types::in_addr {
        s_addr: u32::from(*ipv4).to_be(),
    }
}

#[cfg(windows)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> c_types::in_addr {
    let octets = ipv4.octets();
    let mut in_addr: c_types::in_addr = unsafe { mem::uninitialized() };
    {
        let bytes = unsafe { in_addr.S_un.S_un_b_mut() };
        bytes.s_b1 = octets[0];
        bytes.s_b2 = octets[1];
        bytes.s_b3 = octets[2];
        bytes.s_b4 = octets[3];
    }
    in_addr
}

// Get an Ipv4Addr from an in_addr.
#[cfg(unix)]
pub fn ipv4_from_in_addr(in_addr: &c_types::in_addr) -> Ipv4Addr {
    Ipv4Addr::from(u32::from_be(in_addr.s_addr))
}

#[cfg(windows)]
pub fn ipv4_from_in_addr(in_addr: &c_types::in_addr) -> Ipv4Addr {
    let bytes = unsafe { in_addr.S_un.S_un_b() };
    Ipv4Addr::new(bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4)
}

// Get an in6_addr from an Ipv6Addr.
pub fn ipv6_as_in6_addr(ipv6: &Ipv6Addr) -> c_types::in6_addr {
    let octets = ipv6.octets();
    let mut in6_addr: c_types::in6_addr = unsafe { mem::uninitialized() };
    {
        let bytes = unsafe { in6_addr.u.Byte_mut() };
        bytes.copy_from_slice(&octets);
    }
    in6_addr
}

// Get a sockaddr_in from a SocketAddrV4.
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
          target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv4_as_sockaddr_in(sock_v4: &SocketAddrV4) -> c_types::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    c_types::sockaddr_in {
        sin_len: mem::size_of::<c_types::sockaddr_in>() as u8,
        sin_family: c_types::AF_INET as c_types::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
              target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
              target_os = "bitrig")))]
pub fn socket_addrv4_as_sockaddr_in(sock_v4: &SocketAddrV4) -> c_types::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    c_types::sockaddr_in {
        sin_family: c_types::AF_INET as c_types::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

// Get a sockaddr_in6 from a SocketAddrV6.
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
          target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv6_as_sockaddr_in6(sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_len: mem::size_of::<c_types::sockaddr_in6>() as u8,
        sin6_family: c_types::AF_INET6 as c_types::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

#[cfg(all(unix,
          not(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
                  target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
                  target_os = "bitrig"))))]
pub fn socket_addrv6_as_sockaddr_in6(sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_family: c_types::AF_INET6 as c_types::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

#[cfg(windows)]
pub fn socket_addrv6_as_sockaddr_in6(sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let mut sockaddr_in6: c_types::sockaddr_in6 = unsafe { mem::uninitialized() };
    sockaddr_in6.sin6_family = c_types::AF_INET6 as c_types::sa_family_t;
    sockaddr_in6.sin6_port = sock_v6.port().to_be();
    sockaddr_in6.sin6_addr = ipv6_as_in6_addr(sock_v6.ip());
    sockaddr_in6.sin6_flowinfo = sock_v6.flowinfo();
    {
        let scope_id = unsafe { sockaddr_in6.u.sin6_scope_id_mut() };
        *scope_id = sock_v6.scope_id();
    }
    sockaddr_in6
}

/// Get the version number of the underlying `c-ares` library.
///
/// The version is returned as both a string and an integer.  The integer is
/// built up as 24bit number, with 8 separate bits used for major number, minor
/// number and patch number.  For example, the version string "1.2.3" is
/// returned as hexadecimal number 0x010203 (decimal 66051).
pub fn version() -> (&'static str, u32) {
    let mut int_version: c_int = 0;
    let str_version = unsafe {
        let ptr = c_ares_sys::ares_version(&mut int_version);
        let buf = CStr::from_ptr(ptr).to_bytes();
        str::from_utf8_unchecked(buf)
    };
    (str_version, int_version as u32)
}
<commit_msg>Fix non-windows build<commit_after>use c_types;
use c_ares_sys;

use types::AddressFamily;
use std::ffi::CStr;
use std::mem;
use std::os::raw::c_int;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use std::str;

// Convert an address family into a more strongly typed AddressFamily.
pub fn address_family(family: c_int) -> Option<AddressFamily> {
    match family {
        c_types::AF_INET => Some(AddressFamily::INET),
        c_types::AF_INET6 => Some(AddressFamily::INET6),
        c_types::AF_UNSPEC => Some(AddressFamily::UNSPEC),
        _ => None,
    }
}

// Get an in_addr from an Ipv4Addr.
#[cfg(unix)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> c_types::in_addr {
    c_types::in_addr {
        s_addr: u32::from(*ipv4).to_be(),
    }
}

#[cfg(windows)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> c_types::in_addr {
    let octets = ipv4.octets();
    let mut in_addr: c_types::in_addr = unsafe { mem::uninitialized() };
    {
        let bytes = unsafe { in_addr.S_un.S_un_b_mut() };
        bytes.s_b1 = octets[0];
        bytes.s_b2 = octets[1];
        bytes.s_b3 = octets[2];
        bytes.s_b4 = octets[3];
    }
    in_addr
}

// Get an Ipv4Addr from an in_addr.
#[cfg(unix)]
pub fn ipv4_from_in_addr(in_addr: &c_types::in_addr) -> Ipv4Addr {
    Ipv4Addr::from(u32::from_be(in_addr.s_addr))
}

#[cfg(windows)]
pub fn ipv4_from_in_addr(in_addr: &c_types::in_addr) -> Ipv4Addr {
    let bytes = unsafe { in_addr.S_un.S_un_b() };
    Ipv4Addr::new(bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4)
}

// Get an in6_addr from an Ipv6Addr.
#[cfg(unix)]
pub fn ipv6_as_in6_addr(ipv6: &Ipv6Addr) -> c_types::in6_addr {
    let octets = ipv6.octets();
    let mut in6_addr: c_types::in6_addr = unsafe { mem::uninitialized() };
    in6_addr.s6_addr.copy_from_slice(&octets);
    in6_addr
}

#[cfg(windows)]
pub fn ipv6_as_in6_addr(ipv6: &Ipv6Addr) -> c_types::in6_addr {
    let octets = ipv6.octets();
    let mut in6_addr: c_types::in6_addr = unsafe { mem::uninitialized() };
    {
        let bytes = unsafe { in6_addr.u.Byte_mut() };
        bytes.copy_from_slice(&octets);
    }
    in6_addr
}

// Get a sockaddr_in from a SocketAddrV4.
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
          target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv4_as_sockaddr_in(sock_v4: &SocketAddrV4) -> c_types::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    c_types::sockaddr_in {
        sin_len: mem::size_of::<c_types::sockaddr_in>() as u8,
        sin_family: c_types::AF_INET as c_types::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
              target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
              target_os = "bitrig")))]
pub fn socket_addrv4_as_sockaddr_in(sock_v4: &SocketAddrV4) -> c_types::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    c_types::sockaddr_in {
        sin_family: c_types::AF_INET as c_types::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

// Get a sockaddr_in6 from a SocketAddrV6.
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
          target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv6_as_sockaddr_in6(sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_len: mem::size_of::<c_types::sockaddr_in6>() as u8,
        sin6_family: c_types::AF_INET6 as c_types::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

#[cfg(all(unix,
          not(any(target_os = "macos", target_os = "ios", target_os = "freebsd",
                  target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
                  target_os = "bitrig"))))]
pub fn socket_addrv6_as_sockaddr_in6(sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_family: c_types::AF_INET6 as c_types::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

#[cfg(windows)]
pub fn socket_addrv6_as_sockaddr_in6(sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let mut sockaddr_in6: c_types::sockaddr_in6 = unsafe { mem::uninitialized() };
    sockaddr_in6.sin6_family = c_types::AF_INET6 as c_types::sa_family_t;
    sockaddr_in6.sin6_port = sock_v6.port().to_be();
    sockaddr_in6.sin6_addr = ipv6_as_in6_addr(sock_v6.ip());
    sockaddr_in6.sin6_flowinfo = sock_v6.flowinfo();
    {
        let scope_id = unsafe { sockaddr_in6.u.sin6_scope_id_mut() };
        *scope_id = sock_v6.scope_id();
    }
    sockaddr_in6
}

/// Get the version number of the underlying `c-ares` library.
///
/// The version is returned as both a string and an integer.  The integer is
/// built up as 24bit number, with 8 separate bits used for major number, minor
/// number and patch number.  For example, the version string "1.2.3" is
/// returned as hexadecimal number 0x010203 (decimal 66051).
pub fn version() -> (&'static str, u32) {
    let mut int_version: c_int = 0;
    let str_version = unsafe {
        let ptr = c_ares_sys::ares_version(&mut int_version);
        let buf = CStr::from_ptr(ptr).to_bytes();
        str::from_utf8_unchecked(buf)
    };
    (str_version, int_version as u32)
}
<|endoftext|>


<commit_before>use std::ascii::AsciiExt;
use std::str::Chars;
use std::borrow::Cow;
use unicode_normalization::char::canonical_combining_class;
use unicode_normalization::UnicodeNormalization;
use unidecode::unidecode_char;

const VOWELS: &'static str = "aeiouyAEIOUY";
const HYPHENS: &'static str = "-\u{2010}‑‒–—―−－﹘﹣";

pub fn is_mixed_case(s: &str) -> bool {
    let mut has_lowercase = false;
    let mut has_uppercase = false;

    for c in s.chars() {
        if c.is_uppercase() {
            has_uppercase = true;
        };
        if c.is_lowercase() {
            has_lowercase = true;
        };
        if has_lowercase && has_uppercase {
            return true;
        };
    }

    false
}

pub fn is_capitalized(word: &str) -> bool {
    match word.chars().nth(0) {
        Some(c) => {
            if !c.is_uppercase() {
                return false;
            }
        }
        None => {
            return false;
        }
    }

    word.chars().skip(1).all(|c| c.is_lowercase() || !c.is_alphabetic())
}

#[inline]
pub fn is_combining(c: char) -> bool {
    canonical_combining_class(c) > 0
}

#[inline]
pub fn is_ascii_alphabetic(c: char) -> bool {
    match c {
        'a'...'z' => true,
        'A'...'Z' => true,
        _ => false,
    }
}

// Sadly necessary because string split gives "type of this value must be known"
// compilation error when passed a closure in some contexts
#[inline]
pub fn is_nonalphanumeric(c: char) -> bool {
    !c.is_alphanumeric()
}

#[inline]
pub fn lowercase_if_alpha(c: char) -> Option<char> {
    if c.is_uppercase() {
        c.to_lowercase().next()
    } else if c.is_alphabetic() {
        Some(c)
    } else {
        None
    }
}

#[inline]
pub fn transliterate<'a>(c: char) -> Chars<'a> {
    unidecode_char(c).chars()
}

#[inline]
pub fn to_ascii_letter(c: char) -> Option<char> {
    match c {
        'A'...'Z' => Some(c),
        _ => match transliterate(c).next() {
            Some(c) => c.to_uppercase().next(),
            None => None,
        },
    }
}

pub fn to_ascii(s: &str) -> Cow<str> {
    if s.is_ascii() {
        Cow::Borrowed(s)
    } else {
        let mut capitalized_any = false;

        Cow::Owned(s.chars()
                    .flat_map(transliterate)
                    .filter_map(|c| {
                        if !c.is_alphabetic() {
                            None
                        } else if c.is_uppercase() && !capitalized_any {
                            capitalized_any = true;
                            Some(c)
                        } else if c.is_lowercase() && capitalized_any {
                            Some(c)
                        } else {
                            c.to_lowercase().next()
                        }
                    })
                    .collect())
    }
}

pub fn capitalize_and_normalize(word: &str) -> String {
    let mut capitalize_next = true;

    word.chars()
        .filter_map(|c| {
            let result = if HYPHENS.contains(c) {
                Some('-')
            } else if !c.is_alphanumeric() {
                Some(c)
            } else if capitalize_next {
                c.to_uppercase().next()
            } else {
                c.to_lowercase().next()
            };

            capitalize_next = !c.is_alphanumeric() && !is_combining(c);

            result
        })
        .nfkd()
        .collect()
}

pub fn is_missing_vowels(word: &str) -> bool {
    word.chars().all(|c| !c.is_alphabetic() || (c.is_ascii() && !VOWELS.contains(c)))
}

pub fn starts_with_consonant(word: &str) -> bool {
    match word.chars().nth(0) {
        Some(c) => {
            c.is_alphabetic() && c.is_ascii() && (c == 'y' || c == 'Y' || !VOWELS.contains(c))
        }
        None => false,
    }
}

pub fn has_sequential_alphas(word: &str) -> bool {
    let mut iter = word.chars().peekable();
    loop {
        match iter.next() {
            Some(c) => {
                match iter.peek() {
                    Some(nc) => {
                        if c.is_alphabetic() && nc.is_alphabetic() {
                            return true;
                        }
                    }
                    None => {
                        break;
                    }
                }
            }
            None => {
                break;
            }
        }
    }

    false
}

#[macro_export]
macro_rules! eq_or_starts_with {
    ($a:expr, $b:expr) => { {
        let mut chars_a = $a.chars().filter_map(lowercase_if_alpha);
        let mut chars_b = $b.chars().filter_map(lowercase_if_alpha);
        let result;

        loop {
            let a = chars_a.next();
            let b = chars_b.next();

            if a.is_none() || b.is_none() {
                result = true;
                break;
            } else if a != b {
                result = false;
                break;
            }
        }

        result
    } }
}

#[macro_export]
macro_rules! eq_or_ends_with {
    ($needle:expr, $haystack:expr) => { {
        let mut n_chars = $needle.chars().rev().filter_map(lowercase_if_alpha);
        let mut h_chars = $haystack.chars().rev().filter_map(lowercase_if_alpha);
        let result;

        loop {
            let n = n_chars.next();
            let h = h_chars.next();

            if n.is_none() {
                result = true;
                break;
            } else if n != h {
                result = false;
                break;
            }
        }

        result
    } }
}
<commit_msg>Future-compatible lifetime annotation on transliterate<commit_after>use std::ascii::AsciiExt;
use std::str::Chars;
use std::borrow::Cow;
use unicode_normalization::char::canonical_combining_class;
use unicode_normalization::UnicodeNormalization;
use unidecode::unidecode_char;

const VOWELS: &'static str = "aeiouyAEIOUY";
const HYPHENS: &'static str = "-\u{2010}‑‒–—―−－﹘﹣";

pub fn is_mixed_case(s: &str) -> bool {
    let mut has_lowercase = false;
    let mut has_uppercase = false;

    for c in s.chars() {
        if c.is_uppercase() {
            has_uppercase = true;
        };
        if c.is_lowercase() {
            has_lowercase = true;
        };
        if has_lowercase && has_uppercase {
            return true;
        };
    }

    false
}

pub fn is_capitalized(word: &str) -> bool {
    match word.chars().nth(0) {
        Some(c) => {
            if !c.is_uppercase() {
                return false;
            }
        }
        None => {
            return false;
        }
    }

    word.chars().skip(1).all(|c| c.is_lowercase() || !c.is_alphabetic())
}

#[inline]
pub fn is_combining(c: char) -> bool {
    canonical_combining_class(c) > 0
}

#[inline]
pub fn is_ascii_alphabetic(c: char) -> bool {
    match c {
        'a'...'z' => true,
        'A'...'Z' => true,
        _ => false,
    }
}

// Sadly necessary because string split gives "type of this value must be known"
// compilation error when passed a closure in some contexts
#[inline]
pub fn is_nonalphanumeric(c: char) -> bool {
    !c.is_alphanumeric()
}

#[inline]
pub fn lowercase_if_alpha(c: char) -> Option<char> {
    if c.is_uppercase() {
        c.to_lowercase().next()
    } else if c.is_alphabetic() {
        Some(c)
    } else {
        None
    }
}

#[inline]
pub fn transliterate(c: char) -> Chars<'static> {
    unidecode_char(c).chars()
}

#[inline]
pub fn to_ascii_letter(c: char) -> Option<char> {
    match c {
        'A'...'Z' => Some(c),
        _ => match transliterate(c).next() {
            Some(c) => c.to_uppercase().next(),
            None => None,
        },
    }
}

pub fn to_ascii(s: &str) -> Cow<str> {
    if s.is_ascii() {
        Cow::Borrowed(s)
    } else {
        let mut capitalized_any = false;

        Cow::Owned(s.chars()
                    .flat_map(transliterate)
                    .filter_map(|c| {
                        if !c.is_alphabetic() {
                            None
                        } else if c.is_uppercase() && !capitalized_any {
                            capitalized_any = true;
                            Some(c)
                        } else if c.is_lowercase() && capitalized_any {
                            Some(c)
                        } else {
                            c.to_lowercase().next()
                        }
                    })
                    .collect())
    }
}

pub fn capitalize_and_normalize(word: &str) -> String {
    let mut capitalize_next = true;

    word.chars()
        .filter_map(|c| {
            let result = if HYPHENS.contains(c) {
                Some('-')
            } else if !c.is_alphanumeric() {
                Some(c)
            } else if capitalize_next {
                c.to_uppercase().next()
            } else {
                c.to_lowercase().next()
            };

            capitalize_next = !c.is_alphanumeric() && !is_combining(c);

            result
        })
        .nfkd()
        .collect()
}

pub fn is_missing_vowels(word: &str) -> bool {
    word.chars().all(|c| !c.is_alphabetic() || (c.is_ascii() && !VOWELS.contains(c)))
}

pub fn starts_with_consonant(word: &str) -> bool {
    match word.chars().nth(0) {
        Some(c) => {
            c.is_alphabetic() && c.is_ascii() && (c == 'y' || c == 'Y' || !VOWELS.contains(c))
        }
        None => false,
    }
}

pub fn has_sequential_alphas(word: &str) -> bool {
    let mut iter = word.chars().peekable();
    loop {
        match iter.next() {
            Some(c) => {
                match iter.peek() {
                    Some(nc) => {
                        if c.is_alphabetic() && nc.is_alphabetic() {
                            return true;
                        }
                    }
                    None => {
                        break;
                    }
                }
            }
            None => {
                break;
            }
        }
    }

    false
}

#[macro_export]
macro_rules! eq_or_starts_with {
    ($a:expr, $b:expr) => { {
        let mut chars_a = $a.chars().filter_map(lowercase_if_alpha);
        let mut chars_b = $b.chars().filter_map(lowercase_if_alpha);
        let result;

        loop {
            let a = chars_a.next();
            let b = chars_b.next();

            if a.is_none() || b.is_none() {
                result = true;
                break;
            } else if a != b {
                result = false;
                break;
            }
        }

        result
    } }
}

#[macro_export]
macro_rules! eq_or_ends_with {
    ($needle:expr, $haystack:expr) => { {
        let mut n_chars = $needle.chars().rev().filter_map(lowercase_if_alpha);
        let mut h_chars = $haystack.chars().rev().filter_map(lowercase_if_alpha);
        let result;

        loop {
            let n = n_chars.next();
            let h = h_chars.next();

            if n.is_none() {
                result = true;
                break;
            } else if n != h {
                result = false;
                break;
            }
        }

        result
    } }
}
<|endoftext|>


<commit_before>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::borrow::Borrow;
use std::fmt;
use std::marker::PhantomData;
use std::mem;

use object::{Downcast, IsA, Object};
use translate::*;
use types::StaticType;

use glib_ffi;
use gobject_ffi;

/// A generic value capable of carrying various types.
///
/// Once created the type of the value can't be changed.
///
/// Some types (e.g. `String` and objects) support `None` values while others
/// (e.g. numeric types) don't.
pub struct Value(Box<gobject_ffi::GValue>);

impl Value {
    /// Tries to return a typed borrow of the value.
    ///
    /// Returns `Some(TypedValue<T>)` if the value carries a type corresponding
    /// to `T` and `None` otherwise.
    pub fn typed<T: FromValueOptional>(&self) -> Option<TypedValue<T>> {
        unsafe {
            let ok = gobject_ffi::g_type_check_value_holds(mut_override(self.to_glib_none().0),
                T::static_type().to_glib());
            some_if(ok, || TypedValue(self, PhantomData))
        }
    }

    /// Tries to return a typed mutable borrow of the value.
    ///
    /// Returns `Some(TypedValueMut<T>)` if the value carries a type
    /// corresponding to `T` and `None` otherwise.
    pub fn typed_mut<T: FromValueOptional + SetValue>(&mut self) -> Option<TypedValueMut<T>> {
        unsafe {
            let ok = gobject_ffi::g_type_check_value_holds(self.to_glib_none_mut().0,
                T::static_type().to_glib());
            some_if(ok, move || TypedValueMut(self, PhantomData))
        }
    }

    /// Tries to get a value of type `T`.
    ///
    /// Returns `Some` if the type is correct and the value is not `None`.
    ///
    /// This function doesn't distinguish between type mismatches and correctly
    /// typed `None` values. Use `typed` and `typed_mut` for that.
    pub fn get<T: FromValueOptional>(&self) -> Option<T> {
        self.typed::<T>().and_then(|v| v.get())
    }
}

impl Clone for Value {
    fn clone(&self) -> Self {
        unsafe {
            // FIXME: make this safer by making GValue::g_type public
            let type_ = *(&*self.0 as *const gobject_ffi::GValue as *const glib_ffi::GType);
            let mut ret = Value(Box::new(mem::zeroed()));
            gobject_ffi::g_value_init(ret.to_glib_none_mut().0, type_);
            gobject_ffi::g_value_copy(self.to_glib_none().0, ret.to_glib_none_mut().0);
            ret
        }
    }
}

impl Drop for Value {
    fn drop(&mut self) {
        unsafe { gobject_ffi::g_value_unset(self.to_glib_none_mut().0) }
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        unsafe {
            let s: String = from_glib_full(
                gobject_ffi::g_strdup_value_contents(self.to_glib_none().0));
            write!(f, "{}", s)
        }
    }
}

impl<T: SetValueOptional> From<Option<T>> for Value {
    fn from(value: Option<T>) -> Value {
        unsafe {
            let mut ret = Value::uninitialized();
            gobject_ffi::g_value_init(ret.to_glib_none_mut().0, T::static_type().to_glib());
            T::set_value_optional(&mut ret, value);
            ret
        }
    }
}

impl<T: SetValue> From<T> for Value {
    fn from(value: T) -> Value {
        unsafe {
            let mut ret = Value::uninitialized();
            gobject_ffi::g_value_init(ret.to_glib_none_mut().0, T::static_type().to_glib());
            T::set_value(&mut ret, value);
            ret
        }
    }
}

impl Uninitialized for Value {
    unsafe fn uninitialized() -> Value {
        Value(Box::new(mem::zeroed()))
    }
}

impl<'a> ToGlibPtr<'a, *const gobject_ffi::GValue> for Value {
    type Storage = &'a Value;

    fn to_glib_none(&'a self) -> Stash<'a, *const gobject_ffi::GValue, Self> {
        Stash(&*self.0, self)
    }
}

impl<'a> ToGlibPtrMut<'a, *mut gobject_ffi::GValue> for Value {
    type Storage = &'a mut Value;

    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut gobject_ffi::GValue, Self> {
        StashMut(&mut *self.0, self)
    }
}

/// A typed borrow of a `Value`.
pub struct TypedValue<'a, T>(&'a Value, PhantomData<*const T>);

impl<'a, T: FromValueOptional> TypedValue<'a, T> {
    /// Returns the value.
    ///
    /// Types that don't support a `None` value always return `Some`. See
    /// `get_some`.
    pub fn get(&self) -> Option<T> {
        unsafe { T::from_value_optional(self.0) }
    }

    /// Returns the value.
    ///
    /// This method is only available for types that don't support a `None`
    /// value.
    pub fn get_some(&self) -> T where T: FromValue {
        unsafe { T::from_value(self.0) }
    }
}

pub struct TypedValueMut<'a, T>(&'a mut Value, PhantomData<*const T>);

impl<'a, T: FromValueOptional + SetValue> TypedValueMut<'a, T> {
    /// Returns the value.
    ///
    /// Types that don't support a `None` value always return `Some`. See
    /// `get_some`.
    pub fn get(&self) -> Option<T> {
        unsafe { T::from_value_optional(self.0) }
    }

    /// Returns the value.
    ///
    /// This method is only available for types that don't support a `None`
    /// value.
    pub fn get_some(&self) -> T where T: FromValue {
        unsafe { T::from_value(self.0) }
    }

    /// Sets the value.
    ///
    /// This method is only available for types that support a `None` value.
    pub fn set<'x, U: ?Sized>(&mut self, value: Option<&'x U>)
    where T: Borrow<U>, &'x U: SetValueOptional {
        unsafe { SetValueOptional::set_value_optional(self.0, value) }
    }

    /// Sets the value.
    pub fn set_some<'x, U: ?Sized>(&mut self, value: &'x U)
    where T: Borrow<U>, &'x U: SetValue {
        unsafe { SetValue::set_value(self.0, value) }
    }
}

/// Extracts a value.
///
/// Types that don't support a `None` value always return `Some`.
pub trait FromValueOptional: StaticType + Sized {
    unsafe fn from_value_optional(&Value) -> Option<Self>;
}

/// Extracts a value.
///
/// Only implemented for types that don't support a `None` value.
pub trait FromValue: FromValueOptional {
    unsafe fn from_value(&Value) -> Self;
}

/// Sets a value.
///
/// Only implemented for types that support a `None` value.
pub trait SetValueOptional: SetValue {
    unsafe fn set_value_optional(&mut Value, Option<Self>);
}

/// Sets a value.
pub trait SetValue: StaticType + Sized {
    unsafe fn set_value(&mut Value, Self);
}

impl FromValueOptional for String {
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
        from_glib_none(gobject_ffi::g_value_get_string(value.to_glib_none().0))
    }
}

impl<'a> SetValue for &'a str {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
    }
}

impl<'a> SetValueOptional for &'a str {
    unsafe fn set_value_optional(value: &mut Value, this: Option<Self>) {
        gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
    }
}

impl SetValue for String {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
    }
}

impl SetValueOptional for String {
    unsafe fn set_value_optional(value: &mut Value, this: Option<Self>) {
        gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
    }
}

impl<T: IsA<Object>> FromValueOptional for T {
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
        Option::<Object>::from_glib_full(gobject_ffi::g_value_dup_object(value.to_glib_none().0))
            .map(|o| o.downcast_unchecked())
    }
}

impl<T: IsA<Object>> SetValue for T {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_set_object(value.to_glib_none_mut().0, this.to_glib_none().0)
    }
}

impl<T: IsA<Object>> SetValueOptional for T {
    unsafe fn set_value_optional(value: &mut Value, this: Option<Self>) {
        gobject_ffi::g_value_set_object(value.to_glib_none_mut().0, this.to_glib_none().0)
    }
}

impl<'a, T: IsA<Object>> SetValue for &'a T {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_set_object(value.to_glib_none_mut().0, this.to_glib_none().0)
    }
}

impl<'a, T: IsA<Object>> SetValueOptional for &'a T {
    unsafe fn set_value_optional(value: &mut Value, this: Option<Self>) {
        gobject_ffi::g_value_set_object(value.to_glib_none_mut().0, this.to_glib_none().0)
    }
}

impl FromValueOptional for bool {
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
        Some(from_glib(gobject_ffi::g_value_get_boolean(value.to_glib_none().0)))
    }
}

impl FromValue for bool {
    unsafe fn from_value(value: &Value) -> Self {
        from_glib(gobject_ffi::g_value_get_boolean(value.to_glib_none().0))
    }
}

impl SetValue for bool {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_set_boolean(value.to_glib_none_mut().0, this.to_glib())
    }
}

macro_rules! numeric {
    ($name:ident, $get:ident, $set:ident) => {
        impl FromValueOptional for $name {
            unsafe fn from_value_optional(value: &Value) -> Option<Self> {
                Some(gobject_ffi::$get(value.to_glib_none().0))
            }
        }

        impl FromValue for $name {
            unsafe fn from_value(value: &Value) -> Self {
                gobject_ffi::$get(value.to_glib_none().0)
            }
        }

        impl SetValue for $name {
            unsafe fn set_value(value: &mut Value, this: Self) {
                gobject_ffi::$set(value.to_glib_none_mut().0, this)
            }
        }

        impl<'a> SetValue for &'a $name {
            unsafe fn set_value(value: &mut Value, this: Self) {
                gobject_ffi::$set(value.to_glib_none_mut().0, *this)
            }
        }
    }
}

numeric!(i8, g_value_get_schar, g_value_set_schar);
numeric!(u8, g_value_get_uchar, g_value_set_uchar);
numeric!(i32, g_value_get_int, g_value_set_int);
numeric!(u32, g_value_get_uint, g_value_set_uint);
numeric!(i64, g_value_get_int64, g_value_set_int64);
numeric!(u64, g_value_get_uint64, g_value_set_uint64);
numeric!(f32, g_value_get_float, g_value_set_float);
numeric!(f64, g_value_get_double, g_value_set_double);
<commit_msg>Add `set_none` value setter<commit_after>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::borrow::Borrow;
use std::fmt;
use std::marker::PhantomData;
use std::mem;

use object::{Downcast, IsA, Object};
use translate::*;
use types::StaticType;

use glib_ffi;
use gobject_ffi;

/// A generic value capable of carrying various types.
///
/// Once created the type of the value can't be changed.
///
/// Some types (e.g. `String` and objects) support `None` values while others
/// (e.g. numeric types) don't.
pub struct Value(Box<gobject_ffi::GValue>);

impl Value {
    /// Tries to return a typed borrow of the value.
    ///
    /// Returns `Some(TypedValue<T>)` if the value carries a type corresponding
    /// to `T` and `None` otherwise.
    pub fn typed<T: FromValueOptional>(&self) -> Option<TypedValue<T>> {
        unsafe {
            let ok = gobject_ffi::g_type_check_value_holds(mut_override(self.to_glib_none().0),
                T::static_type().to_glib());
            some_if(ok, || TypedValue(self, PhantomData))
        }
    }

    /// Tries to return a typed mutable borrow of the value.
    ///
    /// Returns `Some(TypedValueMut<T>)` if the value carries a type
    /// corresponding to `T` and `None` otherwise.
    pub fn typed_mut<T: FromValueOptional + SetValue>(&mut self) -> Option<TypedValueMut<T>> {
        unsafe {
            let ok = gobject_ffi::g_type_check_value_holds(self.to_glib_none_mut().0,
                T::static_type().to_glib());
            some_if(ok, move || TypedValueMut(self, PhantomData))
        }
    }

    /// Tries to get a value of type `T`.
    ///
    /// Returns `Some` if the type is correct and the value is not `None`.
    ///
    /// This function doesn't distinguish between type mismatches and correctly
    /// typed `None` values. Use `typed` and `typed_mut` for that.
    pub fn get<T: FromValueOptional>(&self) -> Option<T> {
        self.typed::<T>().and_then(|v| v.get())
    }
}

impl Clone for Value {
    fn clone(&self) -> Self {
        unsafe {
            // FIXME: make this safer by making GValue::g_type public
            let type_ = *(&*self.0 as *const gobject_ffi::GValue as *const glib_ffi::GType);
            let mut ret = Value(Box::new(mem::zeroed()));
            gobject_ffi::g_value_init(ret.to_glib_none_mut().0, type_);
            gobject_ffi::g_value_copy(self.to_glib_none().0, ret.to_glib_none_mut().0);
            ret
        }
    }
}

impl Drop for Value {
    fn drop(&mut self) {
        unsafe { gobject_ffi::g_value_unset(self.to_glib_none_mut().0) }
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        unsafe {
            let s: String = from_glib_full(
                gobject_ffi::g_strdup_value_contents(self.to_glib_none().0));
            write!(f, "{}", s)
        }
    }
}

impl<T: SetValueOptional> From<Option<T>> for Value {
    fn from(value: Option<T>) -> Value {
        unsafe {
            let mut ret = Value::uninitialized();
            gobject_ffi::g_value_init(ret.to_glib_none_mut().0, T::static_type().to_glib());
            T::set_value_optional(&mut ret, value);
            ret
        }
    }
}

impl<T: SetValue> From<T> for Value {
    fn from(value: T) -> Value {
        unsafe {
            let mut ret = Value::uninitialized();
            gobject_ffi::g_value_init(ret.to_glib_none_mut().0, T::static_type().to_glib());
            T::set_value(&mut ret, value);
            ret
        }
    }
}

impl Uninitialized for Value {
    unsafe fn uninitialized() -> Value {
        Value(Box::new(mem::zeroed()))
    }
}

impl<'a> ToGlibPtr<'a, *const gobject_ffi::GValue> for Value {
    type Storage = &'a Value;

    fn to_glib_none(&'a self) -> Stash<'a, *const gobject_ffi::GValue, Self> {
        Stash(&*self.0, self)
    }
}

impl<'a> ToGlibPtrMut<'a, *mut gobject_ffi::GValue> for Value {
    type Storage = &'a mut Value;

    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut gobject_ffi::GValue, Self> {
        StashMut(&mut *self.0, self)
    }
}

/// A typed borrow of a `Value`.
pub struct TypedValue<'a, T>(&'a Value, PhantomData<*const T>);

impl<'a, T: FromValueOptional> TypedValue<'a, T> {
    /// Returns the value.
    ///
    /// Types that don't support a `None` value always return `Some`. See
    /// `get_some`.
    pub fn get(&self) -> Option<T> {
        unsafe { T::from_value_optional(self.0) }
    }

    /// Returns the value.
    ///
    /// This method is only available for types that don't support a `None`
    /// value.
    pub fn get_some(&self) -> T where T: FromValue {
        unsafe { T::from_value(self.0) }
    }
}

pub struct TypedValueMut<'a, T>(&'a mut Value, PhantomData<*const T>);

impl<'a, T: FromValueOptional + SetValue> TypedValueMut<'a, T> {
    /// Returns the value.
    ///
    /// Types that don't support a `None` value always return `Some`. See
    /// `get_some`.
    pub fn get(&self) -> Option<T> {
        unsafe { T::from_value_optional(self.0) }
    }

    /// Returns the value.
    ///
    /// This method is only available for types that don't support a `None`
    /// value.
    pub fn get_some(&self) -> T where T: FromValue {
        unsafe { T::from_value(self.0) }
    }

    /// Sets the value.
    ///
    /// This method is only available for types that support a `None` value.
    pub fn set<'x, U: ?Sized>(&mut self, value: Option<&'x U>)
    where T: Borrow<U>, &'x U: SetValueOptional {
        unsafe { SetValueOptional::set_value_optional(self.0, value) }
    }

    /// Sets the value to `None`.
    ///
    /// This method is only available for types that support a `None` value.
    pub fn set_none(&mut self) where T: SetValueOptional {
        unsafe { T::set_value_optional(self.0, None) }
    }

    /// Sets the value.
    pub fn set_some<'x, U: ?Sized>(&mut self, value: &'x U)
    where T: Borrow<U>, &'x U: SetValue {
        unsafe { SetValue::set_value(self.0, value) }
    }
}

/// Extracts a value.
///
/// Types that don't support a `None` value always return `Some`.
pub trait FromValueOptional: StaticType + Sized {
    unsafe fn from_value_optional(&Value) -> Option<Self>;
}

/// Extracts a value.
///
/// Only implemented for types that don't support a `None` value.
pub trait FromValue: FromValueOptional {
    unsafe fn from_value(&Value) -> Self;
}

/// Sets a value.
///
/// Only implemented for types that support a `None` value.
pub trait SetValueOptional: SetValue {
    unsafe fn set_value_optional(&mut Value, Option<Self>);
}

/// Sets a value.
pub trait SetValue: StaticType + Sized {
    unsafe fn set_value(&mut Value, Self);
}

impl FromValueOptional for String {
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
        from_glib_none(gobject_ffi::g_value_get_string(value.to_glib_none().0))
    }
}

impl<'a> SetValue for &'a str {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
    }
}

impl<'a> SetValueOptional for &'a str {
    unsafe fn set_value_optional(value: &mut Value, this: Option<Self>) {
        gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
    }
}

impl SetValue for String {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
    }
}

impl SetValueOptional for String {
    unsafe fn set_value_optional(value: &mut Value, this: Option<Self>) {
        gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, this.to_glib_full())
    }
}

impl<T: IsA<Object>> FromValueOptional for T {
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
        Option::<Object>::from_glib_full(gobject_ffi::g_value_dup_object(value.to_glib_none().0))
            .map(|o| o.downcast_unchecked())
    }
}

impl<T: IsA<Object>> SetValue for T {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_set_object(value.to_glib_none_mut().0, this.to_glib_none().0)
    }
}

impl<T: IsA<Object>> SetValueOptional for T {
    unsafe fn set_value_optional(value: &mut Value, this: Option<Self>) {
        gobject_ffi::g_value_set_object(value.to_glib_none_mut().0, this.to_glib_none().0)
    }
}

impl<'a, T: IsA<Object>> SetValue for &'a T {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_set_object(value.to_glib_none_mut().0, this.to_glib_none().0)
    }
}

impl<'a, T: IsA<Object>> SetValueOptional for &'a T {
    unsafe fn set_value_optional(value: &mut Value, this: Option<Self>) {
        gobject_ffi::g_value_set_object(value.to_glib_none_mut().0, this.to_glib_none().0)
    }
}

impl FromValueOptional for bool {
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
        Some(from_glib(gobject_ffi::g_value_get_boolean(value.to_glib_none().0)))
    }
}

impl FromValue for bool {
    unsafe fn from_value(value: &Value) -> Self {
        from_glib(gobject_ffi::g_value_get_boolean(value.to_glib_none().0))
    }
}

impl SetValue for bool {
    unsafe fn set_value(value: &mut Value, this: Self) {
        gobject_ffi::g_value_set_boolean(value.to_glib_none_mut().0, this.to_glib())
    }
}

macro_rules! numeric {
    ($name:ident, $get:ident, $set:ident) => {
        impl FromValueOptional for $name {
            unsafe fn from_value_optional(value: &Value) -> Option<Self> {
                Some(gobject_ffi::$get(value.to_glib_none().0))
            }
        }

        impl FromValue for $name {
            unsafe fn from_value(value: &Value) -> Self {
                gobject_ffi::$get(value.to_glib_none().0)
            }
        }

        impl SetValue for $name {
            unsafe fn set_value(value: &mut Value, this: Self) {
                gobject_ffi::$set(value.to_glib_none_mut().0, this)
            }
        }

        impl<'a> SetValue for &'a $name {
            unsafe fn set_value(value: &mut Value, this: Self) {
                gobject_ffi::$set(value.to_glib_none_mut().0, *this)
            }
        }
    }
}

numeric!(i8, g_value_get_schar, g_value_set_schar);
numeric!(u8, g_value_get_uchar, g_value_set_uchar);
numeric!(i32, g_value_get_int, g_value_set_int);
numeric!(u32, g_value_get_uint, g_value_set_uint);
numeric!(i64, g_value_get_int64, g_value_set_int64);
numeric!(u64, g_value_get_uint64, g_value_set_uint64);
numeric!(f32, g_value_get_float, g_value_set_float);
numeric!(f64, g_value_get_double, g_value_set_double);
<|endoftext|>


<commit_before>use cairo::{self, ImageSurface};
use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue, ParseValueToParseError};
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    ImageSurfaceDataExt,
    Pixel,
};
use crate::util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::input::Input;
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// Enumeration of the possible compositing operations.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum Operator {
    Over,
    In,
    Out,
    Atop,
    Xor,
    Arithmetic,
}

/// The `feComposite` filter primitive.
pub struct FeComposite {
    base: PrimitiveWithInput,
    in2: Option<Input>,
    operator: Operator,
    k1: f64,
    k2: f64,
    k3: f64,
    k4: f64,
}

impl Default for FeComposite {
    /// Constructs a new `Composite` with empty properties.
    #[inline]
    fn default() -> FeComposite {
        FeComposite {
            base: PrimitiveWithInput::new::<Self>(),
            in2: None,
            operator: Operator::Over,
            k1: 0.0,
            k2: 0.0,
            k3: 0.0,
            k4: 0.0,
        }
    }
}

impl NodeTrait for FeComposite {
    impl_node_as_filter_effect!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!(svg "in2") => self.in2 = Some(attr.parse_to_parse_error(value)?),
                expanded_name!(svg "operator") => self.operator = attr.parse(value)?,
                expanded_name!(svg "k1") => self.k1 = attr.parse(value)?,
                expanded_name!(svg "k2") => self.k2 = attr.parse(value)?,
                expanded_name!(svg "k3") => self.k3 = attr.parse(value)?,
                expanded_name!(svg "k4") => self.k4 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

/// Performs the arithmetic composite operation. Public for benchmarking.
#[inline]
pub fn composite_arithmetic(
    input_surface: &SharedImageSurface,
    input_2_surface: &SharedImageSurface,
    output_surface: &mut cairo::ImageSurface,
    bounds: IRect,
    k1: f64,
    k2: f64,
    k3: f64,
    k4: f64,
) {
    let output_stride = output_surface.get_stride() as usize;
    {
        let mut output_data = output_surface.get_data().unwrap();

        for (x, y, pixel, pixel_2) in Pixels::new(input_surface, bounds)
            .map(|(x, y, p)| (x, y, p, input_2_surface.get_pixel(x, y)))
        {
            let i1a = f64::from(pixel.a) / 255f64;
            let i2a = f64::from(pixel_2.a) / 255f64;
            let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
            let oa = clamp(oa, 0f64, 1f64);

            // Contents of image surfaces are transparent by default, so if the resulting pixel is
            // transparent there's no need to do anything.
            if oa > 0f64 {
                let compute = |i1, i2| {
                    let i1 = f64::from(i1) / 255f64;
                    let i2 = f64::from(i2) / 255f64;

                    let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
                    let o = clamp(o, 0f64, oa);

                    ((o * 255f64) + 0.5) as u8
                };

                let output_pixel = Pixel {
                    r: compute(pixel.r, pixel_2.r),
                    g: compute(pixel.g, pixel_2.g),
                    b: compute(pixel.b, pixel_2.b),
                    a: ((oa * 255f64) + 0.5) as u8,
                };
                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }
    }
}

impl FilterEffect for FeComposite {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let input_2 = ctx.get_input(draw_ctx, self.in2.as_ref())?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .add_input(&input_2)
            .into_irect(draw_ctx);

        // If we're combining two alpha-only surfaces, the result is alpha-only. Otherwise the
        // result is whatever the non-alpha-only type we're working on (which can be either sRGB or
        // linear sRGB depending on color-interpolation-filters).
        let surface_type = if input.surface().is_alpha_only() {
            input_2.surface().surface_type()
        } else {
            if !input_2.surface().is_alpha_only() {
                // All surface types should match (this is enforced by get_input()).
                assert_eq!(
                    input_2.surface().surface_type(),
                    input.surface().surface_type()
                );
            }

            input.surface().surface_type()
        };

        let output_surface = if self.operator == Operator::Arithmetic {
            let mut output_surface = ImageSurface::create(
                cairo::Format::ARgb32,
                input.surface().width(),
                input.surface().height(),
            )?;

            composite_arithmetic(
                input.surface(),
                input_2.surface(),
                &mut output_surface,
                bounds,
                self.k1,
                self.k2,
                self.k3,
                self.k4,
            );

            output_surface
        } else {
            let output_surface = input_2.surface().copy_surface(bounds)?;

            let cr = cairo::Context::new(&output_surface);
            let r = cairo::Rectangle::from(bounds);
            cr.rectangle(r.x, r.y, r.width, r.height);
            cr.clip();

            input.surface().set_as_source_surface(&cr, 0f64, 0f64);
            cr.set_operator(self.operator.into());
            cr.paint();

            output_surface
        };

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, surface_type)?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

impl Parse for Operator {
    fn parse(parser: &mut Parser<'_, '_>) -> Result<Self, ValueErrorKind> {
        parse_identifiers!(
            parser,
            "over" => Operator::Over,
            "in" => Operator::In,
            "out" => Operator::Out,
            "atop" => Operator::Atop,
            "xor" => Operator::Xor,
            "arithmetic" => Operator::Arithmetic,
        ).map_err(|_| ValueErrorKind::parse_error("parse error"))
    }
}

impl From<Operator> for cairo::Operator {
    #[inline]
    fn from(x: Operator) -> Self {
        match x {
            Operator::Over => cairo::Operator::Over,
            Operator::In => cairo::Operator::In,
            Operator::Out => cairo::Operator::Out,
            Operator::Atop => cairo::Operator::Atop,
            Operator::Xor => cairo::Operator::Xor,
            _ => panic!("can't convert Operator::Arithmetic to a cairo::Operator"),
        }
    }
}
<commit_msg>Convert composite::Operator to CssParseError<commit_after>use cairo::{self, ImageSurface};
use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{ParseToParseError, ParseValueToParseError};
use crate::property_bag::PropertyBag;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    ImageSurfaceDataExt,
    Pixel,
};
use crate::util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::input::Input;
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// Enumeration of the possible compositing operations.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum Operator {
    Over,
    In,
    Out,
    Atop,
    Xor,
    Arithmetic,
}

/// The `feComposite` filter primitive.
pub struct FeComposite {
    base: PrimitiveWithInput,
    in2: Option<Input>,
    operator: Operator,
    k1: f64,
    k2: f64,
    k3: f64,
    k4: f64,
}

impl Default for FeComposite {
    /// Constructs a new `Composite` with empty properties.
    #[inline]
    fn default() -> FeComposite {
        FeComposite {
            base: PrimitiveWithInput::new::<Self>(),
            in2: None,
            operator: Operator::Over,
            k1: 0.0,
            k2: 0.0,
            k3: 0.0,
            k4: 0.0,
        }
    }
}

impl NodeTrait for FeComposite {
    impl_node_as_filter_effect!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!(svg "in2") => self.in2 = Some(attr.parse_to_parse_error(value)?),
                expanded_name!(svg "operator") => self.operator = attr.parse_to_parse_error(value)?,
                expanded_name!(svg "k1") => self.k1 = attr.parse_to_parse_error(value)?,
                expanded_name!(svg "k2") => self.k2 = attr.parse_to_parse_error(value)?,
                expanded_name!(svg "k3") => self.k3 = attr.parse_to_parse_error(value)?,
                expanded_name!(svg "k4") => self.k4 = attr.parse_to_parse_error(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

/// Performs the arithmetic composite operation. Public for benchmarking.
#[inline]
pub fn composite_arithmetic(
    input_surface: &SharedImageSurface,
    input_2_surface: &SharedImageSurface,
    output_surface: &mut cairo::ImageSurface,
    bounds: IRect,
    k1: f64,
    k2: f64,
    k3: f64,
    k4: f64,
) {
    let output_stride = output_surface.get_stride() as usize;
    {
        let mut output_data = output_surface.get_data().unwrap();

        for (x, y, pixel, pixel_2) in Pixels::new(input_surface, bounds)
            .map(|(x, y, p)| (x, y, p, input_2_surface.get_pixel(x, y)))
        {
            let i1a = f64::from(pixel.a) / 255f64;
            let i2a = f64::from(pixel_2.a) / 255f64;
            let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
            let oa = clamp(oa, 0f64, 1f64);

            // Contents of image surfaces are transparent by default, so if the resulting pixel is
            // transparent there's no need to do anything.
            if oa > 0f64 {
                let compute = |i1, i2| {
                    let i1 = f64::from(i1) / 255f64;
                    let i2 = f64::from(i2) / 255f64;

                    let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
                    let o = clamp(o, 0f64, oa);

                    ((o * 255f64) + 0.5) as u8
                };

                let output_pixel = Pixel {
                    r: compute(pixel.r, pixel_2.r),
                    g: compute(pixel.g, pixel_2.g),
                    b: compute(pixel.b, pixel_2.b),
                    a: ((oa * 255f64) + 0.5) as u8,
                };
                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }
    }
}

impl FilterEffect for FeComposite {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let input_2 = ctx.get_input(draw_ctx, self.in2.as_ref())?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .add_input(&input_2)
            .into_irect(draw_ctx);

        // If we're combining two alpha-only surfaces, the result is alpha-only. Otherwise the
        // result is whatever the non-alpha-only type we're working on (which can be either sRGB or
        // linear sRGB depending on color-interpolation-filters).
        let surface_type = if input.surface().is_alpha_only() {
            input_2.surface().surface_type()
        } else {
            if !input_2.surface().is_alpha_only() {
                // All surface types should match (this is enforced by get_input()).
                assert_eq!(
                    input_2.surface().surface_type(),
                    input.surface().surface_type()
                );
            }

            input.surface().surface_type()
        };

        let output_surface = if self.operator == Operator::Arithmetic {
            let mut output_surface = ImageSurface::create(
                cairo::Format::ARgb32,
                input.surface().width(),
                input.surface().height(),
            )?;

            composite_arithmetic(
                input.surface(),
                input_2.surface(),
                &mut output_surface,
                bounds,
                self.k1,
                self.k2,
                self.k3,
                self.k4,
            );

            output_surface
        } else {
            let output_surface = input_2.surface().copy_surface(bounds)?;

            let cr = cairo::Context::new(&output_surface);
            let r = cairo::Rectangle::from(bounds);
            cr.rectangle(r.x, r.y, r.width, r.height);
            cr.clip();

            input.surface().set_as_source_surface(&cr, 0f64, 0f64);
            cr.set_operator(self.operator.into());
            cr.paint();

            output_surface
        };

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, surface_type)?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

impl ParseToParseError for Operator {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "over" => Operator::Over,
            "in" => Operator::In,
            "out" => Operator::Out,
            "atop" => Operator::Atop,
            "xor" => Operator::Xor,
            "arithmetic" => Operator::Arithmetic,
        )?)
    }
}

impl From<Operator> for cairo::Operator {
    #[inline]
    fn from(x: Operator) -> Self {
        match x {
            Operator::Over => cairo::Operator::Over,
            Operator::In => cairo::Operator::In,
            Operator::Out => cairo::Operator::Out,
            Operator::Atop => cairo::Operator::Atop,
            Operator::Xor => cairo::Operator::Xor,
            _ => panic!("can't convert Operator::Arithmetic to a cairo::Operator"),
        }
    }
}
<|endoftext|>