<commit_before>#[cfg(feature = "yaml")]
use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter, Result};

#[cfg(feature = "yaml")]
use yaml_rust::Yaml;

/// `ArgGroup`s are a family of related arguments and way for you to express, "Any of these
/// arguments". By placing arguments in a logical group, you can create easier requirement and
/// exclusion rules instead of having to list each argument individually, or when you want a rule
/// to apply "any but not all" arguments.
///
/// For instance, you can make an entire `ArgGroup` required, this means that one (and *only* one)
/// argument from that group must be present. Using more than one argument from an `ArgGroup`
/// causes a parsing failure.
///
/// You can also do things such as name an entire `ArgGroup` as a conflict or requirement for
/// another argument, meaning any of the arguments that belong to that group will cause a failure
/// if present, or must present respectively.
///
/// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
/// present out of a given set. Imagine that you had multiple arguments, and you want one of them
/// to be required, but making all of them required isn't feasible because perhaps they conflict
/// with each other. For example, lets say that you were building an application where one could
/// set a given version number by supplying a string with an option argument, i.e.
/// `--set-ver v1.2.3`, you also wanted to support automatically using a previous version number
/// and simply incrementing one of the three numbers. So you create three flags `--major`,
/// `--minor`, and `--patch`. All of these arguments shouldn't be used at one time but you want to
/// specify that *at least one* of them is used. For this, you can create a group.
///
/// Finally, you may use `ArgGroup`s to pull a value from a group of arguments when you don't care
/// exaclty which argument was actually used at runtime.
///
/// # Examples
///
/// The following example demonstrates using an `ArgGroup` to ensure that one, and only one, of
/// the arguments from the specified group is present at runtime.
///
/// ```rust
/// # use clap::{App, ArgGroup};
/// App::new("app")
///     .args_from_usage(
///         "--set-ver [ver] 'set the version manually'
///          --major         'auto increase major'
///          --minor         'auto increase minor'
///          --patch         'auto increase patch'")
///     .group(ArgGroup::with_name("vers")
///          .args(&["set-ver", "major", "minor","patch"])
///          .required(true))
/// # ;
/// ```
pub struct ArgGroup<'a> {
    #[doc(hidden)]
    pub name: &'a str,
    #[doc(hidden)]
    pub args: Vec<&'a str>,
    #[doc(hidden)]
    pub required: bool,
    #[doc(hidden)]
    pub requires: Option<Vec<&'a str>>,
    #[doc(hidden)]
    pub conflicts: Option<Vec<&'a str>>,
}

impl<'a> ArgGroup<'a> {
    /// Creates a new instance of `ArgGroup` using a unique string name. The name will be used to
    /// get values from the group or refer to the group inside of conflict and requirement rules.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, ArgGroup};
    /// ArgGroup::with_name("config")
    /// # ;
    /// ```
    pub fn with_name(n: &'a str) -> Self {
        ArgGroup {
            name: n,
            required: false,
            args: vec![],
            requires: None,
            conflicts: None,
        }
    }

    /// Creates a new instance of `ArgGroup` from a .yml (YAML) file.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// # use clap::ArgGroup;
    /// let yml = load_yaml!("group.yml");
    /// let ag = ArgGroup::from_yaml(yml);
    /// ```
    #[cfg(feature = "yaml")]
    pub fn from_yaml<'y>(y: &'y BTreeMap<Yaml, Yaml>) -> ArgGroup<'y> {
        // We WANT this to panic on error...so expect() is good.
        let name_yml = y.keys().nth(0).unwrap();
        let name_str = name_yml.as_str().unwrap();
        let mut a = ArgGroup::with_name(name_str);
        let group_settings = y.get(name_yml).unwrap().as_hash().unwrap();

        for (k, v) in group_settings.iter() {
            a = match k.as_str().unwrap() {
                "required" => a.required(v.as_bool().unwrap()),
                "args" => {
                    for ys in v.as_vec().unwrap() {
                        if let Some(s) = ys.as_str() {
                            a = a.arg(s);
                        }
                    }
                    a
                }
                "arg" => {
                    if let Some(ys) = v.as_str().unwrap() {
                        a = a.arg(ys);
                    }
                    a
                }
                "requires" => {
                    for ys in v.as_vec().unwrap() {
                        if let Some(s) = ys.as_str() {
                            a = a.requires(s);
                        }
                    }
                    a
                }
                "conflicts_with" => {
                    for ys in v.as_vec().unwrap() {
                        if let Some(s) = ys.as_str() {
                            a = a.conflicts_with(s);
                        }
                    }
                    a
                }
                s => panic!("Unknown ArgGroup setting '{}' in YAML file for \
                             ArgGroup '{}'",
                            s,
                            name_str),
            }
        }

        a
    }

    /// Adds an argument to this group by name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, ArgGroup, Arg};
    /// let cfg_arg = Arg::with_name("config");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .arg("config")
    /// # ;
    /// ```
    pub fn arg(mut self, n: &'a str) -> Self {
        assert!(self.name != n,
                "ArgGroup '{}' can not have same name as arg inside it",
                &*self.name);
        self.args.push(n);
        self
    }

    /// Adds multiple arguments to this group by name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{ArgGroup, Arg};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// # ;
    /// ```
    pub fn args(mut self, ns: &[&'a str]) -> Self {
        for n in ns {
            self = self.arg(n);
        }
        self
    }

    /// Sets the group as required or not. A required group will be displayed in the usage string
    /// of the application in the format `[arg|arg2|arg3]`. A required `ArgGroup` simply states
    /// that one, and only one argument from this group *must* be present at runtime (unless
    /// conflicting with another argument).
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("cfg")
    ///     .args(&["config", "input"])
    ///     .required(true)
    /// # ;
    /// ```
    pub fn required(mut self, r: bool) -> Self {
        self.required = r;
        self
    }

    /// Sets the requirement rules of this group. This is not to be confused with a required group.
    /// Requirement rules function just like argument requirement rules, you can name other
    /// arguments or groups that must be present when one of the arguments from this group is used.
    ///
    /// **NOTE:** The name provided may be an argument, or group name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// // ...
    /// # ;
    /// ArgGroup::with_name("other_group")
    ///     .requires("files")
    /// # ;
    /// ```
    pub fn requires(mut self, n: &'a str) -> Self {
        if let Some(ref mut reqs) = self.requires {
            reqs.push(n);
        } else {
            self.requires = Some(vec![n]);
        }
        self
    }

    /// Sets the requirement rules of this group. This is not to be confused with a required group.
    /// Requirement rules function just like argument requirement rules, you can name other
    /// arguments or groups that must be present when one of the arguments from this group is used.
    ///
    /// **NOTE:** The names provided may be an argument, or group name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// // ...
    /// # ;
    /// ArgGroup::with_name("other_group")
    ///     .requires_all(&["config", "input"]) // No different than saying, .requires("files")
    /// # ;
    /// ```
    pub fn requires_all(mut self, ns: &[&'a str]) -> Self {
        for n in ns {
            self = self.requires(n);
        }
        self
    }

    /// Sets the exclusion rules of this group. Exclusion (aka conflict) rules function just like
    /// argument exclusion rules, you can name other arguments or groups that must not be present
    /// when one of the arguments from this group are used.
    ///
    /// **NOTE:** The name provided may be an argument, or group name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// // ...
    /// # ;
    /// ArgGroup::with_name("other_group")
    ///     .conflicts_with("files")
    /// # ;
    /// ```
    pub fn conflicts_with(mut self, n: &'a str) -> Self {
        if let Some(ref mut confs) = self.conflicts {
            confs.push(n);
        } else {
            self.conflicts = Some(vec![n]);
        }
        self
    }

    /// Sets the exclusion rules of this group. Exclusion rules function just like argument
    /// exclusion rules, you can name other arguments or groups that must not be present when one
    /// of the arguments from this group are used.
    ///
    /// **NOTE:** The names provided may be an argument, or group name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// // ...
    /// # ;
    /// ArgGroup::with_name("other_group")
    ///     .conflicts_with_all(&["files", "input"]) // same as saying, conflicts_with("files")
    /// # ;
    /// ```
    pub fn conflicts_with_all(mut self, ns: &[&'a str]) -> Self {
        for n in ns {
            self = self.conflicts_with(n);
        }
        self
    }
}

impl<'a> Debug for ArgGroup<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f,
               "{{
            name:{:?},
            args: {:?},
            required: {:?},
            requires: {:?},
            conflicts: {:?},
            }}",
               self.name,
               self.args,
               self.required,
               self.requires,
               self.conflicts)
    }
}

impl<'a, 'z> From<&'z ArgGroup<'a>> for ArgGroup<'a> {
    fn from(g: &'z ArgGroup<'a>) -> Self {
        ArgGroup {
            name: g.name.clone(),
            required: g.required,
            args: g.args.clone(),
            requires: g.requires.clone(),
            conflicts: g.conflicts.clone(),
        }
    }
}

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

    #[test]
    fn groups() {
        let g = ArgGroup::with_name("test")
                    .arg("a1")
                    .arg("a4")
                    .args(&["a2", "a3"])
                    .required(true)
                    .conflicts_with("c1")
                    .conflicts_with_all(&["c2", "c3"])
                    .conflicts_with("c4")
                    .requires("r1")
                    .requires_all(&["r2", "r3"])
                    .requires("r4");

        let args = vec!["a1", "a4", "a2", "a3"];
        let reqs = vec!["r1", "r2", "r3", "r4"];
        let confs = vec!["c1", "c2", "c3", "c4"];

        assert_eq!(g.args, args);
        assert_eq!(g.requires.unwrap(), reqs);
        assert_eq!(g.conflicts.unwrap(), confs);

    }
}
<commit_msg>fix: fixes yaml build for 2x base<commit_after>#[cfg(feature = "yaml")]
use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter, Result};

#[cfg(feature = "yaml")]
use yaml_rust::Yaml;

