<commit_before>#![allow(dead_code)]
// spell-checker:ignore (change!) each's
// spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr

use clap::{crate_version, App, AppSettings, Arg};
use uucore::error::{UResult, UUsageError};
use uucore::memo;
use uucore::InvalidEncodingHandling;

const VERSION: &str = "version";
const HELP: &str = "help";
const USAGE: &str = "printf FORMATSTRING [ARGUMENT]...";
const ABOUT: &str = "Print output based off of the format string and proceeding arguments.";
static LONGHELP_LEAD: &str = "printf

 USAGE: printf FORMATSTRING [ARGUMENT]...

 basic anonymous string templating:

 prints format string at least once, repeating as long as there are remaining arguments
 output prints escaped literals in the format string as character literals
 output replaces anonymous fields with the next unused argument, formatted according to the field.

Options:
    --help              display this help and exit
    --version           output version information and exit

";
static LONGHELP_BODY: &str = "
  Prints the , replacing escaped character sequences with character literals
    and substitution field sequences with passed arguments

 literally, with the exception of the below
    escaped character sequences, and the substitution sequences described further down.

  ESCAPE SEQUENCES

  The following escape sequences, organized here in alphabetical order,
  will print the corresponding character literal:

  \"   double quote

  \\\\    backslash

  \\a  alert (BEL)

  \\b  backspace

  \\c  End-of-Input

  \\e  escape

  \\f  form feed

  \\n  new line

  \\r  carriage return

  \\t  horizontal tab

  \\v  vertical tab

  \\NNN byte with value expressed in octal value NNN (1 to 3 digits)
        values greater than 256 will be treated

  \\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits)

  \\uHHHH Unicode (IEC 10646) character with value expressed in hexadecimal value HHHH (4 digits)

  \\uHHHH Unicode character with value expressed in hexadecimal value HHHH (8 digits)

  %% a single %

  SUBSTITUTIONS

  SUBSTITUTION QUICK REFERENCE

  Fields

  %s - string
  %b - string parsed for literals
    second parameter is max length

  %c - char
    no second parameter

  %i or %d - 64-bit integer
  %u - 64 bit unsigned integer
  %x or %X - 64-bit unsigned integer as hex
  %o - 64-bit unsigned integer as octal
    second parameter is min-width, integer
     output below that width is padded with leading zeroes

  %f or %F - decimal floating point value
  %e or %E - scientific notation floating point value
  %g or %G - shorter of specially interpreted decimal or SciNote floating point value.
    second parameter is
      -max places after decimal point for floating point output
      -max number of significant digits for scientific notation output

  parameterizing fields

  examples:

  printf '%4.3i' 7
  has a first parameter of 4
     and a second parameter of 3
  will result in ' 007'

  printf '%.1s' abcde
  has no first parameter
     and a second parameter of 1
  will result in 'a'

  printf '%4c' q
  has a first parameter of 4
     and no second parameter
  will result in  '   q'

  The first parameter of a field is the minimum width to pad the output to
   if the output is less than this absolute value of this width,
   it will be padded with leading spaces, or, if the argument is negative,
   with trailing spaces. the default is zero.

  The second parameter of a field is particular to the output field type.
   defaults can be found in the full substitution help below

  special prefixes to numeric arguments
    0 (e.g. 010) - interpret argument as octal (integer output fields only)
    0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only)
    \' (e.g. \'a) - interpret argument as a character constant

  HOW TO USE SUBSTITUTIONS

  Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a
  particular way. E.g.

      printf 'the letter %X comes before the letter %X' 10 11

  will print

     'the letter A comes before the letter B'

  because the substitution field %X means
  'take an integer argument and write it as a hexadecimal number'

  Passing more arguments than are in the format string will cause the format string to be
   repeated for the remaining substitutions

     printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York

  will print

     'it is 22 F in Portland
      it is 25 F in Boston
      it is 27 F in Boston
     '
  If a format string is printed but there are less arguments remaining
   than there are substitution fields, substitution fields without
   an argument will default to empty strings, or for numeric fields
   the value 0

  AVAILABLE SUBSTITUTIONS

  This program, like GNU coreutils printf,
  interprets a modified subset of the POSIX C printf spec,
  a quick reference to substitutions is below.

   STRING SUBSTITUTIONS
    All string fields have a 'max width' parameter
    %.3s means 'print no more than three characters of the original input'

   %s - string

   %b - escaped string - the string will be checked for any escaped literals from
         the escaped literal list above, and translate them to literal characters.
         e.g. \\n will be transformed into a newline character.

        One special rule about %b mode is that octal literals are interpreted differently
        In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
        instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
        still be interpreted and not throw a warning, you will have problems if you use this for a
        literal whose code begins with zero, as it will be viewed as in \\0NNN form.)

   CHAR SUBSTITUTIONS
    The character field does not have a secondary parameter.

   %c - a single character

   INTEGER SUBSTITUTIONS
    All integer fields have a 'pad with zero' parameter
    %.4i means an integer which if it is less than 4 digits in length,
    is padded with leading zeros until it is 4 digits in length.

   %d or %i - 64-bit integer

   %u - 64 bit unsigned integer

   %x or %X - 64 bit unsigned integer printed in Hexadecimal (base 16)
            %X instead of %x means to use uppercase letters for 'a' through 'f'

   %o - 64 bit unsigned integer printed in octal (base 8)

   FLOATING POINT SUBSTITUTIONS

    All floating point fields have a 'max decimal places / max significant digits' parameter
    %.10f means a decimal floating point with 7 decimal places past 0
    %.10e means a scientific notation number with 10 significant digits
    %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
          of each's output.

    Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
    double first before being rendered to text. For both implementations do not expect meaningful
    precision past the 18th decimal place. When using a number of decimal places that is 18 or
    higher, you can expect variation in output between GNU coreutils printf and this printf at the
    18th decimal place of +/- 1

   %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
        default. There is not past-double behavior parity with Coreutils printf, values are not
        estimated or adjusted beyond input values.

   %e or %E - floating point value presented in scientific notation
            7 significant digits by default
            %E means use to use uppercase E for the mantissa.

   %g or %G - floating point value presented in the shorter of decimal and scientific notation
            behaves differently from %f and %E, please see posix printf spec for full details,
            some examples of different behavior:

            Sci Note has 6 significant digits by default
            Trailing zeroes are removed
            Instead of being truncated, digit after last is rounded

   Like other behavior in this utility, the design choices of floating point
    behavior in this utility is selected to reproduce in exact
    the behavior of GNU coreutils' printf from an inputs and outputs standpoint.

  USING PARAMETERS
   Most substitution fields can be parameterized using up to 2 numbers that can
   be passed to the field, between the % sign and the field letter.

   The 1st parameter always indicates the minimum width of output, it is useful for creating
     columnar output. Any output that would be less than this minimum width is padded with
     leading spaces
   The 2nd parameter is proceeded by a dot.
   You do not have to use parameters

  SPECIAL FORMS OF INPUT
   For numeric input, the following additional forms of input are accepted besides decimal:

    Octal (only with integer): if the argument begins with a 0 the proceeding characters
      will be interpreted as octal (base 8) for integer fields

    Hexadecimal: if the argument begins with 0x the proceeding characters will be interpreted
      will be interpreted as hex (base 16) for any numeric fields
      for float fields, hexadecimal input results in a precision
      limit (in converting input past the decimal point) of 10^-15

    Character Constant: if the argument begins with a single quote character, the first byte
      of the next character will be interpreted as an 8-bit unsigned integer. If there are
      additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
      is set)

WRITTEN BY :
  Nathan E. Ross, et al. for the uutils project

MORE INFO :
  https://github.com/uutils/coreutils

COPYRIGHT :
  Copyright 2015 uutils project.
  Licensed under the MIT License, please see LICENSE file for details

