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

use std::path::{Path, PathBuf};

use crate::bindgen::cargo::cargo_expand;
use crate::bindgen::cargo::cargo_lock::{self, Lock};
pub(crate) use crate::bindgen::cargo::cargo_metadata::PackageRef;
use crate::bindgen::cargo::cargo_metadata::{self, Metadata};
use crate::bindgen::cargo::cargo_toml;
use crate::bindgen::config::Profile;
use crate::bindgen::error::Error;
use crate::bindgen::ir::Cfg;

/// Parse a dependency string used in Cargo.lock
fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) {
    let split: Vec<&str> = dep_string.split_whitespace().collect();

    (split[0], split.get(1).cloned())
}

/// A collection of metadata for a library from cargo.
#[derive(Clone, Debug)]
pub(crate) struct Cargo {
    manifest_path: PathBuf,
    binding_crate_name: String,
    lock: Option<Lock>,
    metadata: Metadata,
    clean: bool,
}

impl Cargo {
    /// Gather metadata from cargo for a specific library and binding crate
    /// name. If dependency finding isn't needed then Cargo.lock files don't
    /// need to be parsed.
    pub(crate) fn load(
        crate_dir: &Path,
        lock_file: Option<&str>,
        binding_crate_name: Option<&str>,
        use_cargo_lock: bool,
        clean: bool,
        only_target_dependencies: bool,
        existing_metadata_file: Option<&Path>,
    ) -> Result<Cargo, Error> {
        let toml_path = crate_dir.join("Cargo.toml");
        let metadata =
            cargo_metadata::metadata(&toml_path, existing_metadata_file, only_target_dependencies)
                .map_err(|x| Error::CargoMetadata(toml_path.to_str().unwrap().to_owned(), x))?;
        let lock_path = lock_file
            .map(PathBuf::from)
            .unwrap_or_else(|| Path::new(&metadata.workspace_root).join("Cargo.lock"));

        let lock = if use_cargo_lock {
            match cargo_lock::lock(&lock_path) {
                Ok(lock) => Some(lock),
                Err(x) => {
                    warn!("Couldn't load lock file {:?}: {:?}", lock_path, x);
                    None
                }
            }
        } else {
            None
        };

        // Use the specified binding crate name or infer it from the manifest
        let binding_crate_name = match binding_crate_name {
            Some(s) => s.to_owned(),
            None => {
                let manifest = cargo_toml::manifest(&toml_path)
                    .map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?;
                manifest.package.name
            }
        };

        Ok(Cargo {
            manifest_path: toml_path,
            binding_crate_name,
            lock,
            metadata,
            clean,
        })
    }

    pub(crate) fn binding_crate_name(&self) -> &str {
        &self.binding_crate_name
    }

    pub(crate) fn binding_crate_ref(&self) -> PackageRef {
        match self.find_pkg_ref(&self.binding_crate_name) {
            Some(pkg_ref) => pkg_ref,
            None => panic!(
                "Unable to find {} for {:?}",
                self.binding_crate_name, self.manifest_path
            ),
        }
    }

    pub(crate) fn dependencies(&self, package: &PackageRef) -> Vec<(PackageRef, Option<Cfg>)> {
        let lock = match self.lock {
            Some(ref lock) => lock,
            None => return vec![],
        };

        let mut dependencies = None;

        // Find the dependencies listing in the lockfile
        if let Some(ref root) = lock.root {
            // If the version is not on the lockfile then it shouldn't be
            // ambiguous.
            if root.name == package.name
                && package
                    .version
                    .as_ref()
                    .map_or(true, |v| *v == root.version)
            {
                dependencies = root.dependencies.as_ref();
            }
        }
        if dependencies.is_none() {
            if let Some(ref lock_packages) = lock.package {
                for lock_package in lock_packages {
                    if lock_package.name == package.name
                        && package
                            .version
                            .as_ref()
                            .map_or(true, |v| *v == lock_package.version)
                    {
                        dependencies = lock_package.dependencies.as_ref();
                        break;
                    }
                }
            }
        }
        if dependencies.is_none() {
            return vec![];
        }

        dependencies
            .unwrap()
            .iter()
            .map(|dep| {
                let (dep_name, dep_version) = parse_dep_string(dep);

                // If a version was not specified find the only package with the name of the dependency
                let dep_version = dep_version.or_else(|| {
                    let mut versions = self.metadata.packages.iter().filter_map(|package| {
                        if package.name_and_version.name != dep_name {
                            return None;
                        }
                        package.name_and_version.version.as_deref()
                    });

                    // If the iterator contains more items, meaning multiple versions of the same
                    // package are present, warn! amd abort.
                    let version = versions.next();
                    if versions.next().is_none() {
                        version
                    } else {
                        warn!("when looking for a version for package {}, multiple versions where found", dep_name);
                        None
                    }
                });

                // Try to find the cfgs in the Cargo.toml
                let cfg = self
                    .metadata
                    .packages
                    .get(package)
                    .and_then(|meta_package| meta_package.dependencies.get(dep_name))
                    .and_then(|meta_dep| Cfg::load_metadata(meta_dep));

                let package_ref = PackageRef {
                    name: dep_name.to_owned(),
                    version: dep_version.map(|v| v.to_owned()),
                };

                (package_ref, cfg)
            })
            .collect()
    }

    /// Finds the package reference in `cargo metadata` that has `package_name`
    /// ignoring the version.
    fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
        for package in &self.metadata.packages {
            if package.name_and_version.name == package_name {
                return Some(package.name_and_version.clone());
            }
        }
        None
    }

    /// Finds the directory for a specified package reference.
    #[allow(unused)]
    pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
        self.metadata
            .packages
            .get(package)
            .and_then(|meta_package| {
                Path::new(&meta_package.manifest_path)
                    .parent()
                    .map(|x| x.to_owned())
            })
    }

    /// Finds `src/lib.rs` for a specified package reference.
    pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
        let kind_lib = String::from("lib");
        let kind_staticlib = String::from("staticlib");
        let kind_rlib = String::from("rlib");
        let kind_cdylib = String::from("cdylib");
        let kind_dylib = String::from("dylib");

        self.metadata
            .packages
            .get(package)
            .and_then(|meta_package| {
                for target in &meta_package.targets {
                    if target.kind.contains(&kind_lib)
                        || target.kind.contains(&kind_staticlib)
                        || target.kind.contains(&kind_rlib)
                        || target.kind.contains(&kind_cdylib)
                        || target.kind.contains(&kind_dylib)
                    {
                        return Some(PathBuf::from(&target.src_path));
                    }
                }
                None
            })
    }

    pub(crate) fn expand_crate(
        &self,
        package: &PackageRef,
        expand_all_features: bool,
        expand_default_features: bool,
        expand_features: &Option<Vec<String>>,
        profile: Profile,
    ) -> Result<String, cargo_expand::Error> {
        cargo_expand::expand(
            &self.manifest_path,
            &package.name,
            package.version.as_deref(),
            self.clean,
            expand_all_features,
            expand_default_features,
            expand_features,
            profile,
        )
    }
}
<commit_msg>Address clippy lint.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::path::{Path, PathBuf};

