<commit_before>use sass::output_style::SassOutputStyle;
use token::{Lexeme, Token};
use ast::number_value::NumberValue;
use context::Context;
use error::{Result, SassError, ErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    List(Vec<Expression>),
    Number(NumberValue),
    Operator(Lexeme),
    String(Lexeme),
}

impl Expression {
    #[allow(unused_variables)]
    pub fn to_string(&self, style: SassOutputStyle) -> String {
        match *self {
            Expression::List(ref elements) => {
                elements.iter().map(|e|
                    e.to_string(style)
                ).collect::<Vec<_>>().join(" ")
            },
            Expression::Number(ref nv) => nv.to_string(),
            Expression::Operator(ref lex) => lex.token.to_string(),
            Expression::String(ref lex) => lex.token.to_string(),
        }
    }

    pub fn parse<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::Semicolon => {
                    if list.len() == 1 {
                        return Ok(list.pop().unwrap())
                    } else {
                        return Ok(Expression::List(list))
                    }
                },
                Token::Number(_, _) => {
                    list.push(Expression::Number(NumberValue::from_scalar(lexeme)));
                },
                Token::Plus | Token::Minus | Token::Star | Token::Slash | Token::Percent => {
                    list.push(Expression::Operator(lexeme));
                },
                _ => {
                    list.push(Expression::String(lexeme));
                }
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset().unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected semicolon while parsing a value expression; reached EOF instead."
            ),
        })
    }

    fn evaluate_list(self, context: &Context, force_slash: bool) -> Expression {
        if let Expression::List(exprs) = self {
            let mut last_was_an_operator = true;
            // TODO: I'd love to have more types here, to ensure only values are
            // on the value stack and only operators are on the operator stack...
            let mut value_stack: Vec<Expression> = Vec::new();
            let mut op_stack: Vec<Expression> = Vec::new();

            // Split into value stacks and operator stacks

            let mut exprs = exprs.into_iter();
            while let Some(part) = exprs.next() {
                match part {
                    Expression::Number(nv) => {
                        if last_was_an_operator {
                            value_stack.push(Expression::Number(nv));
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                Expression::Number(nv),
                            );
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::Operator(op) => {
                        op_stack.push(Expression::Operator(op));
                        last_was_an_operator = true;
                    },
                    Expression::String(lex) => {
                        let var_eval = context.get_variable(&lex)
                                        .unwrap_or(Expression::String(lex));

                        if last_was_an_operator {
                            value_stack.push(var_eval);
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                var_eval,
                            );
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::List(list) => {
                        value_stack.push(Expression::List(list));
                        last_was_an_operator = false;
                    },
                }
            }

            // Process the stacks
            while !op_stack.is_empty() {
                let op = op_stack.pop().unwrap();
                let second = value_stack.pop().expect("Expected a second argument on the value stack");
                let first = value_stack.pop().expect("Expected a first argument on the value stack");

                value_stack.push(first.apply_math(op, second, context, force_slash));
            }

            value_stack.pop().unwrap()
        } else {
            self
        }
    }

    pub fn evaluate(self, context: &Context) -> Expression {
        match self {
            Expression::Number(nv) => Expression::Number(nv),
            Expression::Operator(op) => Expression::Operator(op),
            Expression::String(lex) => {
                context.get_variable(&lex).unwrap_or(Expression::String(lex))
            },
            Expression::List(exprs) => {
                Expression::List(exprs).evaluate_list(context, false)
            },
        }
    }

    fn apply_math(self, operator: Expression, second: Expression, context: &Context, force_slash: bool) -> Expression {
        match (self.clone(), second.clone()) {
            (Expression::Number(f), Expression::Number(s)) => {
                match operator {
                    Expression::Operator(ref o) if o.token == Token::Slash => {
                        if force_slash || (f.computed || s.computed) {
                            let result = f.apply_math(&o, &s);
                            Expression::Number(result)
                        } else {
                            Expression::List(vec![self, operator.clone(), second.clone()])
                        }
                    },
                    Expression::Operator(o) => {
                        let result = f.apply_math(&o, &s);
                        Expression::Number(result)
                    },
                    _ => unreachable!(),
                }
            },
            (Expression::List(f), Expression::List(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                let eval_second = Expression::List(s).evaluate_list(context, true);
                eval_first.apply_math(operator, eval_second, context, true)
            },
            (Expression::List(f), Expression::Number(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                eval_first.apply_math(operator, Expression::Number(s), context, true)
            },
            (Expression::Number(f), Expression::List(s)) => {
                let eval_second = Expression::List(s).evaluate_list(context, true);
                Expression::Number(f).apply_math(operator, eval_second, context, true)
            },
            _ => unimplemented!(),
        }
    }
}

fn create_list(head: Option<Expression>, tail: Expression) -> Expression {
    let mut list = match head {
        Some(Expression::List(v)) => v,
        Some(e) => vec![e],
        None => vec![],
    };
    list.push(tail);
    Expression::List(list)
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use ast::number_value::NumberValue;
    use context::Context;

    fn semicolon() -> Lexeme {
        Lexeme { token: Token::Semicolon, offset: None }
    }

    fn blue() -> Lexeme {
        Lexeme { token: Token::String("blue".into()), offset: None }
    }

    fn plus() -> Lexeme {
        Lexeme { token: Token::Plus, offset: None }
    }

    fn slash() -> Lexeme {
        Lexeme { token: Token::Slash, offset: None }
    }

    fn one() -> Lexeme {
        Lexeme { token: Token::Number(1.0, None), offset: None }
    }

    fn two() -> Lexeme {
        Lexeme { token: Token::Number(2.0, None), offset: None }
    }

    fn one_px() -> Lexeme {
        Lexeme { token: Token::Number(1.0, Some("px".into())), offset: None }
    }

    #[test]
    fn it_parses_a_single_string() {
        let mut fake_tokenizer = vec![Ok(blue()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::String(blue()))
        );
    }

    #[test]
    fn it_parses_a_list() {
        let mut fake_tokenizer = vec![
            Ok(one()),
            Ok(plus()),
            Ok(one()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::List(vec![
                Expression::Number(NumberValue::from_scalar(one())),
                Expression::Operator(plus()),
                Expression::Number(NumberValue::from_scalar(one())),
            ]))
        );
    }

    #[test]
    fn it_parses_a_number_without_units() {
        let mut fake_tokenizer = vec![Ok(one()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one())))
        );
    }

    #[test]
    fn it_parses_a_number_with_units() {
        let mut fake_tokenizer = vec![Ok(one_px()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one_px())))
        );
    }

    #[test]
    fn it_evaluates_a_list() {
        let ex = Expression::List(vec![
            Expression::List(vec![
                Expression::Number(NumberValue::from_scalar(one())),
                Expression::Operator(slash()),
                Expression::Number(NumberValue::from_scalar(two())),
            ]),
            Expression::Operator(plus()),
            Expression::List(vec![
                Expression::Number(NumberValue::from_scalar(one())),
                Expression::Operator(slash()),
                Expression::Number(NumberValue::from_scalar(two())),
            ]),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ex.evaluate(&fake_context),
            Expression::Number(NumberValue { scalar: one(), computed: true })
        );
    }
}
<commit_msg>Evaluating should create sublists when approps<commit_after>use sass::output_style::SassOutputStyle;
use token::{Lexeme, Token};
use ast::number_value::NumberValue;
use context::Context;
use error::{Result, SassError, ErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    List(Vec<Expression>),
    Number(NumberValue),
    Operator(Lexeme),
    String(Lexeme),
}

impl Expression {
    #[allow(unused_variables)]
    pub fn to_string(&self, style: SassOutputStyle) -> String {
        match *self {
            Expression::List(ref elements) => {
                elements.iter().map(|e|
                    e.to_string(style)
                ).collect::<Vec<_>>().join(" ")
            },
            Expression::Number(ref nv) => nv.to_string(),
            Expression::Operator(ref lex) => lex.token.to_string(),
            Expression::String(ref lex) => lex.token.to_string(),
        }
    }