";

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    if args.len() <= 1 {
        return Err(UUsageError::new(1, "missing operand"));
    }
    let formatstr = &args[1];

    if formatstr == "--help" {
        print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
    } else if formatstr == "--version" {
        println!("{} {}", uucore::util_name(), crate_version!());
    } else {
        let printf_args = &args[2..];
        memo::Memo::run_all(formatstr, printf_args);
    }
    Ok(())
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(USAGE)
        .arg(Arg::new(HELP).long(HELP).help("Print help information"))
        .arg(
            Arg::new(VERSION)
                .long(VERSION)
                .help("Print version information"),
        )
        .setting(AppSettings::InferLongArgs)
}
<commit_msg>printf: resolve formatting nit in LONGHELP strings<commit_after>#![allow(dead_code)]
// spell-checker:ignore (change!) each's
// spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr

use clap::{crate_version, App, AppSettings, Arg};
use uucore::error::{UResult, UUsageError};
use uucore::memo;
use uucore::InvalidEncodingHandling;

const VERSION: &str = "version";
const HELP: &str = "help";
const USAGE: &str = "printf FORMATSTRING [ARGUMENT]...";
const ABOUT: &str = "Print output based off of the format string and proceeding arguments.";
static LONGHELP_LEAD: &str = "printf

USAGE: printf FORMATSTRING [ARGUMENT]...

basic anonymous string templating:

prints format string at least once, repeating as long as there are remaining arguments
output prints escaped literals in the format string as character literals
output replaces anonymous fields with the next unused argument, formatted according to the field.

Options:
    --help              display this help and exit
    --version           output version information and exit

";
static LONGHELP_BODY: &str = "
Prints the , replacing escaped character sequences with character literals
  and substitution field sequences with passed arguments

literally, with the exception of the below
  escaped character sequences, and the substitution sequences described further down.

ESCAPE SEQUENCES

The following escape sequences, organized here in alphabetical order,
will print the corresponding character literal:

\"   double quote

\\\\    backslash

\\a  alert (BEL)

\\b  backspace

\\c  End-of-Input

\\e  escape

\\f  form feed

\\n  new line

\\r  carriage return

\\t  horizontal tab

\\v  vertical tab

\\NNN byte with value expressed in octal value NNN (1 to 3 digits)
      values greater than 256 will be treated

\\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits)

\\uHHHH Unicode (IEC 10646) character with value expressed in hexadecimal value HHHH (4 digits)

\\uHHHH Unicode character with value expressed in hexadecimal value HHHH (8 digits)

%% a single %

SUBSTITUTIONS

SUBSTITUTION QUICK REFERENCE

Fields

%s - string
%b - string parsed for literals
  second parameter is max length

%c - char
  no second parameter

%i or %d - 64-bit integer
%u - 64 bit unsigned integer
%x or %X - 64-bit unsigned integer as hex
%o - 64-bit unsigned integer as octal
  second parameter is min-width, integer
   output below that width is padded with leading zeroes

%f or %F - decimal floating point value
%e or %E - scientific notation floating point value
%g or %G - shorter of specially interpreted decimal or SciNote floating point value.
  second parameter is
    -max places after decimal point for floating point output
    -max number of significant digits for scientific notation output

parameterizing fields

examples:

printf '%4.3i' 7
has a first parameter of 4
   and a second parameter of 3
will result in ' 007'

printf '%.1s' abcde
has no first parameter
   and a second parameter of 1
will result in 'a'

printf '%4c' q
has a first parameter of 4
   and no second parameter
will result in  '   q'

The first parameter of a field is the minimum width to pad the output to
 if the output is less than this absolute value of this width,
 it will be padded with leading spaces, or, if the argument is negative,
 with trailing spaces. the default is zero.

The second parameter of a field is particular to the output field type.
 defaults can be found in the full substitution help below

special prefixes to numeric arguments
  0 (e.g. 010) - interpret argument as octal (integer output fields only)
  0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only)
  \' (e.g. \'a) - interpret argument as a character constant

HOW TO USE SUBSTITUTIONS

Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a
particular way. E.g.

    printf 'the letter %X comes before the letter %X' 10 11

will print

   'the letter A comes before the letter B'

because the substitution field %X means
'take an integer argument and write it as a hexadecimal number'

Passing more arguments than are in the format string will cause the format string to be
 repeated for the remaining substitutions

   printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York

will print

   'it is 22 F in Portland
    it is 25 F in Boston
    it is 27 F in Boston
   '
If a format string is printed but there are less arguments remaining
 than there are substitution fields, substitution fields without
 an argument will default to empty strings, or for numeric fields
 the value 0

AVAILABLE SUBSTITUTIONS

This program, like GNU coreutils printf,
interprets a modified subset of the POSIX C printf spec,
a quick reference to substitutions is below.

 STRING SUBSTITUTIONS
  All string fields have a 'max width' parameter
  %.3s means 'print no more than three characters of the original input'

 %s - string

 %b - escaped string - the string will be checked for any escaped literals from
       the escaped literal list above, and translate them to literal characters.
       e.g. \\n will be transformed into a newline character.

      One special rule about %b mode is that octal literals are interpreted differently
      In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
      instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
      still be interpreted and not throw a warning, you will have problems if you use this for a
      literal whose code begins with zero, as it will be viewed as in \\0NNN form.)

 CHAR SUBSTITUTIONS
  The character field does not have a secondary parameter.

 %c - a single character

 INTEGER SUBSTITUTIONS
  All integer fields have a 'pad with zero' parameter
  %.4i means an integer which if it is less than 4 digits in length,
  is padded with leading zeros until it is 4 digits in length.

 %d or %i - 64-bit integer

 %u - 64 bit unsigned integer

 %x or %X - 64 bit unsigned integer printed in Hexadecimal (base 16)
          %X instead of %x means to use uppercase letters for 'a' through 'f'

 %o - 64 bit unsigned integer printed in octal (base 8)

 FLOATING POINT SUBSTITUTIONS

  All floating point fields have a 'max decimal places / max significant digits' parameter
  %.10f means a decimal floating point with 7 decimal places past 0
  %.10e means a scientific notation number with 10 significant digits
  %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
        of each's output.

  Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
  double first before being rendered to text. For both implementations do not expect meaningful
  precision past the 18th decimal place. When using a number of decimal places that is 18 or
  higher, you can expect variation in output between GNU coreutils printf and this printf at the
  18th decimal place of +/- 1

 %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
      default. There is not past-double behavior parity with Coreutils printf, values are not
      estimated or adjusted beyond input values.

 %e or %E - floating point value presented in scientific notation
          7 significant digits by default
          %E means use to use uppercase E for the mantissa.

 %g or %G - floating point value presented in the shorter of decimal and scientific notation
          behaves differently from %f and %E, please see posix printf spec for full details,
          some examples of different behavior:

          Sci Note has 6 significant digits by default
          Trailing zeroes are removed
          Instead of being truncated, digit after last is rounded

 Like other behavior in this utility, the design choices of floating point
  behavior in this utility is selected to reproduce in exact
  the behavior of GNU coreutils' printf from an inputs and outputs standpoint.

USING PARAMETERS
 Most substitution fields can be parameterized using up to 2 numbers that can
 be passed to the field, between the % sign and the field letter.

 The 1st parameter always indicates the minimum width of output, it is useful for creating
   columnar output. Any output that would be less than this minimum width is padded with
   leading spaces
 The 2nd parameter is proceeded by a dot.
 You do not have to use parameters

SPECIAL FORMS OF INPUT
 For numeric input, the following additional forms of input are accepted besides decimal:

  Octal (only with integer): if the argument begins with a 0 the proceeding characters
    will be interpreted as octal (base 8) for integer fields

  Hexadecimal: if the argument begins with 0x the proceeding characters will be interpreted
    will be interpreted as hex (base 16) for any numeric fields
    for float fields, hexadecimal input results in a precision
    limit (in converting input past the decimal point) of 10^-15

  Character Constant: if the argument begins with a single quote character, the first byte
    of the next character will be interpreted as an 8-bit unsigned integer. If there are
    additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
    is set)

WRITTEN BY :
  Nathan E. Ross, et al. for the uutils project

MORE INFO :
  https://github.com/uutils/coreutils