use crate::bindgen::cargo::cargo_expand;
use crate::bindgen::cargo::cargo_lock::{self, Lock};
pub(crate) use crate::bindgen::cargo::cargo_metadata::PackageRef;
use crate::bindgen::cargo::cargo_metadata::{self, Metadata};
use crate::bindgen::cargo::cargo_toml;
use crate::bindgen::config::Profile;
use crate::bindgen::error::Error;
use crate::bindgen::ir::Cfg;

/// Parse a dependency string used in Cargo.lock
fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) {
    let split: Vec<&str> = dep_string.split_whitespace().collect();

    (split[0], split.get(1).cloned())
}

/// A collection of metadata for a library from cargo.
#[derive(Clone, Debug)]
pub(crate) struct Cargo {
    manifest_path: PathBuf,
    binding_crate_name: String,
    lock: Option<Lock>,
    metadata: Metadata,
    clean: bool,
}

impl Cargo {
    /// Gather metadata from cargo for a specific library and binding crate
    /// name. If dependency finding isn't needed then Cargo.lock files don't
    /// need to be parsed.
    pub(crate) fn load(
        crate_dir: &Path,
        lock_file: Option<&str>,
        binding_crate_name: Option<&str>,
        use_cargo_lock: bool,
        clean: bool,
        only_target_dependencies: bool,
        existing_metadata_file: Option<&Path>,
    ) -> Result<Cargo, Error> {
        let toml_path = crate_dir.join("Cargo.toml");
        let metadata =
            cargo_metadata::metadata(&toml_path, existing_metadata_file, only_target_dependencies)
                .map_err(|x| Error::CargoMetadata(toml_path.to_str().unwrap().to_owned(), x))?;
        let lock_path = lock_file
            .map(PathBuf::from)
            .unwrap_or_else(|| Path::new(&metadata.workspace_root).join("Cargo.lock"));

        let lock = if use_cargo_lock {
            match cargo_lock::lock(&lock_path) {
                Ok(lock) => Some(lock),
                Err(x) => {
                    warn!("Couldn't load lock file {:?}: {:?}", lock_path, x);
                    None
                }
            }
        } else {
            None
        };

        // Use the specified binding crate name or infer it from the manifest
        let binding_crate_name = match binding_crate_name {
            Some(s) => s.to_owned(),
            None => {
                let manifest = cargo_toml::manifest(&toml_path)
                    .map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?;
                manifest.package.name
            }
        };

        Ok(Cargo {
            manifest_path: toml_path,
            binding_crate_name,
            lock,
            metadata,
            clean,
        })
    }

    pub(crate) fn binding_crate_name(&self) -> &str {
        &self.binding_crate_name
    }

    pub(crate) fn binding_crate_ref(&self) -> PackageRef {
        match self.find_pkg_ref(&self.binding_crate_name) {
            Some(pkg_ref) => pkg_ref,
            None => panic!(
                "Unable to find {} for {:?}",
                self.binding_crate_name, self.manifest_path
            ),
        }
    }

    pub(crate) fn dependencies(&self, package: &PackageRef) -> Vec<(PackageRef, Option<Cfg>)> {
        let lock = match self.lock {
            Some(ref lock) => lock,
            None => return vec![],
        };

        let mut dependencies = None;

        // Find the dependencies listing in the lockfile
        if let Some(ref root) = lock.root {
            // If the version is not on the lockfile then it shouldn't be
            // ambiguous.
            if root.name == package.name
                && package
                    .version
                    .as_ref()
                    .map_or(true, |v| *v == root.version)
            {
                dependencies = root.dependencies.as_ref();
            }
        }
        if dependencies.is_none() {
            if let Some(ref lock_packages) = lock.package {
                for lock_package in lock_packages {
                    if lock_package.name == package.name
                        && package
                            .version
                            .as_ref()
                            .map_or(true, |v| *v == lock_package.version)
                    {
                        dependencies = lock_package.dependencies.as_ref();
                        break;
                    }
                }
            }
        }
        if dependencies.is_none() {
            return vec![];
        }

        dependencies
            .unwrap()
            .iter()
            .map(|dep| {
                let (dep_name, dep_version) = parse_dep_string(dep);

                // If a version was not specified find the only package with the name of the dependency
                let dep_version = dep_version.or_else(|| {
                    let mut versions = self.metadata.packages.iter().filter_map(|package| {
                        if package.name_and_version.name != dep_name {
                            return None;
                        }
                        package.name_and_version.version.as_deref()
                    });

                    // If the iterator contains more items, meaning multiple versions of the same
                    // package are present, warn! amd abort.
                    let version = versions.next();
                    if versions.next().is_none() {
                        version
                    } else {
                        warn!("when looking for a version for package {}, multiple versions where found", dep_name);
                        None
                    }
                });

                // Try to find the cfgs in the Cargo.toml
                let cfg = self
                    .metadata
                    .packages
                    .get(package)
                    .and_then(|meta_package| meta_package.dependencies.get(dep_name))
                    .and_then(Cfg::load_metadata);

                let package_ref = PackageRef {
                    name: dep_name.to_owned(),
                    version: dep_version.map(|v| v.to_owned()),
                };

                (package_ref, cfg)
            })
            .collect()
    }