    pub fn parse<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::Semicolon => {
                    if list.len() == 1 {
                        return Ok(list.pop().unwrap())
                    } else {
                        return Ok(Expression::List(list))
                    }
                },
                Token::Number(_, _) => {
                    list.push(Expression::Number(NumberValue::from_scalar(lexeme)));
                },
                Token::Plus | Token::Minus | Token::Star | Token::Slash | Token::Percent => {
                    list.push(Expression::Operator(lexeme));
                },
                _ => {
                    list.push(Expression::String(lexeme));
                }
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset().unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected semicolon while parsing a value expression; reached EOF instead."
            ),
        })
    }

    fn evaluate_list(self, context: &Context, force_slash: bool) -> Expression {
        if let Expression::List(exprs) = self {
            let mut last_was_an_operator = true;
            // TODO: I'd love to have more types here, to ensure only values are
            // on the value stack and only operators are on the operator stack...
            let mut value_stack: Vec<Expression> = Vec::new();
            let mut op_stack: Vec<Expression> = Vec::new();

            // Split into value stacks and operator stacks

            let mut exprs = exprs.into_iter();
            while let Some(part) = exprs.next() {
                debug!("Processing list item {:#?}", part);
                match part {
                    Expression::Number(nv) => {
                        if last_was_an_operator {
                            debug!("Push on value stack {:#?}", nv);
                            value_stack.push(Expression::Number(nv));
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                Expression::Number(nv),
                            );
                            debug!("Push on list on value stack {:#?}", list);
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::Operator(ref op) if op.token == Token::Slash && !force_slash => {
                        let list = create_list(
                            value_stack.pop(),
                            Expression::Operator(op.clone()),
                        );
                        debug!("Push on list on value stack {:#?}", list);
                        value_stack.push(list);

                    },
                    Expression::Operator(op) => {
                        debug!("Push on op stack {:#?}", op);
                        op_stack.push(Expression::Operator(op));
                        last_was_an_operator = true;
                    },
                    Expression::String(lex) => {
                        let var_eval = context.get_variable(&lex)
                                        .unwrap_or(Expression::String(lex));

                        if last_was_an_operator {
                            value_stack.push(var_eval);
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                var_eval,
                            );
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::List(list) => {
                        debug!("Push list on value stack {:#?}", list);
                        value_stack.push(Expression::List(list));
                        last_was_an_operator = false;
                    },
                }
            }

            // Process the stacks
            while !op_stack.is_empty() {
                let op = op_stack.pop().unwrap();
                let second = value_stack.pop().expect("Expected a second argument on the value stack");
                let first = value_stack.pop().expect("Expected a first argument on the value stack");

                let math_result = first.apply_math(op, second, context, force_slash);
                debug!("Math result: {:#?}", math_result);

                value_stack.push(math_result);
            }

            value_stack.pop().unwrap()
        } else {
            self
        }
    }

    pub fn evaluate(self, context: &Context) -> Expression {
        match self {
            Expression::Number(nv) => Expression::Number(nv),
            Expression::Operator(op) => Expression::Operator(op),
            Expression::String(lex) => {
                context.get_variable(&lex).unwrap_or(Expression::String(lex))
            },
            Expression::List(exprs) => {
                Expression::List(exprs).evaluate_list(context, false)
            },
        }
    }

    fn apply_math(self, operator: Expression, second: Expression, context: &Context, force_slash: bool) -> Expression {
        debug!("Applying math to:\nfirst: {:#?}\nop: {:#?}\nsecond: {:#?}", self, operator, second);
        match (self.clone(), second.clone()) {
            (Expression::Number(f), Expression::Number(s)) => {
                match operator {
                    Expression::Operator(ref o) if o.token == Token::Slash => {
                        if force_slash || (f.computed || s.computed) {
                            let result = f.apply_math(&o, &s);
                            Expression::Number(result)
                        } else {
                            Expression::List(vec![self, operator.clone(), second.clone()])
                        }
                    },
                    Expression::Operator(o) => {
                        let result = f.apply_math(&o, &s);
                        Expression::Number(result)
                    },
                    _ => unreachable!(),
                }
            },
            (Expression::List(f), Expression::List(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                let eval_second = Expression::List(s).evaluate_list(context, true);
                eval_first.apply_math(operator, eval_second, context, true)
            },
            (Expression::List(f), Expression::Number(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                eval_first.apply_math(operator, Expression::Number(s), context, true)
            },
            (Expression::Number(f), Expression::List(s)) => {
                let eval_second = Expression::List(s).evaluate_list(context, true);
                Expression::Number(f).apply_math(operator, eval_second, context, true)
            },
            _ => unimplemented!(),
        }
    }
}

fn create_list(head: Option<Expression>, tail: Expression) -> Expression {
    let mut list = match head {
        Some(Expression::List(v)) => v,
        Some(e) => vec![e],
        None => vec![],
    };
    list.push(tail);
    Expression::List(list)
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use ast::number_value::NumberValue;
    use context::Context;

    fn semicolon() -> Lexeme {
        Lexeme { token: Token::Semicolon, offset: None }
    }

    fn blue() -> Lexeme {
        Lexeme { token: Token::String("blue".into()), offset: None }
    }

    fn plus() -> Lexeme {
        Lexeme { token: Token::Plus, offset: None }
    }

    fn slash() -> Lexeme {
        Lexeme { token: Token::Slash, offset: None }
    }

    fn one() -> Lexeme {
        Lexeme { token: Token::Number(1.0, None), offset: None }
    }

    fn two() -> Lexeme {
        Lexeme { token: Token::Number(2.0, None), offset: None }
    }

    fn one_px() -> Lexeme {
        Lexeme { token: Token::Number(1.0, Some("px".into())), offset: None }
    }

    #[test]
    fn it_parses_a_single_string() {
        let mut fake_tokenizer = vec![Ok(blue()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::String(blue()))
        );
    }

    #[test]
    fn it_parses_a_list() {
        let mut fake_tokenizer = vec![
            Ok(one()),
            Ok(plus()),
            Ok(one()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::List(vec![
                Expression::Number(NumberValue::from_scalar(one())),
                Expression::Operator(plus()),
                Expression::Number(NumberValue::from_scalar(one())),
            ]))
        );
    }

    #[test]
    fn it_parses_a_number_without_units() {
        let mut fake_tokenizer = vec![Ok(one()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one())))
        );
    }

    #[test]
    fn it_parses_a_number_with_units() {
        let mut fake_tokenizer = vec![Ok(one_px()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one_px())))
        );
    }

    #[test]
    fn it_evaluates_a_list() {
        let ex = Expression::List(vec![
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
            Expression::Operator(plus()),
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ex.evaluate(&fake_context),
            Expression::Number(NumberValue { scalar: one(), computed: true })
        );
    }
}
<|endoftext|>


<commit_before>//! TODO Fill in

use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::class::{self, Class, ClassBuilder};

#[derive(Clone, Debug)]
pub struct MouseState {
    // TODO Fill in
    dummy: i32
}

pub struct Mouse<'lua>(Table<'lua>);

impl Default for MouseState {
    fn default() -> Self {
        MouseState {
            dummy: 0
        }
    }
}

impl <'lua> Mouse<'lua> {
    fn new(lua: &Lua) -> rlua::Result<Object> {
        // TODO FIXME
        let class = class::button_class(lua)?;
        Ok(Mouse::allocate(lua, class)?.build())
    }
}

impl Display for MouseState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Mouse: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Mouse<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for MouseState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    method_setup(lua, Class::builder(lua, Some(Rc::new(Mouse::new)), None, None)?)?
        .save_class("mouse")?
        .build()
}

fn method_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy))
}

impl_objectable!(Mouse, MouseState);
<commit_msg>Fixed connect_signal for mouse, made not an object<commit_after>//! TODO Fill in

use std::fmt::{self, Display, Formatter};
use std::default::Default;
use rlua::{self, Table, Lua, UserData, ToLua};
use super::signal;

#[derive(Clone, Debug)]
pub struct MouseState {
    // TODO Fill in
    dummy: i32
}

impl Default for MouseState {
    fn default() -> Self {
        MouseState {
            dummy: 0
        }
    }
}

impl Display for MouseState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Mouse: {:p}", self)
    }
}

impl UserData for MouseState {}

pub fn init(lua: &Lua) -> rlua::Result<()> {
    let mouse_table = lua.create_table();
    state_setup(lua, &mouse_table)?;
    meta_setup(lua, &mouse_table)?;
    method_setup(lua, &mouse_table)?;
    let globals = lua.globals();
    globals.set("mouse", mouse_table)
}

fn state_setup(lua: &Lua, mouse_table: &Table) -> rlua::Result<()> {
    mouse_table.set("__data", MouseState::default().to_lua(lua)?)
}

fn meta_setup(lua: &Lua, mouse_table: &Table) -> rlua::Result<()> {
    let meta_table = lua.create_table();
    mouse_table.set("__tostring", lua.create_function(|_, val: Table| {
        Ok(format!("{}", val.get::<_, MouseState>("__data")?))
    }))?;
    mouse_table.set_metatable(Some(meta_table));
    Ok(())
}