/// `ArgGroup`s are a family of related arguments and way for you to express, "Any of these
/// arguments". By placing arguments in a logical group, you can create easier requirement and
/// exclusion rules instead of having to list each argument individually, or when you want a rule
/// to apply "any but not all" arguments.
///
/// For instance, you can make an entire `ArgGroup` required, this means that one (and *only* one)
/// argument from that group must be present. Using more than one argument from an `ArgGroup`
/// causes a parsing failure.
///
/// You can also do things such as name an entire `ArgGroup` as a conflict or requirement for
/// another argument, meaning any of the arguments that belong to that group will cause a failure
/// if present, or must present respectively.
///
/// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
/// present out of a given set. Imagine that you had multiple arguments, and you want one of them
/// to be required, but making all of them required isn't feasible because perhaps they conflict
/// with each other. For example, lets say that you were building an application where one could
/// set a given version number by supplying a string with an option argument, i.e.
/// `--set-ver v1.2.3`, you also wanted to support automatically using a previous version number
/// and simply incrementing one of the three numbers. So you create three flags `--major`,
/// `--minor`, and `--patch`. All of these arguments shouldn't be used at one time but you want to
/// specify that *at least one* of them is used. For this, you can create a group.
///
/// Finally, you may use `ArgGroup`s to pull a value from a group of arguments when you don't care
/// exaclty which argument was actually used at runtime.
///
/// # Examples
///
/// The following example demonstrates using an `ArgGroup` to ensure that one, and only one, of
/// the arguments from the specified group is present at runtime.
///
/// ```rust
/// # use clap::{App, ArgGroup};
/// App::new("app")
///     .args_from_usage(
///         "--set-ver [ver] 'set the version manually'
///          --major         'auto increase major'
///          --minor         'auto increase minor'
///          --patch         'auto increase patch'")
///     .group(ArgGroup::with_name("vers")
///          .args(&["set-ver", "major", "minor","patch"])
///          .required(true))
/// # ;
/// ```
pub struct ArgGroup<'a> {
    #[doc(hidden)]
    pub name: &'a str,
    #[doc(hidden)]
    pub args: Vec<&'a str>,
    #[doc(hidden)]
    pub required: bool,
    #[doc(hidden)]
    pub requires: Option<Vec<&'a str>>,
    #[doc(hidden)]
    pub conflicts: Option<Vec<&'a str>>,
}

impl<'a> ArgGroup<'a> {
    /// Creates a new instance of `ArgGroup` using a unique string name. The name will be used to
    /// get values from the group or refer to the group inside of conflict and requirement rules.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, ArgGroup};
    /// ArgGroup::with_name("config")
    /// # ;
    /// ```
    pub fn with_name(n: &'a str) -> Self {
        ArgGroup {
            name: n,
            required: false,
            args: vec![],
            requires: None,
            conflicts: None,
        }
    }

    /// Creates a new instance of `ArgGroup` from a .yml (YAML) file.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// # use clap::ArgGroup;
    /// let yml = load_yaml!("group.yml");
    /// let ag = ArgGroup::from_yaml(yml);
    /// ```
    #[cfg(feature = "yaml")]
    pub fn from_yaml<'y>(y: &'y BTreeMap<Yaml, Yaml>) -> ArgGroup<'y> {
        // We WANT this to panic on error...so expect() is good.
        let name_yml = y.keys().nth(0).unwrap();
        let name_str = name_yml.as_str().unwrap();
        let mut a = ArgGroup::with_name(name_str);
        let group_settings = y.get(name_yml).unwrap().as_hash().unwrap();

        for (k, v) in group_settings.iter() {
            a = match k.as_str().unwrap() {
                "required" => a.required(v.as_bool().unwrap()),
                "args" => {
                    for ys in v.as_vec().unwrap() {
                        if let Some(s) = ys.as_str() {
                            a = a.arg(s);
                        }
                    }
                    a
                }
                "arg" => {
                    if let Some(ys) = v.as_str() {
                        a = a.arg(ys);
                    }
                    a
                }
                "requires" => {
                    for ys in v.as_vec().unwrap() {
                        if let Some(s) = ys.as_str() {
                            a = a.requires(s);
                        }
                    }
                    a
                }
                "conflicts_with" => {
                    for ys in v.as_vec().unwrap() {
                        if let Some(s) = ys.as_str() {
                            a = a.conflicts_with(s);
                        }
                    }
                    a
                }
                s => panic!("Unknown ArgGroup setting '{}' in YAML file for \
                             ArgGroup '{}'",
                            s,
                            name_str),
            }
        }

        a
    }

    /// Adds an argument to this group by name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, ArgGroup, Arg};
    /// let cfg_arg = Arg::with_name("config");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .arg("config")
    /// # ;
    /// ```
    pub fn arg(mut self, n: &'a str) -> Self {
        assert!(self.name != n,
                "ArgGroup '{}' can not have same name as arg inside it",
                &*self.name);
        self.args.push(n);
        self
    }

    /// Adds multiple arguments to this group by name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{ArgGroup, Arg};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// # ;
    /// ```
    pub fn args(mut self, ns: &[&'a str]) -> Self {
        for n in ns {
            self = self.arg(n);
        }
        self
    }

    /// Sets the group as required or not. A required group will be displayed in the usage string
    /// of the application in the format `[arg|arg2|arg3]`. A required `ArgGroup` simply states
    /// that one, and only one argument from this group *must* be present at runtime (unless
    /// conflicting with another argument).
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("cfg")
    ///     .args(&["config", "input"])
    ///     .required(true)
    /// # ;
    /// ```
    pub fn required(mut self, r: bool) -> Self {
        self.required = r;
        self
    }

    /// Sets the requirement rules of this group. This is not to be confused with a required group.
    /// Requirement rules function just like argument requirement rules, you can name other
    /// arguments or groups that must be present when one of the arguments from this group is used.
    ///
    /// **NOTE:** The name provided may be an argument, or group name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// // ...
    /// # ;
    /// ArgGroup::with_name("other_group")
    ///     .requires("files")
    /// # ;
    /// ```
    pub fn requires(mut self, n: &'a str) -> Self {
        if let Some(ref mut reqs) = self.requires {
            reqs.push(n);
        } else {
            self.requires = Some(vec![n]);
        }
        self
    }

    /// Sets the requirement rules of this group. This is not to be confused with a required group.
    /// Requirement rules function just like argument requirement rules, you can name other
    /// arguments or groups that must be present when one of the arguments from this group is used.
    ///
    /// **NOTE:** The names provided may be an argument, or group name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// // ...
    /// # ;
    /// ArgGroup::with_name("other_group")
    ///     .requires_all(&["config", "input"]) // No different than saying, .requires("files")
    /// # ;
    /// ```
    pub fn requires_all(mut self, ns: &[&'a str]) -> Self {
        for n in ns {
            self = self.requires(n);
        }
        self
    }

    /// Sets the exclusion rules of this group. Exclusion (aka conflict) rules function just like
    /// argument exclusion rules, you can name other arguments or groups that must not be present
    /// when one of the arguments from this group are used.
    ///
    /// **NOTE:** The name provided may be an argument, or group name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// // ...
    /// # ;
    /// ArgGroup::with_name("other_group")
    ///     .conflicts_with("files")
    /// # ;
    /// ```
    pub fn conflicts_with(mut self, n: &'a str) -> Self {
        if let Some(ref mut confs) = self.conflicts {
            confs.push(n);
        } else {
            self.conflicts = Some(vec![n]);
        }
        self
    }

    /// Sets the exclusion rules of this group. Exclusion rules function just like argument
    /// exclusion rules, you can name other arguments or groups that must not be present when one
    /// of the arguments from this group are used.
    ///
    /// **NOTE:** The names provided may be an argument, or group name
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use clap::{App, Arg, ArgGroup};
    /// let cfg_arg = Arg::with_name("config");
    /// let in_arg = Arg::with_name("input");
    /// // ...
    /// ArgGroup::with_name("files")
    ///     .args(&["config", "input"])
    /// // ...
    /// # ;
    /// ArgGroup::with_name("other_group")
    ///     .conflicts_with_all(&["files", "input"]) // same as saying, conflicts_with("files")
    /// # ;
    /// ```
    pub fn conflicts_with_all(mut self, ns: &[&'a str]) -> Self {
        for n in ns {
            self = self.conflicts_with(n);
        }
        self
    }
}

impl<'a> Debug for ArgGroup<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f,
               "{{
            name:{:?},
            args: {:?},
            required: {:?},
            requires: {:?},
            conflicts: {:?},
            }}",
               self.name,
               self.args,
               self.required,
               self.requires,
               self.conflicts)
    }
}

impl<'a, 'z> From<&'z ArgGroup<'a>> for ArgGroup<'a> {
    fn from(g: &'z ArgGroup<'a>) -> Self {
        ArgGroup {
            name: g.name.clone(),
            required: g.required,
            args: g.args.clone(),
            requires: g.requires.clone(),
            conflicts: g.conflicts.clone(),
        }
    }
}

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

    #[test]
    fn groups() {
        let g = ArgGroup::with_name("test")
                    .arg("a1")
                    .arg("a4")
                    .args(&["a2", "a3"])
                    .required(true)
                    .conflicts_with("c1")
                    .conflicts_with_all(&["c2", "c3"])
                    .conflicts_with("c4")
                    .requires("r1")
                    .requires_all(&["r2", "r3"])
                    .requires("r4");

        let args = vec!["a1", "a4", "a2", "a3"];
        let reqs = vec!["r1", "r2", "r3", "r4"];
        let confs = vec!["c1", "c2", "c3", "c4"];

        assert_eq!(g.args, args);
        assert_eq!(g.requires.unwrap(), reqs);
        assert_eq!(g.conflicts.unwrap(), confs);

    }
}
<|endoftext|>


