<commit_before>// Copyright 2018 Evgeniy Reizner
//
// 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.

use std::fmt;
use std::str;
use std::mem;
use std::iter::{
    Filter,
    Map,
};
use std::slice::{
    Iter,
    IterMut,
};

use {
    Attribute,
    AttributeId,
    AttributeQNameRef,
    AttributeValue,
    WriteBuffer,
};

// TODO: bench with HashTable
// TODO: iter_svg() -> iter().svg() like in dom iterators

/// Filter iterator over SVG attributes.
pub type SvgAttrFilter<'a> = Filter<Iter<'a, Attribute>, fn(&&Attribute) -> bool>;
/// Mutable filter iterator over SVG attributes.
pub type SvgAttrFilterMut<'a> = Filter<IterMut<'a, Attribute>, fn(&&mut Attribute) -> bool>;

/// An attributes list.
pub struct Attributes(Vec<Attribute>);

impl Attributes {
    /// Constructs a new attribute.
    ///
    /// **Warning:** this method is for private use only. Never invoke it directly.
    #[inline]
    pub fn new() -> Attributes {
        Attributes(Vec::new())
    }

    /// Returns an optional reference to [`Attribute`].
    ///
    /// [`Attribute`]: struct.Attribute.html
    #[inline]
    pub fn get<'a, N>(&self, name: N) -> Option<&Attribute>
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        for v in &self.0 {
            if v.name.as_ref() == name {
                return Some(v);
            }
        }

        None
    }

    /// Returns an optional mutable reference to [`Attribute`].
    ///
    /// [`Attribute`]: struct.Attribute.html
    #[inline]
    pub fn get_mut<'a, N>(&mut self, name: N) -> Option<&mut Attribute>
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        for v in &mut self.0 {
            if v.name.as_ref() == name {
                return Some(v);
            }
        }

        None
    }

    /// Returns an optional reference to [`AttributeValue`].
    ///
    /// [`AttributeValue`]: enum.AttributeValue.html
    #[inline]
    pub fn get_value<'a, N>(&self, name: N) -> Option<&AttributeValue>
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        for v in &self.0 {
            if v.name.as_ref() == name {
                return Some(&v.value);
            }
        }

        None
    }

    /// Returns an optional mutable reference to [`AttributeValue`].
    ///
    /// [`AttributeValue`]: enum.AttributeValue.html
    #[inline]
    pub fn get_value_mut<'a, N>(&mut self, name: N) -> Option<&mut AttributeValue>
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        for v in &mut self.0 {
            if v.name.as_ref() == name {
                return Some(&mut v.value);
            }
        }

        None
    }

    /// Inserts a new attribute. Previous will be overwritten.
    ///
    /// # Panics
    ///
    /// During insert of a linked attribute. Use [`Node::set_attribute()`] instead.
    ///
    /// Will panic only in debug build.
    ///
    /// [`Node::set_attribute()`]: type.Node.html#method.set_attribute
    pub fn insert(&mut self, attr: Attribute) {
        if cfg!(debug_assertions) {
            if attr.is_link() || attr.is_func_link() {
                panic!("attribute with Link/FuncLink value must be set only via Node::set_attribute");
            }
        }

        self.insert_impl(attr);
    }

    /// Creates a new attribute from name and value and inserts it. Previous will be overwritten.
    ///
    /// [`Node`] attribute value can be set only via [`Node::set_attribute()`] method.
    ///
    /// [`Node`]: type.Node.html
    /// [`Node::set_attribute()`]: type.Node.html#method.set_attribute
    pub fn insert_from<'a, N, T>(&mut self, name: N, value: T)
        where AttributeQNameRef<'a>: From<N>, AttributeValue: From<T>
    {
        self.insert(Attribute::new(name, value));
    }

    /// Inserts a new link attribute.
    ///
    /// **Warning:** this method is for private use only. Never invoke it directly.
    pub fn insert_impl(&mut self, attr: Attribute) {
        // Increase capacity on first insert.
        if self.0.capacity() == 0 {
            self.0.reserve(16);
        }

        let idx = self.0.iter().position(|x| x.name == attr.name);
        match idx {
            // We use braces to discard return value.
            Some(i) => { mem::replace(&mut self.0[i], attr); }
            None => self.0.push(attr),
        }
    }

    /// Removes an existing attribute.
    ///
    /// # Panics
    ///
    /// During remove of a linked attribute. Use [`Node::remove_attribute()`] instead.
    ///
    /// Will panic only in debug build.
    ///
    /// [`Node::remove_attribute()`]: type.Node.html#method.remove_attribute
    pub fn remove<'a, N>(&mut self, name: N)
        where AttributeQNameRef<'a>: From<N>, N: Copy
    {
        // Checks that removed attribute is not linked.
        //
        // Since this check is expensive - we do it only in debug build.
        if cfg!(debug_assertions) {
            let name = AttributeQNameRef::from(name);
            let attr = self.0.iter().find(|x| x.name.as_ref() == name);
            if let Some(attr) = attr {
                if attr.is_link() || attr.is_func_link() {
                    panic!("attribute with Link/FuncLink value must be remove \
                            only via Node::remove_attribute");
                }
            }
        }

        self.remove_impl(name);
    }

    /// Removes an existing attribute.
    ///
    /// **Warning:** this method is for private use only. Never invoke it directly.
    pub fn remove_impl<'a, N>(&mut self, name: N)
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        let idx = self.0.iter().position(|x| x.name.as_ref() == name);
        if let Some(i) = idx {
            self.0.remove(i);
        }
    }

    /// Returns `true` if the container contains an attribute with such name.
    #[inline]
    pub fn contains<'a, N>(&self, name: N) -> bool
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        self.0.iter().any(|a| a.name.as_ref() == name)
    }

    /// Returns count of the attributes.
    #[inline]
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if attributes is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Returns an iterator.
    #[inline]
    pub fn iter(&self) -> Iter<Attribute> {
        self.0.iter()
    }

    /// Returns a mutable iterator.
    #[inline]
    pub fn iter_mut(&mut self) -> IterMut<Attribute> {
        self.0.iter_mut()
    }

    /// Returns an iterator over SVG attributes.
    ///
    /// Shorthand for: `iter().filter(|a| a.is_svg()).map(|a| (a.id().unwrap(), a))`
    #[inline]
    pub fn iter_svg<'a>(&'a self)
        -> Map<SvgAttrFilter, fn(&'a Attribute) -> (AttributeId, &'a Attribute)>
    {
        fn map_svg(a: &Attribute) -> (AttributeId, &Attribute) { (a.id().unwrap(), a) }
        self.filter_svg().map(map_svg)
    }

    /// Returns a mutable iterator over SVG attributes.
    ///
    /// Shorthand for: `iter_mut().filter(|a| a.is_svg()).map(|a| (a.id().unwrap(), a))`
    #[inline]
    pub fn iter_svg_mut<'a>(&'a mut self)
        -> Map<SvgAttrFilterMut, fn(&'a mut Attribute) -> (AttributeId, &'a mut Attribute)>
    {
        fn map_svg(a: &mut Attribute) -> (AttributeId, &mut Attribute)
        { (a.id().unwrap(), a) }

        self.filter_svg_mut().map(map_svg)
    }

    #[inline]
    fn filter_svg(&self) -> SvgAttrFilter {
        fn is_svg(a: &&Attribute) -> bool { a.is_svg() }
        self.iter().filter(is_svg)
    }

    #[inline]
    fn filter_svg_mut(&mut self) -> SvgAttrFilterMut {
        fn is_svg(a: &&mut Attribute) -> bool { a.is_svg() }
        self.iter_mut().filter(is_svg)
    }

    /// Retains only elements specified by the predicate.
    ///
    /// # Panics
    ///
    /// During remove of a linked attribute. Use [`Node::remove_attribute()`] instead.
    ///
    /// Will panic only in debug build.
    ///
    /// [`Node::remove_attribute()`]: type.Node.html#method.remove_attribute
    #[inline]
    pub fn retain<F>(&mut self, mut f: F)
        where F: FnMut(&Attribute) -> bool
    {
        // Checks that removed attribute is not linked.
        //
        // Since this check is expensive - we do it only in debug build.
        if cfg!(debug_assertions) {
            for attr in &self.0 {
                if !f(attr) {
                    if attr.is_link() || attr.is_func_link() {
                        panic!("attribute with Link/FuncLink value must be remove \
                                only via Node::remove_attribute");
                    }
                }
            }
        }

        self.0.retain(f)
    }

    /// Clears the attributes list, removing all values.
    pub(crate) fn clear(&mut self) {
        self.0.clear();
    }
}

impl fmt::Debug for Attributes {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.is_empty() {
            return write!(f, "Attributes()");
        }

        let mut out = Vec::with_capacity(256);

        out.extend_from_slice(b"Attributes(");

        for attr in self.iter() {
            attr.write_buf(&mut out);
            out.push(b',');
            out.push(b' ');
        }

        out.pop();
        out.pop();
        out.push(b')');

        write!(f, "{}", str::from_utf8(&out).unwrap())
    }
}
<commit_msg>Spelling fixes.<commit_after>// Copyright 2018 Evgeniy Reizner
//
// 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.

use std::fmt;
use std::str;
use std::mem;
use std::iter::{
    Filter,
    Map,
};
use std::slice::{
    Iter,
    IterMut,
};

use {
    Attribute,
    AttributeId,
    AttributeQNameRef,
    AttributeValue,
    WriteBuffer,
};

// TODO: bench with HashTable
// TODO: iter_svg() -> iter().svg() like in dom iterators

/// Filter iterator over SVG attributes.
pub type SvgAttrFilter<'a> = Filter<Iter<'a, Attribute>, fn(&&Attribute) -> bool>;
/// Mutable filter iterator over SVG attributes.
pub type SvgAttrFilterMut<'a> = Filter<IterMut<'a, Attribute>, fn(&&mut Attribute) -> bool>;

/// An attributes list.
pub struct Attributes(Vec<Attribute>);

impl Attributes {
    /// Constructs a new attribute.
    ///
    /// **Warning:** this method is for private use only. Never invoke it directly.
    #[inline]
    pub fn new() -> Attributes {
        Attributes(Vec::new())
    }

    /// Returns an optional reference to [`Attribute`].
    ///
    /// [`Attribute`]: struct.Attribute.html
    #[inline]
    pub fn get<'a, N>(&self, name: N) -> Option<&Attribute>
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        for v in &self.0 {
            if v.name.as_ref() == name {
                return Some(v);
            }
        }