    /// Finds the package reference in `cargo metadata` that has `package_name`
    /// ignoring the version.
    fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
        for package in &self.metadata.packages {
            if package.name_and_version.name == package_name {
                return Some(package.name_and_version.clone());
            }
        }
        None
    }

    /// Finds the directory for a specified package reference.
    #[allow(unused)]
    pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
        self.metadata
            .packages
            .get(package)
            .and_then(|meta_package| {
                Path::new(&meta_package.manifest_path)
                    .parent()
                    .map(|x| x.to_owned())
            })
    }

    /// Finds `src/lib.rs` for a specified package reference.
    pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
        let kind_lib = String::from("lib");
        let kind_staticlib = String::from("staticlib");
        let kind_rlib = String::from("rlib");
        let kind_cdylib = String::from("cdylib");
        let kind_dylib = String::from("dylib");

        self.metadata
            .packages
            .get(package)
            .and_then(|meta_package| {
                for target in &meta_package.targets {
                    if target.kind.contains(&kind_lib)
                        || target.kind.contains(&kind_staticlib)
                        || target.kind.contains(&kind_rlib)
                        || target.kind.contains(&kind_cdylib)
                        || target.kind.contains(&kind_dylib)
                    {
                        return Some(PathBuf::from(&target.src_path));
                    }
                }
                None
            })
    }

    pub(crate) fn expand_crate(
        &self,
        package: &PackageRef,
        expand_all_features: bool,
        expand_default_features: bool,
        expand_features: &Option<Vec<String>>,
        profile: Profile,
    ) -> Result<String, cargo_expand::Error> {
        cargo_expand::expand(
            &self.manifest_path,
            &package.name,
            package.version.as_deref(),
            self.clean,
            expand_all_features,
            expand_default_features,
            expand_features,
            profile,
        )
    }
}
<|endoftext|>


<commit_before>use devices::{NodeId, ServiceKind, Service, Input, Output};
use util::{Exactly, Id};
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<NodeId>>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: Id<NodeId>) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selector.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = InputSelector::new()
///   .with_parent(Id<NodeId>::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Input>>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: Id<Input>) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Output>>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: Id<Output>) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) => false,
            (&Some(_), &None) => false,
            _ => true,
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<commit_msg>Fixed doctest<commit_after>use devices::{NodeId, ServiceKind, Service, Input, Output};
use util::{Exactly, Id};
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<NodeId>>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: Id<NodeId>) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selector.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
/// use fxbox_taxonomy::util::Id;
///
/// let selector = InputSelector::new()
///   .with_parent(Id<NodeId>::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Input>>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: Id<Input>) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Output>>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: Id<Output>) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) => false,
            (&Some(_), &None) => false,
            _ => true,
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<|endoftext|>


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

//!Media module
//!
//!Provides functionality to upload images, GIFs and videos using Twitter Media API

use std::borrow::Cow;
use std::collections::HashMap;
use std::time::Duration;

use futures::{Future, Async, Poll};
use rustc_serialize::{json, base64};
use rustc_serialize::base64::{ToBase64};
use tokio_core::reactor::Timeout;

use common::*;
use error;
use error::Error::InvalidResponse;
use links;
use auth;

use mime;

/// A collection of convenience functions that return media types accepted by Twitter.
pub mod media_types {
    use mime::{self, Mime};

    /// PNG images.
    pub fn image_png() -> Mime {
        mime::IMAGE_PNG
    }

    /// JPG images.
    pub fn image_jpg() -> Mime {
        mime::IMAGE_JPEG
    }

    /// WEBP images.
    pub fn image_webp() -> Mime {
        "image/webp".parse().unwrap()
    }

    /// GIF images, both animated and static.
    pub fn image_gif() -> Mime {
        mime::IMAGE_GIF
    }

    /// MP4 videos.
    pub fn video_mp4() -> Mime {
        "video/mp4".parse().unwrap()
    }
}

///Media's upload progressing info.
#[derive(Debug, PartialEq)]
pub enum ProgressInfo {
    ///Video is pending for processing. Contains number of seconds after which to check.
    Pending(u64),
    ///Video is beeing processed. Contains number of seconds after which to check.
    InProgress(u64),
    ///Video's processing failed. Contains reason.
    Failed(error::MediaError),
    ///Video's processing is finished. Media can be used in other API calls.
    Success
}

impl FromJson for ProgressInfo {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        field_present!(input, state);
        let state: String = try!(field(input, "state"));

        match state.as_ref() {
            "pending" => {
                field_present!(input, check_after_secs);
                Ok(ProgressInfo::Pending(try!(field(input, "check_after_secs"))))
            },
            "in_progress" => {
                field_present!(input, check_after_secs);
                Ok(ProgressInfo::InProgress(try!(field(input, "check_after_secs"))))
            },
            "failed" => {
                field_present!(input, error);
                Ok(ProgressInfo::Failed(try!(field(input, "error"))))
            },
            "succeeded" => Ok(ProgressInfo::Success),
            state => Err(InvalidResponse("Unexpected progress info state", Some(state.to_string())))
        }
    }
}

///Represents media file that is uploaded on twitter.
pub struct Media {
    ///ID that can be used in API calls (e.g. attach to tweet).
    pub id: u64,
    ///Number of second the media can be used in other API calls.
    pub expires_after: u64,
    ///Progress information. If present determines whether Media can be used.
    pub progress: Option<ProgressInfo>
}

impl FromJson for Media {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("Tweet received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, media_id);

        Ok(Media {
            id: try!(field(input, "media_id")),
            //We can miss this field on failed upload in which case 0 is pretty reasonable value.
            expires_after: field(input, "expires_after_secs").unwrap_or(0),
            progress: try!(field(input, "processing_info"))
        })
    }
}

///Uploads image using old twitter API.
///
///The image should be raw binary with content of file.
pub fn upload_image(image: &[u8], token: &auth::Token, handle: &Handle) -> FutureResponse<Media> {
    let mut params = HashMap::new();

    let config = base64::Config {
        char_set: base64::CharacterSet::Standard,
        newline: base64::Newline::LF,
        pad: true,
        line_length: None,
    };

    add_param(&mut params, "media_data", image.to_base64(config));

    let req = auth::post(links::medias::UPLOAD, token, Some(&params));
    make_parsed_future(handle, req)
}