<commit_before>#[macro_use(crate_version, crate_authors)]
extern crate clap;

extern crate jailer;

use clap::{App, Arg};

use jailer::JailerArgs;

fn main() -> jailer::Result<()> {
    // Initially, the uid and gid params had default values, but it turns out that it's quite
    // easy to shoot yourself in the foot by not setting proper permissions when preparing the
    // contents of the jail, so I think their values should be provided explicitly.
    let cmd_arguments = App::new("firejailer")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Jail a microVM.")
        .arg(
            Arg::with_name("id")
                .long("id")
                .help("Jail ID")
                .required(true)
                .takes_value(true),
        )
        .arg(
            Arg::with_name("exec_file")
                .long("exec-file")
                .help("File path to exec into")
                .required(true)
                .takes_value(true),
        )
        .arg(
            Arg::with_name("uid")
                .long("uid")
                .help("Chroot uid")
                .required(true)
                .takes_value(true),
        )
        .arg(
            Arg::with_name("gid")
                .long("gid")
                .help("Chroot gid")
                .required(true)
                .takes_value(true),
        )
        .get_matches();

    // All arguments are either mandatory, or have default values, so the unwraps should not fail.
    let args = JailerArgs::new(
        cmd_arguments.value_of("id").unwrap(),
        cmd_arguments.value_of("exec_file").unwrap(),
        cmd_arguments.value_of("uid").unwrap(),
        cmd_arguments.value_of("gid").unwrap(),
    )?;

    jailer::run(args)
}
<commit_msg>jailer: install seccomp filters<commit_after>#[macro_use(crate_version, crate_authors)]
extern crate clap;

extern crate jailer;
extern crate seccomp_sys;

use clap::{App, Arg};
use std::env;

use jailer::JailerArgs;

const SECCOMP_ENVVAR: &str = "USE_SECCOMP";

fn main() -> jailer::Result<()> {
    if is_seccomp_enabled() {
        // If the seccomp filters installation fails, it's OK to panic.
        seccomp_sys::setup_seccomp().unwrap();
    }

    // Initially, the uid and gid params had default values, but it turns out that it's quite
    // easy to shoot yourself in the foot by not setting proper permissions when preparing the
    // contents of the jail, so I think their values should be provided explicitly.
    let cmd_arguments = App::new("firejailer")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Jail a microVM.")
        .arg(
            Arg::with_name("id")
                .long("id")
                .help("Jail ID")
                .required(true)
                .takes_value(true),
        )
        .arg(
            Arg::with_name("exec_file")
                .long("exec-file")
                .help("File path to exec into")
                .required(true)
                .takes_value(true),
        )
        .arg(
            Arg::with_name("uid")
                .long("uid")
                .help("Chroot uid")
                .required(true)
                .takes_value(true),
        )
        .arg(
            Arg::with_name("gid")
                .long("gid")
                .help("Chroot gid")
                .required(true)
                .takes_value(true),
        )
        .get_matches();

    // All arguments are either mandatory, or have default values, so the unwraps should not fail.
    let args = JailerArgs::new(
        cmd_arguments.value_of("id").unwrap(),
        cmd_arguments.value_of("exec_file").unwrap(),
        cmd_arguments.value_of("uid").unwrap(),
        cmd_arguments.value_of("gid").unwrap(),
    )?;

    jailer::run(args)
}

fn is_seccomp_enabled() -> bool {
    match env::var(SECCOMP_ENVVAR) {
        Ok(val) => val == "1",
        Err(_) => false,
    }
}
<|endoftext|>


<commit_before>use chrono::{Local, NaiveDate};
use failure::{Error, ResultExt};
use input::{self, Answer};
use mal::{SeriesInfo, MAL};
use mal::list::{AnimeEntry, EntryTag, Status};
use std;

fn get_today_naive() -> NaiveDate {
    Local::today().naive_utc()
}

pub fn find_and_select_series_info(mal: &MAL, name: &str) -> Result<SeriesInfo, Error> {
    let mut series = mal.search(name).context("MAL search failed")?;

    if series.len() == 0 {
        bail!("no anime named [{}] found", name);
    } else if series.len() > 1 {
        println!("found multiple anime named [{}] on MAL", name);
        println!("input the number corrosponding with the intended anime:\n");

        for (i, s) in series.iter().enumerate() {
            println!("{} [{}]", 1 + i, s.title);
        }

        let idx = input::read_usize_range(1, series.len())? - 1;
        Ok(series.swap_remove(idx))
    } else {
        Ok(series.swap_remove(0))
    }
}

pub fn add_to_anime_list(mal: &MAL, info: &SeriesInfo) -> Result<AnimeEntry, Error> {
    println!(
        "[{}] is not on your anime list\ndo you want to add it? (Y/n)",
        info.title
    );

    if input::read_yn(Answer::Yes)? {
        let today = get_today_naive();

        mal.add_anime(
            info.id,
            &[
                EntryTag::Status(Status::Watching),
                EntryTag::StartDate(Some(today)),
            ],
        )?;

        Ok(AnimeEntry {
            info: info.clone(),
            watched_episodes: 0,
            start_date: Some(today),
            end_date: None,
            status: Status::Watching,
            score: 0,
            rewatching: false,
        })
    } else {
        // No point in continuing in this case
        std::process::exit(0);
    }
}

/// Adds the `FinishDate` tag to the `tags` parameter.
/// If the `entry` is being rewatched, it will ask the user before adding the tag.
fn add_finish_date(
    entry: &AnimeEntry,
    date: NaiveDate,
    tags: &mut Vec<EntryTag>,
) -> Result<(), Error> {
    // Someone may want to keep the original start / finish date for an
    // anime they're rewatching
    if entry.rewatching && entry.end_date.is_some() {
        println!("do you want to override the finish date? (Y/n)");

        if input::read_yn(Answer::Yes)? {
            tags.push(EntryTag::FinishDate(Some(date)));
        }
    } else {
        tags.push(EntryTag::FinishDate(Some(date)));
    }

    Ok(())
}

fn completed(mal: &MAL, entry: &mut AnimeEntry, mut tags: Vec<EntryTag>) -> Result<(), Error> {
    let today = get_today_naive();

    tags.push(EntryTag::Status(Status::Completed));

    println!(
        "[{}] completed!\ndo you want to rate it? (Y/n)",
        entry.info.title
    );

    if input::read_yn(Answer::Yes)? {
        println!("enter your score between 1-10:");
        let score = input::read_usize_range(1, 10)? as u8;

        tags.push(EntryTag::Score(score));
    }

    if entry.rewatching {
        tags.push(EntryTag::Rewatching(false));
    }

    add_finish_date(entry, today, &mut tags)?;

    mal.update_anime(entry.info.id, &tags)?;
    // Nothing to do now
    std::process::exit(0);
}

pub fn update_watched(mal: &MAL, entry: &mut AnimeEntry) -> Result<(), Error> {
    let tags = vec![EntryTag::Episode(entry.watched_episodes)];

    if entry.watched_episodes >= entry.info.episodes {
        completed(mal, entry, tags)?;
    } else {
        println!(
            "[{}] episode {}/{} completed",
            entry.info.title,
            entry.watched_episodes,
            entry.info.episodes
        );

        mal.update_anime(entry.info.id, &tags)?;
    }

    Ok(())
}

pub fn next_episode_options(mal: &MAL, entry: &AnimeEntry) -> Result<(), Error> {
    println!("\t[d] drop series\n\t[h] put series on hold\n\t[x] exit\n\t[n] watch next episode (default)");

    let input = input::read_line()?.to_lowercase();

    match input.as_str() {
        "d" => {
            let mut tags = vec![EntryTag::Status(Status::Dropped)];
            add_finish_date(entry, get_today_naive(), &mut tags)?;

            mal.update_anime(entry.info.id, &tags)?;
            std::process::exit(0);
        },
        "h" => {
            mal.update_anime(entry.info.id, &[EntryTag::Status(Status::OnHold)])?;
            std::process::exit(0);
        },
        "x" => std::process::exit(0),
        _ => (),
    }

    Ok(())
}

pub fn abnormal_player_exit(mal: &MAL, entry: &mut AnimeEntry) -> Result<(), Error> {
    println!("video player not exited normally");
    println!("do you still want to count the episode as watched? (y/N)");

    if input::read_yn(Answer::No)? {
        update_watched(mal, entry)?;
    }

    Ok(())
}

pub fn rewatch(mal: &MAL, entry: &mut AnimeEntry) -> Result<(), Error> {
    println!("[{}] already completed", entry.info.title);
    println!("do you want to rewatch it? (Y/n)");
    println!("(note that you have to increase the rewatch count manually)");

    if input::read_yn(Answer::Yes)? {
        let id = entry.info.id;
        let mut changeset = entry.new_changeset().rewatching(true).episode(0);

        println!("do you want to reset the start and end date? (Y/n)");

        if input::read_yn(Answer::Yes)? {
            changeset = changeset
                .start_date(Some(get_today_naive()))
                .finish_date(None);
        }

        mal.update_anime(id, &changeset.build())?;
    } else {
        // No point in continuing in this case
        std::process::exit(0);
    }

    Ok(())
}
<commit_msg>Add start date tag when the first episode has been watched Add watching tag when an episode has been watched<commit_after>use chrono::{Local, NaiveDate};
use failure::{Error, ResultExt};
use input::{self, Answer};
use mal::{SeriesInfo, MAL};
use mal::list::{AnimeEntry, EntryTag, Status};
use std;

fn get_today_naive() -> NaiveDate {
    Local::today().naive_utc()
}