fn method_setup(lua: &Lua, mouse_table: &Table) -> rlua::Result<()> {
    mouse_table.set("connect_signal", lua.create_function(signal::global_connect_signal))
}
<|endoftext|>


<commit_before>use cargo::core::features;
use cargo::{self, drop_print, drop_println, CliResult, Config};
use clap::{AppSettings, Arg, ArgMatches};

use super::commands;
use super::list_commands;
use crate::command_prelude::*;

pub fn main(config: &mut Config) -> CliResult {
    // CAUTION: Be careful with using `config` until it is configured below.
    // In general, try to avoid loading config values unless necessary (like
    // the [alias] table).

    if commands::help::handle_embedded_help(config) {
        return Ok(());
    }

    let args = match cli().get_matches_safe() {
        Ok(args) => args,
        Err(e) => {
            if e.kind == clap::ErrorKind::UnrecognizedSubcommand {
                // An unrecognized subcommand might be an external subcommand.
                let cmd = &e.info.as_ref().unwrap()[0].to_owned();
                return super::execute_external_subcommand(config, cmd, &[cmd, "--help"])
                    .map_err(|_| e.into());
            } else {
                return Err(e.into());
            }
        }
    };

    if args.value_of("unstable-features") == Some("help") {
        drop_println!(
            config,
            "
Available unstable (nightly-only) flags:

    -Z avoid-dev-deps      -- Avoid installing dev-dependencies if possible
    -Z extra-link-arg      -- Allow `cargo:rustc-link-arg` in build scripts
    -Z minimal-versions    -- Install minimal dependency versions instead of maximum
    -Z no-index-update     -- Do not update the registry, avoids a network request for benchmarking
    -Z unstable-options    -- Allow the usage of unstable options
    -Z timings             -- Display concurrency information
    -Z doctest-xcompile    -- Compile and run doctests for non-host target using runner config
    -Z terminal-width      -- Provide a terminal width to rustc for error truncation
    -Z namespaced-features -- Allow features with `dep:` prefix
    -Z weak-dep-features   -- Allow `dep_name?/feature` feature syntax

Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
        );
        if !config.nightly_features_allowed {
            drop_println!(
                config,
                "\nUnstable flags are only available on the nightly channel \
                 of Cargo, but this is the `{}` channel.\n\
                 {}",
                features::channel(),
                features::SEE_CHANNELS
            );
        }
        drop_println!(
            config,
            "\nSee https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \
             for more information about these flags."
        );
        return Ok(());
    }

    let is_verbose = args.occurrences_of("verbose") > 0;
    if args.is_present("version") {
        let version = get_version_string(is_verbose);
        drop_print!(config, "{}", version);
        return Ok(());
    }

    if let Some(code) = args.value_of("explain") {
        let mut procss = config.load_global_rustc(None)?.process();
        procss.arg("--explain").arg(code).exec()?;
        return Ok(());
    }

    if args.is_present("list") {
        drop_println!(config, "Installed Commands:");
        for command in list_commands(config) {
            match command {
                CommandInfo::BuiltIn { name, about } => {
                    let summary = about.unwrap_or_default();
                    let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
                    drop_println!(config, "    {:<20} {}", name, summary);
                }
                CommandInfo::External { name, path } => {
                    if is_verbose {
                        drop_println!(config, "    {:<20} {}", name, path.display());
                    } else {
                        drop_println!(config, "    {}", name);
                    }
                }
            }
        }
        return Ok(());
    }

    // Global args need to be extracted before expanding aliases because the
    // clap code for extracting a subcommand discards global options
    // (appearing before the subcommand).
    let (expanded_args, global_args) = expand_aliases(config, args)?;
    let (cmd, subcommand_args) = match expanded_args.subcommand() {
        (cmd, Some(args)) => (cmd, args),
        _ => {
            // No subcommand provided.
            cli().print_help()?;
            return Ok(());
        }
    };
    config_configure(config, &expanded_args, subcommand_args, global_args)?;
    super::init_git_transports(config);

    execute_subcommand(config, cmd, subcommand_args)
}

pub fn get_version_string(is_verbose: bool) -> String {
    let version = cargo::version();
    let mut version_string = version.to_string();
    version_string.push('\n');
    if is_verbose {
        version_string.push_str(&format!(
            "release: {}.{}.{}\n",
            version.major, version.minor, version.patch
        ));
        if let Some(ref cfg) = version.cfg_info {
            if let Some(ref ci) = cfg.commit_info {
                version_string.push_str(&format!("commit-hash: {}\n", ci.commit_hash));
                version_string.push_str(&format!("commit-date: {}\n", ci.commit_date));
            }
        }
    }
    version_string
}

fn expand_aliases(
    config: &mut Config,
    args: ArgMatches<'static>,
) -> Result<(ArgMatches<'static>, GlobalArgs), CliError> {
    if let (cmd, Some(args)) = args.subcommand() {
        match (
            commands::builtin_exec(cmd),
            super::aliased_command(config, cmd)?,
        ) {
            (Some(_), Some(_)) => {
                // User alias conflicts with a built-in subcommand
                config.shell().warn(format!(
                    "user-defined alias `{}` is ignored, because it is shadowed by a built-in command",
                    cmd,
                ))?;
            }
            (_, Some(mut alias)) => {
                alias.extend(
                    args.values_of("")
                        .unwrap_or_default()
                        .map(|s| s.to_string()),
                );
                // new_args strips out everything before the subcommand, so
                // capture those global options now.
                // Note that an alias to an external command will not receive
                // these arguments. That may be confusing, but such is life.
                let global_args = GlobalArgs::new(args);
                let new_args = cli()
                    .setting(AppSettings::NoBinaryName)
                    .get_matches_from_safe(alias)?;
                let (expanded_args, _) = expand_aliases(config, new_args)?;
                return Ok((expanded_args, global_args));
            }
            (_, None) => {}
        }
    };

    Ok((args, GlobalArgs::default()))
}

fn config_configure(
    config: &mut Config,
    args: &ArgMatches<'_>,
    subcommand_args: &ArgMatches<'_>,
    global_args: GlobalArgs,
) -> CliResult {
    let arg_target_dir = &subcommand_args.value_of_path("target-dir", config);
    let verbose = global_args.verbose + args.occurrences_of("verbose") as u32;
    // quiet is unusual because it is redefined in some subcommands in order
    // to provide custom help text.
    let quiet =
        args.is_present("quiet") || subcommand_args.is_present("quiet") || global_args.quiet;
    let global_color = global_args.color; // Extract so it can take reference.
    let color = args.value_of("color").or_else(|| global_color.as_deref());
    let frozen = args.is_present("frozen") || global_args.frozen;
    let locked = args.is_present("locked") || global_args.locked;
    let offline = args.is_present("offline") || global_args.offline;
    let mut unstable_flags = global_args.unstable_flags;
    if let Some(values) = args.values_of("unstable-features") {
        unstable_flags.extend(values.map(|s| s.to_string()));
    }
    let mut config_args = global_args.config_args;
    if let Some(values) = args.values_of("config") {
        config_args.extend(values.map(|s| s.to_string()));
    }
    config.configure(
        verbose,
        quiet,
        color,
        frozen,
        locked,
        offline,
        arg_target_dir,
        &unstable_flags,
        &config_args,
    )?;
    Ok(())
}

fn execute_subcommand(
    config: &mut Config,
    cmd: &str,
    subcommand_args: &ArgMatches<'_>,
) -> CliResult {
    if let Some(exec) = commands::builtin_exec(cmd) {
        return exec(config, subcommand_args);
    }

    let mut ext_args: Vec<&str> = vec![cmd];
    ext_args.extend(subcommand_args.values_of("").unwrap_or_default());
    super::execute_external_subcommand(config, cmd, &ext_args)
}

#[derive(Default)]
struct GlobalArgs {
    verbose: u32,
    quiet: bool,
    color: Option<String>,
    frozen: bool,
    locked: bool,
    offline: bool,
    unstable_flags: Vec<String>,
    config_args: Vec<String>,
}

impl GlobalArgs {
    fn new(args: &ArgMatches<'_>) -> GlobalArgs {
        GlobalArgs {
            verbose: args.occurrences_of("verbose") as u32,
            quiet: args.is_present("quiet"),
            color: args.value_of("color").map(|s| s.to_string()),
            frozen: args.is_present("frozen"),
            locked: args.is_present("locked"),
            offline: args.is_present("offline"),
            unstable_flags: args
                .values_of_lossy("unstable-features")
                .unwrap_or_default(),
            config_args: args
                .values_of("config")
                .unwrap_or_default()
                .map(|s| s.to_string())
                .collect(),
        }
    }
}