        None
    }

    /// Returns an optional mutable reference to [`Attribute`].
    ///
    /// [`Attribute`]: struct.Attribute.html
    #[inline]
    pub fn get_mut<'a, N>(&mut self, name: N) -> Option<&mut Attribute>
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        for v in &mut self.0 {
            if v.name.as_ref() == name {
                return Some(v);
            }
        }

        None
    }

    /// Returns an optional reference to [`AttributeValue`].
    ///
    /// [`AttributeValue`]: enum.AttributeValue.html
    #[inline]
    pub fn get_value<'a, N>(&self, name: N) -> Option<&AttributeValue>
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        for v in &self.0 {
            if v.name.as_ref() == name {
                return Some(&v.value);
            }
        }

        None
    }

    /// Returns an optional mutable reference to [`AttributeValue`].
    ///
    /// [`AttributeValue`]: enum.AttributeValue.html
    #[inline]
    pub fn get_value_mut<'a, N>(&mut self, name: N) -> Option<&mut AttributeValue>
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        for v in &mut self.0 {
            if v.name.as_ref() == name {
                return Some(&mut v.value);
            }
        }

        None
    }

    /// Inserts a new attribute. Previous will be overwritten.
    ///
    /// # Panics
    ///
    /// During insert of a linked attribute. Use [`Node::set_attribute()`] instead.
    ///
    /// Will panic only in debug build.
    ///
    /// [`Node::set_attribute()`]: type.Node.html#method.set_attribute
    pub fn insert(&mut self, attr: Attribute) {
        if cfg!(debug_assertions) {
            if attr.is_link() || attr.is_func_link() {
                panic!("attribute with Link/FuncLink value must be set only via Node::set_attribute");
            }
        }

        self.insert_impl(attr);
    }

    /// Creates a new attribute from name and value and inserts it. Previous will be overwritten.
    ///
    /// [`Node`] attribute value can be set only via [`Node::set_attribute()`] method.
    ///
    /// [`Node`]: type.Node.html
    /// [`Node::set_attribute()`]: type.Node.html#method.set_attribute
    pub fn insert_from<'a, N, T>(&mut self, name: N, value: T)
        where AttributeQNameRef<'a>: From<N>, AttributeValue: From<T>
    {
        self.insert(Attribute::new(name, value));
    }

    /// Inserts a new link attribute.
    ///
    /// **Warning:** this method is for private use only. Never invoke it directly.
    pub fn insert_impl(&mut self, attr: Attribute) {
        // Increase capacity on first insert.
        if self.0.capacity() == 0 {
            self.0.reserve(16);
        }

        let idx = self.0.iter().position(|x| x.name == attr.name);
        match idx {
            // We use braces to discard return value.
            Some(i) => { mem::replace(&mut self.0[i], attr); }
            None => self.0.push(attr),
        }
    }

    /// Removes an existing attribute.
    ///
    /// # Panics
    ///
    /// During remove of a linked attribute. Use [`Node::remove_attribute()`] instead.
    ///
    /// Will panic only in debug build.
    ///
    /// [`Node::remove_attribute()`]: type.Node.html#method.remove_attribute
    pub fn remove<'a, N>(&mut self, name: N)
        where AttributeQNameRef<'a>: From<N>, N: Copy
    {
        // Checks that removed attribute is not linked.
        //
        // Since this check is expensive - we do it only in debug build.
        if cfg!(debug_assertions) {
            let name = AttributeQNameRef::from(name);
            let attr = self.0.iter().find(|x| x.name.as_ref() == name);
            if let Some(attr) = attr {
                if attr.is_link() || attr.is_func_link() {
                    panic!("attribute with Link/FuncLink value must be removed \
                            only via Node::remove_attribute");
                }
            }
        }

        self.remove_impl(name);
    }

    /// Removes an existing attribute.
    ///
    /// **Warning:** this method is for private use only. Never invoke it directly.
    pub fn remove_impl<'a, N>(&mut self, name: N)
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        let idx = self.0.iter().position(|x| x.name.as_ref() == name);
        if let Some(i) = idx {
            self.0.remove(i);
        }
    }

    /// Returns `true` if the container contains an attribute with such name.
    #[inline]
    pub fn contains<'a, N>(&self, name: N) -> bool
        where AttributeQNameRef<'a>: From<N>
    {
        let name = AttributeQNameRef::from(name);
        self.0.iter().any(|a| a.name.as_ref() == name)
    }

    /// Returns count of the attributes.
    #[inline]
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if attributes is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Returns an iterator.
    #[inline]
    pub fn iter(&self) -> Iter<Attribute> {
        self.0.iter()
    }

    /// Returns a mutable iterator.
    #[inline]
    pub fn iter_mut(&mut self) -> IterMut<Attribute> {
        self.0.iter_mut()
    }

    /// Returns an iterator over SVG attributes.
    ///
    /// Shorthand for: `iter().filter(|a| a.is_svg()).map(|a| (a.id().unwrap(), a))`
    #[inline]
    pub fn iter_svg<'a>(&'a self)
        -> Map<SvgAttrFilter, fn(&'a Attribute) -> (AttributeId, &'a Attribute)>
    {
        fn map_svg(a: &Attribute) -> (AttributeId, &Attribute) { (a.id().unwrap(), a) }
        self.filter_svg().map(map_svg)
    }

    /// Returns a mutable iterator over SVG attributes.
    ///
    /// Shorthand for: `iter_mut().filter(|a| a.is_svg()).map(|a| (a.id().unwrap(), a))`
    #[inline]
    pub fn iter_svg_mut<'a>(&'a mut self)
        -> Map<SvgAttrFilterMut, fn(&'a mut Attribute) -> (AttributeId, &'a mut Attribute)>
    {
        fn map_svg(a: &mut Attribute) -> (AttributeId, &mut Attribute)
        { (a.id().unwrap(), a) }

        self.filter_svg_mut().map(map_svg)
    }

    #[inline]
    fn filter_svg(&self) -> SvgAttrFilter {
        fn is_svg(a: &&Attribute) -> bool { a.is_svg() }
        self.iter().filter(is_svg)
    }

    #[inline]
    fn filter_svg_mut(&mut self) -> SvgAttrFilterMut {
        fn is_svg(a: &&mut Attribute) -> bool { a.is_svg() }
        self.iter_mut().filter(is_svg)
    }

    /// Retains only elements specified by the predicate.
    ///
    /// # Panics
    ///
    /// During remove of a linked attribute. Use [`Node::remove_attribute()`] instead.
    ///
    /// Will panic only in debug build.
    ///
    /// [`Node::remove_attribute()`]: type.Node.html#method.remove_attribute
    #[inline]
    pub fn retain<F>(&mut self, mut f: F)
        where F: FnMut(&Attribute) -> bool
    {
        // Checks that removed attribute is not linked.
        //
        // Since this check is expensive - we do it only in debug build.
        if cfg!(debug_assertions) {
            for attr in &self.0 {
                if !f(attr) {
                    if attr.is_link() || attr.is_func_link() {
                        panic!("attribute with Link/FuncLink value must be removed \
                                only via Node::remove_attribute");
                    }
                }
            }
        }

        self.0.retain(f)
    }

    /// Clears the attributes list, removing all values.
    pub(crate) fn clear(&mut self) {
        self.0.clear();
    }
}

impl fmt::Debug for Attributes {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.is_empty() {
            return write!(f, "Attributes()");
        }

        let mut out = Vec::with_capacity(256);

        out.extend_from_slice(b"Attributes(");

        for attr in self.iter() {
            attr.write_buf(&mut out);
            out.push(b',');
            out.push(b' ');
        }

        out.pop();
        out.pop();
        out.push(b')');

        write!(f, "{}", str::from_utf8(&out).unwrap())
    }
}
<|endoftext|>


<commit_before>use std::ffi::{OsString, OsStr};
use std::path::Path;

use core::Source;
use sources::PathSource;
use ops::{self, ExecEngine, ProcessEngine, Compilation};
use util::{self, CargoResult, ProcessError};

pub struct TestOptions<'a, 'b: 'a> {
    pub compile_opts: ops::CompileOptions<'a, 'b>,
    pub no_run: bool,
}

pub fn run_tests(manifest_path: &Path,
                 options: &TestOptions,
                 test_args: &[String]) -> CargoResult<Option<ProcessError>> {
    let config = options.compile_opts.config;
    let compile = match try!(build_and_run(manifest_path, options, test_args)) {
        Ok(compile) => compile,
        Err(e) => return Ok(Some(e)),
    };

    // If a specific test was requested or we're not running any tests at all,
    // don't run any doc tests.
    if let ops::CompileFilter::Only { .. } = options.compile_opts.filter {
        return Ok(None)
    }
    if options.no_run {
        return Ok(None)
    }

    let libs = compile.package.targets().iter()
                      .filter(|t| t.doctested())
                      .map(|t| (t.src_path(), t.name(), t.crate_name()));

    for (lib, name, crate_name) in libs {
        try!(config.shell().status("Doc-tests", name));
        let mut p = try!(compile.rustdoc_process(&compile.package));
        p.arg("--test").arg(lib)
         .arg("--crate-name").arg(&crate_name)
         .arg("-L").arg(&{
             let mut arg = OsString::from("dependency=");
             arg.push(&compile.deps_output);
             arg
         })
         .cwd(compile.package.root());

        if test_args.len() > 0 {
            p.arg("--test-args").arg(&test_args.connect(" "));
        }

        for feat in compile.features.iter() {
            p.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
        }

        for (_, libs) in compile.libraries.iter() {
            for &(ref target, ref lib) in libs.iter() {
                // Note that we can *only* doctest rlib outputs here.  A
                // staticlib output cannot be linked by the compiler (it just
                // doesn't do that). A dylib output, however, can be linked by
                // the compiler, but will always fail. Currently all dylibs are
                // built as "static dylibs" where the standard library is
                // statically linked into the dylib. The doc tests fail,
                // however, for now as they try to link the standard library
                // dynamically as well, causing problems. As a result we only
                // pass `--extern` for rlib deps and skip out on all other
                // artifacts.
                if lib.extension() != Some(OsStr::new("rlib")) &&
                   !target.for_host() {
                    continue
                }
                let mut arg = OsString::from(target.crate_name());
                arg.push("=");
                arg.push(lib);
                p.arg("--extern").arg(&arg);
            }
        }

        try!(config.shell().verbose(|shell| {
            shell.status("Running", p.to_string())
        }));
        match ExecEngine::exec(&mut ProcessEngine, p) {
            Ok(()) => {}
            Err(e) => return Ok(Some(e)),
        }
    }

    Ok(None)
}

pub fn run_benches(manifest_path: &Path,
                   options: &TestOptions,
                   args: &[String]) -> CargoResult<Option<ProcessError>> {
    let mut args = args.to_vec();
    args.push("--bench".to_string());

    Ok(try!(build_and_run(manifest_path, options, &args)).err())
}