COPYRIGHT :
  Copyright 2015 uutils project.
  Licensed under the MIT License, please see LICENSE file for details

";

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    if args.len() <= 1 {
        return Err(UUsageError::new(1, "missing operand"));
    }
    let formatstr = &args[1];

    if formatstr == "--help" {
        print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
    } else if formatstr == "--version" {
        println!("{} {}", uucore::util_name(), crate_version!());
    } else {
        let printf_args = &args[2..];
        memo::Memo::run_all(formatstr, printf_args);
    }
    Ok(())
}

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(USAGE)
        .arg(Arg::new(HELP).long(HELP).help("Print help information"))
        .arg(
            Arg::new(VERSION)
                .long(VERSION)
                .help("Print version information"),
        )
        .setting(AppSettings::InferLongArgs)
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use helpers::HelperDef;
use registry::Registry;
use context::{Context, JsonTruthy, to_json};
use render::{Renderable, RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct WithHelper;

impl HelperDef for WithHelper {
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let param = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"with\"")));

        let path = rc.get_path().clone();
        rc.promote_local_vars();
        if let Some(path_root) = param.path_root() {
            let local_path_root = format!("{}/{}", rc.get_path(), path_root);
            rc.set_local_path_root(local_path_root);
        }

        let not_empty = param.value().is_truthy();
        let template = if not_empty {
            h.template()
        } else {
            h.inverse()
        };

        if not_empty {
            if let Some(inner_path) = param.path() {
                let new_path = format!("{}/{}", path, inner_path);
                rc.set_path(new_path);
            }

            if let Some(block_param) = h.block_param() {
                let mut map = BTreeMap::new();
                map.insert(block_param.to_string(), to_json(param.value()));
                rc.push_block_context(&map);
            }
        }

        let rendered = match template {
            Some(t) => t.render(c, r, rc),
            None => Ok(()),
        };

        rc.set_path(path);
        rc.reset_local_path_root();
        rc.demote_local_vars();
        if not_empty {
            rc.pop_block_context();
        }
        rendered
    }
}

pub static WITH_HELPER: WithHelper = WithHelper;

#[cfg(test)]
#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;
    use serialize::json::{Json, ToJson};

    struct Address {
        city: String,
        country: String,
    }

    impl ToJson for Address {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("city".to_string(), self.city.to_json());
            m.insert("country".to_string(), self.country.to_json());
            Json::Object(m)
        }
    }

    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    impl ToJson for Person {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("age".to_string(), self.age.to_json());
            m.insert("addr".to_string(), self.addr.to_json());
            m.insert("titles".to_string(), self.titles.to_json());
            Json::Object(m)
        }
    }

    #[test]
    fn test_with() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let t0 = Template::compile("{{#with addr}}{{city}}{{/with}}".to_string()).ok().unwrap();
        let t1 = Template::compile("{{#with notfound}}hello{{else}}world{{/with}}".to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{#with addr/country}}{{this}}{{/with}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &person);
        assert_eq!(r0.ok().unwrap(), "Beijing".to_string());

        let r1 = handlebars.render("t1", &person);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t2", &person);
        assert_eq!(r2.ok().unwrap(), "China".to_string());
    }

    #[test]
    fn test_with_block_param() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let t0 = Template::compile("{{#with addr as |a|}}{{a.city}}{{/with}}".to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#with notfound as |c|}}hello{{else}}world{{/with}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{#with addr/country as |t|}}{{t}}{{/with}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &person);
        assert_eq!(r0.ok().unwrap(), "Beijing".to_string());

        let r1 = handlebars.render("t1", &person);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t2", &person);
        assert_eq!(r2.ok().unwrap(), "China".to_string());
    }

    #[test]
    fn test_with_in_each() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let addr2 = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person2 = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr2,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let people = vec![person, person2];

        let t0 = Template::compile("{{#each this}}{{#with addr}}{{city}}{{/with}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#each this}}{{#with addr}}{{../age}}{{/with}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{#each this}}{{#with addr}}{{@../index}}{{/with}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &people);
        assert_eq!(r0.ok().unwrap(), "BeijingBeijing".to_string());

        let r1 = handlebars.render("t1", &people);
        assert_eq!(r1.ok().unwrap(), "2727".to_string());

        let r2 = handlebars.render("t2", &people);
        assert_eq!(r2.ok().unwrap(), "01".to_string());
    }
}
<commit_msg>(fix) fixed path root for with<commit_after>use std::collections::BTreeMap;

use helpers::HelperDef;
use registry::Registry;
use context::{Context, JsonTruthy, to_json};
use render::{Renderable, RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct WithHelper;

impl HelperDef for WithHelper {
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let param = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"with\"")));

        rc.promote_local_vars();

        let result = {
            let mut local_rc = rc.derive();

            let not_empty = param.value().is_truthy();
            let template = if not_empty {
                h.template()
            } else {
                h.inverse()
            };

            if let Some(path_root) = param.path_root() {
                let local_path_root = format!("{}/{}", local_rc.get_path(), path_root);
                local_rc.set_local_path_root(local_path_root);
            }
            if not_empty {
                if let Some(inner_path) = param.path() {
                    let new_path = format!("{}/{}", local_rc.get_path(), inner_path);
                    local_rc.set_path(new_path);
                }

                if let Some(block_param) = h.block_param() {
                    let mut map = BTreeMap::new();
                    map.insert(block_param.to_string(), to_json(param.value()));
                    local_rc.push_block_context(&map);
                }
            }

            match template {
                Some(t) => t.render(c, r, &mut local_rc),
                None => Ok(()),
            }
        };

        rc.demote_local_vars();
        result
    }
}

pub static WITH_HELPER: WithHelper = WithHelper;

#[cfg(test)]
#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;
    use serialize::json::{Json, ToJson};

    struct Address {
        city: String,
        country: String,
    }

    impl ToJson for Address {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("city".to_string(), self.city.to_json());
            m.insert("country".to_string(), self.country.to_json());
            Json::Object(m)
        }
    }

    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    impl ToJson for Person {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("age".to_string(), self.age.to_json());
            m.insert("addr".to_string(), self.addr.to_json());
            m.insert("titles".to_string(), self.titles.to_json());
            Json::Object(m)
        }
    }

    #[test]
    fn test_with() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let t0 = Template::compile("{{#with addr}}{{city}}{{/with}}".to_string()).ok().unwrap();
        let t1 = Template::compile("{{#with notfound}}hello{{else}}world{{/with}}".to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{#with addr/country}}{{this}}{{/with}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &person);
        assert_eq!(r0.ok().unwrap(), "Beijing".to_string());

        let r1 = handlebars.render("t1", &person);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t2", &person);
        assert_eq!(r2.ok().unwrap(), "China".to_string());
    }

    #[test]
    fn test_with_block_param() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let t0 = Template::compile("{{#with addr as |a|}}{{a.city}}{{/with}}".to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#with notfound as |c|}}hello{{else}}world{{/with}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{#with addr/country as |t|}}{{t}}{{/with}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &person);
        assert_eq!(r0.ok().unwrap(), "Beijing".to_string());

        let r1 = handlebars.render("t1", &person);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t2", &person);
        assert_eq!(r2.ok().unwrap(), "China".to_string());
    }

    #[test]
    fn test_with_in_each() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let addr2 = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person2 = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr: addr2,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let people = vec![person, person2];

        let t0 = Template::compile("{{#each this}}{{#with addr}}{{city}}{{/with}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#each this}}{{#with addr}}{{../age}}{{/with}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{#each this}}{{#with addr}}{{@../index}}{{/with}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &people);
        assert_eq!(r0.ok().unwrap(), "BeijingBeijing".to_string());

        let r1 = handlebars.render("t1", &people);
        assert_eq!(r1.ok().unwrap(), "2727".to_string());

        let r2 = handlebars.render("t2", &people);
        assert_eq!(r2.ok().unwrap(), "01".to_string());
    }
}
<|endoftext|>


<commit_before>//! This module implements per-component parallelism.
//! It should be possible to implement per-row parallelism as well,
//! which should also boost performance of grayscale images
//! and allow scaling to more cores.
//! However, that would be more complex, so we use this as a starting point.