fn cli() -> App {
    let is_rustup = std::env::var_os("RUSTUP_HOME").is_some();
    let usage = if is_rustup {
        "cargo [+toolchain] [OPTIONS] [SUBCOMMAND]"
    } else {
        "cargo [OPTIONS] [SUBCOMMAND]"
    };
    App::new("cargo")
        .settings(&[
            AppSettings::UnifiedHelpMessage,
            AppSettings::DeriveDisplayOrder,
            AppSettings::VersionlessSubcommands,
            AppSettings::AllowExternalSubcommands,
        ])
        .usage(usage)
        .template(
            "\
Rust's package manager

USAGE:
    {usage}

OPTIONS:
{unified}

Some common cargo commands are (see all commands with --list):
    build, b    Compile the current package
    check, c    Analyze the current package and report errors, but don't build object files
    clean       Remove the target directory
    doc         Build this package's and its dependencies' documentation
    new         Create a new cargo package
    init        Create a new cargo package in an existing directory
    run, r      Run a binary or example of the local package
    test, t     Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this package to the registry
    install     Install a Rust binary. Default location is $HOME/.cargo/bin
    uninstall   Uninstall a Rust binary

See 'cargo help <command>' for more information on a specific command.\n",
        )
        .arg(opt("version", "Print version info and exit").short("V"))
        .arg(opt("list", "List installed commands"))
        .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
        .arg(
            opt(
                "verbose",
                "Use verbose output (-vv very verbose/build.rs output)",
            )
            .short("v")
            .multiple(true)
            .global(true),
        )
        .arg(opt("quiet", "No output printed to stdout").short("q"))
        .arg(
            opt("color", "Coloring: auto, always, never")
                .value_name("WHEN")
                .global(true),
        )
        .arg(opt("frozen", "Require Cargo.lock and cache are up to date").global(true))
        .arg(opt("locked", "Require Cargo.lock is up to date").global(true))
        .arg(opt("offline", "Run without accessing the network").global(true))
        .arg(
            multi_opt(
                "config",
                "KEY=VALUE",
                "Override a configuration value (unstable)",
            )
            .global(true),
        )
        .arg(
            Arg::with_name("unstable-features")
                .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
                .short("Z")
                .value_name("FLAG")
                .multiple(true)
                .number_of_values(1)
                .global(true),
        )
        .subcommands(commands::builtin())
}
<commit_msg>Auto merge of #9271 - jonhoo:patch-in-config-in-help, r=ehuss<commit_after>use cargo::core::features;
use cargo::{self, drop_print, drop_println, CliResult, Config};
use clap::{AppSettings, Arg, ArgMatches};

use super::commands;
use super::list_commands;
use crate::command_prelude::*;

pub fn main(config: &mut Config) -> CliResult {
    // CAUTION: Be careful with using `config` until it is configured below.
    // In general, try to avoid loading config values unless necessary (like
    // the [alias] table).

    if commands::help::handle_embedded_help(config) {
        return Ok(());
    }

    let args = match cli().get_matches_safe() {
        Ok(args) => args,
        Err(e) => {
            if e.kind == clap::ErrorKind::UnrecognizedSubcommand {
                // An unrecognized subcommand might be an external subcommand.
                let cmd = &e.info.as_ref().unwrap()[0].to_owned();
                return super::execute_external_subcommand(config, cmd, &[cmd, "--help"])
                    .map_err(|_| e.into());
            } else {
                return Err(e.into());
            }
        }
    };

    if args.value_of("unstable-features") == Some("help") {
        drop_println!(
            config,
            "
Available unstable (nightly-only) flags:

    -Z avoid-dev-deps      -- Avoid installing dev-dependencies if possible
    -Z extra-link-arg      -- Allow `cargo:rustc-link-arg` in build scripts
    -Z minimal-versions    -- Install minimal dependency versions instead of maximum
    -Z no-index-update     -- Do not update the registry, avoids a network request for benchmarking
    -Z unstable-options    -- Allow the usage of unstable options
    -Z timings             -- Display concurrency information
    -Z doctest-xcompile    -- Compile and run doctests for non-host target using runner config
    -Z terminal-width      -- Provide a terminal width to rustc for error truncation
    -Z namespaced-features -- Allow features with `dep:` prefix
    -Z weak-dep-features   -- Allow `dep_name?/feature` feature syntax
    -Z patch-in-config     -- Allow `[patch]` sections in .cargo/config.toml files

Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
        );
        if !config.nightly_features_allowed {
            drop_println!(
                config,
                "\nUnstable flags are only available on the nightly channel \
                 of Cargo, but this is the `{}` channel.\n\
                 {}",
                features::channel(),
                features::SEE_CHANNELS
            );
        }
        drop_println!(
            config,
            "\nSee https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \
             for more information about these flags."
        );
        return Ok(());
    }

    let is_verbose = args.occurrences_of("verbose") > 0;
    if args.is_present("version") {
        let version = get_version_string(is_verbose);
        drop_print!(config, "{}", version);
        return Ok(());
    }

    if let Some(code) = args.value_of("explain") {
        let mut procss = config.load_global_rustc(None)?.process();
        procss.arg("--explain").arg(code).exec()?;
        return Ok(());
    }

    if args.is_present("list") {
        drop_println!(config, "Installed Commands:");
        for command in list_commands(config) {
            match command {
                CommandInfo::BuiltIn { name, about } => {
                    let summary = about.unwrap_or_default();
                    let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
                    drop_println!(config, "    {:<20} {}", name, summary);
                }
                CommandInfo::External { name, path } => {
                    if is_verbose {
                        drop_println!(config, "    {:<20} {}", name, path.display());
                    } else {
                        drop_println!(config, "    {}", name);
                    }
                }
            }
        }
        return Ok(());
    }

    // Global args need to be extracted before expanding aliases because the
    // clap code for extracting a subcommand discards global options
    // (appearing before the subcommand).
    let (expanded_args, global_args) = expand_aliases(config, args)?;
    let (cmd, subcommand_args) = match expanded_args.subcommand() {
        (cmd, Some(args)) => (cmd, args),
        _ => {
            // No subcommand provided.
            cli().print_help()?;
            return Ok(());
        }
    };
    config_configure(config, &expanded_args, subcommand_args, global_args)?;
    super::init_git_transports(config);

    execute_subcommand(config, cmd, subcommand_args)
}

pub fn get_version_string(is_verbose: bool) -> String {
    let version = cargo::version();
    let mut version_string = version.to_string();
    version_string.push('\n');
    if is_verbose {
        version_string.push_str(&format!(
            "release: {}.{}.{}\n",
            version.major, version.minor, version.patch
        ));
        if let Some(ref cfg) = version.cfg_info {
            if let Some(ref ci) = cfg.commit_info {
                version_string.push_str(&format!("commit-hash: {}\n", ci.commit_hash));
                version_string.push_str(&format!("commit-date: {}\n", ci.commit_date));
            }
        }
    }
    version_string
}

fn expand_aliases(
    config: &mut Config,
    args: ArgMatches<'static>,
) -> Result<(ArgMatches<'static>, GlobalArgs), CliError> {
    if let (cmd, Some(args)) = args.subcommand() {
        match (
            commands::builtin_exec(cmd),
            super::aliased_command(config, cmd)?,
        ) {
            (Some(_), Some(_)) => {
                // User alias conflicts with a built-in subcommand
                config.shell().warn(format!(
                    "user-defined alias `{}` is ignored, because it is shadowed by a built-in command",
                    cmd,
                ))?;
            }
            (_, Some(mut alias)) => {
                alias.extend(
                    args.values_of("")
                        .unwrap_or_default()
                        .map(|s| s.to_string()),
                );
                // new_args strips out everything before the subcommand, so
                // capture those global options now.
                // Note that an alias to an external command will not receive
                // these arguments. That may be confusing, but such is life.
                let global_args = GlobalArgs::new(args);
                let new_args = cli()
                    .setting(AppSettings::NoBinaryName)
                    .get_matches_from_safe(alias)?;
                let (expanded_args, _) = expand_aliases(config, new_args)?;
                return Ok((expanded_args, global_args));
            }
            (_, None) => {}
        }
    };

    Ok((args, GlobalArgs::default()))
}