fn build_and_run(manifest_path: &Path,
                 options: &TestOptions,
                 test_args: &[String])
                 -> CargoResult<Result<Compilation, ProcessError>> {
    let config = options.compile_opts.config;
    let mut source = try!(PathSource::for_path(&manifest_path.parent().unwrap(),
                                               config));
    try!(source.update());

    let mut compile = try!(ops::compile(manifest_path, &options.compile_opts));
    if options.no_run { return Ok(Ok(compile)) }
    compile.tests.sort();

    let cwd = config.cwd();
    for &(_, ref exe) in &compile.tests {
        let to_display = match util::without_prefix(exe, &cwd) {
            Some(path) => path,
            None => &**exe,
        };
        let mut cmd = try!(compile.target_process(exe, &compile.package));
        cmd.args(test_args);
        try!(config.shell().concise(|shell| {
            shell.status("Running", to_display.display().to_string())
        }));
        try!(config.shell().verbose(|shell| {
            shell.status("Running", cmd.to_string())
        }));
        match ExecEngine::exec(&mut ProcessEngine, cmd) {
            Ok(()) => {}
            Err(e) => return Ok(Err(e))
        }
    }

    Ok(Ok(compile))
}
<commit_msg>Auto merge of #1530 - mvdnes:doctest-nativedep, r=alexcrichton<commit_after>use std::ffi::{OsString, OsStr};
use std::path::Path;

use core::Source;
use sources::PathSource;
use ops::{self, ExecEngine, ProcessEngine, Compilation};
use util::{self, CargoResult, ProcessError};

pub struct TestOptions<'a, 'b: 'a> {
    pub compile_opts: ops::CompileOptions<'a, 'b>,
    pub no_run: bool,
}

pub fn run_tests(manifest_path: &Path,
                 options: &TestOptions,
                 test_args: &[String]) -> CargoResult<Option<ProcessError>> {
    let config = options.compile_opts.config;
    let compile = match try!(build_and_run(manifest_path, options, test_args)) {
        Ok(compile) => compile,
        Err(e) => return Ok(Some(e)),
    };

    // If a specific test was requested or we're not running any tests at all,
    // don't run any doc tests.
    if let ops::CompileFilter::Only { .. } = options.compile_opts.filter {
        return Ok(None)
    }
    if options.no_run {
        return Ok(None)
    }

    let libs = compile.package.targets().iter()
                      .filter(|t| t.doctested())
                      .map(|t| (t.src_path(), t.name(), t.crate_name()));

    for (lib, name, crate_name) in libs {
        try!(config.shell().status("Doc-tests", name));
        let mut p = try!(compile.rustdoc_process(&compile.package));
        p.arg("--test").arg(lib)
         .arg("--crate-name").arg(&crate_name)
         .arg("-L").arg(&{
             let mut arg = OsString::from("dependency=");
             arg.push(&compile.deps_output);
             arg
         })
         .cwd(compile.package.root());

        for native_dep in compile.native_dirs.values() {
            p.arg("-L").arg(native_dep);
        }

        if test_args.len() > 0 {
            p.arg("--test-args").arg(&test_args.connect(" "));
        }

        for feat in compile.features.iter() {
            p.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
        }

        for (_, libs) in compile.libraries.iter() {
            for &(ref target, ref lib) in libs.iter() {
                // Note that we can *only* doctest rlib outputs here.  A
                // staticlib output cannot be linked by the compiler (it just
                // doesn't do that). A dylib output, however, can be linked by
                // the compiler, but will always fail. Currently all dylibs are
                // built as "static dylibs" where the standard library is
                // statically linked into the dylib. The doc tests fail,
                // however, for now as they try to link the standard library
                // dynamically as well, causing problems. As a result we only
                // pass `--extern` for rlib deps and skip out on all other
                // artifacts.
                if lib.extension() != Some(OsStr::new("rlib")) &&
                   !target.for_host() {
                    continue
                }
                let mut arg = OsString::from(target.crate_name());
                arg.push("=");
                arg.push(lib);
                p.arg("--extern").arg(&arg);
            }
        }

        try!(config.shell().verbose(|shell| {
            shell.status("Running", p.to_string())
        }));
        match ExecEngine::exec(&mut ProcessEngine, p) {
            Ok(()) => {}
            Err(e) => return Ok(Some(e)),
        }
    }

    Ok(None)
}

pub fn run_benches(manifest_path: &Path,
                   options: &TestOptions,
                   args: &[String]) -> CargoResult<Option<ProcessError>> {
    let mut args = args.to_vec();
    args.push("--bench".to_string());

    Ok(try!(build_and_run(manifest_path, options, &args)).err())
}