/// Represents the kinda of media that Twitter will accept.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MediaCategory {
    /// Static image. Four can be attached to a single tweet.
    Image,
    /// Animated GIF.
    Gif,
    /// Video.
    Video,
}

/// `Display` impl for `MediaCategory` so that `.to_string()` will return a string suitable for use
/// in an API call. This will turn the enum into `"tweet_image"`, `"tweet_gif"`, and
/// `"tweet_video"`.
impl ::std::fmt::Display for MediaCategory {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            MediaCategory::Image => write!(fmt, "tweet_image"),
            MediaCategory::Gif => write!(fmt, "tweet_gif"),
            MediaCategory::Video => write!(fmt, "tweet_video"),
        }
    }
}

/// A builder struct that allows you to build up parameters to a media upload before initiating it.
pub struct UploadBuilder<'a> {
    data: Cow<'a, [u8]>,
    media_type: mime::Mime,
    chunk_size: Option<usize>,
    category: Option<MediaCategory>,
}

impl<'a> UploadBuilder<'a> {
    /// Begins setting up a media upload call, with the given data and media type.
    ///
    /// For convenience functions to get known `media_type`s that Twitter will accept, see the
    /// [`media_types`] module.
    ///
    /// [`media_types`]: media_types/index.html
    pub fn new<V: Into<Cow<'a, [u8]>>>(data: V, media_type: mime::Mime) -> UploadBuilder<'a> {
        UploadBuilder {
            data: data.into(),
            media_type,
            chunk_size: None,
            category: None,
        }
    }

    /// Sets how many bytes to upload in one network call. By default this is set to 512 KiB.
    pub fn chunk_size(self, chunk_size: usize) -> Self {
        UploadBuilder {
            chunk_size: Some(chunk_size),
            ..self
        }
    }

    /// Sets the `media_category` sent to Twitter. When unset, it behaves as if you sent
    /// `MediaCategory::Image`.
    pub fn category(self, category: MediaCategory) -> Self {
        UploadBuilder {
            category: Some(category),
            ..self
        }
    }

    /// Collects the built-up parameters and begins the chunked upload.
    pub fn call(self, token: &auth::Token, handle: &Handle) -> UploadFuture<'a> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "INIT");
        add_param(&mut params, "total_bytes", self.data.len().to_string());
        add_param(&mut params, "media_type", self.media_type.to_string());

        if let Some(category) = self.category {
            add_param(&mut params, "media_category", category.to_string());
        }

        let req = auth::post(links::medias::UPLOAD, token, Some(&params));
        let loader = make_parsed_future(handle, req);
        UploadFuture {
            data: self.data,
            token: token.clone(),
            handle: handle.clone(),
            chunk_size: self.chunk_size.unwrap_or(1024 * 512), // 512 KiB default
            status: UploadInner::WaitingForInit(loader),
        }
    }
}

/// A `Future` that represents an in-progress media upload.
pub struct UploadFuture<'a> {
    data: Cow<'a, [u8]>,
    token: auth::Token,
    handle: Handle,
    chunk_size: usize,
    status: UploadInner,
}

/// The current status of an `UploadFuture`.
enum UploadInner {
    /// The `UploadFuture` is waiting to initialize the media upload session.
    WaitingForInit(FutureResponse<Media>),
    /// The `UploadFuture` is in the progress of uploading data.
    UploadingChunk(u64, usize, FutureResponse<()>),
    /// The `UploadFuture` is currently finalizing the media with Twitter.
    Finalizing(FutureResponse<Media>),
    /// The `UploadFuture` is waiting on Twitter to finish processing a video or gif.
    PostProcessing(u64, Timeout),
    /// The `UploadFuture` has completed, or has encountered an error.
    Invalid,
}

impl<'a> UploadFuture<'a> {
    fn get_chunk(&self, chunk_num: usize) -> Option<&[u8]> {
        let start = chunk_num * self.chunk_size;
        let end = (chunk_num + 1) * self.chunk_size;

        if start >= self.data.len() {
            None
        } else if end >= self.data.len() {
            Some(&self.data[start..])
        } else {
            Some(&self.data[start..end])
        }
    }

    fn append(&self, chunk_num: usize, media_id: u64) -> Option<FutureResponse<()>> {
        let mut chunk = self.get_chunk(chunk_num);
        if chunk.is_none() && chunk_num == 0 {
            chunk = Some(&[][..]);
        }

        if let Some(chunk) = chunk {
            let mut params = HashMap::new();

            let config = base64::Config {
                char_set: base64::CharacterSet::Standard,
                newline: base64::Newline::LF,
                pad: true,
                line_length: None,
            };

            add_param(&mut params, "command", "APPEND");
            add_param(&mut params, "media_id", media_id.to_string());
            add_param(&mut params, "media_data", chunk.to_base64(config));
            add_param(&mut params, "segment_index", chunk_num.to_string());

            let req = auth::post(links::medias::UPLOAD, &self.token, Some(&params));
            Some(make_parsed_future(&self.handle, req))
        } else {
            None
        }
    }

    fn finalize(&self, media_id: u64) -> FutureResponse<Media> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "FINALIZE");
        add_param(&mut params, "media_id", media_id.to_string());

        let req = auth::post(links::medias::UPLOAD, &self.token, Some(&params));
        make_parsed_future(&self.handle, req)
    }

    fn status(&self, media_id: u64) -> FutureResponse<Media> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "STATUS");
        add_param(&mut params, "media_id", media_id.to_string());

        let req = auth::get(links::medias::UPLOAD, &self.token, Some(&params));
        make_parsed_future(&self.handle, req)
    }
}

impl<'a> Future for UploadFuture<'a> {
    type Item = Response<Media>;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        use std::mem::replace;