use decoder::MAX_COMPONENTS;
use error::Result;
use std::{mem, sync::mpsc::{self, Sender}};
use std::thread;
use super::{RowData, Worker};
use super::immediate::ImmediateWorker;

enum WorkerMsg {
    Start(RowData),
    AppendRow(Vec<i16>),
    GetResult(Sender<Vec<u8>>),
}
pub struct MultiThreadedWorker {
    senders: [Option<Sender<WorkerMsg>>; MAX_COMPONENTS]
}

impl Worker for MultiThreadedWorker {
    fn new() -> Result<Self> {
        Ok(MultiThreadedWorker {
            senders: [None, None, None, None]
        })
    }
    fn start(&mut self, row_data: RowData) -> Result<()> {
        // if there is no worker thread for this component yet, start one
        let component = row_data.index;
        if let None = self.senders[component] {
            let sender = spawn_worker_thread(component)?;
            self.senders[component] = Some(sender);
        }
        // we do the "take out value and put it back in once we're done" dance here
        // and in all other message-passing methods because there's not that many rows
        // and this should be cheaper than spawning MAX_COMPONENTS many threads up front
        let sender = mem::replace(&mut self.senders[component], None).unwrap();
        sender.send(WorkerMsg::Start(row_data)).expect("jpeg-decoder worker thread error");
        self.senders[component] = Some(sender);
        Ok(())
    }
    fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()> {
        let component = row.0;
        let sender = mem::replace(&mut self.senders[component], None).unwrap();
        sender.send(WorkerMsg::AppendRow(row.1)).expect("jpeg-decoder worker thread error");
        self.senders[component] = Some(sender);
        Ok(())
    }
    fn get_result(&mut self, index: usize) -> Result<Vec<u8>> {
        let (tx, rx) = mpsc::channel();
        let sender = mem::replace(&mut self.senders[index], None).unwrap();
        sender.send(WorkerMsg::GetResult(tx)).expect("jpeg-decoder worker thread error");
        Ok(rx.recv().expect("jpeg-decoder worker thread error"))
        //TODO: join thread here
    }
}

fn spawn_worker_thread(component: usize) -> Result<Sender<WorkerMsg>> {
    let thread_builder = thread::Builder::new().name(format!("worker thread for component {}", component));
    let (tx, rx) = mpsc::channel();

    thread_builder.spawn(move || {
        let mut worker = ImmediateWorker::new_immediate();

        while let Ok(message) = rx.recv() {
            match message {
                WorkerMsg::Start(mut data) => {
                    // we always set component index to 0 for worker threads
                    // because they only ever handle one per thread and we don't want them
                    // to attempt to access nonexistent components
                    data.index = 0;
                    worker.start_immediate(data);
                },
                WorkerMsg::AppendRow(row) => {
                    worker.append_row_immediate((0, row));
                },
                WorkerMsg::GetResult(chan) => {
                    let _ = chan.send(worker.get_result_immediate(0));
                },
            }
        }
    })?;

    Ok(tx)
}<commit_msg>Stop listening for messages in spawned thread once results are requested. Not sure if that's gonna stop the thread leak or not<commit_after>//! This module implements per-component parallelism.
//! It should be possible to implement per-row parallelism as well,
//! which should also boost performance of grayscale images
//! and allow scaling to more cores.
//! However, that would be more complex, so we use this as a starting point.

use decoder::MAX_COMPONENTS;
use error::Result;
use std::{mem, sync::mpsc::{self, Sender}};
use std::thread;
use super::{RowData, Worker};
use super::immediate::ImmediateWorker;

enum WorkerMsg {
    Start(RowData),
    AppendRow(Vec<i16>),
    GetResult(Sender<Vec<u8>>),
}
pub struct MultiThreadedWorker {
    senders: [Option<Sender<WorkerMsg>>; MAX_COMPONENTS]
}

impl Worker for MultiThreadedWorker {
    fn new() -> Result<Self> {
        Ok(MultiThreadedWorker {
            senders: [None, None, None, None]
        })
    }
    fn start(&mut self, row_data: RowData) -> Result<()> {
        // if there is no worker thread for this component yet, start one
        let component = row_data.index;
        if let None = self.senders[component] {
            let sender = spawn_worker_thread(component)?;
            self.senders[component] = Some(sender);
        }
        // we do the "take out value and put it back in once we're done" dance here
        // and in all other message-passing methods because there's not that many rows
        // and this should be cheaper than spawning MAX_COMPONENTS many threads up front
        let sender = mem::replace(&mut self.senders[component], None).unwrap();
        sender.send(WorkerMsg::Start(row_data)).expect("jpeg-decoder worker thread error");
        self.senders[component] = Some(sender);
        Ok(())
    }
    fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()> {
        let component = row.0;
        let sender = mem::replace(&mut self.senders[component], None).unwrap();
        sender.send(WorkerMsg::AppendRow(row.1)).expect("jpeg-decoder worker thread error");
        self.senders[component] = Some(sender);
        Ok(())
    }
    fn get_result(&mut self, index: usize) -> Result<Vec<u8>> {
        let (tx, rx) = mpsc::channel();
        let sender = mem::replace(&mut self.senders[index], None).unwrap();
        sender.send(WorkerMsg::GetResult(tx)).expect("jpeg-decoder worker thread error");
        Ok(rx.recv().expect("jpeg-decoder worker thread error"))
    }
}

fn spawn_worker_thread(component: usize) -> Result<Sender<WorkerMsg>> {
    let thread_builder = thread::Builder::new().name(format!("worker thread for component {}", component));
    let (tx, rx) = mpsc::channel();

    thread_builder.spawn(move || {
        let mut worker = ImmediateWorker::new_immediate();

        while let Ok(message) = rx.recv() {
            match message {
                WorkerMsg::Start(mut data) => {
                    // we always set component index to 0 for worker threads
                    // because they only ever handle one per thread and we don't want them
                    // to attempt to access nonexistent components
                    data.index = 0;
                    worker.start_immediate(data);
                },
                WorkerMsg::AppendRow(row) => {
                    worker.append_row_immediate((0, row));
                },
                WorkerMsg::GetResult(chan) => {
                    let _ = chan.send(worker.get_result_immediate(0));
                    break;
                },
            }
        }
    })?;

    Ok(tx)
}<|endoftext|>


<commit_before>use std::{
    error::Error,
    fs::{
        remove_file,
        File,
    },
    io::{
        prelude::*,
        BufReader,
        BufWriter,
    },
    path::{
        Path,
        PathBuf,
    },
};

use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
struct Opt {
    /// This sets the maximum number of sequences allowed in one chunk.
    #[structopt(short, long, default_value = "10000")]
    max_sequences: u64,

    /// This flag will cause this program to delete the original file once it is split
    #[structopt(short, long)]
    remove_file: bool,

    /// The name of the file to read from, using '-' means stdin.
    #[structopt(parse(from_os_str))]
    raw: PathBuf,

    /// The name of the output directory to write chunks into
    #[structopt(parse(from_os_str))]
    output_directory: PathBuf,
}

fn chunk_writer(
    directory: &Path,
    basename: &str,
    index: u64,
) -> Result<BufWriter<File>, Box<dyn Error>> {
    let mut filename = String::from(basename);
    filename.push_str("-chunk");
    filename.push_str(&index.to_string());
    let mut final_name = PathBuf::from(directory);
    final_name.push(filename);
    final_name.set_extension("ncr");

    let file = File::create(final_name)?;
    return Ok(BufWriter::new(file));
}