pub fn find_and_select_series_info(mal: &MAL, name: &str) -> Result<SeriesInfo, Error> {
    let mut series = mal.search(name).context("MAL search failed")?;

    if series.len() == 0 {
        bail!("no anime named [{}] found", name);
    } else if series.len() > 1 {
        println!("found multiple anime named [{}] on MAL", name);
        println!("input the number corrosponding with the intended anime:\n");

        for (i, s) in series.iter().enumerate() {
            println!("{} [{}]", 1 + i, s.title);
        }

        let idx = input::read_usize_range(1, series.len())? - 1;
        Ok(series.swap_remove(idx))
    } else {
        Ok(series.swap_remove(0))
    }
}

pub fn add_to_anime_list(mal: &MAL, info: &SeriesInfo) -> Result<AnimeEntry, Error> {
    println!(
        "[{}] is not on your anime list\ndo you want to add it? (Y/n)",
        info.title
    );

    if input::read_yn(Answer::Yes)? {
        let today = get_today_naive();

        mal.add_anime(
            info.id,
            &[
                EntryTag::Status(Status::Watching),
                EntryTag::StartDate(Some(today)),
            ],
        )?;

        Ok(AnimeEntry {
            info: info.clone(),
            watched_episodes: 0,
            start_date: Some(today),
            end_date: None,
            status: Status::Watching,
            score: 0,
            rewatching: false,
        })
    } else {
        // No point in continuing in this case
        std::process::exit(0);
    }
}

/// Adds the `FinishDate` tag to the `tags` parameter.
/// If the `entry` is being rewatched, it will ask the user before adding the tag.
fn add_finish_date(
    entry: &AnimeEntry,
    date: NaiveDate,
    tags: &mut Vec<EntryTag>,
) -> Result<(), Error> {
    // Someone may want to keep the original start / finish date for an
    // anime they're rewatching
    if entry.rewatching && entry.end_date.is_some() {
        println!("do you want to override the finish date? (Y/n)");

        if input::read_yn(Answer::Yes)? {
            tags.push(EntryTag::FinishDate(Some(date)));
        }
    } else {
        tags.push(EntryTag::FinishDate(Some(date)));
    }

    Ok(())
}

fn completed(mal: &MAL, entry: &mut AnimeEntry, mut tags: Vec<EntryTag>) -> Result<(), Error> {
    let today = get_today_naive();

    tags.push(EntryTag::Status(Status::Completed));

    println!(
        "[{}] completed!\ndo you want to rate it? (Y/n)",
        entry.info.title
    );

    if input::read_yn(Answer::Yes)? {
        println!("enter your score between 1-10:");
        let score = input::read_usize_range(1, 10)? as u8;

        tags.push(EntryTag::Score(score));
    }

    if entry.rewatching {
        tags.push(EntryTag::Rewatching(false));
    }

    add_finish_date(entry, today, &mut tags)?;

    mal.update_anime(entry.info.id, &tags)?;
    // Nothing to do now
    std::process::exit(0);
}

pub fn update_watched(mal: &MAL, entry: &mut AnimeEntry) -> Result<(), Error> {
    let mut tags = vec![EntryTag::Episode(entry.watched_episodes)];

    if entry.watched_episodes >= entry.info.episodes {
        completed(mal, entry, tags)?;
    } else {
        println!(
            "[{}] episode {}/{} completed",
            entry.info.title,
            entry.watched_episodes,
            entry.info.episodes
        );

        if !entry.rewatching {
            tags.push(EntryTag::Status(Status::Watching));

            if entry.watched_episodes <= 1 {
                tags.push(EntryTag::StartDate(Some(get_today_naive())));
            }
        }

        mal.update_anime(entry.info.id, &tags)?;
    }

    Ok(())
}

pub fn next_episode_options(mal: &MAL, entry: &AnimeEntry) -> Result<(), Error> {
    println!("\t[d] drop series\n\t[h] put series on hold\n\t[x] exit\n\t[n] watch next episode (default)");

    let input = input::read_line()?.to_lowercase();

    match input.as_str() {
        "d" => {
            let mut tags = vec![EntryTag::Status(Status::Dropped)];
            add_finish_date(entry, get_today_naive(), &mut tags)?;

            mal.update_anime(entry.info.id, &tags)?;
            std::process::exit(0);
        },
        "h" => {
            mal.update_anime(entry.info.id, &[EntryTag::Status(Status::OnHold)])?;
            std::process::exit(0);
        },
        "x" => std::process::exit(0),
        _ => (),
    }

    Ok(())
}

pub fn abnormal_player_exit(mal: &MAL, entry: &mut AnimeEntry) -> Result<(), Error> {
    println!("video player not exited normally");
    println!("do you still want to count the episode as watched? (y/N)");

    if input::read_yn(Answer::No)? {
        update_watched(mal, entry)?;
    }

    Ok(())
}

pub fn rewatch(mal: &MAL, entry: &mut AnimeEntry) -> Result<(), Error> {
    println!("[{}] already completed", entry.info.title);
    println!("do you want to rewatch it? (Y/n)");
    println!("(note that you have to increase the rewatch count manually)");

    if input::read_yn(Answer::Yes)? {
        let id = entry.info.id;
        let mut changeset = entry.new_changeset().rewatching(true).episode(0);

        println!("do you want to reset the start and end date? (Y/n)");

        if input::read_yn(Answer::Yes)? {
            changeset = changeset
                .start_date(Some(get_today_naive()))
                .finish_date(None);
        }

        mal.update_anime(id, &changeset.build())?;
    } else {
        // No point in continuing in this case
        std::process::exit(0);
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use stem;
use tokenize::tokenize;

pub struct NaiveBayesClassifier {
  documents: HashMap<String, HashMap<String, usize>>,
  total_document_count: usize,
}

impl NaiveBayesClassifier {
  pub fn new() -> NaiveBayesClassifier {
    NaiveBayesClassifier {
      documents: HashMap::new(),
      total_document_count: 0,
    }
  }

  pub fn train(&mut self, text: &str, classification: &str) {
    let classification_map = self.documents.entry(classification.to_string())
                                           .or_insert_with(|| HashMap::new());
    let stemmed_and_tokenized = get_tokenized_and_stemmed(text);
    for stemmed_word in stemmed_and_tokenized.into_iter() {
      let stemmed_word_entry = classification_map.entry(stemmed_word).or_insert(1);
      *stemmed_word_entry += 1;
    }
    self.total_document_count += 1;
  }

  pub fn guess(&self, text: &str) -> String {
    let stemmed_and_tokenized = get_tokenized_and_stemmed(text);
    let mut label_probabilities = HashMap::new();
    for (k, v) in self.documents.iter() {
      //Get the probability that the passed-in text is each class
      let mut probability = 0.0f32;
      for stemmed_word in &stemmed_and_tokenized {
        if v.contains_key(stemmed_word) {
          probability += (1.0 / v.len() as f32).ln();
        }
      }
      if probability.abs() < 0.0001 {
        label_probabilities.insert(k, 0.0);
      } else {
        label_probabilities.insert(k, (v.len() as f32 * probability.abs() / self.total_document_count as f32));
      }
    }

    let mut answer_label = String::new();
    let mut answer_probability = 0.0;
    for (k, v) in label_probabilities.into_iter() {
      if answer_probability <= v {
        answer_label = k.clone();
        answer_probability = v;
      }
    }
    answer_label
  }
}

fn get_tokenized_and_stemmed(text: &str) -> Vec<String> {
  let tokenized_text = tokenize(text);
  tokenized_text.into_iter()
                .map(|text| stem::get(text).unwrap())
                .collect()
}
<commit_msg>Clean up classifier<commit_after>use std::collections::HashMap;
use stem;
use tokenize::tokenize;

pub struct NaiveBayesClassifier {
  documents: HashMap<String, HashMap<String, usize>>,
  total_document_count: usize,
}

impl NaiveBayesClassifier {
  pub fn new() -> NaiveBayesClassifier {
    NaiveBayesClassifier {
      documents: HashMap::new(),
      total_document_count: 0,
    }
  }

  // Add counts of terms in some text to a classification
  pub fn train(&mut self, text: &str, classification: &str) {
    let classification_map = self.documents.entry(classification.to_string())
                                           .or_insert(HashMap::new());
    let stemmed_and_tokenized = get_tokenized_and_stemmed(text);
    for stemmed_word in stemmed_and_tokenized.into_iter() {
      let stemmed_word_entry = classification_map.entry(stemmed_word).or_insert(1);
      *stemmed_word_entry += 1;
    }
    self.total_document_count += 1;
  }

  // Get a guess of input text based on existing unigram counts
  pub fn guess(&self, text: &str) -> String {
    let stemmed_and_tokenized = get_tokenized_and_stemmed(text);
    let mut label_probabilities = HashMap::new();

    for (classification, word_counts) in self.documents.iter() {
      //Get the probability that the passed-in text is each class
      let mut probability = 0.0f32;
      for stemmed_word in &stemmed_and_tokenized {
        if word_counts.contains_key(stemmed_word) {
          probability += (1.0 / word_counts.len() as f32).ln();
        }
      }

      // store the calculated probability for the classification
      if probability.abs() < 0.0001 {
        label_probabilities.insert(classification, 0.0);
      } else {
        let normalized_prob = word_counts.len() as f32 * probability.abs() /
                              self.total_document_count as f32;

        label_probabilities.insert(classification, normalized_prob);
      }
    }

    // determine the label of the highest probability
    let mut result_label = String::new();
    let mut result_probability = 0.0;
    for (classification, prob) in label_probabilities.into_iter() {
      if result_probability <= prob {
        result_label = classification.clone();
        result_probability = prob;
      }
    }
    result_label
  }
}

fn get_tokenized_and_stemmed(text: &str) -> Vec<String> {
  let tokenized_text = tokenize(text);
  tokenized_text.into_iter()
                .map(|text| stem::get(text).unwrap())
                .collect()
}
<|endoftext|>


<commit_before>//! A simple, thread-safe, and async-friendly IRC client library.

pub mod conn;
pub mod data;
pub mod server;
pub mod transport;

pub mod prelude {
    //! A client-side IRC prelude, re-exporting all the necessary basics.
    pub use client::data::Config;
    pub use client::server::{IrcServer, Server};
    pub use client::server::utils::ServerExt;
    pub use proto::{Capability, ChannelExt, Command, Message, NegotiationVersion, Response};
    pub use proto::{ChannelMode, Mode, UserMode};

    pub use futures::{Future, Stream};
}
<commit_msg>Added EachIncomingExt to prelude.<commit_after>//! A simple, thread-safe, and async-friendly IRC client library.

pub mod conn;
pub mod data;
pub mod server;
pub mod transport;

pub mod prelude {
    //! A client-side IRC prelude, re-exporting all the necessary basics.
    pub use client::data::Config;
    pub use client::server::{EachIncomingExt, IrcServer, Server};
    pub use client::server::utils::ServerExt;
    pub use proto::{Capability, ChannelExt, Command, Message, NegotiationVersion, Response};
    pub use proto::{ChannelMode, Mode, UserMode};

    pub use futures::{Future, Stream};
}
<|endoftext|>


<commit_before>use std::convert::TryInto;
use std::fmt::{Debug, Error, Formatter};

/// Maximum size of a [`Memory`](struct.Memory.html) block in
/// [`Word`](struct.Word.html)s
///
/// = 2^24
const MAX_MEM_SIZE: u32 = 16_777_216;

#[derive(Clone,Default)]
pub struct Word {
    val: [u8; 3],
}

impl Word {
    pub fn new() -> Self {
        Word { val: [0; 3] }
    }
}

impl Debug for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        f.debug_struct("Word")
            .field("val", &format_args!("[{}, {}, {}]",
                                        &self.val[0],
                                        &self.val[1],
                                        &self.val[2]))
            .finish()
    }
}