        match replace(&mut self.status, UploadInner::Invalid) {
            UploadInner::WaitingForInit(mut init) => {
                match init.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::WaitingForInit(init);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(media)) => {
                        let id = media.id;
                        let loader = self.append(0, id).unwrap();
                        self.status = UploadInner::UploadingChunk(id, 0, loader);
                        self.poll()
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::UploadingChunk(id, chunk_idx, mut upload) => {
                match upload.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::UploadingChunk(id, chunk_idx, upload);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(_)) => {
                        let chunk_idx = chunk_idx + 1;
                        if let Some(upload) = self.append(chunk_idx, id) {
                            self.status = UploadInner::UploadingChunk(id, chunk_idx, upload);
                        } else {
                            let loader = self.finalize(id);
                            self.status = UploadInner::Finalizing(loader);
                        }

                        self.poll()
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::Finalizing(mut finalize) => {
                match finalize.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::Finalizing(finalize);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(media)) => {
                        if media.progress.is_none() || media.progress == Some(ProgressInfo::Success) {
                            return Ok(Async::Ready(media));
                        }

                        match media.progress {
                            Some(ProgressInfo::Pending(time)) |
                                Some(ProgressInfo::InProgress(time)) =>
                            {
                                let timer = try!(Timeout::new(Duration::from_secs(time), &self.handle));
                                self.status = UploadInner::PostProcessing(media.id, timer);
                                self.poll()
                            },
                            Some(ProgressInfo::Failed(ref err)) =>
                                Err(error::Error::MediaError(err.clone())),
                            None | Some(ProgressInfo::Success) => unreachable!(),
                        }
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::PostProcessing(id, mut timer) => {
                match timer.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::PostProcessing(id, timer);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(())) => {
                        let loader = self.status(id);
                        self.status = UploadInner::Finalizing(loader);
                        self.poll()
                    },
                    Err(e) => Err(e.into()),
                }
            },
            UploadInner::Invalid => Err(error::Error::FutureAlreadyCompleted),
        }
    }
}

#[cfg(test)]
mod tests {
    use common::FromJson;

    use super::Media;

    use std::fs::File;
    use std::io::Read;

    fn load_media(path: &str) -> Media {
        let mut file = File::open(path).unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();
        Media::from_str(&content).unwrap()
    }

    #[test]
    fn parse_media() {
        let media = load_media("src/media/media.json");

        assert_eq!(media.id, 710511363345354753);
        assert_eq!(media.expires_after, 86400);
    }

    #[test]
    fn parse_media_pending() {
        let media = load_media("src/media/media_pending.json");

        assert_eq!(media.id, 13);
        assert_eq!(media.expires_after, 86400);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::Pending(5)) => (),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }

    #[test]
    fn parse_media_in_progress() {
        let media = load_media("src/media/media_in_progress.json");

        assert_eq!(media.id, 13);
        assert_eq!(media.expires_after, 3595);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::InProgress(10)) => (),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }

    #[test]
    fn parse_media_fail() {
        let media = load_media("src/media/media_fail.json");

        assert_eq!(media.id, 710511363345354753);
        assert_eq!(media.expires_after, 0);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::Failed(error)) => assert_eq!(error, "InvalidMedia: Unsupported video format"),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }
}
<commit_msg>take out media::upload_media<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//!Media module
//!
//!Provides functionality to upload images, GIFs and videos using Twitter Media API

use std::borrow::Cow;
use std::collections::HashMap;
use std::time::Duration;

use futures::{Future, Async, Poll};
use rustc_serialize::{json, base64};
use rustc_serialize::base64::{ToBase64};
use tokio_core::reactor::Timeout;

use common::*;
use error;
use error::Error::InvalidResponse;
use links;
use auth;

use mime;

/// A collection of convenience functions that return media types accepted by Twitter.
pub mod media_types {
    use mime::{self, Mime};

    /// PNG images.
    pub fn image_png() -> Mime {
        mime::IMAGE_PNG
    }

    /// JPG images.
    pub fn image_jpg() -> Mime {
        mime::IMAGE_JPEG
    }

    /// WEBP images.
    pub fn image_webp() -> Mime {
        "image/webp".parse().unwrap()
    }

    /// GIF images, both animated and static.
    pub fn image_gif() -> Mime {
        mime::IMAGE_GIF
    }

    /// MP4 videos.
    pub fn video_mp4() -> Mime {
        "video/mp4".parse().unwrap()
    }
}

///Media's upload progressing info.
#[derive(Debug, PartialEq)]
pub enum ProgressInfo {
    ///Video is pending for processing. Contains number of seconds after which to check.
    Pending(u64),
    ///Video is beeing processed. Contains number of seconds after which to check.
    InProgress(u64),
    ///Video's processing failed. Contains reason.
    Failed(error::MediaError),
    ///Video's processing is finished. Media can be used in other API calls.
    Success
}

impl FromJson for ProgressInfo {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        field_present!(input, state);
        let state: String = try!(field(input, "state"));

        match state.as_ref() {
            "pending" => {
                field_present!(input, check_after_secs);
                Ok(ProgressInfo::Pending(try!(field(input, "check_after_secs"))))
            },
            "in_progress" => {
                field_present!(input, check_after_secs);
                Ok(ProgressInfo::InProgress(try!(field(input, "check_after_secs"))))
            },
            "failed" => {
                field_present!(input, error);
                Ok(ProgressInfo::Failed(try!(field(input, "error"))))
            },
            "succeeded" => Ok(ProgressInfo::Success),
            state => Err(InvalidResponse("Unexpected progress info state", Some(state.to_string())))
        }
    }
}

///Represents media file that is uploaded on twitter.
pub struct Media {
    ///ID that can be used in API calls (e.g. attach to tweet).
    pub id: u64,
    ///Number of second the media can be used in other API calls.
    pub expires_after: u64,
    ///Progress information. If present determines whether Media can be used.
    pub progress: Option<ProgressInfo>
}

impl FromJson for Media {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("Tweet received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, media_id);

        Ok(Media {
            id: try!(field(input, "media_id")),
            //We can miss this field on failed upload in which case 0 is pretty reasonable value.
            expires_after: field(input, "expires_after_secs").unwrap_or(0),
            progress: try!(field(input, "processing_info"))
        })
    }
}

/// Represents the kinda of media that Twitter will accept.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MediaCategory {
    /// Static image. Four can be attached to a single tweet.
    Image,
    /// Animated GIF.
    Gif,
    /// Video.
    Video,
}

/// `Display` impl for `MediaCategory` so that `.to_string()` will return a string suitable for use
/// in an API call. This will turn the enum into `"tweet_image"`, `"tweet_gif"`, and
/// `"tweet_video"`.
impl ::std::fmt::Display for MediaCategory {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            MediaCategory::Image => write!(fmt, "tweet_image"),
            MediaCategory::Gif => write!(fmt, "tweet_gif"),
            MediaCategory::Video => write!(fmt, "tweet_video"),
        }
    }
}