fn config_configure(
    config: &mut Config,
    args: &ArgMatches<'_>,
    subcommand_args: &ArgMatches<'_>,
    global_args: GlobalArgs,
) -> CliResult {
    let arg_target_dir = &subcommand_args.value_of_path("target-dir", config);
    let verbose = global_args.verbose + args.occurrences_of("verbose") as u32;
    // quiet is unusual because it is redefined in some subcommands in order
    // to provide custom help text.
    let quiet =
        args.is_present("quiet") || subcommand_args.is_present("quiet") || global_args.quiet;
    let global_color = global_args.color; // Extract so it can take reference.
    let color = args.value_of("color").or_else(|| global_color.as_deref());
    let frozen = args.is_present("frozen") || global_args.frozen;
    let locked = args.is_present("locked") || global_args.locked;
    let offline = args.is_present("offline") || global_args.offline;
    let mut unstable_flags = global_args.unstable_flags;
    if let Some(values) = args.values_of("unstable-features") {
        unstable_flags.extend(values.map(|s| s.to_string()));
    }
    let mut config_args = global_args.config_args;
    if let Some(values) = args.values_of("config") {
        config_args.extend(values.map(|s| s.to_string()));
    }
    config.configure(
        verbose,
        quiet,
        color,
        frozen,
        locked,
        offline,
        arg_target_dir,
        &unstable_flags,
        &config_args,
    )?;
    Ok(())
}

fn execute_subcommand(
    config: &mut Config,
    cmd: &str,
    subcommand_args: &ArgMatches<'_>,
) -> CliResult {
    if let Some(exec) = commands::builtin_exec(cmd) {
        return exec(config, subcommand_args);
    }

    let mut ext_args: Vec<&str> = vec![cmd];
    ext_args.extend(subcommand_args.values_of("").unwrap_or_default());
    super::execute_external_subcommand(config, cmd, &ext_args)
}

#[derive(Default)]
struct GlobalArgs {
    verbose: u32,
    quiet: bool,
    color: Option<String>,
    frozen: bool,
    locked: bool,
    offline: bool,
    unstable_flags: Vec<String>,
    config_args: Vec<String>,
}

impl GlobalArgs {
    fn new(args: &ArgMatches<'_>) -> GlobalArgs {
        GlobalArgs {
            verbose: args.occurrences_of("verbose") as u32,
            quiet: args.is_present("quiet"),
            color: args.value_of("color").map(|s| s.to_string()),
            frozen: args.is_present("frozen"),
            locked: args.is_present("locked"),
            offline: args.is_present("offline"),
            unstable_flags: args
                .values_of_lossy("unstable-features")
                .unwrap_or_default(),
            config_args: args
                .values_of("config")
                .unwrap_or_default()
                .map(|s| s.to_string())
                .collect(),
        }
    }
}

fn cli() -> App {
    let is_rustup = std::env::var_os("RUSTUP_HOME").is_some();
    let usage = if is_rustup {
        "cargo [+toolchain] [OPTIONS] [SUBCOMMAND]"
    } else {
        "cargo [OPTIONS] [SUBCOMMAND]"
    };
    App::new("cargo")
        .settings(&[
            AppSettings::UnifiedHelpMessage,
            AppSettings::DeriveDisplayOrder,
            AppSettings::VersionlessSubcommands,
            AppSettings::AllowExternalSubcommands,
        ])
        .usage(usage)
        .template(
            "\
Rust's package manager

USAGE:
    {usage}

OPTIONS:
{unified}

Some common cargo commands are (see all commands with --list):
    build, b    Compile the current package
    check, c    Analyze the current package and report errors, but don't build object files
    clean       Remove the target directory
    doc         Build this package's and its dependencies' documentation
    new         Create a new cargo package
    init        Create a new cargo package in an existing directory
    run, r      Run a binary or example of the local package
    test, t     Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this package to the registry
    install     Install a Rust binary. Default location is $HOME/.cargo/bin
    uninstall   Uninstall a Rust binary

See 'cargo help <command>' for more information on a specific command.\n",
        )
        .arg(opt("version", "Print version info and exit").short("V"))
        .arg(opt("list", "List installed commands"))
        .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
        .arg(
            opt(
                "verbose",
                "Use verbose output (-vv very verbose/build.rs output)",
            )
            .short("v")
            .multiple(true)
            .global(true),
        )
        .arg(opt("quiet", "No output printed to stdout").short("q"))
        .arg(
            opt("color", "Coloring: auto, always, never")
                .value_name("WHEN")
                .global(true),
        )
        .arg(opt("frozen", "Require Cargo.lock and cache are up to date").global(true))
        .arg(opt("locked", "Require Cargo.lock is up to date").global(true))
        .arg(opt("offline", "Run without accessing the network").global(true))
        .arg(
            multi_opt(
                "config",
                "KEY=VALUE",
                "Override a configuration value (unstable)",
            )
            .global(true),
        )
        .arg(
            Arg::with_name("unstable-features")
                .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
                .short("Z")
                .value_name("FLAG")
                .multiple(true)
                .number_of_values(1)
                .global(true),
        )
        .subcommands(commands::builtin())
}
<|endoftext|>


<commit_before><commit_msg>reduce concurrent_logging runs<commit_after><|endoftext|>


<commit_before>extern crate difference;
extern crate file;

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

use difference::Changeset;

/// Read file and normalize newlines.
///
/// `rustc` seems to always normalize `\r\n` newlines to `\n`:
///
/// ```
/// let s = "
/// ";
/// assert_eq!(s.as_bytes(), &[10]);
/// ```
///
/// so this should always be correct.
fn read_text(path: &Path) -> String {
    file::get_text(path).unwrap().replace("\r\n", "\n")
}

pub fn dir_tests<F>(paths: &[&str], f: F)
    where
        F: Fn(&str) -> String,
{
    for path in collect_tests(paths) {
        let actual = {
            let text = read_text(&path);
            f(&text)
        };
        let path = path.with_extension("txt");
        if !path.exists() {
            println!("\nfile: {}", path.display());
            println!("No .txt file with expected result, creating...");
            file::put_text(&path, actual).unwrap();
            panic!("No expected result")
        }
        let expected = read_text(&path);
        let expected = expected.as_str();
        let actual = actual.as_str();
        assert_equal_text(expected, actual, &path);
    }
}

fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
    if expected != actual {
        print_difference(expected, actual, path)
    }
}

fn collect_tests(paths: &[&str]) -> Vec<PathBuf> {
    paths
        .iter()
        .flat_map(|path| {
            let path = test_data_dir().join(path);
            test_from_dir(&path).into_iter()
        })
        .collect()
}

fn test_from_dir(dir: &Path) -> Vec<PathBuf> {
    let mut acc = Vec::new();
    for file in read_dir(&dir).unwrap() {
        let file = file.unwrap();
        let path = file.path();
        if path.extension().unwrap_or_default() == "rs" {
            acc.push(path);
        }
    }
    acc.sort();
    acc
}

const REWRITE: bool = false;

fn print_difference(expected: &str, actual: &str, path: &Path) {
    let dir = project_dir();
    let path = path.strip_prefix(&dir).unwrap_or_else(|_| path);
    if expected.trim() == actual.trim() {
        println!("whitespace difference, rewriting");
        println!("file: {}\n", path.display());
        file::put_text(path, actual).unwrap();
        return;
    }
    if REWRITE {
        println!("rewriting {}", path.display());
        file::put_text(path, actual).unwrap();
        return;
    }
    let changeset = Changeset::new(actual, expected, "\n");
    print!("{}", changeset);
    println!("file: {}\n", path.display());
    panic!("Comparison failed")
}

fn project_dir() -> PathBuf {
    let dir = env!("CARGO_MANIFEST_DIR");
    PathBuf::from(dir)
        .parent()
        .unwrap()
        .parent()
        .unwrap()
        .to_owned()
}

fn test_data_dir() -> PathBuf {
    project_dir().join("tests/data")
}
<commit_msg>Reformat<commit_after>extern crate difference;
extern crate file;

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

use difference::Changeset;

/// Read file and normalize newlines.
///
/// `rustc` seems to always normalize `\r\n` newlines to `\n`:
///
/// ```
/// let s = "
/// ";
/// assert_eq!(s.as_bytes(), &[10]);
/// ```
///
/// so this should always be correct.
fn read_text(path: &Path) -> String {
    file::get_text(path).unwrap().replace("\r\n", "\n")
}

pub fn dir_tests<F>(paths: &[&str], f: F)
where
    F: Fn(&str) -> String,
{
    for path in collect_tests(paths) {
        let actual = {
            let text = read_text(&path);
            f(&text)
        };
        let path = path.with_extension("txt");
        if !path.exists() {
            println!("\nfile: {}", path.display());
            println!("No .txt file with expected result, creating...");
            file::put_text(&path, actual).unwrap();
            panic!("No expected result")
        }
        let expected = read_text(&path);
        let expected = expected.as_str();
        let actual = actual.as_str();
        assert_equal_text(expected, actual, &path);
    }
}

fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
    if expected != actual {
        print_difference(expected, actual, path)
    }
}

fn collect_tests(paths: &[&str]) -> Vec<PathBuf> {
    paths
        .iter()
        .flat_map(|path| {
            let path = test_data_dir().join(path);
            test_from_dir(&path).into_iter()
        })
        .collect()
}

fn test_from_dir(dir: &Path) -> Vec<PathBuf> {
    let mut acc = Vec::new();
    for file in read_dir(&dir).unwrap() {
        let file = file.unwrap();
        let path = file.path();
        if path.extension().unwrap_or_default() == "rs" {
            acc.push(path);
        }
    }
    acc.sort();
    acc
}

const REWRITE: bool = false;

fn print_difference(expected: &str, actual: &str, path: &Path) {
    let dir = project_dir();
    let path = path.strip_prefix(&dir).unwrap_or_else(|_| path);
    if expected.trim() == actual.trim() {
        println!("whitespace difference, rewriting");
        println!("file: {}\n", path.display());
        file::put_text(path, actual).unwrap();
        return;
    }
    if REWRITE {
        println!("rewriting {}", path.display());
        file::put_text(path, actual).unwrap();
        return;
    }
    let changeset = Changeset::new(actual, expected, "\n");
    print!("{}", changeset);
    println!("file: {}\n", path.display());
    panic!("Comparison failed")
}

fn project_dir() -> PathBuf {
    let dir = env!("CARGO_MANIFEST_DIR");
    PathBuf::from(dir)
        .parent()
        .unwrap()
        .parent()
        .unwrap()
        .to_owned()
}

fn test_data_dir() -> PathBuf {
    project_dir().join("tests/data")
}
<|endoftext|>


<commit_before>use std::io::net::udp::UdpSocket;
use std::io::net::ip::{Ipv4Addr, SocketAddr};

#[allow(dead_code,non_camel_case_types)]
enum UtpPacketType {
    ST_DATA  = 0,
    ST_FIN   = 1,
    ST_STATE = 2,
    ST_RESET = 3,
    ST_SYN   = 4,
}

#[allow(dead_code)]
struct UtpPacketHeader {
    type_ver: u8, // type: u4, ver: u4
    extension: u8,
    connection_id: u16,
    timestamp_microseconds: u32,
    timestamp_difference_microseconds: u32,
    wnd_size: u32,
    seq_nr: u16,
    ack_nr: u16,
}

impl UtpPacketHeader {
    fn bytes(&self) -> &[u8] {
        unsafe {
            let buf: &[u8, ..20] = std::mem::transmute(self);
            return buf.as_slice();
        }
    }

    fn len(&self) -> uint {
        return 24;
    }
}

#[allow(dead_code)]
struct UtpPacket {
    header: UtpPacketHeader,
    payload: Vec<u8>,
}

impl UtpPacket {
    /// Constructs a new, empty UtpPacket.
    fn new() -> UtpPacket {
        UtpPacket {
            header: UtpPacketHeader {
                type_ver: ST_DATA as u8 << 4 | 1,
                extension: 0,
                connection_id: 0,
                timestamp_microseconds: 0,
                timestamp_difference_microseconds: 0,
                wnd_size: 0,
                seq_nr: 0,
                ack_nr: 0,
            },
            payload: Vec::new(),
        }
    }

    fn bytes(&self) -> Vec<u8> {
        let mut buf: Vec<u8> = Vec::with_capacity(self.len());
        buf.push_all(self.header.bytes());
        buf.push_all(self.payload.as_slice());
        return buf;
    }

    fn len(&self) -> uint {
        self.header.len() + self.payload.len()
    }
}

fn main() {
    let mut buf = [0, ..512];
    let mut sock = UdpSocket::bind(SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 8080 }).unwrap();

    match sock.recvfrom(buf) {
        Ok((_, src)) => {
            let mut packet = UtpPacket::new();
            packet.payload = String::from_str("Hello\n").into_bytes();

            let _ = sock.sendto(packet.bytes().as_slice(), src);
        }
        Err(_) => {}
    }
    drop(sock);
}
<commit_msg>Fix header size.<commit_after>use std::io::net::udp::UdpSocket;
use std::io::net::ip::{Ipv4Addr, SocketAddr};

#[allow(dead_code,non_camel_case_types)]
enum UtpPacketType {
    ST_DATA  = 0,
    ST_FIN   = 1,
    ST_STATE = 2,
    ST_RESET = 3,
    ST_SYN   = 4,
}

#[allow(dead_code)]
struct UtpPacketHeader {
    type_ver: u8, // type: u4, ver: u4
    extension: u8,
    connection_id: u16,
    timestamp_microseconds: u32,
    timestamp_difference_microseconds: u32,
    wnd_size: u32,
    seq_nr: u16,
    ack_nr: u16,
}

impl UtpPacketHeader {
    fn bytes(&self) -> &[u8] {
        unsafe {
            let buf: &[u8, ..20] = std::mem::transmute(self);
            return buf.as_slice();
        }
    }

    fn len(&self) -> uint {
        let header_size = 20;
        return header_size;
    }
}

#[allow(dead_code)]
struct UtpPacket {
    header: UtpPacketHeader,
    payload: Vec<u8>,
}

impl UtpPacket {
    /// Constructs a new, empty UtpPacket.
    fn new() -> UtpPacket {
        UtpPacket {
            header: UtpPacketHeader {
                type_ver: ST_DATA as u8 << 4 | 1,
                extension: 0,
                connection_id: 0,
                timestamp_microseconds: 0,
                timestamp_difference_microseconds: 0,
                wnd_size: 0,
                seq_nr: 0,
                ack_nr: 0,
            },
            payload: Vec::new(),
        }
    }

    fn bytes(&self) -> Vec<u8> {
        let mut buf: Vec<u8> = Vec::with_capacity(self.len());
        buf.push_all(self.header.bytes());
        buf.push_all(self.payload.as_slice());
        return buf;
    }

    fn len(&self) -> uint {
        self.header.len() + self.payload.len()
    }
}

fn main() {
    let mut buf = [0, ..512];
    let mut sock = UdpSocket::bind(SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 8080 }).unwrap();

    match sock.recvfrom(buf) {
        Ok((_, src)) => {
            let mut packet = UtpPacket::new();
            packet.payload = String::from_str("Hello\n").into_bytes();

            let _ = sock.sendto(packet.bytes().as_slice(), src);
        }
        Err(_) => {}
    }
    drop(sock);
}
<|endoftext|>


<commit_before>#![warn(clippy::same_item_push)]

const VALUE: u8 = 7;

fn mutate_increment(x: &mut u8) -> u8 {
    *x += 1;
    *x
}

fn increment(x: u8) -> u8 {
    x + 1
}

fn main() {
    // Test for basic case
    let mut spaces = Vec::with_capacity(10);
    for _ in 0..10 {
        spaces.push(vec![b' ']);
    }

    let mut vec2: Vec<u8> = Vec::new();
    let item = 2;
    for _ in 5..=20 {
        vec2.push(item);
    }

    let mut vec3: Vec<u8> = Vec::new();
    for _ in 0..15 {
        let item = 2;
        vec3.push(item);
    }

    let mut vec4: Vec<u8> = Vec::new();
    for _ in 0..15 {
        vec4.push(13);
    }

    // Suggestion should not be given as pushed variable can mutate
    let mut vec5: Vec<u8> = Vec::new();
    let mut item: u8 = 2;
    for _ in 0..30 {
        vec5.push(mutate_increment(&mut item));
    }

    let mut vec6: Vec<u8> = Vec::new();
    let mut item: u8 = 2;
    let mut item2 = &mut mutate_increment(&mut item);
    for _ in 0..30 {
        vec6.push(mutate_increment(item2));
    }

    let mut vec7: Vec<usize> = Vec::new();
    for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() {
        vec7.push(a);
    }

    let mut vec8: Vec<u8> = Vec::new();
    for i in 0..30 {
        vec8.push(increment(i));
    }

    let mut vec9: Vec<u8> = Vec::new();
    for i in 0..30 {
        vec9.push(i + i * i);
    }

    // Suggestion should not be given as there are multiple pushes that are not the same
    let mut vec10: Vec<u8> = Vec::new();
    let item: u8 = 2;
    for _ in 0..30 {
        vec10.push(item);
        vec10.push(item * 2);
    }

    // Suggestion should not be given as Vec is not involved
    for _ in 0..5 {
        println!("Same Item Push");
    }

    struct A {
        kind: u32,
    }
    let mut vec_a: Vec<A> = Vec::new();
    for i in 0..30 {
        vec_a.push(A { kind: i });
    }
    let mut vec12: Vec<u8> = Vec::new();
    for a in vec_a {
        vec12.push(2u8.pow(a.kind));
    }

    // Fix #5902
    let mut vec13: Vec<u8> = Vec::new();
    let mut item = 0;
    for _ in 0..10 {
        vec13.push(item);
        item += 10;
    }

    // Fix #5979
    let mut vec14: Vec<std::fs::File> = Vec::new();
    for _ in 0..10 {
        vec14.push(std::fs::File::open("foobar").unwrap());
    }
    // Fix #5979
    #[derive(Clone)]
    struct S {}

    trait T {}
    impl T for S {}

    let mut vec15: Vec<Box<dyn T>> = Vec::new();
    for _ in 0..10 {
        vec15.push(Box::new(S {}));
    }

    let mut vec16 = Vec::new();
    for _ in 0..20 {
        vec16.push(VALUE);
    }

    let mut vec17 = Vec::new();
    let item = VALUE;
    for _ in 0..20 {
        vec17.push(item);
    }
}
<commit_msg>Add some tests to `same_item_push`<commit_after>#![warn(clippy::same_item_push)]