fn build_and_run(manifest_path: &Path,
                 options: &TestOptions,
                 test_args: &[String])
                 -> CargoResult<Result<Compilation, ProcessError>> {
    let config = options.compile_opts.config;
    let mut source = try!(PathSource::for_path(&manifest_path.parent().unwrap(),
                                               config));
    try!(source.update());

    let mut compile = try!(ops::compile(manifest_path, &options.compile_opts));
    if options.no_run { return Ok(Ok(compile)) }
    compile.tests.sort();

    let cwd = config.cwd();
    for &(_, ref exe) in &compile.tests {
        let to_display = match util::without_prefix(exe, &cwd) {
            Some(path) => path,
            None => &**exe,
        };
        let mut cmd = try!(compile.target_process(exe, &compile.package));
        cmd.args(test_args);
        try!(config.shell().concise(|shell| {
            shell.status("Running", to_display.display().to_string())
        }));
        try!(config.shell().verbose(|shell| {
            shell.status("Running", cmd.to_string())
        }));
        match ExecEngine::exec(&mut ProcessEngine, cmd) {
            Ok(()) => {}
            Err(e) => return Ok(Err(e))
        }
    }

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


<commit_before>use crate::{cluster::Cluster, instance::Instance};
use admission_control_proto::proto::{
    admission_control::SubmitTransactionRequest, admission_control_grpc::AdmissionControlClient,
};
use grpcio::{ChannelBuilder, EnvBuilder};
use proto_conv::IntoProto;
use std::{
    env, slice,
    str::FromStr,
    sync::Arc,
    thread,
    time::{Duration, Instant},
};

use crypto::{
    ed25519::{Ed25519PrivateKey, Ed25519PublicKey},
    test_utils::KeyPair,
    traits::Uniform,
};
use failure::{
    self,
    prelude::{bail, format_err},
};
use generate_keypair::load_key_from_file;
use itertools::zip;
use proto_conv::FromProto;
use rand::{
    rngs::{EntropyRng, StdRng},
    Rng, SeedableRng,
};
use types::{
    account_address::AccountAddress,
    account_config::{association_address, get_account_resource_or_default},
    get_with_proof::ResponseItem,
    proto::get_with_proof::{
        GetAccountStateRequest, RequestItem, RequestItem_oneof_requested_items,
        UpdateToLatestLedgerRequest,
    },
    transaction::{Script, TransactionPayload},
    transaction_helpers::create_signed_txn,
};

const ACCOUNT_PER_CLIENT_DEFAULT: usize = 10;
const THREADS_PER_CLIENT_DEFAULT: usize = 1;
const MAX_TXN_BATCH_SIZE: usize = 100; // Max transactions per account in mempool

pub struct TxEmitter {
    clients: Vec<(Instance, AdmissionControlClient)>,
}

impl TxEmitter {
    pub fn new(cluster: &Cluster) -> Self {
        let clients = Self::create_ac_clients(cluster);

        Self { clients }
    }

    fn create_ac_clients(cluster: &Cluster) -> Vec<(Instance, AdmissionControlClient)> {
        let mut clients = vec![];
        let threads_per_client = get_env("THREADS_PER_CLIENT", THREADS_PER_CLIENT_DEFAULT);
        for instance in cluster.instances() {
            let address = format!("{}:8000", instance.ip());
            for _ in 0..threads_per_client {
                let env_builder = Arc::new(EnvBuilder::new().name_prefix("ac-grpc-").build());
                let ch = ChannelBuilder::new(env_builder).connect(&address);
                clients.push((instance.clone(), AdmissionControlClient::new(ch)));
            }
        }
        clients
    }

    pub fn run(self) {
        let mint_client = &self.clients[0].1;
        let mut faucet_account = load_faucet_account(mint_client, "mint.key");
        let account_per_client = get_env("ACCOUNT_PER_CLIENT", ACCOUNT_PER_CLIENT_DEFAULT);
        let num_accounts = account_per_client * self.clients.len();
        println!("Minting accounts");
        let mut all_accounts: Vec<AccountData> = vec![];
        for _ in 0..(num_accounts + MAX_TXN_BATCH_SIZE - 1) / MAX_TXN_BATCH_SIZE {
            let mut accounts = gen_random_accounts(MAX_TXN_BATCH_SIZE);
            let mint_requests = gen_mint_txn_requests(&mut faucet_account, &accounts);
            execute_and_wait_transactions(&mint_client, &faucet_account, mint_requests);
            all_accounts.append(&mut accounts);
        }
        println!("Mint is done");
        let mut join_handles = vec![];
        let mut all_accounts = all_accounts.into_iter();
        for (index, (instance, client)) in self.clients.into_iter().enumerate() {
            let accounts = (&mut all_accounts).take(account_per_client).collect();
            let thread = SubmissionThread {
                accounts,
                instance,
                client,
            };
            let join_handle = thread::Builder::new()
                .name(format!("thread-{}", index))
                .spawn(move || thread.run())
                .unwrap();
            join_handles.push(join_handle);
            thread::sleep(Duration::from_millis(10)); // Small stagger between starting threads
        }
        println!("Threads started");
        for join_handle in join_handles {
            join_handle.join().unwrap();
        }
    }
}

fn get_env<F: FromStr>(name: &str, default: F) -> F {
    match env::var(name) {
        Ok(v) => match v.parse() {
            Ok(v) => v,
            _ => panic!("Failed to parse env {}", name),
        },
        _ => default,
    }
}

struct SubmissionThread {
    accounts: Vec<AccountData>,
    instance: Instance,
    client: AdmissionControlClient,
}

impl SubmissionThread {
    fn run(mut self) {
        let wait_millis = get_env("WAIT_MILLIS", 50);
        let wait = Duration::from_millis(wait_millis);
        let wait_committed = get_env("WAIT_COMMITTED", true);
        loop {
            let gen_requests = gen_ring_requests(&mut self.accounts);
            for request in gen_requests {
                let wait_util = Instant::now() + wait;
                let resp = self.client.submit_transaction(&request);
                match resp {
                    Err(e) => println!("Failed to submit request to {}: {:?}", self.instance, e),
                    Ok(_r) => {
                        //                        println!("r: {:?}", _r)
                    }
                }
                let now = Instant::now();
                if wait_util > now {
                    thread::sleep(wait_util - now);
                } else {
                    println!("Thread for {} won't sleep", self.instance);
                }
            }
            if wait_committed {
                wait_for_accounts_sequence(&self.client, &self.accounts);
            }
        }
    }
}

fn wait_for_accounts_sequence(client: &AdmissionControlClient, accounts: &[AccountData]) {
    let addresses: Vec<_> = accounts.iter().map(|d| d.address).collect();
    loop {
        match query_sequence_numbers(client, &addresses) {
            Err(e) => println!("Failed to query ledger info: {:?}", e),
            Ok(sequence_numbers) => {
                if is_sequence_equal(accounts, &sequence_numbers) {
                    break;
                }
            }
        }
        thread::sleep(Duration::from_millis(100));
    }
}

fn is_sequence_equal(accounts: &[AccountData], sequence_numbers: &[u64]) -> bool {
    for (account, sequence_number) in zip(accounts, sequence_numbers) {
        if *sequence_number != account.sequence_number {
            return false;
        }
    }
    true
}

fn query_sequence_numbers(
    client: &AdmissionControlClient,
    addresses: &[AccountAddress],
) -> failure::Result<Vec<u64>> {
    let mut update_request = UpdateToLatestLedgerRequest::new();
    for address in addresses {
        let mut request_item = RequestItem::new();
        let mut account_state_request = GetAccountStateRequest::new();
        account_state_request.address = address.to_vec();
        request_item.requested_items = Some(
            RequestItem_oneof_requested_items::get_account_state_request(account_state_request),
        );
        update_request.requested_items.push(request_item);
    }
    let resp = client
        .update_to_latest_ledger(&update_request)
        .map_err(|e| format_err!("update_to_latest_ledger failed: {:?} ", e))?;
    let mut result = Vec::with_capacity(resp.response_items.len());
    for item in resp.response_items.into_iter() {
        let item = ResponseItem::from_proto(item)
            .map_err(|e| format_err!("ResponseItem::from_proto failed: {:?} ", e))?;
        if let ResponseItem::GetAccountState {
            account_state_with_proof,
        } = item
        {
            let account_resource = get_account_resource_or_default(&account_state_with_proof.blob)
                .map_err(|e| format_err!("get_account_resource_or_default failed: {:?} ", e))?;
            result.push(account_resource.sequence_number());
        } else {
            bail!(
                "Unexpected item in UpdateToLatestLedgerResponse: {:?}",
                item
            );
        }
    }
    Ok(result)
}

const MAX_GAS_AMOUNT: u64 = 1_000_000;
const GAS_UNIT_PRICE: u64 = 0;
const TXN_EXPIRATION: i64 = 100;

fn gen_submit_transaction_request(
    script: Script,
    sender_account: &mut AccountData,
) -> SubmitTransactionRequest {
    let signed_txn = create_signed_txn(
        &sender_account.key_pair,
        TransactionPayload::Script(script),
        sender_account.address,
        sender_account.sequence_number,
        MAX_GAS_AMOUNT,
        GAS_UNIT_PRICE,
        TXN_EXPIRATION,
    )
    .expect("Failed to create signed transaction");
    let mut req = SubmitTransactionRequest::new();
    req.set_signed_txn(signed_txn.into_proto());
    sender_account.sequence_number += 1;
    req
}

fn gen_transfer_txn_request(
    sender: &mut AccountData,
    receiver: &AccountAddress,
    num_coins: u64,
) -> SubmitTransactionRequest {
    let script = transaction_builder::encode_transfer_script(&receiver, num_coins);
    gen_submit_transaction_request(script, sender)
}

fn gen_ring_requests(accounts: &mut [AccountData]) -> Vec<SubmitTransactionRequest> {
    let mut receiver_addrs: Vec<AccountAddress> =
        accounts.iter().map(|account| account.address).collect();
    receiver_addrs.rotate_left(1);
    accounts
        .iter_mut()
        .zip(receiver_addrs.iter())
        .map(|(sender, receiver_addr)| gen_transfer_txn_request(sender, receiver_addr, 1))
        .collect()
}

fn gen_random_account(rng: &mut StdRng) -> AccountData {
    let key_pair = KeyPair::generate_for_testing(rng);
    AccountData {
        address: AccountAddress::from_public_key(&key_pair.public_key),
        key_pair,
        sequence_number: 0,
    }
}

fn gen_random_accounts(num_accounts: usize) -> Vec<AccountData> {
    let seed: [u8; 32] = EntropyRng::new().gen();
    let mut rng = StdRng::from_seed(seed);
    (0..num_accounts)
        .map(|_| gen_random_account(&mut rng))
        .collect()
}

fn gen_mint_txn_request(
    faucet_account: &mut AccountData,
    receiver: &AccountAddress,
) -> SubmitTransactionRequest {
    let program = transaction_builder::encode_mint_script(receiver, 1_000_000);
    gen_submit_transaction_request(program, faucet_account)
}

fn gen_mint_txn_requests(
    faucet_account: &mut AccountData,
    accounts: &[AccountData],
) -> Vec<SubmitTransactionRequest> {
    accounts
        .iter()
        .map(|account| gen_mint_txn_request(faucet_account, &account.address))
        .collect()
}

/// Executes many transactions for single client
/// This method batches transactions in batches of MAX_TXN_BATCH_SIZE size
/// Usage example: mint
fn execute_and_wait_transactions(
    client: &AdmissionControlClient,
    account: &AccountData,
    txn: Vec<SubmitTransactionRequest>,
) {
    for request in txn {
        let resp = client.submit_transaction(&request);
        match resp {
            Err(e) => println!("Failed to submit request: {:?}", e),
            Ok(_r) => {}
        }
    }
    wait_for_accounts_sequence(client, slice::from_ref(account));
}

fn load_faucet_account(client: &AdmissionControlClient, faucet_account_path: &str) -> AccountData {
    let key_pair: KeyPair<Ed25519PrivateKey, Ed25519PublicKey> =
        load_key_from_file(faucet_account_path).expect("invalid faucet keypair file");
    let address = association_address();
    let sequence_number = query_sequence_numbers(client, &[address])
        .expect("query_sequence_numbers for faucet account failed")[0];
    AccountData {
        address,
        key_pair,
        sequence_number,
    }
}

struct AccountData {
    pub address: AccountAddress,
    pub key_pair: KeyPair<Ed25519PrivateKey, Ed25519PublicKey>,
    pub sequence_number: u64,
}
<commit_msg>[cluster_test] Check ac status in tx emitter<commit_after>use crate::{cluster::Cluster, instance::Instance};
use admission_control_proto::proto::{
    admission_control::SubmitTransactionRequest, admission_control_grpc::AdmissionControlClient,
};
use grpcio::{ChannelBuilder, EnvBuilder};
use proto_conv::IntoProto;
use std::{
    env, slice,
    str::FromStr,
    sync::Arc,
    thread,
    time::{Duration, Instant},
};

use admission_control_proto::{AdmissionControlStatus, SubmitTransactionResponse};
use crypto::{
    ed25519::{Ed25519PrivateKey, Ed25519PublicKey},
    test_utils::KeyPair,
    traits::Uniform,
};
use failure::{
    self,
    prelude::{bail, format_err},
};
use generate_keypair::load_key_from_file;
use itertools::zip;
use proto_conv::FromProto;
use rand::{
    rngs::{EntropyRng, StdRng},
    Rng, SeedableRng,
};
use types::{
    account_address::AccountAddress,
    account_config::{association_address, get_account_resource_or_default},
    get_with_proof::ResponseItem,
    proto::get_with_proof::{
        GetAccountStateRequest, RequestItem, RequestItem_oneof_requested_items,
        UpdateToLatestLedgerRequest,
    },
    transaction::{Script, TransactionPayload},
    transaction_helpers::create_signed_txn,
};

const ACCOUNT_PER_CLIENT_DEFAULT: usize = 10;
const THREADS_PER_CLIENT_DEFAULT: usize = 1;
const MAX_TXN_BATCH_SIZE: usize = 100; // Max transactions per account in mempool

pub struct TxEmitter {
    clients: Vec<(Instance, AdmissionControlClient)>,
}

impl TxEmitter {
    pub fn new(cluster: &Cluster) -> Self {
        let clients = Self::create_ac_clients(cluster);

        Self { clients }
    }

    fn create_ac_clients(cluster: &Cluster) -> Vec<(Instance, AdmissionControlClient)> {
        let mut clients = vec![];
        let threads_per_client = get_env("THREADS_PER_CLIENT", THREADS_PER_CLIENT_DEFAULT);
        for instance in cluster.instances() {
            let address = format!("{}:8000", instance.ip());
            for _ in 0..threads_per_client {
                let env_builder = Arc::new(EnvBuilder::new().name_prefix("ac-grpc-").build());
                let ch = ChannelBuilder::new(env_builder).connect(&address);
                clients.push((instance.clone(), AdmissionControlClient::new(ch)));
            }
        }
        clients
    }

    pub fn run(self) {
        let mint_client = &self.clients[0].1;
        let mut faucet_account = load_faucet_account(mint_client, "mint.key");
        let account_per_client = get_env("ACCOUNT_PER_CLIENT", ACCOUNT_PER_CLIENT_DEFAULT);
        let num_accounts = account_per_client * self.clients.len();
        println!("Minting accounts");
        let mut all_accounts: Vec<AccountData> = vec![];
        for _ in 0..(num_accounts + MAX_TXN_BATCH_SIZE - 1) / MAX_TXN_BATCH_SIZE {
            let mut accounts = gen_random_accounts(MAX_TXN_BATCH_SIZE);
            let mint_requests = gen_mint_txn_requests(&mut faucet_account, &accounts);
            execute_and_wait_transactions(&mint_client, &faucet_account, mint_requests);
            all_accounts.append(&mut accounts);
        }
        println!("Mint is done");
        let mut join_handles = vec![];
        let mut all_accounts = all_accounts.into_iter();
        for (index, (instance, client)) in self.clients.into_iter().enumerate() {
            let accounts = (&mut all_accounts).take(account_per_client).collect();
            let thread = SubmissionThread {
                accounts,
                instance,
                client,
            };
            let join_handle = thread::Builder::new()
                .name(format!("thread-{}", index))
                .spawn(move || thread.run())
                .unwrap();
            join_handles.push(join_handle);
            thread::sleep(Duration::from_millis(10)); // Small stagger between starting threads
        }
        println!("Threads started");
        for join_handle in join_handles {
            join_handle.join().unwrap();
        }
    }
}

fn get_env<F: FromStr>(name: &str, default: F) -> F {
    match env::var(name) {
        Ok(v) => match v.parse() {
            Ok(v) => v,
            _ => panic!("Failed to parse env {}", name),
        },
        _ => default,
    }
}

struct SubmissionThread {
    accounts: Vec<AccountData>,
    instance: Instance,
    client: AdmissionControlClient,
}

impl SubmissionThread {
    fn run(mut self) {
        let wait_millis = get_env("WAIT_MILLIS", 50);
        let wait = Duration::from_millis(wait_millis);
        let wait_committed = get_env("WAIT_COMMITTED", true);
        loop {
            let gen_requests = gen_ring_requests(&mut self.accounts);
            for request in gen_requests {
                let wait_util = Instant::now() + wait;
                let resp = self.client.submit_transaction(&request);
                match resp {
                    Err(e) => println!("Failed to submit request to {}: {:?}", self.instance, e),
                    Ok(r) => {
                        let r = SubmitTransactionResponse::from_proto(r)
                            .expect("Failed to parse SubmitTransactionResponse");
                        if !is_accepted(&r) {
                            println!("Request declined: {:?}", r);
                        }
                    }
                }
                let now = Instant::now();
                if wait_util > now {
                    thread::sleep(wait_util - now);
                } else {
                    println!("Thread for {} won't sleep", self.instance);
                }
            }
            if wait_committed {
                wait_for_accounts_sequence(&self.client, &self.accounts);
            }
        }
    }
}

fn wait_for_accounts_sequence(client: &AdmissionControlClient, accounts: &[AccountData]) {
    let addresses: Vec<_> = accounts.iter().map(|d| d.address).collect();
    loop {
        match query_sequence_numbers(client, &addresses) {
            Err(e) => println!("Failed to query ledger info: {:?}", e),
            Ok(sequence_numbers) => {
                if is_sequence_equal(accounts, &sequence_numbers) {
                    break;
                }
            }
        }
        thread::sleep(Duration::from_millis(100));
    }
}

fn is_sequence_equal(accounts: &[AccountData], sequence_numbers: &[u64]) -> bool {
    for (account, sequence_number) in zip(accounts, sequence_numbers) {
        if *sequence_number != account.sequence_number {
            return false;
        }
    }
    true
}

fn query_sequence_numbers(
    client: &AdmissionControlClient,
    addresses: &[AccountAddress],
) -> failure::Result<Vec<u64>> {
    let mut update_request = UpdateToLatestLedgerRequest::new();
    for address in addresses {
        let mut request_item = RequestItem::new();
        let mut account_state_request = GetAccountStateRequest::new();
        account_state_request.address = address.to_vec();
        request_item.requested_items = Some(
            RequestItem_oneof_requested_items::get_account_state_request(account_state_request),
        );
        update_request.requested_items.push(request_item);
    }
    let resp = client
        .update_to_latest_ledger(&update_request)
        .map_err(|e| format_err!("update_to_latest_ledger failed: {:?} ", e))?;
    let mut result = Vec::with_capacity(resp.response_items.len());
    for item in resp.response_items.into_iter() {
        let item = ResponseItem::from_proto(item)
            .map_err(|e| format_err!("ResponseItem::from_proto failed: {:?} ", e))?;
        if let ResponseItem::GetAccountState {
            account_state_with_proof,
        } = item
        {
            let account_resource = get_account_resource_or_default(&account_state_with_proof.blob)
                .map_err(|e| format_err!("get_account_resource_or_default failed: {:?} ", e))?;
            result.push(account_resource.sequence_number());
        } else {
            bail!(
                "Unexpected item in UpdateToLatestLedgerResponse: {:?}",
                item
            );
        }
    }
    Ok(result)
}

const MAX_GAS_AMOUNT: u64 = 1_000_000;
const GAS_UNIT_PRICE: u64 = 0;
const TXN_EXPIRATION: i64 = 100;

fn gen_submit_transaction_request(
    script: Script,
    sender_account: &mut AccountData,
) -> SubmitTransactionRequest {
    let signed_txn = create_signed_txn(
        &sender_account.key_pair,
        TransactionPayload::Script(script),
        sender_account.address,
        sender_account.sequence_number,
        MAX_GAS_AMOUNT,
        GAS_UNIT_PRICE,
        TXN_EXPIRATION,
    )
    .expect("Failed to create signed transaction");
    let mut req = SubmitTransactionRequest::new();
    req.set_signed_txn(signed_txn.into_proto());
    sender_account.sequence_number += 1;
    req
}

fn gen_transfer_txn_request(
    sender: &mut AccountData,
    receiver: &AccountAddress,
    num_coins: u64,
) -> SubmitTransactionRequest {
    let script = transaction_builder::encode_transfer_script(&receiver, num_coins);
    gen_submit_transaction_request(script, sender)
}

fn gen_ring_requests(accounts: &mut [AccountData]) -> Vec<SubmitTransactionRequest> {
    let mut receiver_addrs: Vec<AccountAddress> =
        accounts.iter().map(|account| account.address).collect();
    receiver_addrs.rotate_left(1);
    accounts
        .iter_mut()
        .zip(receiver_addrs.iter())
        .map(|(sender, receiver_addr)| gen_transfer_txn_request(sender, receiver_addr, 1))
        .collect()
}

fn gen_random_account(rng: &mut StdRng) -> AccountData {
    let key_pair = KeyPair::generate_for_testing(rng);
    AccountData {
        address: AccountAddress::from_public_key(&key_pair.public_key),
        key_pair,
        sequence_number: 0,
    }
}

fn gen_random_accounts(num_accounts: usize) -> Vec<AccountData> {
    let seed: [u8; 32] = EntropyRng::new().gen();
    let mut rng = StdRng::from_seed(seed);
    (0..num_accounts)
        .map(|_| gen_random_account(&mut rng))
        .collect()
}

fn gen_mint_txn_request(
    faucet_account: &mut AccountData,
    receiver: &AccountAddress,
) -> SubmitTransactionRequest {
    let program = transaction_builder::encode_mint_script(receiver, 1_000_000);
    gen_submit_transaction_request(program, faucet_account)
}

fn gen_mint_txn_requests(
    faucet_account: &mut AccountData,
    accounts: &[AccountData],
) -> Vec<SubmitTransactionRequest> {
    accounts
        .iter()
        .map(|account| gen_mint_txn_request(faucet_account, &account.address))
        .collect()
}

fn execute_and_wait_transactions(
    client: &AdmissionControlClient,
    account: &AccountData,
    txn: Vec<SubmitTransactionRequest>,
) {
    for request in txn {
        let resp = client.submit_transaction(&request);
        match resp {
            Err(e) => println!("Failed to submit request: {:?}", e),
            Ok(r) => {
                let r = SubmitTransactionResponse::from_proto(r)
                    .expect("Failed to parse SubmitTransactionResponse");
                if !is_accepted(&r) {
                    println!("Request declined: {:?}", r);
                }
            }
        }
    }
    wait_for_accounts_sequence(client, slice::from_ref(account));
}

fn load_faucet_account(client: &AdmissionControlClient, faucet_account_path: &str) -> AccountData {
    let key_pair: KeyPair<Ed25519PrivateKey, Ed25519PublicKey> =
        load_key_from_file(faucet_account_path).expect("invalid faucet keypair file");
    let address = association_address();
    let sequence_number = query_sequence_numbers(client, &[address])
        .expect("query_sequence_numbers for faucet account failed")[0];
    AccountData {
        address,
        key_pair,
        sequence_number,
    }
}

struct AccountData {
    pub address: AccountAddress,
    pub key_pair: KeyPair<Ed25519PrivateKey, Ed25519PublicKey>,
    pub sequence_number: u64,
}

fn is_accepted(resp: &SubmitTransactionResponse) -> bool {
    if let Some(ref status) = resp.ac_status {
        return *status == AdmissionControlStatus::Accepted;
    }
    false
}
<|endoftext|>


<commit_before>use rustc::hir::*;
use rustc::hir::intravisit::FnKind;
use rustc::hir::def_id::DefId;
use rustc::lint::*;
use rustc::ty::{self, TypeFoldable};
use rustc::traits;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use syntax::ast::NodeId;
use syntax_pos::Span;
use syntax::errors::DiagnosticBuilder;
use utils::{in_macro, is_self, is_copy, implements_trait, get_trait_def_id, match_type, snippet, span_lint_and_then,
            multispan_sugg, paths};
use std::collections::{HashSet, HashMap};

/// **What it does:** Checks for functions taking arguments by value, but not consuming them in its
/// body.
///
/// **Why is this bad?** Taking arguments by reference is more flexible and can sometimes avoid
/// unnecessary allocations.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// fn foo(v: Vec<i32>) {
///     assert_eq!(v.len(), 42);
/// }
/// ```
declare_lint! {
    pub NEEDLESS_PASS_BY_VALUE,
    Warn,
    "functions taking arguments by value, but not consuming them in its body"
}

pub struct NeedlessPassByValue;

impl LintPass for NeedlessPassByValue {
    fn get_lints(&self) -> LintArray {
        lint_array![NEEDLESS_PASS_BY_VALUE]
    }
}

macro_rules! need {
    ($e: expr) => { if let Some(x) = $e { x } else { return; } };
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        node_id: NodeId
    ) {
        if in_macro(span) {
            return;
        }

        match kind {
            FnKind::ItemFn(.., attrs) => {
                for a in attrs {
                    if_let_chain!{[
                        a.meta_item_list().is_some(),
                        let Some(name) = a.name(),
                        name == "proc_macro_derive",
                    ], {
                        return;
                    }}
                }
            },
            _ => return,
        }

        // Allows these to be passed by value.
        let fn_trait = need!(cx.tcx.lang_items.fn_trait());
        let asref_trait = need!(get_trait_def_id(cx, &paths::ASREF_TRAIT));
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));

        let preds: Vec<ty::Predicate> = {
            let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
            traits::elaborate_predicates(cx.tcx, parameter_env.caller_bounds.clone())
                .filter(|p| !p.is_global())
                .collect()
        };

        let fn_def_id = cx.tcx.hir.local_def_id(node_id);

        // Collect moved variables and spans which will need dereferencings from the function body.
        let MovedVariablesCtxt { moved_vars, spans_need_deref, .. } = {
            let mut ctx = MovedVariablesCtxt::new(cx);
            let infcx = cx.tcx.borrowck_fake_infer_ctxt(body.id());
            let region_maps = &cx.tcx.region_maps(fn_def_id);
            {
                let mut v = euv::ExprUseVisitor::new(&mut ctx, region_maps, &infcx);
                v.consume_body(body);
            }
            ctx
        };

        let param_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
        let fn_sig = cx.tcx.type_of(fn_def_id).fn_sig();
        let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, &fn_sig);

        for ((input, &ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {

            // Determines whether `ty` implements `Borrow<U>` (U != ty) specifically.
            // This is needed due to the `Borrow<T> for T` blanket impl.
            let implements_borrow_trait = preds.iter()
                .filter_map(|pred| if let ty::Predicate::Trait(ref poly_trait_ref) = *pred {
                    Some(poly_trait_ref.skip_binder())
                } else {
                    None
                })
                .filter(|tpred| tpred.def_id() == borrow_trait && tpred.self_ty() == ty)
                .any(|tpred| tpred.input_types().nth(1).expect("Borrow trait must have an parameter") != ty);

            if_let_chain! {[
                !is_self(arg),
                !ty.is_mutable_pointer(),
                !is_copy(cx, ty, node_id),
                !implements_trait(cx, ty, fn_trait, &[], Some(node_id)),
                !implements_trait(cx, ty, asref_trait, &[], Some(node_id)),
                !implements_borrow_trait,

                let PatKind::Binding(mode, defid, ..) = arg.pat.node,
                !moved_vars.contains(&defid),
            ], {
                // Note: `toplevel_ref_arg` warns if `BindByRef`
                let m = match mode {
                    BindingMode::BindByRef(m) | BindingMode::BindByValue(m) => m,
                };
                if m == Mutability::MutMutable {
                    continue;
                }

                // Suggestion logic
                let sugg = |db: &mut DiagnosticBuilder| {
                    let deref_span = spans_need_deref.get(&defid);
                    if_let_chain! {[
                        match_type(cx, ty, &paths::VEC),
                        let TyPath(QPath::Resolved(_, ref path)) = input.node,
                        let Some(elem_ty) = path.segments.iter()
                            .find(|seg| seg.name == "Vec")
                            .map(|ps| ps.parameters.types()[0]),
                    ], {
                        let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                        db.span_suggestion(input.span,
                                        "consider changing the type to",
                                        slice_ty);
                        assert!(deref_span.is_none());
                        return; // `Vec` and `String` cannot be destructured - no need for `*` suggestion
                    }}

                    if match_type(cx, ty, &paths::STRING) {
                        db.span_suggestion(input.span,
                                           "consider changing the type to",
                                           "&str".to_string());
                        assert!(deref_span.is_none());
                        return;
                    }

                    let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                    // Suggests adding `*` to dereference the added reference.
                    if let Some(deref_span) = deref_span {
                        spans.extend(deref_span.iter().cloned()
                                     .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))));
                        spans.sort_by_key(|&(span, _)| span);
                    }
                    multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
                };

                span_lint_and_then(cx,
                          NEEDLESS_PASS_BY_VALUE,
                          input.span,
                          "this argument is passed by value, but not consumed in the function body",
                          sugg);
            }}
        }
    }
}