fn main() -> Result<(), Box<dyn Error>> {
    let opt = Opt::from_args();
    let basename = opt.raw.file_stem().unwrap().to_str().unwrap();
    let file = File::open(&opt.raw)?;
    let mut reader = BufReader::new(file);

    let mut buf = String::new();
    let mut current_count = 0;
    let mut current_chunk = 0;
    let mut writer = chunk_writer(&opt.output_directory, basename, current_chunk)?;
    loop {
        match reader.read_line(&mut buf)? {
            0 => break,
            _ => {
                writer.write_all(buf.as_bytes())?;
                if buf.starts_with("//") {
                    current_count += 1;
                    if current_count == opt.max_sequences {
                        current_count = 0;
                        current_chunk += 1;
                        writer.flush()?;
                        writer = chunk_writer(&opt.output_directory, basename, current_chunk)?;
                    }
                };
                buf.clear();
            },
        }
    }

    writer.flush()?;

    if opt.remove_file {
        remove_file(&opt.raw)?;
    }

    Ok(())
}
<commit_msg>Fix incorrect header lines in ENA<commit_after>use std::{
    error::Error,
    fs::{
        remove_file,
        File,
    },
    io::{
        prelude::*,
        BufReader,
        BufWriter,
    },
    path::{
        Path,
        PathBuf,
    },
};

use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
struct Opt {
    /// This sets the maximum number of sequences allowed in one chunk.
    #[structopt(short, long, default_value = "10000")]
    max_sequences: u64,

    /// This flag will cause this program to delete the original file once it is split
    #[structopt(short, long)]
    remove_file: bool,

    /// The name of the file to read from, using '-' means stdin.
    #[structopt(parse(from_os_str))]
    raw: PathBuf,

    /// The name of the output directory to write chunks into
    #[structopt(parse(from_os_str))]
    output_directory: PathBuf,
}

fn chunk_writer(
    directory: &Path,
    basename: &str,
    index: u64,
) -> Result<BufWriter<File>, Box<dyn Error>> {
    let mut filename = String::from(basename);
    filename.push_str("-chunk");
    filename.push_str(&index.to_string());
    let mut final_name = PathBuf::from(directory);
    final_name.push(filename);
    final_name.set_extension("ncr");

    let file = File::create(final_name)?;
    return Ok(BufWriter::new(file));
}

fn main() -> Result<(), Box<dyn Error>> {
    let opt = Opt::from_args();
    let basename = opt.raw.file_stem().unwrap().to_str().unwrap();
    let file = File::open(&opt.raw)?;
    let mut reader = BufReader::new(file);

    let mut buf = String::new();
    let mut current_count = 0;
    let mut current_chunk = 0;
    let mut writer = chunk_writer(&opt.output_directory, basename, current_chunk)?;
    loop {
        match reader.read_line(&mut buf)? {
            0 => break,
            _ => {
                let start = &buf[0..2];
                match start {
                    "ID" => {
                        let line = buf.replace("linear; XXX;", "linear; RNA;");
                        writer.write_all(line.as_bytes())?;
                    },
                    "//" => {
                        writer.write_all(buf.as_bytes())?;
                        current_count += 1;
                        if current_count == opt.max_sequences {
                            current_count = 0;
                            current_chunk += 1;
                            writer.flush()?;
                            writer = chunk_writer(&opt.output_directory, basename, current_chunk)?;
                        }
                    },
                    _ => {
                        writer.write_all(buf.as_bytes())?;
                    },
                }
                buf.clear();
            },
        }
    }

    writer.flush()?;

    if opt.remove_file {
        remove_file(&opt.raw)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Guillaume Pinot <texitoi(a)texitoi.eu>
//
// This work is free. You can redistribute it and/or modify it under
// the terms of the Do What The Fuck You Want To Public License,
// Version 2, as published by Sam Hocevar. See the COPYING file for
// more details.

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;
use syn::*;

#[proc_macro_derive(StructOpt, attributes(structopt))]
pub fn structopt(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();
    let gen = impl_structopt(&ast);
    gen.parse().unwrap()
}

enum Ty {
    Bool,
    U64,
    Vec,
    Option,
    Other,
}

fn ty(t: &syn::Ty) -> Ty {
    if let syn::Ty::Path(None, syn::Path { segments: ref segs, .. }) = *t {
        match segs.last().unwrap().ident.as_ref() {
            "bool" => Ty::Bool,
            "u64" => Ty::U64,
            "Option" => Ty::Option,
            "Vec" => Ty::Vec,
            _ => Ty::Other,
        }
    } else {
        Ty::Other
    }
}

fn sub_type(t: &syn::Ty) -> Option<&syn::Ty> {
    let segs = match *t {
        syn::Ty::Path(None, syn::Path { ref segments, .. }) => segments,
        _ => return None,
    };
    match *segs.last().unwrap() {
        PathSegment {
            parameters: PathParameters::AngleBracketed(
                AngleBracketedParameterData { ref types, .. }),
            ..
        } if !types.is_empty() => Some(&types[0]),
            _ => None,
    }
}

fn extract_attrs<'a>(attrs: &'a [Attribute]) -> Box<Iterator<Item = (&'a Ident, &'a Lit)> + 'a> {
    let iter = attrs.iter()
        .filter_map(|attr| match attr.value {
            MetaItem::List(ref i, ref v) if i.as_ref() == "structopt" => Some(v),
            _ => None,
        }).flat_map(|v| v.iter().filter_map(|mi| match *mi {
            NestedMetaItem::MetaItem(MetaItem::NameValue(ref i, ref l)) => Some((i, l)),
            _ => None,
        }));
    Box::new(iter)
}

fn from_attr_or(attrs: &[(&Ident, &Lit)], key: &str, default: &str) -> Lit {
    attrs.iter()
        .find(|&&(i, _)| i.as_ref() == key)
        .map(|&(_, l)| l.clone())
        .unwrap_or_else(|| Lit::Str(default.into(), StrStyle::Cooked))
 }