/// A builder struct that allows you to build up parameters to a media upload before initiating it.
pub struct UploadBuilder<'a> {
    data: Cow<'a, [u8]>,
    media_type: mime::Mime,
    chunk_size: Option<usize>,
    category: Option<MediaCategory>,
}

impl<'a> UploadBuilder<'a> {
    /// Begins setting up a media upload call, with the given data and media type.
    ///
    /// For convenience functions to get known `media_type`s that Twitter will accept, see the
    /// [`media_types`] module.
    ///
    /// [`media_types`]: media_types/index.html
    pub fn new<V: Into<Cow<'a, [u8]>>>(data: V, media_type: mime::Mime) -> UploadBuilder<'a> {
        UploadBuilder {
            data: data.into(),
            media_type,
            chunk_size: None,
            category: None,
        }
    }

    /// Sets how many bytes to upload in one network call. By default this is set to 512 KiB.
    pub fn chunk_size(self, chunk_size: usize) -> Self {
        UploadBuilder {
            chunk_size: Some(chunk_size),
            ..self
        }
    }

    /// Sets the `media_category` sent to Twitter. When unset, it behaves as if you sent
    /// `MediaCategory::Image`.
    pub fn category(self, category: MediaCategory) -> Self {
        UploadBuilder {
            category: Some(category),
            ..self
        }
    }

    /// Collects the built-up parameters and begins the chunked upload.
    pub fn call(self, token: &auth::Token, handle: &Handle) -> UploadFuture<'a> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "INIT");
        add_param(&mut params, "total_bytes", self.data.len().to_string());
        add_param(&mut params, "media_type", self.media_type.to_string());

        if let Some(category) = self.category {
            add_param(&mut params, "media_category", category.to_string());
        }

        let req = auth::post(links::medias::UPLOAD, token, Some(&params));
        let loader = make_parsed_future(handle, req);
        UploadFuture {
            data: self.data,
            token: token.clone(),
            handle: handle.clone(),
            chunk_size: self.chunk_size.unwrap_or(1024 * 512), // 512 KiB default
            status: UploadInner::WaitingForInit(loader),
        }
    }
}

/// A `Future` that represents an in-progress media upload.
pub struct UploadFuture<'a> {
    data: Cow<'a, [u8]>,
    token: auth::Token,
    handle: Handle,
    chunk_size: usize,
    status: UploadInner,
}

/// The current status of an `UploadFuture`.
enum UploadInner {
    /// The `UploadFuture` is waiting to initialize the media upload session.
    WaitingForInit(FutureResponse<Media>),
    /// The `UploadFuture` is in the progress of uploading data.
    UploadingChunk(u64, usize, FutureResponse<()>),
    /// The `UploadFuture` is currently finalizing the media with Twitter.
    Finalizing(FutureResponse<Media>),
    /// The `UploadFuture` is waiting on Twitter to finish processing a video or gif.
    PostProcessing(u64, Timeout),
    /// The `UploadFuture` has completed, or has encountered an error.
    Invalid,
}

impl<'a> UploadFuture<'a> {
    fn get_chunk(&self, chunk_num: usize) -> Option<&[u8]> {
        let start = chunk_num * self.chunk_size;
        let end = (chunk_num + 1) * self.chunk_size;

        if start >= self.data.len() {
            None
        } else if end >= self.data.len() {
            Some(&self.data[start..])
        } else {
            Some(&self.data[start..end])
        }
    }

    fn append(&self, chunk_num: usize, media_id: u64) -> Option<FutureResponse<()>> {
        let mut chunk = self.get_chunk(chunk_num);
        if chunk.is_none() && chunk_num == 0 {
            chunk = Some(&[][..]);
        }

        if let Some(chunk) = chunk {
            let mut params = HashMap::new();

            let config = base64::Config {
                char_set: base64::CharacterSet::Standard,
                newline: base64::Newline::LF,
                pad: true,
                line_length: None,
            };

            add_param(&mut params, "command", "APPEND");
            add_param(&mut params, "media_id", media_id.to_string());
            add_param(&mut params, "media_data", chunk.to_base64(config));
            add_param(&mut params, "segment_index", chunk_num.to_string());

            let req = auth::post(links::medias::UPLOAD, &self.token, Some(&params));
            Some(make_parsed_future(&self.handle, req))
        } else {
            None
        }
    }

    fn finalize(&self, media_id: u64) -> FutureResponse<Media> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "FINALIZE");
        add_param(&mut params, "media_id", media_id.to_string());

        let req = auth::post(links::medias::UPLOAD, &self.token, Some(&params));
        make_parsed_future(&self.handle, req)
    }

    fn status(&self, media_id: u64) -> FutureResponse<Media> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "STATUS");
        add_param(&mut params, "media_id", media_id.to_string());

        let req = auth::get(links::medias::UPLOAD, &self.token, Some(&params));
        make_parsed_future(&self.handle, req)
    }
}

impl<'a> Future for UploadFuture<'a> {
    type Item = Response<Media>;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        use std::mem::replace;