struct MovedVariablesCtxt<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    moved_vars: HashSet<DefId>,
    /// Spans which need to be prefixed with `*` for dereferencing the suggested additional
    /// reference.
    spans_need_deref: HashMap<DefId, HashSet<Span>>,
}

impl<'a, 'tcx: 'a> MovedVariablesCtxt<'a, 'tcx> {
    fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
        MovedVariablesCtxt {
            cx: cx,
            moved_vars: HashSet::new(),
            spans_need_deref: HashMap::new(),
        }
    }

    fn move_common(&mut self, _consume_id: NodeId, _span: Span, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if_let_chain! {[
            let mc::Categorization::Local(vid) = cmt.cat,
            let Some(def_id) = self.cx.tcx.hir.opt_local_def_id(vid),
        ], {
                self.moved_vars.insert(def_id);
        }}
    }

    fn non_moving_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if_let_chain! {[
            let mc::Categorization::Local(vid) = cmt.cat,
            let Some(def_id) = self.cx.tcx.hir.opt_local_def_id(vid),
        ], {
            let mut id = matched_pat.id;
            loop {
                let parent = self.cx.tcx.hir.get_parent_node(id);
                if id == parent {
                    // no parent
                    return;
                }
                id = parent;

                if let Some(node) = self.cx.tcx.hir.find(id) {
                    match node {
                        map::Node::NodeExpr(e) => {
                            // `match` and `if let`
                            if let ExprMatch(ref c, ..) = e.node {
                                self.spans_need_deref
                                    .entry(def_id)
                                    .or_insert_with(HashSet::new)
                                    .insert(c.span);
                            }
                        }

                        map::Node::NodeStmt(s) => {
                            // `let <pat> = x;`
                            if_let_chain! {[
                                let StmtDecl(ref decl, _) = s.node,
                                let DeclLocal(ref local) = decl.node,
                            ], {
                                self.spans_need_deref
                                    .entry(def_id)
                                    .or_insert_with(HashSet::new)
                                    .insert(local.init
                                        .as_ref()
                                        .map(|e| e.span)
                                        .expect("`let` stmt without init aren't caught by match_pat"));
                            }}
                        }

                        _ => {}
                    }
                }
            }
        }}
    }
}