#[derive(Debug)]
pub struct Memory {
    data: Vec<Word>,
    size: u32,
}

impl Memory {
    /// Creates a new `Memory` block of the given `size`.
    ///
    /// The maximum size of a `Memory` block is
    /// [`MAX_MEM_SIZE`](constant.MAX_MEM_SIZE.html). If `size` is greater
    /// than that, the created `Memory` block will be
    /// [`MAX_MEM_SIZE`](constant.MAX_MEM_SIZE.html) long.
    pub fn new(size: u32) -> Self {
        let size = if size > MAX_MEM_SIZE {
            MAX_MEM_SIZE
        } else {
            size
        };
        Memory { data: vec![Word::default(); size.try_into().unwrap()], size }
    }
}
<commit_msg>Should probably use Debug, not Display, to implement Debug<commit_after>use std::convert::TryInto;
use std::fmt::{Debug, Error, Formatter};

/// Maximum size of a [`Memory`](struct.Memory.html) block in
/// [`Word`](struct.Word.html)s
///
/// = 2^24
const MAX_MEM_SIZE: u32 = 16_777_216;

#[derive(Clone,Default)]
pub struct Word {
    val: [u8; 3],
}

impl Word {
    pub fn new() -> Self {
        Word { val: [0; 3] }
    }
}

impl Debug for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        f.debug_struct("Word")
            .field("val", &format_args!("[{:?}, {:?}, {:?}]",
                                        &self.val[0],
                                        &self.val[1],
                                        &self.val[2]))
            .finish()
    }
}

#[derive(Debug)]
pub struct Memory {
    data: Vec<Word>,
    size: u32,
}