        match replace(&mut self.status, UploadInner::Invalid) {
            UploadInner::WaitingForInit(mut init) => {
                match init.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::WaitingForInit(init);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(media)) => {
                        let id = media.id;
                        let loader = self.append(0, id).unwrap();
                        self.status = UploadInner::UploadingChunk(id, 0, loader);
                        self.poll()
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::UploadingChunk(id, chunk_idx, mut upload) => {
                match upload.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::UploadingChunk(id, chunk_idx, upload);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(_)) => {
                        let chunk_idx = chunk_idx + 1;
                        if let Some(upload) = self.append(chunk_idx, id) {
                            self.status = UploadInner::UploadingChunk(id, chunk_idx, upload);
                        } else {
                            let loader = self.finalize(id);
                            self.status = UploadInner::Finalizing(loader);
                        }

                        self.poll()
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::Finalizing(mut finalize) => {
                match finalize.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::Finalizing(finalize);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(media)) => {
                        if media.progress.is_none() || media.progress == Some(ProgressInfo::Success) {
                            return Ok(Async::Ready(media));
                        }

                        match media.progress {
                            Some(ProgressInfo::Pending(time)) |
                                Some(ProgressInfo::InProgress(time)) =>
                            {
                                let timer = try!(Timeout::new(Duration::from_secs(time), &self.handle));
                                self.status = UploadInner::PostProcessing(media.id, timer);
                                self.poll()
                            },
                            Some(ProgressInfo::Failed(ref err)) =>
                                Err(error::Error::MediaError(err.clone())),
                            None | Some(ProgressInfo::Success) => unreachable!(),
                        }
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::PostProcessing(id, mut timer) => {
                match timer.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::PostProcessing(id, timer);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(())) => {
                        let loader = self.status(id);
                        self.status = UploadInner::Finalizing(loader);
                        self.poll()
                    },
                    Err(e) => Err(e.into()),
                }
            },
            UploadInner::Invalid => Err(error::Error::FutureAlreadyCompleted),
        }
    }
}

#[cfg(test)]
mod tests {
    use common::FromJson;

    use super::Media;

    use std::fs::File;
    use std::io::Read;

    fn load_media(path: &str) -> Media {
        let mut file = File::open(path).unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();
        Media::from_str(&content).unwrap()
    }

    #[test]
    fn parse_media() {
        let media = load_media("src/media/media.json");

        assert_eq!(media.id, 710511363345354753);
        assert_eq!(media.expires_after, 86400);
    }

    #[test]
    fn parse_media_pending() {
        let media = load_media("src/media/media_pending.json");

        assert_eq!(media.id, 13);
        assert_eq!(media.expires_after, 86400);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::Pending(5)) => (),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }

    #[test]
    fn parse_media_in_progress() {
        let media = load_media("src/media/media_in_progress.json");

        assert_eq!(media.id, 13);
        assert_eq!(media.expires_after, 3595);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::InProgress(10)) => (),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }

    #[test]
    fn parse_media_fail() {
        let media = load_media("src/media/media_fail.json");

        assert_eq!(media.id, 710511363345354753);
        assert_eq!(media.expires_after, 0);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::Failed(error)) => assert_eq!(error, "InvalidMedia: Unsupported video format"),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Debug, Formatter};
use std::iter::FromIterator;
use std::path::PathBuf;

use indexmap::IndexSet;
use nix::NixPath;

#[derive(Clone)]
pub struct PathVec {
    vec: VecDeque<PathBuf>,
}

impl Debug for PathVec {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        Debug::fmt(&self.vec, f)
    }
}

impl Default for PathVec {
    fn default() -> Self {
        PathVec::new()
    }
}

impl From<OsString> for PathVec {
    fn from(v: OsString) -> Self {
        Self::from_osstr(&v)
    }
}

impl From<&OsStr> for PathVec {
    fn from(v: &OsStr) -> Self {
        Self::from_osstr(v)
    }
}

impl PathVec {
    pub fn join(&self) -> OsString {
        return env::join_paths(self.clone().unique()).expect("join path error");
    }

    pub fn unique(self) -> Self {
        Self::from_iter(IndexSet::<_>::from_iter(self.vec))
    }

    pub fn without_version_managers(self) -> Self {
        self.vec
            .into_iter()
            .filter(|p| !p.ends_with(".pyenv/shims") && !p.ends_with(".rbenv/shims"))
            .collect()
    }

    pub fn push_front<T: Into<PathBuf>>(&mut self, path: T) {
        self.vec.push_front(path.into())
    }

    #[allow(dead_code)]
    pub fn push_back<T: Into<PathBuf>>(&mut self, path: T) {
        self.vec.push_back(path.into())
    }

    pub fn from_osstr(input: &OsStr) -> Self {
        env::split_paths(input).filter(|p| p.len() > 0).collect()
    }

    pub fn new() -> Self {
        Self {
            vec: VecDeque::new(),
        }
    }
}

impl FromIterator<PathBuf> for PathVec {
    fn from_iter<T: IntoIterator<Item = PathBuf>>(iter: T) -> Self {
        Self {
            vec: FromIterator::from_iter(iter),
        }
    }
}

impl IntoIterator for PathVec {
    type Item = PathBuf;
    type IntoIter = std::collections::vec_deque::IntoIter<PathBuf>;

    fn into_iter(self) -> Self::IntoIter {
        self.vec.into_iter()
    }
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    const FOO: &str = "/foo";
    const BAR: &str = "/bar";

    #[test]
    fn test() {
        // no item
        let ps = PathVec::from(OsString::new());
        let actual = ps.join();
        assert_eq!(actual, "");

        // no item add one to front
        let mut ps = PathVec::from(OsString::new());
        ps.push_front(FOO.to_string());
        let actual = ps.join();
        assert_eq!(actual, FOO);

        // no item add one to back
        let mut ps = PathVec::from(OsString::new());
        ps.push_back(FOO.to_string());
        let actual = ps.join();
        assert_eq!(actual, FOO);

        // no item add two items to back
        let mut ps = PathVec::from(OsString::new());
        ps.push_back(FOO.to_string());
        ps.push_back(BAR.to_string());
        let actual = ps.join();
        let expected = OsString::from(format!("{}:{}", FOO, BAR));
        assert_eq!(actual, expected);

        // one item
        let ps = PathVec::from(OsString::from(FOO));
        let actual = ps.join();
        assert_eq!(actual, FOO);

        // should fix following cases
        let ps = PathVec::from(OsString::from(":/foo"));
        let actual = ps.join();
        assert_eq!(actual, "/foo");
        let ps = PathVec::from(OsString::from("/foo:"));
        let actual = ps.join();
        assert_eq!(actual, "/foo");

        // two items
        let input = OsString::from(format!("{}:{}", FOO, BAR));
        let ps = PathVec::from(input.as_os_str());
        let actual = ps.join();
        assert_eq!(actual, input);

        // move order correctly
        let input = OsString::from("/a:/b:/c");
        let mut ps = PathVec::from(input);
        ps.push_front("/c".to_string());
        let actual = ps.join();
        let expect = OsString::from("/c:/a:/b");
        assert_eq!(actual, expect);

        // remove pyenv, rbenv, and node path
        let input = OsString::from("/a:/home/.pyenv/shims:/home/.rbenv/shims");
        let ps = PathVec::from(input).without_version_managers();
        let actual = ps.join();
        assert_eq!(actual, OsString::from("/a"))
    }