impl<'a, 'tcx: 'a> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
    fn consume(&mut self, consume_id: NodeId, consume_span: Span, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_id, consume_span, cmt);
        }
    }

    fn matched_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::MatchMode) {
        if let euv::MatchMode::MovingMatch = mode {
            self.move_common(matched_pat.id, matched_pat.span, cmt);
        } else {
            self.non_moving_pat(matched_pat, cmt);
        }
    }

    fn consume_pat(&mut self, consume_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_pat.id, consume_pat.span, cmt);
        }
    }

    fn borrow(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: ty::Region, _: ty::BorrowKind, _: euv::LoanCause) {}

    fn mutate(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: euv::MutateMode) {}

    fn decl_without_init(&mut self, _: NodeId, _: Span) {}
}

fn unwrap_downcast_or_interior(mut cmt: mc::cmt) -> mc::cmt {
    loop {
        match cmt.cat.clone() {
            mc::Categorization::Downcast(c, _) |
            mc::Categorization::Interior(c, _) => {
                cmt = c;
            },
            _ => return cmt,
        }
    }
}
<commit_msg>Fix for rustc-1.19.0-nightly (2017-04-04)<commit_after>use rustc::hir::*;
use rustc::hir::intravisit::FnKind;
use rustc::hir::def_id::DefId;
use rustc::lint::*;
use rustc::ty::{self, TypeFoldable};
use rustc::traits;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use syntax::ast::NodeId;
use syntax_pos::Span;
use syntax::errors::DiagnosticBuilder;
use utils::{in_macro, is_self, is_copy, implements_trait, get_trait_def_id, match_type, snippet, span_lint_and_then,
            multispan_sugg, paths};
use std::collections::{HashSet, HashMap};

/// **What it does:** Checks for functions taking arguments by value, but not consuming them in its
/// body.
///
/// **Why is this bad?** Taking arguments by reference is more flexible and can sometimes avoid
/// unnecessary allocations.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// fn foo(v: Vec<i32>) {
///     assert_eq!(v.len(), 42);
/// }
/// ```
declare_lint! {
    pub NEEDLESS_PASS_BY_VALUE,
    Warn,
    "functions taking arguments by value, but not consuming them in its body"
}

pub struct NeedlessPassByValue;

impl LintPass for NeedlessPassByValue {
    fn get_lints(&self) -> LintArray {
        lint_array![NEEDLESS_PASS_BY_VALUE]
    }
}

macro_rules! need {
    ($e: expr) => { if let Some(x) = $e { x } else { return; } };
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        node_id: NodeId
    ) {
        if in_macro(span) {
            return;
        }

        match kind {
            FnKind::ItemFn(.., attrs) => {
                for a in attrs {
                    if_let_chain!{[
                        a.meta_item_list().is_some(),
                        let Some(name) = a.name(),
                        name == "proc_macro_derive",
                    ], {
                        return;
                    }}
                }
            },
            _ => return,
        }

        // Allows these to be passed by value.
        let fn_trait = need!(cx.tcx.lang_items.fn_trait());
        let asref_trait = need!(get_trait_def_id(cx, &paths::ASREF_TRAIT));
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));

        let preds: Vec<ty::Predicate> = {
            let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
            traits::elaborate_predicates(cx.tcx, parameter_env.caller_bounds.to_vec())
                .filter(|p| !p.is_global())
                .collect()
        };

        let fn_def_id = cx.tcx.hir.local_def_id(node_id);

        // Collect moved variables and spans which will need dereferencings from the function body.
        let MovedVariablesCtxt { moved_vars, spans_need_deref, .. } = {
            let mut ctx = MovedVariablesCtxt::new(cx);
            let infcx = cx.tcx.borrowck_fake_infer_ctxt(body.id());
            let region_maps = &cx.tcx.region_maps(fn_def_id);
            {
                let mut v = euv::ExprUseVisitor::new(&mut ctx, region_maps, &infcx);
                v.consume_body(body);
            }
            ctx
        };

        let param_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
        let fn_sig = cx.tcx.type_of(fn_def_id).fn_sig();
        let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, &fn_sig);

        for ((input, &ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {

            // Determines whether `ty` implements `Borrow<U>` (U != ty) specifically.
            // This is needed due to the `Borrow<T> for T` blanket impl.
            let implements_borrow_trait = preds.iter()
                .filter_map(|pred| if let ty::Predicate::Trait(ref poly_trait_ref) = *pred {
                    Some(poly_trait_ref.skip_binder())
                } else {
                    None
                })
                .filter(|tpred| tpred.def_id() == borrow_trait && tpred.self_ty() == ty)
                .any(|tpred| tpred.input_types().nth(1).expect("Borrow trait must have an parameter") != ty);

            if_let_chain! {[
                !is_self(arg),
                !ty.is_mutable_pointer(),
                !is_copy(cx, ty, node_id),
                !implements_trait(cx, ty, fn_trait, &[], Some(node_id)),
                !implements_trait(cx, ty, asref_trait, &[], Some(node_id)),
                !implements_borrow_trait,

                let PatKind::Binding(mode, defid, ..) = arg.pat.node,
                !moved_vars.contains(&defid),
            ], {
                // Note: `toplevel_ref_arg` warns if `BindByRef`
                let m = match mode {
                    BindingMode::BindByRef(m) | BindingMode::BindByValue(m) => m,
                };
                if m == Mutability::MutMutable {
                    continue;
                }

                // Suggestion logic
                let sugg = |db: &mut DiagnosticBuilder| {
                    let deref_span = spans_need_deref.get(&defid);
                    if_let_chain! {[
                        match_type(cx, ty, &paths::VEC),
                        let TyPath(QPath::Resolved(_, ref path)) = input.node,
                        let Some(elem_ty) = path.segments.iter()
                            .find(|seg| seg.name == "Vec")
                            .map(|ps| ps.parameters.types()[0]),
                    ], {
                        let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                        db.span_suggestion(input.span,
                                        "consider changing the type to",
                                        slice_ty);
                        assert!(deref_span.is_none());
                        return; // `Vec` and `String` cannot be destructured - no need for `*` suggestion
                    }}

                    if match_type(cx, ty, &paths::STRING) {
                        db.span_suggestion(input.span,
                                           "consider changing the type to",
                                           "&str".to_string());
                        assert!(deref_span.is_none());
                        return;
                    }

                    let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                    // Suggests adding `*` to dereference the added reference.
                    if let Some(deref_span) = deref_span {
                        spans.extend(deref_span.iter().cloned()
                                     .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))));
                        spans.sort_by_key(|&(span, _)| span);
                    }
                    multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
                };

                span_lint_and_then(cx,
                          NEEDLESS_PASS_BY_VALUE,
                          input.span,
                          "this argument is passed by value, but not consumed in the function body",
                          sugg);
            }}
        }
    }
}

struct MovedVariablesCtxt<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    moved_vars: HashSet<DefId>,
    /// Spans which need to be prefixed with `*` for dereferencing the suggested additional
    /// reference.
    spans_need_deref: HashMap<DefId, HashSet<Span>>,
}