fn impl_structopt(ast: &syn::DeriveInput) -> quote::Tokens {
    let struct_name = &ast.ident;
    let s = if let Body::Struct(VariantData::Struct(ref s)) = ast.body {
        s
    } else {
        panic!("Only struct is supported")
    };

    let struct_attrs: Vec<_> = extract_attrs(&ast.attrs).collect();
    let name = from_attr_or(&struct_attrs, "name", env!("CARGO_PKG_NAME"));
    let version = from_attr_or(&struct_attrs, "version", env!("CARGO_PKG_VERSION"));
    let author = from_attr_or(&struct_attrs, "author", env!("CARGO_PKG_AUTHORS"));
    let about = from_attr_or(&struct_attrs, "about", env!("CARGO_PKG_DESCRIPTION"));

    let args = s.iter().map(|f| {
        let ident = f.ident.as_ref().unwrap();
        let cur_type = ty(&f.ty);
        let convert_type = match cur_type {
            Ty::Vec | Ty::Option => sub_type(&f.ty).unwrap_or(&f.ty),
            _ => &f.ty,
        };
        let validator = quote! {
            validator(|s| s.parse::<#convert_type>()
                      .map(|_| ())
                      .map_err(|e| e.description().into()))
        };
        let modifier = match cur_type {
            Ty::Bool => quote! {
                .takes_value(false)
                .multiple(false)
            },
            Ty::U64 => quote! {
                .takes_value(false)
                .multiple(true)
            },
            Ty::Option => quote! {
                .takes_value(true)
                .multiple(false)
                .#validator
            },
            Ty::Vec => quote! {
                .takes_value(true)
                .multiple(true)
                .#validator
            },
            Ty::Other => {
                let required = extract_attrs(&f.attrs)
                    .find(|&(i, _)| i.as_ref() == "default_value")
                    .is_none();
                quote! {
                    .takes_value(true)
                    .multiple(false)
                    .required(#required)
                    .#validator
                }
            },
        };
        let from_attr = extract_attrs(&f.attrs).map(|(i, l)| quote!(.#i(#l)));
        quote! {
            .arg(_clap::Arg::with_name(stringify!(#ident))
                 #modifier
                 #(#from_attr)*)
        }
    });
    let clap = quote! {
        fn clap<'a, 'b>() -> _clap::App<'a, 'b> {
            use std::error::Error;
            _clap::App::new(#name)
                .version(#version)
                .author(#author)
                .about(#about)
                #( #args )*
        }
    };

    let fields = s.iter().map(|f| {
        let ident = f.ident.as_ref().unwrap();
        let convert = match ty(&f.ty) {
            Ty::Bool => quote!(is_present(stringify!(#ident))),
            Ty::U64 => quote!(occurrences_of(stringify!(#ident))),
            Ty::Option => quote! {
                value_of(stringify!(#ident))
                    .as_ref()
                    .map(|s| s.parse().unwrap())
            },
            Ty::Vec => quote! {
                values_of(stringify!(#ident))
                    .map(|v| v.map(|s| s.parse().unwrap()).collect())
                    .unwrap_or_else(Vec::new)
            },
            Ty::Other => quote! {
                value_of(stringify!(#ident))
                    .as_ref()
                    .unwrap()
                    .parse()
                    .unwrap()
            },
        };
        quote!( #ident: matches.#convert, )
    });
    let from_clap = quote! {
        fn from_clap(matches: _clap::ArgMatches) -> Self {
            #struct_name {
                #( #fields )*
            }
        }
    };

    let dummy_const = Ident::new(format!("_IMPL_STRUCTOPT_FOR_{}", struct_name));
    quote! {
        #[allow(non_upper_case_globals, unused_attributes, unused_imports)]
        const #dummy_const: () = {
            extern crate clap as _clap;
            extern crate structopt as _structopt;
            impl _structopt::StructOpt for #struct_name {
                #clap
                #from_clap
            }
        };
    }
}
<commit_msg>refactor<commit_after>// Copyright (c) 2017 Guillaume Pinot <texitoi(a)texitoi.eu>
//
// This work is free. You can redistribute it and/or modify it under
// the terms of the Do What The Fuck You Want To Public License,
// Version 2, as published by Sam Hocevar. See the COPYING file for
// more details.

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;
use syn::*;

#[proc_macro_derive(StructOpt, attributes(structopt))]
pub fn structopt(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();
    let gen = impl_structopt(&ast);
    gen.parse().unwrap()
}

enum Ty {
    Bool,
    U64,
    Vec,
    Option,
    Other,
}

fn ty(t: &syn::Ty) -> Ty {
    if let syn::Ty::Path(None, syn::Path { segments: ref segs, .. }) = *t {
        match segs.last().unwrap().ident.as_ref() {
            "bool" => Ty::Bool,
            "u64" => Ty::U64,
            "Option" => Ty::Option,
            "Vec" => Ty::Vec,
            _ => Ty::Other,
        }
    } else {
        Ty::Other
    }
}

fn sub_type(t: &syn::Ty) -> Option<&syn::Ty> {
    let segs = match *t {
        syn::Ty::Path(None, syn::Path { ref segments, .. }) => segments,
        _ => return None,
    };
    match *segs.last().unwrap() {
        PathSegment {
            parameters: PathParameters::AngleBracketed(
                AngleBracketedParameterData { ref types, .. }),
            ..
        } if !types.is_empty() => Some(&types[0]),
            _ => None,
    }
}

fn extract_attrs<'a>(attrs: &'a [Attribute]) -> Box<Iterator<Item = (&'a Ident, &'a Lit)> + 'a> {
    let iter = attrs.iter()
        .filter_map(|attr| match attr.value {
            MetaItem::List(ref i, ref v) if i.as_ref() == "structopt" => Some(v),
            _ => None,
        }).flat_map(|v| v.iter().filter_map(|mi| match *mi {
            NestedMetaItem::MetaItem(MetaItem::NameValue(ref i, ref l)) => Some((i, l)),
            _ => None,
        }));
    Box::new(iter)
}

fn from_attr_or(attrs: &[(&Ident, &Lit)], key: &str, default: &str) -> Lit {
    attrs.iter()
        .find(|&&(i, _)| i.as_ref() == key)
        .map(|&(_, l)| l.clone())
        .unwrap_or_else(|| Lit::Str(default.into(), StrStyle::Cooked))
}

fn gen_from_clap(struct_name: &Ident, s: &[Field]) -> quote::Tokens {
    let fields = s.iter().map(|f| {
        let ident = f.ident.as_ref().unwrap();
        let convert = match ty(&f.ty) {
            Ty::Bool => quote!(is_present(stringify!(#ident))),
            Ty::U64 => quote!(occurrences_of(stringify!(#ident))),
            Ty::Option => quote! {
                value_of(stringify!(#ident))
                    .as_ref()
                    .map(|s| s.parse().unwrap())
            },
            Ty::Vec => quote! {
                values_of(stringify!(#ident))
                    .map(|v| v.map(|s| s.parse().unwrap()).collect())
                    .unwrap_or_else(Vec::new)
            },
            Ty::Other => quote! {
                value_of(stringify!(#ident))
                    .as_ref()
                    .unwrap()
                    .parse()
                    .unwrap()
            },
        };
        quote!( #ident: matches.#convert, )
    });
    quote! {
        fn from_clap(matches: _clap::ArgMatches) -> Self {
            #struct_name {
                #( #fields )*
            }
        }
    }
}

fn gen_clap(ast: &DeriveInput, s: &[Field]) -> quote::Tokens {
    let struct_attrs: Vec<_> = extract_attrs(&ast.attrs).collect();
    let name = from_attr_or(&struct_attrs, "name", env!("CARGO_PKG_NAME"));
    let version = from_attr_or(&struct_attrs, "version", env!("CARGO_PKG_VERSION"));
    let author = from_attr_or(&struct_attrs, "author", env!("CARGO_PKG_AUTHORS"));
    let about = from_attr_or(&struct_attrs, "about", env!("CARGO_PKG_DESCRIPTION"));

    let args = s.iter().map(|f| {
        let ident = f.ident.as_ref().unwrap();
        let cur_type = ty(&f.ty);
        let convert_type = match cur_type {
            Ty::Vec | Ty::Option => sub_type(&f.ty).unwrap_or(&f.ty),
            _ => &f.ty,
        };
        let validator = quote! {
            validator(|s| s.parse::<#convert_type>()
                      .map(|_| ())
                      .map_err(|e| e.description().into()))
        };
        let modifier = match cur_type {
            Ty::Bool => quote!( .takes_value(false).multiple(false) ),
            Ty::U64 => quote!( .takes_value(false).multiple(true) ),
            Ty::Option => quote!( .takes_value(true).multiple(false).#validator ),
            Ty::Vec => quote!( .takes_value(true).multiple(true).#validator ),
            Ty::Other => {
                let required = extract_attrs(&f.attrs)
                    .find(|&(i, _)| i.as_ref() == "default_value")
                    .is_none();
                quote!( .takes_value(true).multiple(false).required(#required).#validator )
            },
        };
        let from_attr = extract_attrs(&f.attrs).map(|(i, l)| quote!(.#i(#l)));
        quote!( .arg(_clap::Arg::with_name(stringify!(#ident)) #modifier #(#from_attr)*) )
    });
    quote! {
        fn clap<'a, 'b>() -> _clap::App<'a, 'b> {
            use std::error::Error;
            _clap::App::new(#name)
                .version(#version)
                .author(#author)
                .about(#about)
                #( #args )*
        }
    }
}

fn impl_structopt(ast: &syn::DeriveInput) -> quote::Tokens {
    let struct_name = &ast.ident;
    let s = match ast.body {
        Body::Struct(VariantData::Struct(ref s)) => s,
        _ => panic!("Only struct is supported"),
    };

    let clap = gen_clap(ast, s);
    let from_clap = gen_from_clap(struct_name, s);
    let dummy_const = Ident::new(format!("_IMPL_STRUCTOPT_FOR_{}", struct_name));
    quote! {
        #[allow(non_upper_case_globals, unused_attributes, unused_imports)]
        const #dummy_const: () = {
            extern crate clap as _clap;
            extern crate structopt as _structopt;
            impl _structopt::StructOpt for #struct_name {
                #clap
                #from_clap
            }
        };
    }
}
<|endoftext|>


<commit_before>use super::*;

fn default_parse_cli_args(mut args: Vec<&str>) -> CliArgs {
    let global_config = GlobalConfig::new();
    args.insert(0, "makers");
    parse_args(&global_config, "makers", false, Some(args))
}

fn default_parsed_cli_args() -> CliArgs {
    let mut cli_args = CliArgs::new();

    cli_args.command = "makers".to_string();
    cli_args.task = "default".to_string();
    cli_args.profile = Some("development".to_string());

    if ci_info::is_ci() {
        cli_args.time_summary = Some(true);
    }

    cli_args
}

fn assert_cli_args(cli_args_ref1: &CliArgs, cli_args_ref2: &CliArgs) {
    let cli_args1 = cli_args_ref1.clone();
    let cli_args2 = cli_args_ref2.clone();

    assert_eq!(cli_args1.command, cli_args2.command);
    assert_eq!(cli_args1.build_file, cli_args2.build_file);
    assert_eq!(cli_args1.task, cli_args2.task);
    assert_eq!(cli_args1.profile.unwrap(), cli_args2.profile.unwrap());
    assert_eq!(cli_args1.log_level, cli_args2.log_level);
    assert_eq!(cli_args1.disable_color, cli_args2.disable_color);
    assert_eq!(cli_args1.cwd, cli_args2.cwd);
    assert_eq!(cli_args1.env, cli_args2.env);
    assert_eq!(cli_args1.env_file, cli_args2.env_file);
    assert_eq!(cli_args1.disable_workspace, cli_args2.disable_workspace);
    assert_eq!(cli_args1.disable_on_error, cli_args2.disable_on_error);
    assert_eq!(cli_args1.allow_private, cli_args2.allow_private);
    assert_eq!(cli_args1.skip_init_end_tasks, cli_args2.skip_init_end_tasks);
    assert_eq!(cli_args1.skip_tasks_pattern, cli_args2.skip_tasks_pattern);
    assert_eq!(
        cli_args1.disable_check_for_updates,
        cli_args2.disable_check_for_updates
    );
    assert_eq!(cli_args1.print_only, cli_args2.print_only);
    assert_eq!(cli_args1.list_all_steps, cli_args2.list_all_steps);
    assert_eq!(cli_args1.diff_execution_plan, cli_args2.diff_execution_plan);
    assert_eq!(cli_args1.experimental, cli_args2.experimental);
    assert_eq!(cli_args1.arguments, cli_args2.arguments);
    assert_eq!(cli_args1.output_format, cli_args2.output_format);
    assert_eq!(cli_args1.output_file, cli_args2.output_file);
    assert_eq!(cli_args1.print_time_summary, cli_args2.print_time_summary);
}

#[test]
fn parse_args_makers() {
    let global_config = GlobalConfig::new();
    let cli_args = parse_args(&global_config, "makers", false, Some(vec!["makers"]));

    let expected = default_parsed_cli_args();

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_cargo_make() {
    let global_config = GlobalConfig::new();
    let cli_args = parse_args(&global_config, "make", true, Some(vec!["cargo", "make"]));

    let mut expected = default_parsed_cli_args();
    expected.command = "cargo make".to_string();

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_makefile() {
    let mut cli_args = default_parse_cli_args(vec!["--makefile", "./mymakefile.toml"]);

    let mut expected = default_parsed_cli_args();
    expected.build_file = Some("./mymakefile.toml".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--makefile", "./mymakefile.toml", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_task() {
    let mut cli_args = default_parse_cli_args(vec!["--task", "sometask"]);

    let mut expected = default_parsed_cli_args();
    expected.task = "sometask".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-t", "sometask"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["sometask"]);
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_profile() {
    let mut cli_args = default_parse_cli_args(vec!["--profile", "someprofile"]);

    let mut expected = default_parsed_cli_args();
    expected.profile = Some("someprofile".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-p", "someprofile"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--profile", "someprofile", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_cwd() {
    let cli_args = default_parse_cli_args(vec!["--cwd", "./mydir/subdir/"]);

    let mut expected = default_parsed_cli_args();
    expected.cwd = Some("./mydir/subdir/".to_string());

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_no_workspace() {
    let cli_args = default_parse_cli_args(vec!["--no-workspace"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_workspace = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_allow_private() {
    let cli_args = default_parse_cli_args(vec!["--allow-private"]);

    let mut expected = default_parsed_cli_args();
    expected.allow_private = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_skip_init_end_tasks() {
    let cli_args = default_parse_cli_args(vec!["--skip-init-end-tasks"]);

    let mut expected = default_parsed_cli_args();
    expected.skip_init_end_tasks = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_skip_tasks() {
    let mut cli_args = default_parse_cli_args(vec!["--skip-tasks", "pre-.*"]);

    let mut expected = default_parsed_cli_args();
    expected.skip_tasks_pattern = Some("pre-.*".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--skip-tasks", "pre-.*", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_env_file() {
    let mut cli_args = default_parse_cli_args(vec!["--env-file", "./.env"]);

    let mut expected = default_parsed_cli_args();
    expected.env_file = Some("./.env".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--env-file", "./.env", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_env() {
    let mut cli_args = default_parse_cli_args(vec!["--env", "K=V"]);

    let mut expected = default_parsed_cli_args();
    expected.env = Some(vec!["K=V".to_string()]);

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-e", "K=V"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-e", "K1=V1", "-e", "K2=V2", "--env", "K3=V3"]);
    expected.env = Some(vec![
        "K1=V1".to_string(),
        "K2=V2".to_string(),
        "K3=V3".to_string(),
    ]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec![
        "-e", "K1=V1", "-e", "K2=V2", "--env", "K3=V3", "taskname",
    ]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_loglevel() {
    let mut cli_args = default_parse_cli_args(vec!["--loglevel", "verbose"]);

    let mut expected = default_parsed_cli_args();
    expected.log_level = "verbose".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-l", "verbose"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--loglevel", "verbose", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_verbose() {
    let mut cli_args = default_parse_cli_args(vec!["--verbose"]);

    let mut expected = default_parsed_cli_args();
    expected.log_level = "verbose".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-v"]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_no_color() {
    let cli_args = default_parse_cli_args(vec!["--no-color"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_color = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_time_summary() {
    let cli_args = default_parse_cli_args(vec!["--time-summary"]);

    let mut expected = default_parsed_cli_args();
    expected.print_time_summary = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_experimental() {
    let cli_args = default_parse_cli_args(vec!["--experimental"]);

    let mut expected = default_parsed_cli_args();
    expected.experimental = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_disable_check_for_updates() {
    let cli_args = default_parse_cli_args(vec!["--disable-check-for-updates"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_check_for_updates = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_output_format() {
    let mut cli_args = default_parse_cli_args(vec!["--output-format", "autocomplete"]);

    let mut expected = default_parsed_cli_args();
    expected.output_format = "autocomplete".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--output-format", "autocomplete", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_output_file() {
    let mut cli_args = default_parse_cli_args(vec!["--output-file", "./out"]);

    let mut expected = default_parsed_cli_args();
    expected.output_file = Some("./out".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--output-file", "./out", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_print_steps() {
    let cli_args = default_parse_cli_args(vec!["--print-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.print_only = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_list_all_steps() {
    let cli_args = default_parse_cli_args(vec!["--list-all-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.list_all_steps = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_list_category_steps() {
    let mut cli_args = default_parse_cli_args(vec!["--list-category-steps", "build"]);

    let mut expected = default_parsed_cli_args();
    expected.list_category_steps = Some("build".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--list-category-steps", "build", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_diff_steps() {
    let cli_args = default_parse_cli_args(vec!["--diff-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.diff_execution_plan = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_task_cmd() {
    let mut cli_args = default_parse_cli_args(vec!["task1"]);

    let mut expected = default_parsed_cli_args();
    expected.task = "task1".to_string();
    expected.arguments = Some(vec![]);

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--", "task1"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["task1", "arg1", "arg2"]);
    expected.arguments = Some(vec!["arg1".to_string(), "arg2".to_string()]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--", "task1", "arg1", "arg2"]);
    assert_cli_args(&cli_args, &expected);
}
<commit_msg>fix tests for CI<commit_after>use super::*;

fn default_parse_cli_args(mut args: Vec<&str>) -> CliArgs {
    let global_config = GlobalConfig::new();
    args.insert(0, "makers");
    parse_args(&global_config, "makers", false, Some(args))
}

fn default_parsed_cli_args() -> CliArgs {
    let mut cli_args = CliArgs::new();

    cli_args.command = "makers".to_string();
    cli_args.task = "default".to_string();
    cli_args.profile = Some("development".to_string());

    if ci_info::is_ci() {
        cli_args.print_time_summary = true;
    }

    cli_args
}

fn assert_cli_args(cli_args_ref1: &CliArgs, cli_args_ref2: &CliArgs) {
    let cli_args1 = cli_args_ref1.clone();
    let cli_args2 = cli_args_ref2.clone();

    assert_eq!(cli_args1.command, cli_args2.command);
    assert_eq!(cli_args1.build_file, cli_args2.build_file);
    assert_eq!(cli_args1.task, cli_args2.task);
    assert_eq!(cli_args1.profile.unwrap(), cli_args2.profile.unwrap());
    assert_eq!(cli_args1.log_level, cli_args2.log_level);
    assert_eq!(cli_args1.disable_color, cli_args2.disable_color);
    assert_eq!(cli_args1.cwd, cli_args2.cwd);
    assert_eq!(cli_args1.env, cli_args2.env);
    assert_eq!(cli_args1.env_file, cli_args2.env_file);
    assert_eq!(cli_args1.disable_workspace, cli_args2.disable_workspace);
    assert_eq!(cli_args1.disable_on_error, cli_args2.disable_on_error);
    assert_eq!(cli_args1.allow_private, cli_args2.allow_private);
    assert_eq!(cli_args1.skip_init_end_tasks, cli_args2.skip_init_end_tasks);
    assert_eq!(cli_args1.skip_tasks_pattern, cli_args2.skip_tasks_pattern);
    assert_eq!(
        cli_args1.disable_check_for_updates,
        cli_args2.disable_check_for_updates
    );
    assert_eq!(cli_args1.print_only, cli_args2.print_only);
    assert_eq!(cli_args1.list_all_steps, cli_args2.list_all_steps);
    assert_eq!(cli_args1.diff_execution_plan, cli_args2.diff_execution_plan);
    assert_eq!(cli_args1.experimental, cli_args2.experimental);
    assert_eq!(cli_args1.arguments, cli_args2.arguments);
    assert_eq!(cli_args1.output_format, cli_args2.output_format);
    assert_eq!(cli_args1.output_file, cli_args2.output_file);
    assert_eq!(cli_args1.print_time_summary, cli_args2.print_time_summary);
}

#[test]
fn parse_args_makers() {
    let global_config = GlobalConfig::new();
    let cli_args = parse_args(&global_config, "makers", false, Some(vec!["makers"]));

    let expected = default_parsed_cli_args();

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_cargo_make() {
    let global_config = GlobalConfig::new();
    let cli_args = parse_args(&global_config, "make", true, Some(vec!["cargo", "make"]));

    let mut expected = default_parsed_cli_args();
    expected.command = "cargo make".to_string();

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_makefile() {
    let mut cli_args = default_parse_cli_args(vec!["--makefile", "./mymakefile.toml"]);

    let mut expected = default_parsed_cli_args();
    expected.build_file = Some("./mymakefile.toml".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--makefile", "./mymakefile.toml", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_task() {
    let mut cli_args = default_parse_cli_args(vec!["--task", "sometask"]);

    let mut expected = default_parsed_cli_args();
    expected.task = "sometask".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-t", "sometask"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["sometask"]);
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_profile() {
    let mut cli_args = default_parse_cli_args(vec!["--profile", "someprofile"]);

    let mut expected = default_parsed_cli_args();
    expected.profile = Some("someprofile".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-p", "someprofile"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--profile", "someprofile", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_cwd() {
    let cli_args = default_parse_cli_args(vec!["--cwd", "./mydir/subdir/"]);

    let mut expected = default_parsed_cli_args();
    expected.cwd = Some("./mydir/subdir/".to_string());

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_no_workspace() {
    let cli_args = default_parse_cli_args(vec!["--no-workspace"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_workspace = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_allow_private() {
    let cli_args = default_parse_cli_args(vec!["--allow-private"]);

    let mut expected = default_parsed_cli_args();
    expected.allow_private = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_skip_init_end_tasks() {
    let cli_args = default_parse_cli_args(vec!["--skip-init-end-tasks"]);

    let mut expected = default_parsed_cli_args();
    expected.skip_init_end_tasks = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_skip_tasks() {
    let mut cli_args = default_parse_cli_args(vec!["--skip-tasks", "pre-.*"]);

    let mut expected = default_parsed_cli_args();
    expected.skip_tasks_pattern = Some("pre-.*".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--skip-tasks", "pre-.*", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_env_file() {
    let mut cli_args = default_parse_cli_args(vec!["--env-file", "./.env"]);

    let mut expected = default_parsed_cli_args();
    expected.env_file = Some("./.env".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--env-file", "./.env", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_env() {
    let mut cli_args = default_parse_cli_args(vec!["--env", "K=V"]);

    let mut expected = default_parsed_cli_args();
    expected.env = Some(vec!["K=V".to_string()]);

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-e", "K=V"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-e", "K1=V1", "-e", "K2=V2", "--env", "K3=V3"]);
    expected.env = Some(vec![
        "K1=V1".to_string(),
        "K2=V2".to_string(),
        "K3=V3".to_string(),
    ]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec![
        "-e", "K1=V1", "-e", "K2=V2", "--env", "K3=V3", "taskname",
    ]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_loglevel() {
    let mut cli_args = default_parse_cli_args(vec!["--loglevel", "verbose"]);

    let mut expected = default_parsed_cli_args();
    expected.log_level = "verbose".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-l", "verbose"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--loglevel", "verbose", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_verbose() {
    let mut cli_args = default_parse_cli_args(vec!["--verbose"]);

    let mut expected = default_parsed_cli_args();
    expected.log_level = "verbose".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-v"]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_no_color() {
    let cli_args = default_parse_cli_args(vec!["--no-color"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_color = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_time_summary() {
    let cli_args = default_parse_cli_args(vec!["--time-summary"]);

    let mut expected = default_parsed_cli_args();
    expected.print_time_summary = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_experimental() {
    let cli_args = default_parse_cli_args(vec!["--experimental"]);

    let mut expected = default_parsed_cli_args();
    expected.experimental = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_disable_check_for_updates() {
    let cli_args = default_parse_cli_args(vec!["--disable-check-for-updates"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_check_for_updates = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_output_format() {
    let mut cli_args = default_parse_cli_args(vec!["--output-format", "autocomplete"]);

    let mut expected = default_parsed_cli_args();
    expected.output_format = "autocomplete".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--output-format", "autocomplete", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_output_file() {
    let mut cli_args = default_parse_cli_args(vec!["--output-file", "./out"]);

    let mut expected = default_parsed_cli_args();
    expected.output_file = Some("./out".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--output-file", "./out", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_print_steps() {
    let cli_args = default_parse_cli_args(vec!["--print-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.print_only = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_list_all_steps() {
    let cli_args = default_parse_cli_args(vec!["--list-all-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.list_all_steps = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_list_category_steps() {
    let mut cli_args = default_parse_cli_args(vec!["--list-category-steps", "build"]);

    let mut expected = default_parsed_cli_args();
    expected.list_category_steps = Some("build".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--list-category-steps", "build", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_diff_steps() {
    let cli_args = default_parse_cli_args(vec!["--diff-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.diff_execution_plan = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_task_cmd() {
    let mut cli_args = default_parse_cli_args(vec!["task1"]);

    let mut expected = default_parsed_cli_args();
    expected.task = "task1".to_string();
    expected.arguments = Some(vec![]);

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--", "task1"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["task1", "arg1", "arg2"]);
    expected.arguments = Some(vec!["arg1".to_string(), "arg2".to_string()]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--", "task1", "arg1", "arg2"]);
    assert_cli_args(&cli_args, &expected);
}
<|endoftext|>