const VALUE: u8 = 7;

fn mutate_increment(x: &mut u8) -> u8 {
    *x += 1;
    *x
}

fn increment(x: u8) -> u8 {
    x + 1
}

fn fun() -> usize {
    42
}

fn main() {
    // Test for basic case
    let mut spaces = Vec::with_capacity(10);
    for _ in 0..10 {
        spaces.push(vec![b' ']);
    }

    let mut vec2: Vec<u8> = Vec::new();
    let item = 2;
    for _ in 5..=20 {
        vec2.push(item);
    }

    let mut vec3: Vec<u8> = Vec::new();
    for _ in 0..15 {
        let item = 2;
        vec3.push(item);
    }

    let mut vec4: Vec<u8> = Vec::new();
    for _ in 0..15 {
        vec4.push(13);
    }

    // Suggestion should not be given as pushed variable can mutate
    let mut vec5: Vec<u8> = Vec::new();
    let mut item: u8 = 2;
    for _ in 0..30 {
        vec5.push(mutate_increment(&mut item));
    }

    let mut vec6: Vec<u8> = Vec::new();
    let mut item: u8 = 2;
    let mut item2 = &mut mutate_increment(&mut item);
    for _ in 0..30 {
        vec6.push(mutate_increment(item2));
    }

    let mut vec7: Vec<usize> = Vec::new();
    for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() {
        vec7.push(a);
    }

    let mut vec8: Vec<u8> = Vec::new();
    for i in 0..30 {
        vec8.push(increment(i));
    }

    let mut vec9: Vec<u8> = Vec::new();
    for i in 0..30 {
        vec9.push(i + i * i);
    }

    // Suggestion should not be given as there are multiple pushes that are not the same
    let mut vec10: Vec<u8> = Vec::new();
    let item: u8 = 2;
    for _ in 0..30 {
        vec10.push(item);
        vec10.push(item * 2);
    }

    // Suggestion should not be given as Vec is not involved
    for _ in 0..5 {
        println!("Same Item Push");
    }

    struct A {
        kind: u32,
    }
    let mut vec_a: Vec<A> = Vec::new();
    for i in 0..30 {
        vec_a.push(A { kind: i });
    }
    let mut vec12: Vec<u8> = Vec::new();
    for a in vec_a {
        vec12.push(2u8.pow(a.kind));
    }

    // Fix #5902
    let mut vec13: Vec<u8> = Vec::new();
    let mut item = 0;
    for _ in 0..10 {
        vec13.push(item);
        item += 10;
    }

    // Fix #5979
    let mut vec14: Vec<std::fs::File> = Vec::new();
    for _ in 0..10 {
        vec14.push(std::fs::File::open("foobar").unwrap());
    }
    // Fix #5979
    #[derive(Clone)]
    struct S {}

    trait T {}
    impl T for S {}

    let mut vec15: Vec<Box<dyn T>> = Vec::new();
    for _ in 0..10 {
        vec15.push(Box::new(S {}));
    }

    let mut vec16 = Vec::new();
    for _ in 0..20 {
        vec16.push(VALUE);
    }

    let mut vec17 = Vec::new();
    let item = VALUE;
    for _ in 0..20 {
        vec17.push(item);
    }

    let mut vec18 = Vec::new();
    let item = 42;
    let item = fun();
    for _ in 0..20 {
        vec18.push(item);
    }

    let mut vec19 = Vec::new();
    let key = 1;
    for _ in 0..20 {
        let item = match key {
            1 => 10,
            _ => 0,
        };
        vec19.push(item);
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use toml::Value;
use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;
use chrono::NaiveDateTime;
use chrono::Local;
use chrono::NaiveDate;

use error::HabitError as HE;
use error::HabitErrorKind as HEK;
use error::*;
use iter::HabitInstanceStoreIdIterator;
use util::date_to_string;
use util::date_from_string;

use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Entry;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagstore::storeid::IntoStoreId;

/// A HabitTemplate is a "template" of a habit. A user may define a habit "Eat vegetable".
/// If the user ate a vegetable, she should create a HabitInstance from the Habit with the
/// appropriate date (and optionally a comment) set.
pub trait HabitTemplate : Sized {

    /// Create an instance from this habit template
    ///
    /// By default creates an instance with the name of the template, the current time and the
    /// current date and copies the comment from the template to the instance.
    ///
    /// It uses `Store::retrieve()` underneath
    fn create_instance<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>>;

    /// Check whether the instance is a habit by checking its headers for the habit data
    fn is_habit_template(&self) -> Result<bool>;

    fn habit_name(&self) -> Result<String>;
    fn habit_basedate(&self) -> Result<String>;
    fn habit_recur_spec(&self) -> Result<String>;
    fn habit_comment(&self) -> Result<String>;

}

impl HabitTemplate for Entry {

    fn create_instance<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>> {
        use module_path::ModuleEntryPath;
        let date = date_to_string(&Local::today().naive_local());
        let name = self.habit_name()?;
        let comment = self.habit_comment()?;
        let id = ModuleEntryPath::new(format!("instance/{}-{}", name, date))
            .into_storeid()
            .map_err(HE::from)?;

        store.retrieve(id)
            .map_err(From::from)
            .and_then(|mut entry| {
                {
                    let mut hdr = entry.get_header_mut();
                    try!(hdr.insert("habit.instance.name",    Value::String(name)));
                    try!(hdr.insert("habit.instance.date",    Value::String(date)));
                    try!(hdr.insert("habit.instance.comment", Value::String(comment)));
                }
                Ok(entry)
            })
    }

    /// Check whether the instance is a habit by checking its headers for the habit data
    fn is_habit_template(&self) -> Result<bool> {
        [
            "habit.template.name",
            "habit.template.basedate",
            "habit.template.comment",
        ].iter().fold(Ok(true), |acc, path| acc.and_then(|b| {
            self.get_header()
                .read(path)
                .map(|o| is_match!(o, Some(&Value::String(_))))
                .map_err(From::from)
        }))
    }

    fn habit_name(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.name")
    }

    fn habit_basedate(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.basedate")
    }

    fn habit_recur_spec(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.recurspec")
    }

    fn habit_comment(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.comment")
    }

}

#[inline]
fn get_string_header_from_habit(e: &Entry, path: &'static str) -> Result<String> {
    match e.get_header().read(path)? {
        Some(&Value::String(ref s)) => Ok(s.clone()),
        Some(_) => Err(HEK::HeaderTypeError(path, "String").into()),
        None    => Err(HEK::HeaderFieldMissing(path).into()),
    }
}

pub mod builder {
    use toml::Value;
    use toml_query::insert::TomlValueInsertExt;
    use chrono::NaiveDate;

    use libimagstore::store::Store;
    use libimagstore::storeid::StoreId;
    use libimagstore::storeid::IntoStoreId;
    use libimagstore::store::FileLockEntry;

    use error::HabitError as HE;
    use error::HabitErrorKind as HEK;
    use error::*;
    use util::date_to_string;
    use util::date_from_string;

    pub struct HabitBuilder {
        name: Option<String>,
        comment: Option<String>,
        basedate: Option<NaiveDate>,
        recurspec: Option<String>,
    }

    impl HabitBuilder {

        pub fn with_name(&mut self, name: String) -> &mut Self {
            self.name = Some(name);
            self
        }

        pub fn with_comment(&mut self, comment: String) -> &mut Self {
            self.comment = Some(comment);
            self
        }

        pub fn with_basedate(&mut self, date: NaiveDate) -> &mut Self {
            self.basedate = Some(date);
            self
        }

        pub fn with_recurspec(&mut self, spec: String) -> &mut Self {
            self.recurspec = Some(spec);
            self
        }

        pub fn build<'a>(self, store: &'a Store) -> Result<FileLockEntry<'a>> {
            #[inline]
            fn mkerr(s: &'static str) -> HE {
                HE::from_kind(HEK::HabitBuilderMissing(s))
            }

            let name      = try!(self.name.ok_or_else(|| mkerr("name")));
            let dateobj   = try!(self.basedate.ok_or_else(|| mkerr("date")));
            let recur     = try!(self.recurspec.ok_or_else(|| mkerr("recurspec")));
            if let Err(e) = ::kairos::parser::parse(&recur) {
                return Err(e).map_err(From::from);
            }
            let date      = date_to_string(&dateobj);
            let comment   = self.comment.unwrap_or_else(|| String::new());
            let sid       = try!(build_habit_template_sid(&name));
            let mut entry = try!(store.create(sid));

            try!(entry.get_header_mut().insert("habit.template.name", Value::String(name)));
            try!(entry.get_header_mut().insert("habit.template.basedate", Value::String(date)));
            try!(entry.get_header_mut().insert("habit.template.recurspec", Value::String(recur)));
            try!(entry.get_header_mut().insert("habit.template.comment", Value::String(comment)));

            Ok(entry)
        }

    }

    impl Default for HabitBuilder {
        fn default() -> Self {
            HabitBuilder {
                name: None,
                comment: None,
                basedate: None,
                recurspec: None,
            }
        }
    }

    /// Buld a StoreId for a Habit from a date object and a name of a habit
    fn build_habit_template_sid(name: &String) -> Result<StoreId> {
        use module_path::ModuleEntryPath;
        ModuleEntryPath::new(format!("template/{}", name)).into_storeid().map_err(From::from)
    }

}