impl Memory {
    /// Creates a new `Memory` block of the given `size`.
    ///
    /// The maximum size of a `Memory` block is
    /// [`MAX_MEM_SIZE`](constant.MAX_MEM_SIZE.html). If `size` is greater
    /// than that, the created `Memory` block will be
    /// [`MAX_MEM_SIZE`](constant.MAX_MEM_SIZE.html) long.
    pub fn new(size: u32) -> Self {
        let size = if size > MAX_MEM_SIZE {
            MAX_MEM_SIZE
        } else {
            size
        };
        Memory { data: vec![Word::default(); size.try_into().unwrap()], size }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::path::{PathBuf, Path};
use std::io::{self, Read, Write};
use std::fs::{self, File};

use handlebars::Handlebars;
use walkdir::WalkDir;

use cargo_shim::{
    TargetKind,
    CargoPackage,
    CargoTarget,
    CargoResult
};

use error::Error;
use utils::read_bytes;

const DEFAULT_INDEX_HTML_TEMPLATE: &'static str = r#"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=1" name="viewport" />
    <script>
        var Module = {};
        var __cargo_web = {};
        Object.defineProperty( Module, 'canvas', {
            get: function() {
                if( __cargo_web.canvas ) {
                    return __cargo_web.canvas;
                }

                var canvas = document.createElement( 'canvas' );
                document.querySelector( 'body' ).appendChild( canvas );
                __cargo_web.canvas = canvas;

                return canvas;
            }
        });
    </script>
</head>
<body>
    <script src="{{{js_url}}}"></script>
</body>
</html>
"#;

fn generate_index_html( filename: &str ) -> String {
    let handlebars = Handlebars::new();
    let mut template_data = BTreeMap::new();
    template_data.insert( "js_url", filename.to_owned() );
    handlebars.template_render( DEFAULT_INDEX_HTML_TEMPLATE, &template_data ).unwrap()
}

enum RouteKind {
    Blob( Vec< u8 > ),
    StaticDirectory( PathBuf )
}

struct Route {
    key: String,
    kind: RouteKind,
    can_be_deployed: bool
}

pub struct Deployment {
    routes: Vec< Route >
}

pub enum ArtifactKind {
    Data( Vec< u8 > ),
    File( File )
}

pub struct Artifact {
    pub mime_type: &'static str,
    pub kind: ArtifactKind
}

impl Artifact {
    pub fn map_text< F: FnOnce( String ) -> String >( self, callback: F ) -> io::Result< Self > {
        let mime_type = self.mime_type;
        let data = match self.kind {
            ArtifactKind::Data( data ) => data,
            ArtifactKind::File( mut fp ) => {
                let mut data = Vec::new();
                fp.read_to_end( &mut data )?;
                data
            }
        };

        let mut text = String::from_utf8_lossy( &data ).into_owned();
        text = callback( text );
        let data = text.into();
        Ok( Artifact {
            mime_type,
            kind: ArtifactKind::Data( data )
        })
    }
}

impl Deployment {
    pub fn new( package: &CargoPackage, target: &CargoTarget, result: &CargoResult ) -> Result< Self, Error > {
        let crate_static_path = package.crate_root.join( "static" );
        let target_static_path = match target.kind {
            TargetKind::Example => Some( target.source_directory.join( format!( "{}-static", target.name ) ) ),
            TargetKind::Bin => Some( target.source_directory.join( "static" ) ),
            _ => None
        };

        let js_name = format!( "{}.js", target.name );

        let mut routes = Vec::new();
        for path in result.artifacts() {
            let (is_js, key) = match path.extension() {
                Some( ext ) if ext == "js" => (true, js_name.clone()),
                Some( ext ) if ext == "wasm" => (false, path.file_name().unwrap().to_string_lossy().into_owned()),
                _ => continue
            };

            let contents = match read_bytes( &path ) {
                Ok( contents ) => contents,
                Err( error ) => return Err( Error::CannotLoadFile( path.clone(), error ) )
            };

            if is_js {
                // TODO: Remove this eventually. We're keeping it for now
                //       to not break compatibility with already written
                //       `index.html` files.
                routes.push( Route {
                    key: "js/app.js".to_owned(),
                    kind: RouteKind::Blob( contents.clone() ),
                    can_be_deployed: false
                });
            }

            routes.push( Route {
                key,
                kind: RouteKind::Blob( contents ),
                can_be_deployed: true
            });
        }

        if let Some( target_static_path ) = target_static_path {
            routes.push( Route {
                key: "".to_owned(),
                kind: RouteKind::StaticDirectory( target_static_path.to_owned() ),
                can_be_deployed: true
            });
        }

        routes.push( Route {
            key: "".to_owned(),
            kind: RouteKind::StaticDirectory( crate_static_path.to_owned() ),
            can_be_deployed: true
        });

        routes.push( Route {
            key: "index.html".to_owned(),
            kind: RouteKind::Blob( generate_index_html( &js_name ).into() ),
            can_be_deployed: true
        });

        Ok( Deployment {
            routes
        })
    }

    pub fn js_url( &self ) -> &str {
        let route = self.routes.iter().find( |route| route.can_be_deployed && route.key.ends_with( ".js" ) ).unwrap();
        &route.key
    }

    pub fn get_by_url( &self, mut url: &str ) -> Option< Artifact > {
        if url.starts_with( "/" ) {
            url = &url[ 1.. ];
        }

        if url == "" {
            url = "index.html";
        }

        // TODO: Support more mime types. Use the mime_guess crate.
        let mime_type =
            if url.ends_with( ".js" ) { "application/javascript" }
            else if url.ends_with( ".wasm" ) { "application/wasm" }
            else if url.ends_with( ".html" ) { "text/html" }
            else if url.ends_with( ".css" ) { "text/css" }
            else if url.ends_with( ".svg" ) { "image/svg+xml" }
            else if url.ends_with( ".png" ) { "image/png" }
            else if url.ends_with( ".gif" ) { "image/gif" }
            else if url.ends_with( ".jpeg" ) { "image/jpeg" }
            else if url.ends_with( ".jpg" ) { "image/jpeg" }
            else { "application/octet-stream" };

        for route in &self.routes {
            match route.kind {
                RouteKind::Blob( ref bytes ) => {
                    if url != route.key {
                        continue;
                    }

                    trace!( "Get by URL of {:?}: found blob", url );
                    return Some( Artifact {
                        mime_type,
                        kind: ArtifactKind::Data( bytes.clone() )
                    });
                },
                RouteKind::StaticDirectory( ref path ) => {
                    let mut target_path = path.clone();
                    for chunk in url.split( "/" ) {
                        target_path = target_path.join( chunk );
                    }

                    trace!( "Get by URL of {:?}: path {:?} exists: {}", url, target_path, target_path.exists() );
                    if target_path.exists() {
                        match File::open( &target_path ) {
                            Ok( fp ) => {
                                return Some( Artifact {
                                    mime_type,
                                    kind: ArtifactKind::File( fp )
                                });
                            },
                            Err( error ) => {
                                warn!( "Cannot open {:?}: {:?}", target_path, error );
                                return None;
                            }
                        }
                    }
                }
            }
        }

        trace!( "Get by URL of {:?}: not found", url );
        None
    }

    pub fn deploy_to( &self, root_directory: &Path ) -> Result< (), Error > {
        for route in &self.routes {
            if !route.can_be_deployed {
                continue;
            }

            match route.kind {
                RouteKind::Blob( ref bytes ) => {
                    let mut target_path = root_directory.to_owned();
                    for chunk in route.key.split( "/" ) {
                        target_path = target_path.join( chunk );
                    }

                    if target_path.exists() {
                        continue;
                    }

                    let target_dir = target_path.parent().unwrap();
                    fs::create_dir_all( target_dir )
                        .map_err( |err| Error::CannotCreateFile( target_dir.to_owned(), err ) )?; // TODO: Different error type?

                    let mut fp = File::create( &target_path ).map_err( |err| Error::CannotCreateFile( target_path.to_owned(), err ) )?;
                    fp.write_all( &bytes ).map_err( |err| Error::CannotWriteToFile( target_path.to_owned(), err ) )?;
                },
                RouteKind::StaticDirectory( ref source_dir ) => {
                    if !source_dir.exists() {
                        continue;
                    }

                    for entry in WalkDir::new( source_dir ) {
                        let entry = entry.map_err( |err| {
                            let err_path = err.path().map( |path| path.to_owned() ).unwrap_or_else( || source_dir.clone() );
                            let err: io::Error = err.into();
                            Error::CannotLoadFile( err_path, err ) // TODO: Different error type?
                        })?;

                        let source_path = entry.path();
                        let relative_path = source_path.strip_prefix( source_dir ).unwrap();
                        let target_path = root_directory.join( relative_path );
                        if target_path.exists() {
                            continue;
                        }

                        if source_path.is_dir() {
                            fs::create_dir_all( &target_path )
                                .map_err( |err| Error::CannotCreateFile( target_path.to_owned(), err ) )?; // TODO: Different error type?

                            continue;
                        }

                        let target_dir = target_path.parent().unwrap();
                        fs::create_dir_all( target_dir )
                            .map_err( |err| Error::CannotCreateFile( target_dir.to_owned(), err ) )?; // TODO: Different error type?

                        fs::copy( &source_path, &target_path )
                            .map_err( |err| Error::CannotCopyFile( source_path.to_owned(), target_path.to_owned(), err ) )?;
                    }
                }
            }
        }

        Ok(())
    }
}
<commit_msg>Set proper mime type for served JSON files<commit_after>use std::collections::BTreeMap;
use std::path::{PathBuf, Path};
use std::io::{self, Read, Write};
use std::fs::{self, File};

use handlebars::Handlebars;
use walkdir::WalkDir;

use cargo_shim::{
    TargetKind,
    CargoPackage,
    CargoTarget,
    CargoResult
};

use error::Error;
use utils::read_bytes;

const DEFAULT_INDEX_HTML_TEMPLATE: &'static str = r#"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=1" name="viewport" />
    <script>
        var Module = {};
        var __cargo_web = {};
        Object.defineProperty( Module, 'canvas', {
            get: function() {
                if( __cargo_web.canvas ) {
                    return __cargo_web.canvas;
                }

                var canvas = document.createElement( 'canvas' );
                document.querySelector( 'body' ).appendChild( canvas );
                __cargo_web.canvas = canvas;

                return canvas;
            }
        });
    </script>
</head>
<body>
    <script src="{{{js_url}}}"></script>
</body>
</html>
"#;

fn generate_index_html( filename: &str ) -> String {
    let handlebars = Handlebars::new();
    let mut template_data = BTreeMap::new();
    template_data.insert( "js_url", filename.to_owned() );
    handlebars.template_render( DEFAULT_INDEX_HTML_TEMPLATE, &template_data ).unwrap()
}

enum RouteKind {
    Blob( Vec< u8 > ),
    StaticDirectory( PathBuf )
}

struct Route {
    key: String,
    kind: RouteKind,
    can_be_deployed: bool
}

pub struct Deployment {
    routes: Vec< Route >
}

pub enum ArtifactKind {
    Data( Vec< u8 > ),
    File( File )
}

pub struct Artifact {
    pub mime_type: &'static str,
    pub kind: ArtifactKind
}

impl Artifact {
    pub fn map_text< F: FnOnce( String ) -> String >( self, callback: F ) -> io::Result< Self > {
        let mime_type = self.mime_type;
        let data = match self.kind {
            ArtifactKind::Data( data ) => data,
            ArtifactKind::File( mut fp ) => {
                let mut data = Vec::new();
                fp.read_to_end( &mut data )?;
                data
            }
        };

        let mut text = String::from_utf8_lossy( &data ).into_owned();
        text = callback( text );
        let data = text.into();
        Ok( Artifact {
            mime_type,
            kind: ArtifactKind::Data( data )
        })
    }
}

impl Deployment {
    pub fn new( package: &CargoPackage, target: &CargoTarget, result: &CargoResult ) -> Result< Self, Error > {
        let crate_static_path = package.crate_root.join( "static" );
        let target_static_path = match target.kind {
            TargetKind::Example => Some( target.source_directory.join( format!( "{}-static", target.name ) ) ),
            TargetKind::Bin => Some( target.source_directory.join( "static" ) ),
            _ => None
        };

        let js_name = format!( "{}.js", target.name );

        let mut routes = Vec::new();
        for path in result.artifacts() {
            let (is_js, key) = match path.extension() {
                Some( ext ) if ext == "js" => (true, js_name.clone()),
                Some( ext ) if ext == "wasm" => (false, path.file_name().unwrap().to_string_lossy().into_owned()),
                _ => continue
            };

            let contents = match read_bytes( &path ) {
                Ok( contents ) => contents,
                Err( error ) => return Err( Error::CannotLoadFile( path.clone(), error ) )
            };

            if is_js {
                // TODO: Remove this eventually. We're keeping it for now
                //       to not break compatibility with already written
                //       `index.html` files.
                routes.push( Route {
                    key: "js/app.js".to_owned(),
                    kind: RouteKind::Blob( contents.clone() ),
                    can_be_deployed: false
                });
            }

            routes.push( Route {
                key,
                kind: RouteKind::Blob( contents ),
                can_be_deployed: true
            });
        }

        if let Some( target_static_path ) = target_static_path {
            routes.push( Route {
                key: "".to_owned(),
                kind: RouteKind::StaticDirectory( target_static_path.to_owned() ),
                can_be_deployed: true
            });
        }

        routes.push( Route {
            key: "".to_owned(),
            kind: RouteKind::StaticDirectory( crate_static_path.to_owned() ),
            can_be_deployed: true
        });

        routes.push( Route {
            key: "index.html".to_owned(),
            kind: RouteKind::Blob( generate_index_html( &js_name ).into() ),
            can_be_deployed: true
        });

        Ok( Deployment {
            routes
        })
    }

    pub fn js_url( &self ) -> &str {
        let route = self.routes.iter().find( |route| route.can_be_deployed && route.key.ends_with( ".js" ) ).unwrap();
        &route.key
    }

    pub fn get_by_url( &self, mut url: &str ) -> Option< Artifact > {
        if url.starts_with( "/" ) {
            url = &url[ 1.. ];
        }

        if url == "" {
            url = "index.html";
        }

        // TODO: Support more mime types. Use the mime_guess crate.
        let mime_type =
            if url.ends_with( ".js" ) { "application/javascript" }
            else if url.ends_with( ".json" ) { "application/json" }
            else if url.ends_with( ".wasm" ) { "application/wasm" }
            else if url.ends_with( ".html" ) { "text/html" }
            else if url.ends_with( ".css" ) { "text/css" }
            else if url.ends_with( ".svg" ) { "image/svg+xml" }
            else if url.ends_with( ".png" ) { "image/png" }
            else if url.ends_with( ".gif" ) { "image/gif" }
            else if url.ends_with( ".jpeg" ) { "image/jpeg" }
            else if url.ends_with( ".jpg" ) { "image/jpeg" }
            else { "application/octet-stream" };

        for route in &self.routes {
            match route.kind {
                RouteKind::Blob( ref bytes ) => {
                    if url != route.key {
                        continue;
                    }

                    trace!( "Get by URL of {:?}: found blob", url );
                    return Some( Artifact {
                        mime_type,
                        kind: ArtifactKind::Data( bytes.clone() )
                    });
                },
                RouteKind::StaticDirectory( ref path ) => {
                    let mut target_path = path.clone();
                    for chunk in url.split( "/" ) {
                        target_path = target_path.join( chunk );
                    }

                    trace!( "Get by URL of {:?}: path {:?} exists: {}", url, target_path, target_path.exists() );
                    if target_path.exists() {
                        match File::open( &target_path ) {
                            Ok( fp ) => {
                                return Some( Artifact {
                                    mime_type,
                                    kind: ArtifactKind::File( fp )
                                });
                            },
                            Err( error ) => {
                                warn!( "Cannot open {:?}: {:?}", target_path, error );
                                return None;
                            }
                        }
                    }
                }
            }
        }

        trace!( "Get by URL of {:?}: not found", url );
        None
    }

    pub fn deploy_to( &self, root_directory: &Path ) -> Result< (), Error > {
        for route in &self.routes {
            if !route.can_be_deployed {
                continue;
            }

            match route.kind {
                RouteKind::Blob( ref bytes ) => {
                    let mut target_path = root_directory.to_owned();
                    for chunk in route.key.split( "/" ) {
                        target_path = target_path.join( chunk );
                    }

                    if target_path.exists() {
                        continue;
                    }

                    let target_dir = target_path.parent().unwrap();
                    fs::create_dir_all( target_dir )
                        .map_err( |err| Error::CannotCreateFile( target_dir.to_owned(), err ) )?; // TODO: Different error type?

                    let mut fp = File::create( &target_path ).map_err( |err| Error::CannotCreateFile( target_path.to_owned(), err ) )?;
                    fp.write_all( &bytes ).map_err( |err| Error::CannotWriteToFile( target_path.to_owned(), err ) )?;
                },
                RouteKind::StaticDirectory( ref source_dir ) => {
                    if !source_dir.exists() {
                        continue;
                    }

                    for entry in WalkDir::new( source_dir ) {
                        let entry = entry.map_err( |err| {
                            let err_path = err.path().map( |path| path.to_owned() ).unwrap_or_else( || source_dir.clone() );
                            let err: io::Error = err.into();
                            Error::CannotLoadFile( err_path, err ) // TODO: Different error type?
                        })?;

                        let source_path = entry.path();
                        let relative_path = source_path.strip_prefix( source_dir ).unwrap();
                        let target_path = root_directory.join( relative_path );
                        if target_path.exists() {
                            continue;
                        }

                        if source_path.is_dir() {
                            fs::create_dir_all( &target_path )
                                .map_err( |err| Error::CannotCreateFile( target_path.to_owned(), err ) )?; // TODO: Different error type?

                            continue;
                        }

                        let target_dir = target_path.parent().unwrap();
                        fs::create_dir_all( target_dir )
                            .map_err( |err| Error::CannotCreateFile( target_dir.to_owned(), err ) )?; // TODO: Different error type?

                        fs::copy( &source_path, &target_path )
                            .map_err( |err| Error::CannotCopyFile( source_path.to_owned(), target_path.to_owned(), err ) )?;
                    }
                }
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::convert::From;
use std::fmt;
use stack::Stack;
use operate::Operate;
use try_from_iterator::TryFromIterator;
use try_from_ref::{TryFromRef, TryIntoRef};

/// Used to specify an `Operand` or an `Operator`.
#[derive(Debug, Copy, Clone)]
pub enum Arithm<T, O: Operate<T>> {
    Operand(T),
    Operator(O),
}

/// Interpret a [`Reverse Polish notated`] expression (cf. `3 4 +`).
///
/// `Operate` method returns the valid result or an [`Operate::Err`] if an operation fails.
///
/// `Expressions` are constructed from [`str`] the most of the time.
/// Use the [`try_into()`] method to create an `Expression` type,
/// the result contain informations about the possible error at conversion time.
///
/// [`Reverse Polish notated`]: https://en.wikipedia.org/wiki/Reverse_Polish_notation
/// [`Operate::Err`]: ../operate/trait.Operate.html#associatedtype.Err
/// [`str`]: https://doc.rust-lang.org/std/str/index.html
/// [`try_into()`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html#tymethod.try_into
#[derive(Debug)]
pub struct Expression<T, O: Operate<T>> {
    stack_max: usize,
    expr: Vec<Arithm<T, O>>,
}

impl<T: Copy, O: Operate<T> + Copy> Expression<T, O> {
    /// Evaluate the `RPN` expression. Returns the result
    /// or the [`operate Error`](../operate/trait.Operate.html#associatedtype.Err).
    pub fn operate(&self) -> Result<T, O::Err> {
        let mut stack = Stack::with_capacity(self.stack_max);
        for arithm in &self.expr {
            match *arithm {
                Arithm::Operand(operand) => stack.push(operand),
                Arithm::Operator(operator) => operator.operate(&mut stack)?,
            }
        }
        Ok(stack.pop().unwrap())
    }
}

impl TryFromIterator for Expression<T, O>
    where T: TryFromRef,
          O: Operate<T> + TryFromRef
{
    type Err = ExprResult<<T as TryFromRef>::Err, <O as TryFromRef>::Err>;
    fn try_from_iter(iter: I) -> Result<Self, Self::Err> where I: IntoIterator<Item=A> {
        use self::ExprResult::*;
        let mut final_expr = Vec::new();
        for token in iter {
            let arithm = match TryIntoRef::<T>::try_into_ref(&token) {
                Ok(operand) => Arithm::Operand(operand),
                Err(operand_err) => {
                    match TryIntoRef::<O>::try_into_ref(&token) {
                        Ok(operator) => Arithm::Operator(operator),
                        Err(operator_err) => return Err(InvalidToken(operand_err, operator_err)),
                    }
                }
            };
            final_expr.push(arithm);
        }
        match Expression::check_validity(&final_expr) {
            Ok(_) => Ok(Expression {
                stack_max: Expression::compute_stack_max(&final_expr),
                expr: final_expr
            }),
            Err(err) => Err(ExprResult::OperandErr(err)),
        }
    }
}

/// Used to specify the error during the convertion.
#[derive(Debug, PartialEq)]
pub enum ExprResult {
    OperandErr(OperandErr),
    InvalidToken(A, B),
}

#[derive(Debug, PartialEq)]
struct InvalidToken(A, B);

#[derive(Debug, PartialEq)]
pub enum OperandErr {
    TooManyOperands,
    NotEnoughOperand,
}

impl<T, O: Operate<T>> Expression<T, O> {
    fn check_validity(expr: &[Arithm<T, O>]) -> Result<(), OperandErr> {
        // TODO make this more Rusty
        use self::OperandErr::*;
        let mut num_operands: usize = 0;
        for arithm in expr {
            match *arithm {
                Arithm::Operand(_) => num_operands += 1,
                Arithm::Operator(ref operator) => {
                    let needed = operator.operands_needed();
                    num_operands = num_operands.checked_sub(needed).ok_or(NotEnoughOperand)?;
                    num_operands += operator.operands_generated();
                },
            }
        }
        match num_operands {
            0 => Err(NotEnoughOperand),
            1 => Ok(()),
            _ => Err(TooManyOperands),
        }
    }
}

impl<T, O: Operate<T>> Expression<T, O> {
    fn compute_stack_max(expr: &[Arithm<T, O>]) -> usize {
        expr.iter().map(|arithm| {
            match *arithm {
                Arithm::Operand(_) => 1,
                Arithm::Operator(ref op) => {
                    op.operands_generated() as isize - op.operands_needed() as isize
                }
            }
        }).fold((0, 0isize), |(max, acc_count), count| {
            let acc = (acc_count + count) as usize;
            if acc > max { (acc, acc as isize) } else { (max, acc as isize) }
        }).0
    }
}

// impl<'a, T, O> TryFrom<&'a str> for Expression<T, O>
//     where T: FromStr,
//           O: Operate<T> + TryFrom<&'a str>
// {
//     type Err = ExprResult<<T as FromStr>::Err, <O as TryFrom<&'a str>>::Err>;
//     fn try_from(expr: &'a str) -> Result<Self, Self::Err> {
//         use self::ExprResult::*;
//         let mut final_expr = Vec::new();
//         for token in expr.split_whitespace() {
//             let arithm = match token.parse() {
//                 Ok(operand) => Arithm::Operand(operand),
//                 Err(operand_err) => {
//                     match TryInto::<O>::try_into(token) {
//                         Ok(operator) => Arithm::Operator(operator),
//                         Err(operator_err) => return Err(InvalidToken(operand_err, operator_err)),
//                     }
//                 }
//             };
//             final_expr.push(arithm);
//         }
//         match Expression::check_validity(&final_expr) {
//             Ok(_) => Ok(Expression {
//                 stack_max: Expression::compute_stack_max(&final_expr),
//                 expr: final_expr
//             }),
//             Err(err) => Err(ExprResult::OperandErr(err)),
//         }
//     }
// }

impl<T, O: Operate<T>> From<Vec<Arithm<T, O>>> for Expression<T, O> {
    fn from(expr: Vec<Arithm<T, O>>) -> Self {
        Expression {
            stack_max: Expression::compute_stack_max(&expr),
            expr: expr
        }
    }
}

impl<T, O: Operate<T>> Into<Vec<Arithm<T, O>>> for Expression<T, O> {
    fn into(self) -> Vec<Arithm<T, O>> {
        self.expr
    }
}

impl<T, O> fmt::Display for Expression<T, O>
    where T: fmt::Display,
          O: Operate<T> + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let len = self.expr.len();
        for (i, arithm) in self.expr.iter().enumerate() {
            match *arithm {
                Arithm::Operand(ref operand) => operand.fmt(f)?,
                Arithm::Operator(ref operator) => operator.fmt(f)?,
            }
            if i != len - 1 {
                f.write_str(" ")?
            }
        }
        Ok(())
    }
}
<commit_msg>expression: Remove From/Into Vec<commit_after>use std::fmt;
use stack::Stack;
use operate::Operate;
use try_from_iterator::TryFromIterator;
use try_from_ref::{TryFromRef, TryIntoRef};

/// Used to specify an `Operand` or an `Operator`.
#[derive(Debug, Copy, Clone)]
pub enum Arithm<T, O: Operate<T>> {
    Operand(T),
    Operator(O),
}

/// Interpret a [`Reverse Polish notated`] expression (cf. `3 4 +`).
///
/// `Operate` method returns the valid result or an [`Operate::Err`] if an operation fails.
///
/// `Expressions` are constructed from [`str`] the most of the time.
/// Use the [`try_into()`] method to create an `Expression` type,
/// the result contain informations about the possible error at conversion time.
///
/// [`Reverse Polish notated`]: https://en.wikipedia.org/wiki/Reverse_Polish_notation
/// [`Operate::Err`]: ../operate/trait.Operate.html#associatedtype.Err
/// [`str`]: https://doc.rust-lang.org/std/str/index.html
/// [`try_into()`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html#tymethod.try_into
#[derive(Debug)]
pub struct Expression<T, O: Operate<T>> {
    stack_max: usize,
    expr: Vec<Arithm<T, O>>,
}

impl<T: Copy, O: Operate<T> + Copy> Expression<T, O> {
    /// Evaluate the `RPN` expression. Returns the result
    /// or the [`operate Error`](../operate/trait.Operate.html#associatedtype.Err).
    pub fn operate(&self) -> Result<T, O::Err> {
        let mut stack = Stack::with_capacity(self.stack_max);
        for arithm in &self.expr {
            match *arithm {
                Arithm::Operand(operand) => stack.push(operand),
                Arithm::Operator(operator) => operator.operate(&mut stack)?,
            }
        }
        Ok(stack.pop().unwrap())
    }
}

impl TryFromIterator for Expression<T, O>
    where T: TryFromRef,
          O: Operate<T> + TryFromRef
{
    type Err = ExprResult<<T as TryFromRef>::Err, <O as TryFromRef>::Err>;
    fn try_from_iter(iter: I) -> Result<Self, Self::Err> where I: IntoIterator<Item=A> {
        use self::ExprResult::*;
        let mut final_expr = Vec::new();
        for token in iter {
            let arithm = match TryIntoRef::<T>::try_into_ref(&token) {
                Ok(operand) => Arithm::Operand(operand),
                Err(operand_err) => {
                    match TryIntoRef::<O>::try_into_ref(&token) {
                        Ok(operator) => Arithm::Operator(operator),
                        Err(operator_err) => return Err(InvalidToken(operand_err, operator_err)),
                    }
                }
            };
            final_expr.push(arithm);
        }
        match Expression::check_validity(&final_expr) {
            Ok(_) => Ok(Expression {
                stack_max: Expression::compute_stack_max(&final_expr),
                expr: final_expr
            }),
            Err(err) => Err(ExprResult::OperandErr(err)),
        }
    }
}

/// Used to specify the error during the convertion.
#[derive(Debug, PartialEq)]
pub enum ExprResult {
    OperandErr(OperandErr),
    InvalidToken(A, B),
}

#[derive(Debug, PartialEq)]
struct InvalidToken(A, B);

#[derive(Debug, PartialEq)]
pub enum OperandErr {
    TooManyOperands,
    NotEnoughOperand,
}

impl<T, O: Operate<T>> Expression<T, O> {
    fn check_validity(expr: &[Arithm<T, O>]) -> Result<(), OperandErr> {
        // TODO make this more Rusty
        use self::OperandErr::*;
        let mut num_operands: usize = 0;
        for arithm in expr {
            match *arithm {
                Arithm::Operand(_) => num_operands += 1,
                Arithm::Operator(ref operator) => {
                    let needed = operator.operands_needed();
                    num_operands = num_operands.checked_sub(needed).ok_or(NotEnoughOperand)?;
                    num_operands += operator.operands_generated();
                },
            }
        }
        match num_operands {
            0 => Err(NotEnoughOperand),
            1 => Ok(()),
            _ => Err(TooManyOperands),
        }
    }
}

impl<T, O: Operate<T>> Expression<T, O> {
    fn compute_stack_max(expr: &[Arithm<T, O>]) -> usize {
        expr.iter().map(|arithm| {
            match *arithm {
                Arithm::Operand(_) => 1,
                Arithm::Operator(ref op) => {
                    op.operands_generated() as isize - op.operands_needed() as isize
                }
            }
        }).fold((0, 0isize), |(max, acc_count), count| {
            let acc = (acc_count + count) as usize;
            if acc > max { (acc, acc as isize) } else { (max, acc as isize) }
        }).0
    }
}

impl<T, O> fmt::Display for Expression<T, O>
    where T: fmt::Display,
          O: Operate<T> + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let len = self.expr.len();
        for (i, arithm) in self.expr.iter().enumerate() {
            match *arithm {
                Arithm::Operand(ref operand) => operand.fmt(f)?,
                Arithm::Operator(ref operator) => operator.fmt(f)?,
            }
            if i != len - 1 {
                f.write_str(" ")?
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains types and functions to support formatting specific line ranges.
use std::{cmp, iter, str};

use itertools::Itertools;
use multimap::MultiMap;
use rustc_serialize::{self, json};

use codemap::LineRange;

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcDecodable)]
struct Range {
    pub lo: usize,
    pub hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    fn new(lo: usize, hi: usize) -> Range {
        Range { lo: lo, hi: hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi) ||
            (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(cmp::min(self.lo, other.lo), cmp::max(self.hi, other.hi)))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<MultiMap<String, Range>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(map: &mut MultiMap<String, Range>) {
    for (_, ranges) in map.iter_all_mut() {
        ranges.sort_by_key(|x| x.lo);
        let merged = ranges.drain(..).coalesce(|x, y| x.merge(y).ok_or((x, y))).collect();
        *ranges = merged;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub fn all() -> FileLines {
        FileLines(None)
    }

    /// Creates a `FileLines` from a `MultiMap`, ensuring that the invariants hold.
    fn from_multimap(map: MultiMap<String, Range>) -> FileLines {
        let mut map = map;
        normalize_ranges(&mut map);
        FileLines(Some(map))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(MultiMap::keys))
    }

    /// Returns true if `range` is fully contained in `self`.
    pub fn contains(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match map.get_vec(range.file_name()) {
            None => false,
            Some(ranges) => ranges.iter().any(|r| r.contains(Range::from(range))),
        }
    }

    /// Returns true if any lines in `range` are in `self`.
    pub fn intersects(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match map.get_vec(range.file_name()) {
            None => false,
            Some(ranges) => ranges.iter().any(|r| r.intersects(Range::from(range))),
        }
    }
}

/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = try!(json::decode(s).map_err(|e| e.to_string()));
        let m = v.into_iter().map(JsonSpan::into_tuple).collect();
        Ok(FileLines::from_multimap(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, RustcDecodable)]
struct JsonSpan {
    file: String,
    range: (usize, usize),
}

impl JsonSpan {
    // To allow `collect()`ing into a `MultiMap`.
    fn into_tuple(self) -> (String, Range) {
        let (lo, hi) = self.range;
        (self.file, Range::new(lo, hi))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl rustc_serialize::Decodable for FileLines {
    fn decode<D: rustc_serialize::Decoder>(_: &mut D) -> Result<Self, D::Error> {
        panic!("FileLines cannot be deserialized from a project rustfmt.toml file: please \
                specify it via the `--file-lines` option instead");
    }
}

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

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(4, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(5, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(6, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 7).merge(Range::new(4, 5)));
    }
}
<commit_msg>Canonicalize file paths within the map of file line ranges (#1098)<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains types and functions to support formatting specific line ranges.
use std::{cmp, iter, path, str};

use itertools::Itertools;
use multimap::MultiMap;
use rustc_serialize::{self, json};

use codemap::LineRange;

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcDecodable)]
struct Range {
    pub lo: usize,
    pub hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    fn new(lo: usize, hi: usize) -> Range {
        Range { lo: lo, hi: hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi) ||
            (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(cmp::min(self.lo, other.lo), cmp::max(self.hi, other.hi)))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<MultiMap<String, Range>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(map: &mut MultiMap<String, Range>) {
    for (_, ranges) in map.iter_all_mut() {
        ranges.sort_by_key(|x| x.lo);
        let merged = ranges.drain(..).coalesce(|x, y| x.merge(y).ok_or((x, y))).collect();
        *ranges = merged;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub fn all() -> FileLines {
        FileLines(None)
    }

    /// Creates a `FileLines` from a `MultiMap`, ensuring that the invariants hold.
    fn from_multimap(map: MultiMap<String, Range>) -> FileLines {
        let mut map = map;
        normalize_ranges(&mut map);
        FileLines(Some(map))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(MultiMap::keys))
    }

    /// Returns true if `range` is fully contained in `self`.
    pub fn contains(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(range.file_name())
            .and_then(|canonical| map.get_vec(&canonical).ok_or(())) {
            Ok(ranges) => ranges.iter().any(|r| r.contains(Range::from(range))),
            Err(_) => false,
        }
    }

    /// Returns true if any lines in `range` are in `self`.
    pub fn intersects(&self, range: &LineRange) -> bool {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match map.get_vec(range.file_name()) {
            None => false,
            Some(ranges) => ranges.iter().any(|r| r.intersects(Range::from(range))),
        }
    }
}

/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(s: &str) -> Result<String, ()> {
    match path::PathBuf::from(s).canonicalize() {
        Ok(canonicalized) => canonicalized.to_str().map(|s| s.to_string()).ok_or(()),
        _ => Err(()),
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = try!(json::decode(s).map_err(|e| e.to_string()));
        let m = try!(v.into_iter().map(JsonSpan::into_tuple).collect());
        Ok(FileLines::from_multimap(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, RustcDecodable)]
struct JsonSpan {
    file: String,
    range: (usize, usize),
}

impl JsonSpan {
    // To allow `collect()`ing into a `MultiMap`.
    fn into_tuple(self) -> Result<(String, Range), String> {
        let (lo, hi) = self.range;
        let canonical = try!(canonicalize_path_string(&self.file)
            .map_err(|_| format!("Can't canonicalize {}", &self.file)));
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl rustc_serialize::Decodable for FileLines {
    fn decode<D: rustc_serialize::Decoder>(_: &mut D) -> Result<Self, D::Error> {
        panic!("FileLines cannot be deserialized from a project rustfmt.toml file: please \
                specify it via the `--file-lines` option instead");
    }
}

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

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(4, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(5, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 5).merge(Range::new(6, 7)));
        assert_eq!(Some(Range::new(3, 7)),
                   Range::new(3, 7).merge(Range::new(4, 5)));
    }
}
<|endoftext|>