impl<'a, 'tcx: 'a> MovedVariablesCtxt<'a, 'tcx> {
    fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
        MovedVariablesCtxt {
            cx: cx,
            moved_vars: HashSet::new(),
            spans_need_deref: HashMap::new(),
        }
    }

    fn move_common(&mut self, _consume_id: NodeId, _span: Span, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if_let_chain! {[
            let mc::Categorization::Local(vid) = cmt.cat,
            let Some(def_id) = self.cx.tcx.hir.opt_local_def_id(vid),
        ], {
                self.moved_vars.insert(def_id);
        }}
    }

    fn non_moving_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if_let_chain! {[
            let mc::Categorization::Local(vid) = cmt.cat,
            let Some(def_id) = self.cx.tcx.hir.opt_local_def_id(vid),
        ], {
            let mut id = matched_pat.id;
            loop {
                let parent = self.cx.tcx.hir.get_parent_node(id);
                if id == parent {
                    // no parent
                    return;
                }
                id = parent;

                if let Some(node) = self.cx.tcx.hir.find(id) {
                    match node {
                        map::Node::NodeExpr(e) => {
                            // `match` and `if let`
                            if let ExprMatch(ref c, ..) = e.node {
                                self.spans_need_deref
                                    .entry(def_id)
                                    .or_insert_with(HashSet::new)
                                    .insert(c.span);
                            }
                        }

                        map::Node::NodeStmt(s) => {
                            // `let <pat> = x;`
                            if_let_chain! {[
                                let StmtDecl(ref decl, _) = s.node,
                                let DeclLocal(ref local) = decl.node,
                            ], {
                                self.spans_need_deref
                                    .entry(def_id)
                                    .or_insert_with(HashSet::new)
                                    .insert(local.init
                                        .as_ref()
                                        .map(|e| e.span)
                                        .expect("`let` stmt without init aren't caught by match_pat"));
                            }}
                        }

                        _ => {}
                    }
                }
            }
        }}
    }
}

impl<'a, 'tcx: 'a> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
    fn consume(&mut self, consume_id: NodeId, consume_span: Span, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_id, consume_span, cmt);
        }
    }

    fn matched_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::MatchMode) {
        if let euv::MatchMode::MovingMatch = mode {
            self.move_common(matched_pat.id, matched_pat.span, cmt);
        } else {
            self.non_moving_pat(matched_pat, cmt);
        }
    }

    fn consume_pat(&mut self, consume_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_pat.id, consume_pat.span, cmt);
        }
    }

    fn borrow(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: ty::Region, _: ty::BorrowKind, _: euv::LoanCause) {}

    fn mutate(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: euv::MutateMode) {}

    fn decl_without_init(&mut self, _: NodeId, _: Span) {}
}