    #[test]
    fn test_unique() {
        let input = OsString::from("/a:/b:/a");
        let ps = PathVec::from(input);
        let actual = ps.join();
        assert_eq!(actual, OsString::from("/a:/b"))
    }
}
<commit_msg>refactor PathVec<commit_after>use std::collections::VecDeque;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Debug, Formatter};
use std::iter::FromIterator;
use std::path::PathBuf;

use indexmap::IndexSet;
use nix::NixPath;

#[derive(Clone)]
pub struct PathVec {
    vec: VecDeque<PathBuf>,
}

impl Debug for PathVec {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        Debug::fmt(&self.vec, f)
    }
}

impl Default for PathVec {
    fn default() -> Self {
        PathVec::new()
    }
}

impl From<OsString> for PathVec {
    fn from(v: OsString) -> Self {
        Self::from_os_str(&v)
    }
}

impl From<&OsStr> for PathVec {
    fn from(v: &OsStr) -> Self {
        Self::from_os_str(v)
    }
}

impl PathVec {
    pub fn join(&self) -> OsString {
        return env::join_paths(self.clone().unique()).expect("join path error");
    }

    pub fn unique(self) -> Self {
        Self::from_iter(IndexSet::<_>::from_iter(self.vec))
    }

    pub fn without_version_managers(self) -> Self {
        self.vec
            .into_iter()
            .filter(|p| !p.ends_with(".pyenv/shims") && !p.ends_with(".rbenv/shims"))
            .collect()
    }

    pub fn push_front<T: Into<PathBuf>>(&mut self, path: T) {
        self.vec.push_front(path.into())
    }

    #[allow(dead_code)]
    pub fn push_back<T: Into<PathBuf>>(&mut self, path: T) {
        self.vec.push_back(path.into())
    }

    fn from_os_str(input: &OsStr) -> Self {
        env::split_paths(input).filter(|p| p.len() > 0).collect()
    }

    pub fn new() -> Self {
        Self {
            vec: VecDeque::new(),
        }
    }
}

impl FromIterator<PathBuf> for PathVec {
    fn from_iter<T: IntoIterator<Item = PathBuf>>(iter: T) -> Self {
        Self {
            vec: FromIterator::from_iter(iter),
        }
    }
}

impl IntoIterator for PathVec {
    type Item = PathBuf;
    type IntoIter = std::collections::vec_deque::IntoIter<PathBuf>;

    fn into_iter(self) -> Self::IntoIter {
        self.vec.into_iter()
    }
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    const FOO: &str = "/foo";
    const BAR: &str = "/bar";

    #[test]
    fn test() {
        // no item
        let ps = PathVec::from(OsString::new());
        let actual = ps.join();
        assert_eq!(actual, "");

        // no item add one to front
        let mut ps = PathVec::from(OsString::new());
        ps.push_front(FOO.to_string());
        let actual = ps.join();
        assert_eq!(actual, FOO);

        // no item add one to back
        let mut ps = PathVec::from(OsString::new());
        ps.push_back(FOO.to_string());
        let actual = ps.join();
        assert_eq!(actual, FOO);

        // no item add two items to back
        let mut ps = PathVec::from(OsString::new());
        ps.push_back(FOO.to_string());
        ps.push_back(BAR.to_string());
        let actual = ps.join();
        let expected = OsString::from(format!("{}:{}", FOO, BAR));
        assert_eq!(actual, expected);

        // one item
        let ps = PathVec::from(OsString::from(FOO));
        let actual = ps.join();
        assert_eq!(actual, FOO);

        // should fix following cases
        let ps = PathVec::from(OsString::from(":/foo"));
        let actual = ps.join();
        assert_eq!(actual, "/foo");
        let ps = PathVec::from(OsString::from("/foo:"));
        let actual = ps.join();
        assert_eq!(actual, "/foo");

        // two items
        let input = OsString::from(format!("{}:{}", FOO, BAR));
        let ps = PathVec::from(input.as_os_str());
        let actual = ps.join();
        assert_eq!(actual, input);

        // move order correctly
        let input = OsString::from("/a:/b:/c");
        let mut ps = PathVec::from(input);
        ps.push_front("/c".to_string());
        let actual = ps.join();
        let expect = OsString::from("/c:/a:/b");
        assert_eq!(actual, expect);

        // remove pyenv, rbenv, and node path
        let input = OsString::from("/a:/home/.pyenv/shims:/home/.rbenv/shims");
        let ps = PathVec::from(input).without_version_managers();
        let actual = ps.join();
        assert_eq!(actual, OsString::from("/a"))
    }

    #[test]
    fn test_unique() {
        let input = OsString::from("/a:/b:/a");
        let ps = PathVec::from(input);
        let actual = ps.join();
        assert_eq!(actual, OsString::from("/a:/b"))
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2019 Fabian Schuiki

//! An implementation of the verilog type system.

/// A verilog type.
pub type Type<'t> = &'t TypeKind<'t>;

/// Type data.
#[derive(Debug)]
pub enum TypeKind<'t> {
    Void,
    Bit,
    Logic,
    Reg,
    Dummy(std::marker::PhantomData<&'t ()>),
}

impl<'t> TypeKind<'t> {
    /// Check if this is the void type.
    pub fn is_void(&self) -> bool {
        match *self {
            TypeKind::Void => true,
            _ => false,
        }
    }
}
<commit_msg>svlog: make type hashable and comparable<commit_after>// Copyright (c) 2016-2019 Fabian Schuiki

//! An implementation of the verilog type system.

/// A verilog type.
pub type Type<'t> = &'t TypeKind<'t>;

/// Type data.
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum TypeKind<'t> {
    Void,
    Bit,
    Logic,
    Reg,
    Dummy(std::marker::PhantomData<&'t ()>),
}

impl<'t> TypeKind<'t> {
    /// Check if this is the void type.
    pub fn is_void(&self) -> bool {
        match *self {
            TypeKind::Void => true,
            _ => false,
        }
    }
}
<|endoftext|>