<commit_msg>Outsource instance id creation to helper fn<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use toml::Value;
use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;
use chrono::NaiveDateTime;
use chrono::Local;
use chrono::NaiveDate;

use error::HabitError as HE;
use error::HabitErrorKind as HEK;
use error::*;
use iter::HabitInstanceStoreIdIterator;
use util::date_to_string;
use util::date_from_string;

use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Entry;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;

/// A HabitTemplate is a "template" of a habit. A user may define a habit "Eat vegetable".
/// If the user ate a vegetable, she should create a HabitInstance from the Habit with the
/// appropriate date (and optionally a comment) set.
pub trait HabitTemplate : Sized {

    /// Create an instance from this habit template
    ///
    /// By default creates an instance with the name of the template, the current time and the
    /// current date and copies the comment from the template to the instance.
    ///
    /// It uses `Store::retrieve()` underneath
    fn create_instance<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>>;

    /// Check whether the instance is a habit by checking its headers for the habit data
    fn is_habit_template(&self) -> Result<bool>;

    fn habit_name(&self) -> Result<String>;
    fn habit_basedate(&self) -> Result<String>;
    fn habit_recur_spec(&self) -> Result<String>;
    fn habit_comment(&self) -> Result<String>;

    /// Create a StoreId for a habit name and a date the habit should be instantiated for
    fn instance_id_for(habit_name: &String, habit_date: &NaiveDate) -> Result<StoreId>;
}

impl HabitTemplate for Entry {

    fn create_instance<'a>(&self, store: &'a Store) -> Result<FileLockEntry<'a>> {
        let name    = self.habit_name()?;
        let comment = self.habit_comment()?;
        let date    = date_to_string(&Local::today().naive_local());
        let id      = instance_id_for_name_and_datestr(&name, &date)?;

        store.retrieve(id)
            .map_err(From::from)
            .and_then(|mut entry| {
                {
                    let mut hdr = entry.get_header_mut();
                    try!(hdr.insert("habit.instance.name",    Value::String(name)));
                    try!(hdr.insert("habit.instance.date",    Value::String(date)));
                    try!(hdr.insert("habit.instance.comment", Value::String(comment)));
                }
                Ok(entry)
            })
    }

    /// Check whether the instance is a habit by checking its headers for the habit data
    fn is_habit_template(&self) -> Result<bool> {
        [
            "habit.template.name",
            "habit.template.basedate",
            "habit.template.comment",
        ].iter().fold(Ok(true), |acc, path| acc.and_then(|b| {
            self.get_header()
                .read(path)
                .map(|o| is_match!(o, Some(&Value::String(_))))
                .map_err(From::from)
        }))
    }

    fn habit_name(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.name")
    }

    fn habit_basedate(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.basedate")
    }

    fn habit_recur_spec(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.recurspec")
    }

    fn habit_comment(&self) -> Result<String> {
        get_string_header_from_habit(self, "habit.template.comment")
    }

    fn instance_id_for(habit_name: &String, habit_date: &NaiveDate) -> Result<StoreId> {
        instance_id_for_name_and_datestr(habit_name, &date_to_string(habit_date))
    }

}

fn instance_id_for_name_and_datestr(habit_name: &String, habit_date: &String) -> Result<StoreId> {
    use module_path::ModuleEntryPath;

    ModuleEntryPath::new(format!("instance/{}-{}", habit_name, habit_date))
        .into_storeid()
        .map_err(HE::from)
}

#[inline]
fn get_string_header_from_habit(e: &Entry, path: &'static str) -> Result<String> {
    match e.get_header().read(path)? {
        Some(&Value::String(ref s)) => Ok(s.clone()),
        Some(_) => Err(HEK::HeaderTypeError(path, "String").into()),
        None    => Err(HEK::HeaderFieldMissing(path).into()),
    }
}

pub mod builder {
    use toml::Value;
    use toml_query::insert::TomlValueInsertExt;
    use chrono::NaiveDate;

    use libimagstore::store::Store;
    use libimagstore::storeid::StoreId;
    use libimagstore::storeid::IntoStoreId;
    use libimagstore::store::FileLockEntry;

    use error::HabitError as HE;
    use error::HabitErrorKind as HEK;
    use error::*;
    use util::date_to_string;
    use util::date_from_string;

    pub struct HabitBuilder {
        name: Option<String>,
        comment: Option<String>,
        basedate: Option<NaiveDate>,
        recurspec: Option<String>,
    }

    impl HabitBuilder {

        pub fn with_name(&mut self, name: String) -> &mut Self {
            self.name = Some(name);
            self
        }

        pub fn with_comment(&mut self, comment: String) -> &mut Self {
            self.comment = Some(comment);
            self
        }

        pub fn with_basedate(&mut self, date: NaiveDate) -> &mut Self {
            self.basedate = Some(date);
            self
        }

        pub fn with_recurspec(&mut self, spec: String) -> &mut Self {
            self.recurspec = Some(spec);
            self
        }

        pub fn build<'a>(self, store: &'a Store) -> Result<FileLockEntry<'a>> {
            #[inline]
            fn mkerr(s: &'static str) -> HE {
                HE::from_kind(HEK::HabitBuilderMissing(s))
            }

            let name      = try!(self.name.ok_or_else(|| mkerr("name")));
            let dateobj   = try!(self.basedate.ok_or_else(|| mkerr("date")));
            let recur     = try!(self.recurspec.ok_or_else(|| mkerr("recurspec")));
            if let Err(e) = ::kairos::parser::parse(&recur) {
                return Err(e).map_err(From::from);
            }
            let date      = date_to_string(&dateobj);
            let comment   = self.comment.unwrap_or_else(|| String::new());
            let sid       = try!(build_habit_template_sid(&name));
            let mut entry = try!(store.create(sid));

            try!(entry.get_header_mut().insert("habit.template.name", Value::String(name)));
            try!(entry.get_header_mut().insert("habit.template.basedate", Value::String(date)));
            try!(entry.get_header_mut().insert("habit.template.recurspec", Value::String(recur)));
            try!(entry.get_header_mut().insert("habit.template.comment", Value::String(comment)));

            Ok(entry)
        }

    }

    impl Default for HabitBuilder {
        fn default() -> Self {
            HabitBuilder {
                name: None,
                comment: None,
                basedate: None,
                recurspec: None,
            }
        }
    }

    /// Buld a StoreId for a Habit from a date object and a name of a habit
    fn build_habit_template_sid(name: &String) -> Result<StoreId> {
        use module_path::ModuleEntryPath;
        ModuleEntryPath::new(format!("template/{}", name)).into_storeid().map_err(From::from)
    }

}

<|endoftext|>