fn unwrap_downcast_or_interior(mut cmt: mc::cmt) -> mc::cmt {
    loop {
        match cmt.cat.clone() {
            mc::Categorization::Downcast(c, _) |
            mc::Categorization::Interior(c, _) => {
                cmt = c;
            },
            _ => return cmt,
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::str::FromStr;

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

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum AlignMode {
    XminYmin,
    XmidYmin,
    XmaxYmin,
    XminYmid,
    XmidYmid,
    XmaxYmid,
    XminYmax,
    XmidYmax,
    XmaxYmax
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Align {
    None,
    Aligned {
        align: AlignMode,
        fit: FitMode
    }
}

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

impl Default for Align {
    fn default () -> Align {
        Align::Aligned {
            align: AlignMode::XmidYmid,
            fit: FitMode::Meet
        }
    }
}

bitflags! {
    flags AspectRatioFlags: u32 {
        const XMIN_YMIN = (1 << 0),
        const XMID_YMIN = (1 << 1),
        const XMAX_YMIN = (1 << 2),
        const XMIN_YMID = (1 << 3),
        const XMID_YMID = (1 << 4),
        const XMAX_YMID = (1 << 5),
        const XMIN_YMAX = (1 << 6),
        const XMID_YMAX = (1 << 7),
        const XMAX_YMAX = (1 << 8),
        const SLICE = (1 << 30),
        const DEFER = (1 << 31)
    }
}

pub fn aspect_ratio_to_u32 (a: AspectRatio) -> u32 {
    let mut val = AspectRatioFlags::empty ();

    if a.defer { val = val | DEFER; }

    match a.align {
        Align::None => { },

        Align::Aligned { align, fit } => {
            match align {
                AlignMode::XminYmin => { val = val | XMIN_YMIN; },
                AlignMode::XmidYmin => { val = val | XMID_YMIN; },
                AlignMode::XmaxYmin => { val = val | XMAX_YMIN; },
                AlignMode::XminYmid => { val = val | XMIN_YMID; },
                AlignMode::XmidYmid => { val = val | XMID_YMID; },
                AlignMode::XmaxYmid => { val = val | XMAX_YMID; },
                AlignMode::XminYmax => { val = val | XMIN_YMAX; },
                AlignMode::XmidYmax => { val = val | XMID_YMAX; },
                AlignMode::XmaxYmax => { val = val | XMAX_YMAX; },
            }

            match fit {
                FitMode::Meet  => { },
                FitMode::Slice => { val = val | SLICE; }
            }
        }
    }

    val.bits ()
}

pub fn u32_to_aspect_ratio (val: u32) -> AspectRatio {
    let val = AspectRatioFlags::from_bits (val).unwrap ();

    let defer = val.contains (DEFER);

    let mut aligned: bool = true;

    let align: AlignMode = {
        if val.contains (XMIN_YMIN)      { AlignMode::XminYmin }
        else if val.contains (XMID_YMIN) { AlignMode::XmidYmin }
        else if val.contains (XMAX_YMIN) { AlignMode::XmaxYmin }
        else if val.contains (XMIN_YMID) { AlignMode::XminYmid }
        else if val.contains (XMID_YMID) { AlignMode::XmidYmid }
        else if val.contains (XMAX_YMID) { AlignMode::XmaxYmid }
        else if val.contains (XMIN_YMAX) { AlignMode::XminYmax }
        else if val.contains (XMID_YMAX) { AlignMode::XmidYmax }
        else if val.contains (XMAX_YMAX) { AlignMode::XmaxYmax }
        else {
            aligned = false;
            AlignMode::XmidYmid
        }
    };

    let fit: FitMode = if val.contains(SLICE) { FitMode::Slice } else { FitMode::Meet };

    AspectRatio {
        defer: defer,
        align: if aligned {
            Align::Aligned {
                align: align,
                fit: fit
            }
        } else {
            Align::None
        }
    }
}

fn parse_align_mode (s: &str) -> Option<Align> {
    match s {
        "none"     => { Some (Align::None) },
        "XminYmin" => { Some (Align::Aligned { align: AlignMode::XminYmin, fit: FitMode::Meet } ) },
        "XmidYmin" => { Some (Align::Aligned { align: AlignMode::XmidYmin, fit: FitMode::Meet } ) },
        "XmaxYmin" => { Some (Align::Aligned { align: AlignMode::XmaxYmin, fit: FitMode::Meet } ) },
        "XminYmid" => { Some (Align::Aligned { align: AlignMode::XminYmid, fit: FitMode::Meet } ) },
        "XmidYmid" => { Some (Align::Aligned { align: AlignMode::XmidYmid, fit: FitMode::Meet } ) },
        "XmaxYmid" => { Some (Align::Aligned { align: AlignMode::XmaxYmid, fit: FitMode::Meet } ) },
        "XminYmax" => { Some (Align::Aligned { align: AlignMode::XminYmax, fit: FitMode::Meet } ) },
        "XmidYmax" => { Some (Align::Aligned { align: AlignMode::XmidYmax, fit: FitMode::Meet } ) },
        "XmaxYmax" => { Some (Align::Aligned { align: AlignMode::XmaxYmax, fit: FitMode::Meet } ) },
        _          => { None }
    }
}

fn parse_fit_mode (s: &str) -> Option<FitMode> {
    match s {
        "meet"  => { Some (FitMode::Meet) },
        "slice" => { Some (FitMode::Slice) },
        _       => { None }
    }
}

enum ParseState {
    Defer,
    Align,
    Fit,
    Finished
}

impl FromStr for AspectRatio {
    type Err = ParseAspectRatioError;

    fn from_str(s: &str) -> Result<AspectRatio, ParseAspectRatioError> {
        let mut defer = false;
        let mut align: Align = Default::default ();
        let mut fit_mode = FitMode::Meet;

        let mut state = ParseState::Defer;
        let mut iter = s.split_whitespace ();

        while let Some (v) = iter.next () {
            match state {
                ParseState::Defer => {
                    if v == "defer" {
                        defer = true;
                        state = ParseState::Align;
                    } else if let Some (parsed_align) = parse_align_mode (v) {
                        align = parsed_align;
                        state = ParseState::Fit;
                    } else {
                        return Err(ParseAspectRatioError);
                    }
                },

                ParseState::Align => {
                    if let Some (parsed_align) = parse_align_mode (v) {
                        align = parsed_align;
                        state = ParseState::Fit;
                    } else {
                        return Err(ParseAspectRatioError);
                    }
                },

                ParseState::Fit => {
                    if let Some (parsed_fit) = parse_fit_mode (v) {
                        fit_mode = parsed_fit;
                        state = ParseState::Finished;
                    } else {
                        return Err(ParseAspectRatioError);
                    }
                },

                _ => {
                    return Err(ParseAspectRatioError);
                }
            }
        }

        // The string must match "[defer] <align> [meet | slice]".
        // Since the meet|slice is optional, we can end up in either
        // of the following states:
        match state {
            ParseState::Fit | ParseState::Finished => {},
            _ => { return Err(ParseAspectRatioError); }
        }

        Ok (AspectRatio {
            defer: defer,
            align: match align {
                Align::None => { Align::None },
                Align::Aligned { align, ..} => {
                    Align::Aligned {
                        align: align,
                        fit: fit_mode
                    }
                }
            }
        })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseAspectRatioError;

impl fmt::Display for ParseAspectRatioError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "provided string did not match `[defer] <align> [meet | slice]`".fmt (f)
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error () {
        assert_eq! (AspectRatio::from_str (""), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer foo"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer xmidymid"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer XmidYmid foo"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("xmidymid"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("XmidYmid foo"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer XmidYmid meet foo"), Err(ParseAspectRatioError));
    }

    #[test]
    fn parses_valid_strings () {
        assert_eq! (AspectRatio::from_str ("XmidYmid"),
                    Ok (AspectRatio { defer: false,
                                      align: Align::Aligned { align: AlignMode::XmidYmid,
                                                              fit: FitMode::Meet } }));

        assert_eq! (AspectRatio::from_str ("defer XmidYmid"),
                    Ok (AspectRatio { defer: true,
                                      align: Align::Aligned { align: AlignMode::XmidYmid,
                                                              fit: FitMode::Meet } }));

        assert_eq! (AspectRatio::from_str ("defer XminYmax"),
                    Ok (AspectRatio { defer: true,
                                      align: Align::Aligned { align: AlignMode::XminYmax,
                                                              fit: FitMode::Meet } }));

        assert_eq! (AspectRatio::from_str ("defer XmaxYmid meet"),
                    Ok (AspectRatio { defer: true,
                                      align: Align::Aligned { align: AlignMode::XmaxYmid,
                                                              fit: FitMode::Meet } }));

        assert_eq! (AspectRatio::from_str ("defer XminYmax slice"),
                    Ok (AspectRatio { defer: true,
                                      align: Align::Aligned { align: AlignMode::XminYmax,
                                                              fit: FitMode::Slice } }));
    }

    fn test_roundtrip (s: &str) {
        let a = AspectRatio::from_str (s).unwrap ();

        assert_eq! (u32_to_aspect_ratio (aspect_ratio_to_u32 (a)), a);
    }

    #[test]
    fn conversion_to_u32_roundtrips () {
        test_roundtrip ("defer XmidYmid");
        test_roundtrip ("defer XminYmax slice");
        test_roundtrip ("XmaxYmax meet");
        test_roundtrip ("XminYmid slice");
    }
}
<commit_msg>aspect_ratio.rs: Initial docs<commit_after>//! Handling of preserveAspectRatio values
//!
//! This module handles preserveAspectRatio values [per the SVG specification][spec].
//! We have an [`AspectRatio`] struct which encapsulates such a value.
//!
//! [`AspectRatio`] implements `FromStr`, so it can be parsed easily:
//!
//! ```
//! assert_eq! (AspectRatio::from_str ("XmidYmid"),
//!             Ok (AspectRatio { defer: false,
//!                               align: Align::Aligned { align: AlignMode::XmidYmid,
//!                                                       fit: FitMode::Meet } }));
//! ```
//!
//! [`AspectRatio`]: struct.AspectRatio.html
//! [spec]: https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

use std::fmt;
use std::str::FromStr;

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

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum AlignMode {
    XminYmin,
    XmidYmin,
    XmaxYmin,
    XminYmid,
    XmidYmid,
    XmaxYmid,
    XminYmax,
    XmidYmax,
    XmaxYmax
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Align {
    None,
    Aligned {
        align: AlignMode,
        fit: FitMode
    }
}

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

impl Default for Align {
    fn default () -> Align {
        Align::Aligned {
            align: AlignMode::XmidYmid,
            fit: FitMode::Meet
        }
    }
}

bitflags! {
    flags AspectRatioFlags: u32 {
        const XMIN_YMIN = (1 << 0),
        const XMID_YMIN = (1 << 1),
        const XMAX_YMIN = (1 << 2),
        const XMIN_YMID = (1 << 3),
        const XMID_YMID = (1 << 4),
        const XMAX_YMID = (1 << 5),
        const XMIN_YMAX = (1 << 6),
        const XMID_YMAX = (1 << 7),
        const XMAX_YMAX = (1 << 8),
        const SLICE = (1 << 30),
        const DEFER = (1 << 31)
    }
}

pub fn aspect_ratio_to_u32 (a: AspectRatio) -> u32 {
    let mut val = AspectRatioFlags::empty ();

    if a.defer { val = val | DEFER; }

    match a.align {
        Align::None => { },

        Align::Aligned { align, fit } => {
            match align {
                AlignMode::XminYmin => { val = val | XMIN_YMIN; },
                AlignMode::XmidYmin => { val = val | XMID_YMIN; },
                AlignMode::XmaxYmin => { val = val | XMAX_YMIN; },
                AlignMode::XminYmid => { val = val | XMIN_YMID; },
                AlignMode::XmidYmid => { val = val | XMID_YMID; },
                AlignMode::XmaxYmid => { val = val | XMAX_YMID; },
                AlignMode::XminYmax => { val = val | XMIN_YMAX; },
                AlignMode::XmidYmax => { val = val | XMID_YMAX; },
                AlignMode::XmaxYmax => { val = val | XMAX_YMAX; },
            }

            match fit {
                FitMode::Meet  => { },
                FitMode::Slice => { val = val | SLICE; }
            }
        }
    }

    val.bits ()
}

pub fn u32_to_aspect_ratio (val: u32) -> AspectRatio {
    let val = AspectRatioFlags::from_bits (val).unwrap ();

    let defer = val.contains (DEFER);

    let mut aligned: bool = true;

    let align: AlignMode = {
        if val.contains (XMIN_YMIN)      { AlignMode::XminYmin }
        else if val.contains (XMID_YMIN) { AlignMode::XmidYmin }
        else if val.contains (XMAX_YMIN) { AlignMode::XmaxYmin }
        else if val.contains (XMIN_YMID) { AlignMode::XminYmid }
        else if val.contains (XMID_YMID) { AlignMode::XmidYmid }
        else if val.contains (XMAX_YMID) { AlignMode::XmaxYmid }
        else if val.contains (XMIN_YMAX) { AlignMode::XminYmax }
        else if val.contains (XMID_YMAX) { AlignMode::XmidYmax }
        else if val.contains (XMAX_YMAX) { AlignMode::XmaxYmax }
        else {
            aligned = false;
            AlignMode::XmidYmid
        }
    };

    let fit: FitMode = if val.contains(SLICE) { FitMode::Slice } else { FitMode::Meet };

    AspectRatio {
        defer: defer,
        align: if aligned {
            Align::Aligned {
                align: align,
                fit: fit
            }
        } else {
            Align::None
        }
    }
}

fn parse_align_mode (s: &str) -> Option<Align> {
    match s {
        "none"     => { Some (Align::None) },
        "XminYmin" => { Some (Align::Aligned { align: AlignMode::XminYmin, fit: FitMode::Meet } ) },
        "XmidYmin" => { Some (Align::Aligned { align: AlignMode::XmidYmin, fit: FitMode::Meet } ) },
        "XmaxYmin" => { Some (Align::Aligned { align: AlignMode::XmaxYmin, fit: FitMode::Meet } ) },
        "XminYmid" => { Some (Align::Aligned { align: AlignMode::XminYmid, fit: FitMode::Meet } ) },
        "XmidYmid" => { Some (Align::Aligned { align: AlignMode::XmidYmid, fit: FitMode::Meet } ) },
        "XmaxYmid" => { Some (Align::Aligned { align: AlignMode::XmaxYmid, fit: FitMode::Meet } ) },
        "XminYmax" => { Some (Align::Aligned { align: AlignMode::XminYmax, fit: FitMode::Meet } ) },
        "XmidYmax" => { Some (Align::Aligned { align: AlignMode::XmidYmax, fit: FitMode::Meet } ) },
        "XmaxYmax" => { Some (Align::Aligned { align: AlignMode::XmaxYmax, fit: FitMode::Meet } ) },
        _          => { None }
    }
}

fn parse_fit_mode (s: &str) -> Option<FitMode> {
    match s {
        "meet"  => { Some (FitMode::Meet) },
        "slice" => { Some (FitMode::Slice) },
        _       => { None }
    }
}

enum ParseState {
    Defer,
    Align,
    Fit,
    Finished
}

impl FromStr for AspectRatio {
    type Err = ParseAspectRatioError;

    fn from_str(s: &str) -> Result<AspectRatio, ParseAspectRatioError> {
        let mut defer = false;
        let mut align: Align = Default::default ();
        let mut fit_mode = FitMode::Meet;

        let mut state = ParseState::Defer;
        let mut iter = s.split_whitespace ();

        while let Some (v) = iter.next () {
            match state {
                ParseState::Defer => {
                    if v == "defer" {
                        defer = true;
                        state = ParseState::Align;
                    } else if let Some (parsed_align) = parse_align_mode (v) {
                        align = parsed_align;
                        state = ParseState::Fit;
                    } else {
                        return Err(ParseAspectRatioError);
                    }
                },

                ParseState::Align => {
                    if let Some (parsed_align) = parse_align_mode (v) {
                        align = parsed_align;
                        state = ParseState::Fit;
                    } else {
                        return Err(ParseAspectRatioError);
                    }
                },

                ParseState::Fit => {
                    if let Some (parsed_fit) = parse_fit_mode (v) {
                        fit_mode = parsed_fit;
                        state = ParseState::Finished;
                    } else {
                        return Err(ParseAspectRatioError);
                    }
                },

                _ => {
                    return Err(ParseAspectRatioError);
                }
            }
        }

        // The string must match "[defer] <align> [meet | slice]".
        // Since the meet|slice is optional, we can end up in either
        // of the following states:
        match state {
            ParseState::Fit | ParseState::Finished => {},
            _ => { return Err(ParseAspectRatioError); }
        }

        Ok (AspectRatio {
            defer: defer,
            align: match align {
                Align::None => { Align::None },
                Align::Aligned { align, ..} => {
                    Align::Aligned {
                        align: align,
                        fit: fit_mode
                    }
                }
            }
        })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseAspectRatioError;

impl fmt::Display for ParseAspectRatioError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "provided string did not match `[defer] <align> [meet | slice]`".fmt (f)
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error () {
        assert_eq! (AspectRatio::from_str (""), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer foo"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer xmidymid"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer XmidYmid foo"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("xmidymid"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("XmidYmid foo"), Err(ParseAspectRatioError));

        assert_eq! (AspectRatio::from_str ("defer XmidYmid meet foo"), Err(ParseAspectRatioError));
    }

    #[test]
    fn parses_valid_strings () {
        assert_eq! (AspectRatio::from_str ("XmidYmid"),
                    Ok (AspectRatio { defer: false,
                                      align: Align::Aligned { align: AlignMode::XmidYmid,
                                                              fit: FitMode::Meet } }));

        assert_eq! (AspectRatio::from_str ("defer XmidYmid"),
                    Ok (AspectRatio { defer: true,
                                      align: Align::Aligned { align: AlignMode::XmidYmid,
                                                              fit: FitMode::Meet } }));

        assert_eq! (AspectRatio::from_str ("defer XminYmax"),
                    Ok (AspectRatio { defer: true,
                                      align: Align::Aligned { align: AlignMode::XminYmax,
                                                              fit: FitMode::Meet } }));

        assert_eq! (AspectRatio::from_str ("defer XmaxYmid meet"),
                    Ok (AspectRatio { defer: true,
                                      align: Align::Aligned { align: AlignMode::XmaxYmid,
                                                              fit: FitMode::Meet } }));

        assert_eq! (AspectRatio::from_str ("defer XminYmax slice"),
                    Ok (AspectRatio { defer: true,
                                      align: Align::Aligned { align: AlignMode::XminYmax,
                                                              fit: FitMode::Slice } }));
    }

    fn test_roundtrip (s: &str) {
        let a = AspectRatio::from_str (s).unwrap ();

        assert_eq! (u32_to_aspect_ratio (aspect_ratio_to_u32 (a)), a);
    }

    #[test]
    fn conversion_to_u32_roundtrips () {
        test_roundtrip ("defer XmidYmid");
        test_roundtrip ("defer XminYmax slice");
        test_roundtrip ("XmaxYmax meet");
        test_roundtrip ("XminYmid slice");
    }
}
<|endoftext|>