argparsnip/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2extern crate alloc;
3
4#[cfg(not(feature = "std"))]
5mod std;
6
7#[cfg(feature = "derive")]
8use serde::{Deserialize, Serialize};
9
10pub mod value;
11
12use std::iter::Peekable;
13use std::string::String;
14use std::vec::Vec;
15use std::{
16    collections::{
17        hash_map::Entry::{Occupied, Vacant},
18        HashMap,
19    },
20    process::exit,
21};
22
23use crate::value::{cast_type, check_type, Type, Value};
24use unicode_segmentation::UnicodeSegmentation;
25
26#[cfg(feature = "debug")]
27use log::debug;
28
29/// noop macro for when debug logging is disabled (and so log::debug isn't imported)
30#[cfg(not(feature = "debug"))]
31macro_rules! debug {
32    ($($arg:tt)+) => {};
33}
34
35#[derive(PartialEq, Debug)]
36pub enum Error<'a> {
37    UnknownArg,
38    InvalidArg,
39    MissingRequiredArgs(Vec<&'a str>),
40    WrongNumValues(&'a str, &'a NumValues, Value),
41    WrongValueType(Value),
42    WrongCastType(String),
43    InvalidValue(String, String),
44    Override(&'a str),
45    UnknownArgs(Vec<String>),
46    BadInput,
47}
48
49/// The number of values users are able to assign an arg
50/// If None we will treat the arg as a flag. This is a special case because
51/// if flags are seen multiple times (e.g -vvv) we need to handle that as extra verbose by exposing the number of repetitions.
52/// This isn't needed for normal flags though - just now we overwrite them, but in the future we may move to appending there
53#[allow(dead_code)]
54#[derive(PartialEq, Debug)]
55#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
56pub enum NumValues {
57    None,
58    Fixed(usize),
59    AtLeast(usize),
60    Between(usize, usize),
61    Any,
62}
63
64impl Default for NumValues {
65    fn default() -> Self {
66        NumValues::Fixed(1)
67    }
68}
69
70/// Struct defining a single arg we can accept
71#[cfg_attr(feature = "derive", derive(Deserialize))]
72pub struct Arg<'a> {
73    /// unique key (per Args) to identify this arg
74    pub name: &'a str,
75    /// short (single dash, e.g -v) alias to match this arg with
76    /// @note: if multiple args have the same "short" or "long" name then
77    /// we have a race condition. We do not check for this but which we use should be considered undefined behaviour
78    #[cfg_attr(feature = "derive", serde(default))]
79    pub short: Option<&'a str>,
80    /// long (double dash, e.g --verbose) alias to match this arg with
81    #[cfg_attr(feature = "derive", serde(default))]
82    pub long: Option<&'a str>,
83    /// info about this arg, used when printing --help
84    #[cfg_attr(feature = "derive", serde(default))]
85    pub about: &'a str,
86    /// number of parameters this arg accepts. See NumValues for more details
87    #[cfg_attr(feature = "derive", serde(default))]
88    pub num_values: NumValues,
89    /// name for the value of this arg in --help, used when printing --help
90    #[cfg_attr(feature = "derive", serde(default))]
91    pub value_name: Option<&'a str>,
92    /// default value for this arg if it is missing. By default no default is given
93    #[cfg_attr(feature = "derive", serde(skip, default))]
94    pub default: Option<fn() -> Value>,
95    /// whether this arg is required
96    #[cfg_attr(feature = "derive", serde(default))]
97    pub required: bool,
98    /// type for values, if given argparsing will fail if given the wrong type + Error::WrongValueType
99    #[cfg_attr(feature = "derive", serde(default))]
100    pub value_type: Type,
101    /// additional validation for this arg. By default is a noop
102    /// If this returns Err(String) argparsing will fail with the given string + Error::InvalidValue
103    #[cfg_attr(feature = "derive", serde(skip, default = "default_validation"))]
104    pub validation: fn(&Value) -> Result<(), String>,
105}
106
107fn default_validation() -> fn(&Value) -> Result<(), String> {
108    |_| Ok(())
109}
110
111impl<'a> std::default::Default for Arg<'a> {
112    fn default() -> Self {
113        Arg {
114            name: Default::default(),
115            short: Default::default(),
116            long: Default::default(),
117            about: Default::default(),
118            num_values: Default::default(),
119            value_name: Default::default(),
120            default: Default::default(),
121            required: Default::default(),
122            value_type: Default::default(),
123            validation: default_validation(),
124        }
125    }
126}
127
128/// How to match filters:
129///     All -> the filter passes if all are true
130///     Any -> the filter passes if any are true
131#[cfg_attr(feature = "debug", derive(Debug))]
132#[cfg_attr(feature = "derive", derive(Deserialize))]
133pub enum FilterType {
134    All,
135    Any,
136}
137
138/// Collection of filters used to constrain the combinations of arguments that can be matched.
139/// For example, to say we cannot get both args A && B, or we can only be passed C if we've also been passed D.
140#[derive(Default)]
141#[cfg_attr(feature = "derive", derive(Deserialize))]
142pub struct Filters<'a> {
143    #[cfg_attr(feature = "derive", serde(default))]
144    pub filter_type: FilterType,
145    #[cfg_attr(feature = "derive", serde(default, borrow))]
146    pub filters: Vec<Filter<'a>>,
147    #[cfg_attr(feature = "derive", serde(default))]
148    pub inverse: bool,
149}
150
151/// A specific combination of args that must be matched in Filters.
152/// E.g Filter { args: vec!["foo", "bar"], filter_type: Any} will pass on foo | bar
153/// E.g Filter { args: vec!["foo", "bar"], filter_type: All} will only pass on foo & bar
154/// E.g Filter { args: vec!["foo", "bar"], filter_type: Any, inverse: true} will pass on foo ^ bar
155/// E.g Filter { args: vec!["foo", "bar"], filter_type: All, inverse: true} will only pass on !(foo & bar)
156#[cfg_attr(feature = "debug", derive(Debug))]
157#[derive(Default)]
158#[cfg_attr(feature = "derive", derive(Deserialize))]
159pub struct Filter<'a> {
160    #[cfg_attr(feature = "derive", serde(default))]
161    pub inverse: bool,
162    #[cfg_attr(feature = "derive", serde(default))]
163    pub filter_type: FilterType,
164    /// A list of Arg names to filter on.
165    #[cfg_attr(feature = "derive", serde(default, borrow))]
166    pub args: Vec<&'a str>,
167}
168
169impl<'a> Filter<'a> {
170    /// method to check if our filter condition holds. returns 1 on true, 0 on false
171    fn test(&self, builder: &Builder<'a>) -> usize {
172        debug!("applying filter {:?} to {:?}", self, builder);
173        let res = match self.filter_type {
174            FilterType::All => self.args.iter().all(|p| builder.args.contains_key(p) || builder.flags.contains_key(p)),
175            FilterType::Any => self.args.iter().any(|p| builder.args.contains_key(p) || builder.flags.contains_key(p)),
176        };
177        debug!("filter result: {}, using inverse: {}", res, self.inverse);
178        (res ^ self.inverse) as usize
179    }
180}
181
182impl Default for FilterType {
183    fn default() -> Self {
184        FilterType::Any
185    }
186}
187
188/// Struct defining the collection of args we support
189#[cfg_attr(feature = "derive", derive(Deserialize))]
190pub struct Args<'a, T = Results<'a>> {
191    /// name of this command (if it's the root) or subcommand
192    /// if ommitted this will default to the cargo package name (even with subcommands)
193    #[cfg_attr(feature = "derive", serde(default = "name_default"))]
194    pub name: &'a str,
195    /// If this subcommand is matched this field will be returned alongside the Results,
196    /// with the intention that it can be used to uniquely identify which Args struct was matched
197    /// (although uniqueness is not enforced). This is needed because the name field can be repeated in nested subcommands, e.g
198    /// ./foo help
199    /// ./foo subcommand help
200    /// where "help" is the name
201    /// This field is also used in the --version && --help command, so we recommend that this mirrors the cli call used, e.g
202    /// if name = "subcommand" then
203    /// path = "root subcommand"
204    #[cfg_attr(feature = "derive", serde(default))]
205    pub path: Option<&'a str>,
206    /// The program version, used by the --version command
207    /// if ommitted this will default to the cargo package version (even with subcommands)
208    #[cfg_attr(feature = "derive", serde(default = "version_default"))]
209    pub version: &'a str,
210    /// About string included in the --help command
211    /// if ommitted this will default to the cargo package description (even with subcommands)
212    #[cfg_attr(feature = "derive", serde(default = "about_default"))]
213    pub about: &'a str,
214    /// Collections of args that we will look for under this command. Any matched will be included in the Results params or flags.
215    #[cfg_attr(feature = "derive", serde(default))]
216    pub args: Vec<Arg<'a>>,
217    /// Whether to fail if an arg is seen multiple times. Defaults to false
218    #[cfg_attr(feature = "derive", serde(default))]
219    pub disable_overrides: bool,
220    /// Whether to fail if any unknown args are given. Defaults to false
221    #[cfg_attr(feature = "derive", serde(default))]
222    pub fail_on_unknown_args: bool,
223    /// List of subcommands underneath this command
224    #[cfg_attr(feature = "derive", serde(default = "default_vec"))]
225    pub subcommands: Vec<Args<'a, T>>,
226    /// handler to invoke when this command has been found.
227    /// This is not called if a subcommand is invoked
228    #[cfg_attr(feature = "derive", serde(skip, bound(deserialize = "T: Handler<'a, T>"), default = "T::handler"))]
229    pub handler: fn(Results<'a>) -> T,
230    /// Filter conditions to restrict the combinations of args this command supports
231    #[cfg_attr(feature = "derive", serde(default))]
232    pub filters: Filters<'a>,
233}
234
235fn name_default<'a>() -> &'a str {
236    env!("CARGO_PKG_NAME")
237}
238
239fn version_default<'a>() -> &'a str {
240    env!("CARGO_PKG_VERSION")
241}
242
243fn about_default<'a>() -> &'a str {
244    env!("CARGO_PKG_DESCRIPTION")
245}
246
247#[cfg(feature = "derive")]
248fn default_vec<'a, T>() -> Vec<Args<'a, T>> {
249    vec![]
250}
251
252pub trait Handler<'a, T> {
253    fn handler() -> fn(Results<'a>) -> T;
254}
255
256// @todo: I don't like having this but we can't have negative constraints :/
257impl<'a, T: Default> Handler<'a, T> for T {
258    fn handler() -> fn(Results<'a>) -> Self {
259        |_| Default::default()
260    }
261}
262
263impl<'a> Handler<'a, Results<'a>> for Results<'a> {
264    fn handler() -> fn(Results<'a>) -> Self {
265        |results| results
266    }
267}
268
269impl<'a, T: Handler<'a, T>> Default for Args<'a, T> {
270    fn default() -> Self {
271        Args {
272            name: name_default(),
273            path: Default::default(),
274            version: version_default(),
275            about: about_default(),
276            args: Default::default(),
277            disable_overrides: Default::default(),
278            fail_on_unknown_args: Default::default(),
279            subcommands: Default::default(),
280            handler: T::handler(),
281            filters: Default::default(),
282        }
283    }
284}
285
286/// Results object returned by arg parsing (or passed to the handler)
287#[derive(Debug, PartialEq)]
288#[cfg_attr(feature = "derive", derive(Deserialize))]
289pub struct Results<'a> {
290    /// the Args struct we matched's path, used to uniquely identify the command/subcommand
291    pub path: &'a str,
292    /// mapping of flags to the number of times they were seen
293    pub flags: HashMap<&'a str, i32>,
294    /// mapping of args to the values seen for them
295    /// if it is specified that the arg should only match one time this will be a single Value,
296    /// otherwise it will be a Value::Array
297    pub args: HashMap<&'a str, Value>,
298    /// list of params seen that were not recognised
299    pub unknown_args: Vec<String>,
300    /// list of positional arguments seen
301    /// note that while in the current implementation these can be interspersed between args,
302    /// e.g --flag positional --flag2 is considered valid
303    /// we do not guarantee that this will be true in future versions
304    pub positional: Vec<String>,
305}
306
307/// Internal type to pass around matched args
308struct Builder<'a> {
309    flags: HashMap<&'a str, i32>,
310    args: HashMap<&'a str, Value>,
311}
312
313pub trait IntoStr {
314    fn into(&self) -> &str;
315}
316
317impl IntoStr for &str {
318    fn into(&self) -> &str {
319        self
320    }
321}
322
323impl IntoStr for String {
324    fn into(&self) -> &str {
325        self.as_str()
326    }
327}
328
329/// this trait is to work around the lack of support for type aliases inside "impl" blocks.
330/// It is not intended to be generic, but to reduce the amount of boilerplate generic code so things are easier to read
331trait ArgsMethods<'a, R, S: IntoStr, T: Iterator<Item = S>> {
332    /// loops until the next string starting with "-" is found in args, and returns everything in between as an array of Values
333    fn get_values_unbounded(&self, arg: &Arg, args: &mut Peekable<T>, est: usize) -> Result<Vec<Value>, Error>;
334    /// same as get_values_unbounded, but with an upper bound on how many iterations we'll do.
335    fn get_values_bounded(&self, arg: &Arg, args: &mut Peekable<T>, est: usize) -> Result<Vec<Value>, Error>;
336    /// given an Arg attempts to extra the relevant number of Values from the input args,
337    /// and inserts them in Builder. If 0 args should be matched it will be inserted as a flag, otherwise it will be as params
338    fn update_values(&'a self, arg: &'a Arg, args: &mut Peekable<T>, out: &mut Builder<'a>) -> Result<(), Error>;
339    /// given a str starting with '-' tries to find a matching arg (using either short ('-') or long ("--") keys).
340    /// if the str is a multi-character short (e.g -rli) this will parse it as a flag combination.
341    /// if a match is found update_values is called
342    fn handle_arg(&'a self, arg: &str, args: &mut Peekable<T>, out: &mut Builder<'a>) -> Result<(), Error>;
343    /// loops through all the args looking for matching Arg definitions
344    fn apply(&'a self, args: T) -> Result<R, Error>;
345}
346
347impl<'a, R, S: IntoStr, T: Iterator<Item = S>> ArgsMethods<'a, R, S, T> for Args<'a, R> {
348    fn get_values_unbounded(&self, arg: &Arg, args: &mut Peekable<T>, est: usize) -> Result<Vec<Value>, Error> {
349        let mut params = Vec::with_capacity(est);
350        while let Some(param) = args.peek() {
351            let param = param.into();
352            if param.starts_with("-") {
353                break;
354            }
355            let val = cast_type(&arg.value_type, param)?;
356            if let Err(e) = (arg.validation)(&val) {
357                debug!("validation failed, reason: {}", e);
358                return Err(Error::InvalidValue(param.to_string(), e));
359            }
360            params.push(val);
361            args.next();
362        }
363        debug!("found params {:?}", params);
364        Ok(params)
365    }
366
367    fn get_values_bounded(&self, arg: &Arg, args: &mut Peekable<T>, est: usize) -> Result<Vec<Value>, Error> {
368        let mut params = Vec::with_capacity(est);
369        while let Some(param) = args.peek() {
370            let param = param.into();
371            if param.starts_with("-") || params.len() == est {
372                break;
373            }
374            let val = cast_type(&arg.value_type, param)?;
375            if let Err(e) = (arg.validation)(&val) {
376                debug!("validation failed, reason: {}", e);
377                return Err(Error::InvalidValue(param.to_string(), e));
378            }
379            params.push(val);
380            args.next();
381        }
382        debug!("found params {:?}", params);
383        Ok(params)
384    }
385
386    fn update_values(&'a self, arg: &'a Arg, args: &mut Peekable<T>, out: &mut Builder<'a>) -> Result<(), Error> {
387        debug!("getting values for {}", arg.name);
388        let value = match arg.num_values {
389            NumValues::Any => Ok(self.get_values_unbounded(arg, args, 0)?.into()),
390            NumValues::AtLeast(i) => {
391                debug!("looking for at x > {} values", i);
392                let params = self.get_values_unbounded(arg, args, i)?;
393                if params.len() >= i {
394                    Ok(params.into())
395                } else {
396                    debug!("too few values found, expected {}, got {}: {:?}", i, params.len(), params);
397                    Err(Error::WrongNumValues(arg.name, &arg.num_values, Value::from(params)))
398                }
399            }
400            NumValues::Between(low, high) => {
401                debug!("looking for value where {} >= x >= {}", high, low);
402                let params = self.get_values_bounded(arg, args, high)?;
403                if params.len() >= low && params.len() <= high {
404                    Ok(params.into())
405                } else {
406                    debug!(
407                        "wrong number of values found, expected {} >= x >= {}, got {}: {:?}",
408                        high,
409                        low,
410                        params.len(),
411                        params
412                    );
413                    Err(Error::WrongNumValues(arg.name, &arg.num_values, Value::from(params)))
414                }
415            }
416            NumValues::Fixed(i) => {
417                debug!("looking for x = {} values", i);
418                let mut params = self.get_values_bounded(arg, args, i)?;
419                if params.len() != i {
420                    debug!("wrong number of found, expected {}, got {}: {:?}", i, params.len(), params);
421                    Err(Error::WrongNumValues(arg.name, &arg.num_values, Value::from(params)))
422                } else if params.len() == 1 {
423                    debug!("single param found, simplifying vec");
424                    Ok(params.pop().unwrap())
425                } else {
426                    Ok(params.into())
427                }
428            }
429            NumValues::None => return self.try_insert_flag(arg.name, out),
430        }?;
431        self.try_insert_param(arg.name, value, out)
432    }
433
434    fn handle_arg(&'a self, arg: &str, args: &mut Peekable<T>, out: &mut Builder<'a>) -> Result<(), Error> {
435        if arg.starts_with("--") {
436            let arg = &arg[2..];
437            debug!("handling long {}", arg);
438            for a in &self.args {
439                if let Some(i) = a.long {
440                    if i == arg {
441                        debug!("found arg {}", a.name);
442                        return self.update_values(a, args, out);
443                    }
444                }
445            }
446            self.handle_arg_missing(arg, out)
447        } else {
448            let arg = &arg[1..];
449            debug!("handling short(s) {}", arg);
450            let mut matches = arg
451                .graphemes(true)
452                // @todo: use Option::contains once it's stable
453                .filter_map(|g| self.args.iter().filter(|a| a.short.is_some()).find(|a| a.short.unwrap() == g));
454            match (matches.next(), matches.next()) {
455                (Some(first), None) => {
456                    debug!("single short found {}, trying to expand", arg);
457                    self.update_values(first, args, out)
458                }
459                (Some(first), Some(second)) => {
460                    debug!("flag combination found {}", arg);
461                    self.try_insert_flag(first.name, out)?;
462                    self.try_insert_flag(second.name, out)?;
463                    for res in matches {
464                        self.try_insert_flag(res.name, out)?;
465                    }
466                    Ok(())
467                }
468                (None, _) => {
469                    debug!("no arg found for {}, looking for default", arg);
470                    self.handle_arg_missing(arg, out)
471                }
472            }
473        }
474    }
475
476    fn apply(&'a self, args: T) -> Result<R, Error> {
477        debug!("parsing args using command {}", self.name);
478        // @todo: I wonder if we can avoid hashing with compile time magic?
479        let mut builder = Builder {
480            args: HashMap::new(),
481            flags: HashMap::new(),
482        };
483        let mut unknown_args = Vec::with_capacity(0);
484        let mut positional = Vec::with_capacity(0);
485        let mut args = args.peekable();
486        while let Some(a) = args.next() {
487            let arg = (&a).into();
488            // an argument :O time to start searching!
489            if arg.starts_with("-") {
490                if arg == "--" {
491                    debug!("found --, treating everything after as positional");
492                    (&mut args).for_each(|a| positional.push((&a).into().to_string()));
493                } else {
494                    debug!("found arg {}", arg);
495                    match self.handle_arg(&arg, &mut args, &mut builder) {
496                        Err(Error::UnknownArg) => unknown_args.push(arg.to_string()),
497                        Err(e) => return Err(e),
498                        _ => {}
499                    }
500                }
501            } else {
502                debug!("found positional arg {}", arg);
503                positional.push(arg.to_string());
504            }
505        }
506        debug!("finished looping through args, running postprocessing");
507        if self.fail_on_unknown_args && !unknown_args.is_empty() {
508            return Err(Error::UnknownArgs(unknown_args));
509        }
510
511        // @todo: is there a way we can make this opt-in?
512        let mut missing = Vec::with_capacity(0);
513        for param in self.args.iter().filter(|p| p.num_values != NumValues::None) {
514            if param.required && !builder.args.contains_key(param.name) {
515                missing.push(param.name);
516            } else if let Some(default) = &param.default {
517                if let Vacant(v) = builder.args.entry(param.name) {
518                    debug!("using default value for {}", param.name);
519                    let val = v.insert(default());
520                    if !check_type(&param.value_type, val) {
521                        return Err(Error::WrongValueType(val.clone()));
522                    }
523                }
524            }
525        }
526        if !missing.is_empty() {
527            debug!("missing required args! {:?}", missing);
528            return Err(Error::MissingRequiredArgs(missing));
529        }
530
531        if !self.filters.filters.is_empty() {
532            let is_ok = match self.filters.filter_type {
533                FilterType::All => self.filters.filters.iter().all(|f| f.test(&builder) == 1),
534                FilterType::Any => self.filters.filters.iter().any(|f| f.test(&builder) == 1),
535            };
536            debug!("filter result: {}, using inverse: {}", is_ok, self.filters.inverse);
537            if is_ok == self.filters.inverse {
538                debug!("filtering failed");
539                return Err(Error::BadInput);
540            }
541        }
542
543        Ok((self.handler)(Results {
544            path: self.path.unwrap_or(self.name),
545            flags: builder.flags,
546            args: builder.args,
547            unknown_args,
548            positional,
549        }))
550    }
551}
552
553impl<'a, R> Args<'a, R> {
554    #[allow(dead_code)]
555    pub fn parse<S: IntoStr, T: IntoIterator<Item = S>>(&'a self, args: T) -> Result<R, Error<'a>> {
556        debug!("starting arg parsing");
557        let mut command = self;
558        let mut args = args.into_iter().skip(1).peekable();
559        while let Some(arg) = args.peek() {
560            let arg = arg.into();
561            debug!("checking if {} is a subcommand of {}", arg, command.name);
562            if let Some(arg) = command.subcommands.iter().find(|&i| i.name == arg) {
563                // now we need to actually take what was peeked
564                args.next();
565                command = arg;
566            } else if arg == "help" {
567                print!("{}", command.generate_help());
568                exit(0);
569            } else if arg == "version" {
570                println!("{} {}", self.path.unwrap_or(self.name), command.version);
571                exit(0)
572            } else {
573                break;
574            }
575        }
576        debug!("applying command {}", command.name);
577        return command.apply(args);
578    }
579
580    /// Generates help text
581    /// Format:
582    /// ${about}
583    ///
584    /// USAGE:
585    ///     ${name} [SUBCOMMAND] [OPTIONS]
586    /// OPTIONS:
587    ///     -V, --version Print version info and exit
588    ///     ${short}, ${long} ${value_name} ${num_values} ${about}
589    ///     -h, --help Prints help information
590    /// SUBCOMMANDS (use ${name} ${subcommand} ${help} for more details):
591    ///     ${name} - ${about}
592    ///
593    /// @todo: currently value_name & num_values are not added
594    fn generate_help(&self) -> String {
595        let mut help = format!(
596            "{}\nUSAGE:\n\t{} [SUBCOMMAND] [OPTIONS]\nOPTIONS:\n",
597            self.about,
598            self.path.unwrap_or(self.name)
599        );
600        help.push_str("\t-V, --version Print version info and exit\n");
601        if !self.args.is_empty() {
602            help = self.args.iter().fold(help, |mut opt, arg| {
603                let has_comma = if arg.short.is_some() && arg.long.is_some() { 1 } else { 0 };
604                let size = arg.short.map_or(0, |s| s.len() + 2)
605                    + arg.long.map_or(0, |s| s.len() + 3)
606                    + has_comma
607                    + arg.value_name.map_or(0, |s| s.len() + 1)
608                    + arg.about.len();
609                opt.reserve(size + 2);
610                opt.push_str("\t");
611                if let Some(s) = arg.short {
612                    opt.push('-');
613                    opt.push_str(s);
614                    if has_comma == 1 {
615                        opt.push(',');
616                    }
617                    opt.push(' ');
618                }
619                if let Some(s) = arg.long {
620                    opt.push_str("--");
621                    opt.push_str(s);
622                    opt.push(' ');
623                }
624                if let Some(s) = arg.value_name {
625                    opt.push_str(s);
626                    opt.push(' ');
627                }
628                opt.push_str(arg.about);
629                opt.push('\n');
630                opt
631            });
632        }
633        help.push_str("\t-h, --help Prints help information\n");
634        if !self.subcommands.is_empty() {
635            help.push_str("SUBCOMMANDS (use ");
636            help.push_str(self.name);
637            help.push_str(" [SUBCOMMAND] help for more details):\n");
638            help = self.subcommands.iter().fold(help, |mut opt, arg| {
639                let size = arg.name.len() + 1 + arg.about.len();
640                opt.reserve(size + 1);
641                opt.push_str(arg.name);
642                opt.push(' ');
643                opt.push_str(arg.about);
644                opt.push('\n');
645                opt
646            });
647        }
648        help
649    }
650
651    /// handles inserting or updating params in the builder. Note that just now updating is done by replacement
652    /// in future revisions this may be replaced with appending
653    fn try_insert_param(&self, key: &'a str, value: Value, out: &mut Builder<'a>) -> Result<(), Error> {
654        if !self.disable_overrides {
655            out.args.insert(key, value);
656            return Ok(());
657        }
658        match out.args.entry(key) {
659            Occupied(_) => Err(Error::Override(key)),
660            Vacant(e) => {
661                e.insert(value);
662                Ok(())
663            }
664        }
665    }
666
667    /// handles inserting or updating flags in the builder
668    fn try_insert_flag(&self, key: &'a str, out: &mut Builder<'a>) -> Result<(), Error> {
669        debug!("inserting flag {}", key);
670        match out.flags.entry(key) {
671            Occupied(mut e) => {
672                if self.disable_overrides {
673                    Err(Error::Override(key))
674                } else {
675                    debug!("incrementing count {}", e.get());
676                    e.insert(e.get() + 1);
677                    Ok(())
678                }
679            }
680            Vacant(e) => {
681                debug!("new flag found");
682                e.insert(1);
683                Ok(())
684            }
685        }
686    }
687
688    /// Attempts to fallback to library defined args (e.g help, version) if no match is found
689    /// note that because this is only called if matching is failed, it's safe to override by simply defining your own arg
690    /// These fallbacks will early exit, as is expected from --help --version commands
691    fn handle_arg_missing(&self, arg: &str, _: &mut Builder<'a>) -> Result<(), Error> {
692        match arg {
693            "help" | "h" => {
694                print!("{}", self.generate_help());
695                exit(0);
696            }
697            "version" | "V" => {
698                println!("{} {}", self.path.unwrap_or(self.name), self.version);
699                exit(0);
700            }
701            _ => Err(Error::UnknownArg),
702        }
703    }
704}
705
706/// Small macro to avoid writing ..Default::default everywhere
707#[cfg(feature = "macros")]
708#[macro_export]
709macro_rules! args {
710    ($($key:ident : $value:expr),* $(,)?) => {
711        Args {
712            $($key: $value),+,
713            ..Default::default()
714        }
715    }
716}
717
718/// Small macro to avoid writing ..Default::default everywhere
719#[cfg(feature = "macros")]
720#[macro_export]
721macro_rules! arg {
722    ($($key:ident : $value:expr),* $(,)?) => {
723        Arg {
724            $($key: $value),+,
725            ..Default::default()
726        }
727    }
728}
729
730#[cfg(test)]
731mod tests {
732    use std::{collections::HashMap, convert::TryInto, sync::Once};
733
734    use crate::{value::Type, Arg, Args, Error, Filter, FilterType, Filters, NumValues, Results, Value};
735    use pretty_assertions::assert_eq;
736    use simple_logger::SimpleLogger;
737
738    static INIT: Once = Once::new();
739
740    macro_rules! test {
741        ($name:ident() $block:block) => {
742            #[test]
743            fn $name() {
744                INIT.call_once(|| {
745                    SimpleLogger::new().init().unwrap();
746                });
747                $block
748            }
749        };
750    }
751
752    macro_rules! assert_has {
753        ($expected:expr, $results:ident, $key:literal) => {
754            if let Some(arg) = $results.args.get($key) {
755                assert_eq!(Ok($expected), arg.try_into());
756                1
757            } else {
758                0
759            }
760        };
761    }
762
763    test!(test_returning_results() {
764        let args = args! {
765            subcommands: vec![args! {
766                name: "sub",
767                path: Some("main/sub"),
768                args: vec![arg! {
769                    name: "arg",
770                    long: Some("arg"),
771                    num_values: NumValues::None,
772                }],
773            }],
774        };
775        let mut flags = HashMap::with_capacity(1);
776        flags.insert("arg", 1);
777
778        assert_eq!(Ok(Results {
779            path: "main/sub",
780            flags,
781            args: HashMap::new(),
782            unknown_args: vec!["-u".to_string()],
783            positional: vec!["lol".to_string()],
784        }), args.parse(vec!["prog", "sub", "--arg", "lol", "-u"]));
785    });
786
787    test!(test_flag() {
788        let args = args! {
789            args: vec![arg! {
790                name: "arg",
791                long: Some("arg"),
792                num_values: NumValues::None,
793            }],
794            handler: |r| if r.flags.contains_key("arg") { 1 } else { 0 },
795        };
796        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg"]));
797    });
798
799    test!(test_arg_with_fixed_num_values_fails_on_none() {
800        let args = args! {
801            args: vec![arg! {
802                name: "arg",
803                long: Some("arg"),
804                num_values: NumValues::Fixed(1),
805            }],
806            handler: |r| if r.args.contains_key("arg") { 1 } else { 0 },
807        };
808        assert_eq!(
809            Err(Error::WrongNumValues("arg", &NumValues::Fixed(1), Value::from(vec![]))),
810            args.parse(vec!["prog", "--arg"])
811        );
812    });
813
814    test!(test_arg_with_fixed_num_values() {
815        let args = args! {
816            args: vec![arg! {
817                name: "arg",
818                long: Some("arg"),
819                num_values: NumValues::Fixed(1),
820            }],
821            handler: |r| assert_has!("lol", r, "arg"),
822        };
823        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "lol"]));
824    });
825
826    test!(test_arg_with_any_values_none() {
827        let args = args! {
828            args: vec![arg! {
829                name: "arg",
830                long: Some("arg"),
831                num_values: NumValues::Any,
832            }],
833            handler: |r| assert_has!(&vec![], r, "arg"),
834        };
835        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg"]));
836    });
837
838    test!(test_arg_with_any_values_single() {
839        let args = args! {
840            args: vec![arg! {
841                name: "arg",
842                long: Some("arg"),
843                num_values: NumValues::Any,
844            }],
845            handler: |r| assert_has!(vec!["lol"], r, "arg"),
846        };
847        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "lol"]));
848    });
849
850    test!(test_arg_with_any_values_multiple() {
851        let args = args! {
852            args: vec![arg! {
853                name: "arg",
854                long: Some("arg"),
855                num_values: NumValues::Any,
856            }],
857            handler: |r| assert_has!(vec!["lol", "lol2"], r, "arg"),
858        };
859        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "lol", "lol2"]));
860    });
861
862    test!(test_arg_with_fixed_num_values_too_many_values() {
863        let args = args! {
864            args: vec![arg! {
865                name: "arg",
866                long: Some("arg"),
867                num_values: NumValues::Fixed(1),
868            }],
869            handler: |r| assert_has!("lol", r, "arg") == 1 && r.positional.first().filter(|i| i.as_str() == "no").is_some(),
870        };
871        assert_eq!(Ok(true), args.parse(vec!["prog", "--arg", "lol", "no"]));
872    });
873
874    test!(test_arg_with_fixed_num_values_and_other_args() {
875        let args = args! {
876            args: vec![arg! {
877                name: "arg",
878                long: Some("arg"),
879                num_values: NumValues::Fixed(1),
880            }],
881            handler: |r| assert_has!("lol", r, "arg"),
882        };
883        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "lol", "--arg2"]));
884    });
885
886    test!(test_arg_with_fixed_num_values_and_other_args_double_dash() {
887        let args = args! {
888            args: vec![arg! {
889                name: "arg",
890                long: Some("arg"),
891                num_values: NumValues::Fixed(1),
892            }],
893            handler: |r| assert_has!("lol", r, "arg"),
894        };
895        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "lol", "--arg2"]));
896    });
897
898    test!(test_multiple_args() {
899        let args = args! {
900            args: vec![
901                arg! {
902                    name: "arg",
903                    long: Some("arg"),
904                    num_values: NumValues::Fixed(2),
905                },
906                arg! {
907                    name: "arg2",
908                    long: Some("arg2"),
909                    num_values: NumValues::Any,
910                },
911            ],
912            handler: |r| match assert_has!(vec!["1", "2"], r, "arg") {
913                1 => assert_has!(&vec![], r, "arg2"),
914                _ => 0,
915            },
916        };
917        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "1", "2", "--arg2"]));
918    });
919
920    test!(test_missing_arg() {
921        let args = args! {
922            args: vec![arg! {
923                name: "arg",
924                long: Some("arg"),
925                num_values: NumValues::None,
926            }],
927            handler: |r| if r.args.contains_key("arg") { 1 } else { 0 },
928        };
929        assert_eq!(Ok(0), args.parse(vec!["prog"]));
930    });
931
932    test!(test_sub_command_not_called() {
933        let args = args! {
934            subcommands: vec![args! {
935                name: "sub",
936            }],
937            handler: |_| 1,
938        };
939        assert_eq!(Ok(1), args.parse(vec!["prog"]));
940    });
941
942    test!(test_sub_commands() {
943        let args = args! {
944            subcommands: vec![args! {
945                name: "sub",
946                subcommands: vec![args! {
947                    name: "sub",
948                    handler: |_| 1,
949                }],
950            }],
951        };
952        assert_eq!(Ok(1), args.parse(vec!["prog", "sub", "sub"]));
953    });
954
955    test!(test_default_arg() {
956        let args = args! {
957            args: vec![arg! {
958                name: "arg",
959                long: Some("arg"),
960                default: Some(|| "lol".into()),
961            }],
962            handler: |r| assert_has!("lol", r, "arg"),
963        };
964        assert_eq!(Ok(1), args.parse(vec!["prog"]));
965    });
966
967    test!(test_required_arg_missing() {
968        let args: Args<()> = args! {
969            args: vec![arg! {
970                name: "arg",
971                long: Some("arg"),
972                required: true,
973                num_values: NumValues::Fixed(1),
974            }],
975        };
976        assert_eq!(Err(Error::MissingRequiredArgs(vec!["arg"])), args.parse(vec!["prog"]));
977    });
978
979    test!(test_required_arg() {
980        let args = args! {
981            args: vec![arg! {
982                name: "arg",
983                long: Some("arg"),
984                required: true,
985                num_values: NumValues::Fixed(1),
986            }],
987            handler: |r| assert_has!("lol", r, "arg"),
988        };
989        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "lol"]));
990    });
991
992    test!(test_wrong_type() {
993        let args = args! {
994            args: vec![arg! {
995                name: "arg",
996                long: Some("arg"),
997                value_type: Type::Bool,
998                num_values: NumValues::Fixed(1),
999            }],
1000            handler: |r| assert_has!("lol", r, "arg"),
1001        };
1002        assert_eq!(Err(Error::WrongCastType("lol".to_owned())), args.parse(vec!["prog", "--arg", "lol"]));
1003    });
1004
1005    test!(test_right_type_bool() {
1006        let args = args! {
1007            args: vec![arg! {
1008                name: "arg",
1009                long: Some("arg"),
1010                value_type: Type::Bool,
1011                num_values: NumValues::Fixed(1),
1012            }],
1013            handler: |r| assert_has!(&true, r, "arg"),
1014        };
1015        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "true"]));
1016    });
1017
1018    test!(test_right_type_int() {
1019        let args = args! {
1020            args: vec![arg! {
1021                name: "arg",
1022                long: Some("arg"),
1023                value_type: Type::Int,
1024                num_values: NumValues::Fixed(1),
1025            }],
1026            handler: |r| assert_has!(&3, r, "arg"),
1027        };
1028        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "3"]));
1029    });
1030
1031    test!(test_right_type_long() {
1032        let args = args! {
1033            args: vec![arg! {
1034                name: "arg",
1035                long: Some("arg"),
1036                value_type: Type::Long,
1037                num_values: NumValues::Fixed(1),
1038            }],
1039            handler: |r| assert_has!(&i64::max_value(), r, "arg"),
1040        };
1041        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", i64::max_value().to_string().as_str()]));
1042    });
1043
1044    test!(test_right_type_float() {
1045        let args = args! {
1046            args: vec![arg! {
1047                name: "arg",
1048                long: Some("arg"),
1049                value_type: Type::Float,
1050                num_values: NumValues::Fixed(1),
1051            }],
1052            handler: |r| assert_has!(&f32::MAX, r, "arg"),
1053        };
1054        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", f32::MAX.to_string().as_str()]));
1055    });
1056
1057    test!(test_right_type_double() {
1058        let args = args! {
1059            args: vec![arg! {
1060                name: "arg",
1061                long: Some("arg"),
1062                value_type: Type::Double,
1063                num_values: NumValues::Fixed(1),
1064            }],
1065            handler: |r| assert_has!(&f64::MAX, r, "arg"),
1066        };
1067        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", f64::MAX.to_string().as_str()]));
1068    });
1069
1070    test!(test_right_type_string() {
1071        let args = args! {
1072            args: vec![arg! {
1073                name: "arg",
1074                long: Some("arg"),
1075                value_type: Type::String,
1076                num_values: NumValues::Fixed(1),
1077            }],
1078            handler: |r| assert_has!("woop", r, "arg"),
1079        };
1080        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "woop"]));
1081    });
1082
1083    test!(test_right_type_array() {
1084        let args = args! {
1085            args: vec![arg! {
1086                name: "arg",
1087                long: Some("arg"),
1088                value_type: Type::Array(Box::from(Type::Int)),
1089                num_values: NumValues::Any
1090            }],
1091            handler: |r| assert_has!(vec![&23, &32], r, "arg"),
1092        };
1093        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "23", "32"]));
1094    });
1095
1096    test!(test_right_type_array_single() {
1097        let args = args! {
1098            args: vec![arg! {
1099                name: "arg",
1100                long: Some("arg"),
1101                value_type: Type::Array(Box::from(Type::Int)),
1102                num_values: NumValues::Any
1103            }],
1104            handler: |r| assert_has!(vec![&23], r, "arg"),
1105        };
1106        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "23"]));
1107    });
1108
1109    test!(test_wrong_type_array() {
1110        let args = args! {
1111            args: vec![arg! {
1112                name: "arg",
1113                long: Some("arg"),
1114                value_type: Type::Array(Box::from(Type::Int)),
1115            }],
1116            handler: |r| assert_has!(vec![&23], r, "arg"),
1117        };
1118        assert_eq!(Err(Error::WrongCastType("true".to_owned())), args.parse(vec!["prog", "--arg", "true"]));
1119    });
1120
1121    test!(test_default_returns_wrong_type() {
1122        let args: Args<()> = args! {
1123            args: vec![arg! {
1124                name: "arg",
1125                long: Some("arg"),
1126                value_type: Type::Int,
1127                default: Some(|| "lol".into()),
1128            }],
1129        };
1130        assert_eq!(Err(Error::WrongValueType("lol".into())), args.parse(vec!["prog"]));
1131    });
1132
1133    test!(test_property() {
1134        let args = args! {
1135            handler: |r| r.positional.first().filter(|i| i.as_str() == "prop").is_some(),
1136        };
1137        assert_eq!(Ok(true), args.parse(vec!["prog", "prop"]));
1138    });
1139
1140    test!(test_property_after_arg() {
1141        let args = args! {
1142            args: vec![arg! {
1143                name: "arg",
1144                long: Some("arg"),
1145                num_values: NumValues::None,
1146            }],
1147            handler: |r| r.positional.first().filter(|i| i.as_str() == "prop").is_some(),
1148        };
1149        assert_eq!(Ok(true), args.parse(vec!["prog", "--arg", "prop"]));
1150    });
1151
1152    test!(test_long_arg_ignores_single_dash() {
1153        let args = args! {
1154            args: vec![arg! {
1155                name: "arg",
1156                long: Some("arg"),
1157                num_values: NumValues::None,
1158            }],
1159            handler: |r| !r.args.contains_key("arg"),
1160        };
1161        assert_eq!(Ok(true), args.parse(vec!["prog", "-arg"]));
1162    });
1163
1164    test!(test_short_arg_ignores_mario_kart_double_dash() {
1165        let args = args! {
1166            args: vec![arg! {
1167                name: "arg",
1168                short: Some("a"),
1169                num_values: NumValues::None,
1170            }],
1171            handler: |r| !r.flags.contains_key("arg"),
1172        };
1173        assert_eq!(Ok(true), args.parse(vec!["prog", "--a"]));
1174    });
1175
1176    test!(test_short_arg() {
1177        let args = args! {
1178            args: vec![arg! {
1179                name: "arg",
1180                short: Some("a"),
1181                num_values: NumValues::None,
1182            }],
1183            handler: |r| r.flags.contains_key("arg"),
1184        };
1185        assert_eq!(Ok(true), args.parse(vec!["prog", "-a"]));
1186    });
1187
1188    test!(test_unicode_short_arg() {
1189        let args = args! {
1190            args: vec![arg! {
1191                name: "arg",
1192                short: Some("Ẩ"),
1193                num_values: NumValues::None,
1194            }],
1195            handler: |r| r.flags.contains_key("arg"),
1196        };
1197        assert_eq!(Ok(true), args.parse(vec!["prog", "-Ẩ"]));
1198    });
1199
1200    test!(test_unicode_short_arg_no_match() {
1201        let args = args! {
1202            args: vec![arg! {
1203                name: "arg",
1204                short: Some("A"),
1205                num_values: NumValues::None,
1206            }],
1207            handler: |r| r.flags.contains_key("arg"),
1208        };
1209        assert_eq!(Ok(false), args.parse(vec!["prog", "-Ẩ"]));
1210    });
1211
1212    test!(test_combinations() {
1213        let args = args! {
1214            args: vec![arg! {
1215                name: "arg",
1216                short: Some("Ẩ"),
1217                num_values: NumValues::None,
1218            }, arg! {
1219                name: "arg2",
1220                short: Some("A"),
1221                num_values: NumValues::None,
1222            }],
1223            handler: |r| r.flags.contains_key("arg") && r.flags.contains_key("arg2"),
1224        };
1225        assert_eq!(Ok(true), args.parse(vec!["prog", "-ẨA"]));
1226    });
1227
1228    test!(test_flag_repeats() {
1229        let args = args! {
1230            args: vec![arg! {
1231                name: "arg",
1232                long: Some("arg"),
1233                short: Some("A"),
1234                num_values: NumValues::None,
1235            }],
1236            handler: |r| r.flags["arg"],
1237        };
1238        assert_eq!(Ok(4), args.parse(vec!["prog", "-AA", "--arg", "-A"]));
1239    });
1240
1241    test!(test_positional_after_double_dash() {
1242        let args = args! {
1243            args: vec![arg! {
1244                name: "arg",
1245                short: Some("a"),
1246                num_values: NumValues::None,
1247            }, arg! {
1248                name: "arg2",
1249                short: Some("b"),
1250                num_values: NumValues::None,
1251            }],
1252            handler: |r| r.flags.contains_key("arg2") && !r.flags.contains_key("arg") && r.positional.first().filter(|f| f.as_str() == "-a").is_some(),
1253        };
1254        assert_eq!(Ok(true), args.parse(vec!["prog", "-b", "--", "-a"]));
1255    });
1256
1257    test!(test_sub_commands_after_arg_is_not_called() {
1258        let args = args! {
1259            subcommands: vec![args! {
1260                name: "sub",
1261                handler: |_| 1,
1262            }],
1263            handler: |_| 0,
1264        };
1265        assert_eq!(Ok(0), args.parse(vec!["prog", "-arg", "sub"]));
1266    });
1267
1268    test!(test_validation() {
1269        let args = args! {
1270            args: vec![arg! {
1271                name: "arg",
1272                short: Some("a"),
1273                num_values: NumValues::Fixed(1),
1274                validation: |v| {
1275                    let s: String = v.into();
1276                    if "abc" == s.as_str() {
1277                        Ok(())
1278                    } else {
1279                        Err("oh noes".to_string())
1280                    }
1281                },
1282            }],
1283            handler: |r| r.args.contains_key("arg"),
1284        };
1285        assert_eq!(Ok(true), args.parse(vec!["prog", "-a", "abc"]));
1286    });
1287
1288    test!(test_validation_vec() {
1289        let args = args! {
1290            args: vec![arg! {
1291                name: "arg",
1292                short: Some("a"),
1293                num_values: NumValues::AtLeast(1),
1294                value_type: Type::Int,
1295                validation: |v| {
1296                    let s: &i32 = v.try_into().unwrap();
1297                    if 2 >= *s {
1298                        Ok(())
1299                    } else {
1300                        Err("oh noes".to_string())
1301                    }
1302                },
1303            }],
1304            handler: |r| assert_has!(vec![&1, &2], r, "arg"),
1305        };
1306        assert_eq!(Ok(1), args.parse(vec!["prog", "-a", "1", "2"]));
1307    });
1308
1309    test!(test_validation_unbounded() {
1310        let args = args! {
1311            args: vec![arg! {
1312                name: "arg",
1313                short: Some("a"),
1314                num_values: NumValues::Any,
1315                validation: |v| {
1316                    let s: String = v.into();
1317                    if "abc" == s.as_str() {
1318                        Ok(())
1319                    } else {
1320                        Err("oh noes".to_string())
1321                    }
1322                },
1323            }],
1324            handler: |r| r.args.contains_key("arg"),
1325        };
1326        assert_eq!(Ok(true), args.parse(vec!["prog", "-a", "abc"]));
1327    });
1328
1329    test!(test_validation_fails() {
1330        let args = args! {
1331            args: vec![arg! {
1332                name: "arg",
1333                short: Some("a"),
1334                num_values: NumValues::Fixed(1),
1335                validation: |v| {
1336                    let s: String = v.into();
1337                    if "abc" == s.as_str() {
1338                        Ok(())
1339                    } else {
1340                        Err("oh noes".to_string())
1341                    }
1342                },
1343            }],
1344            handler: |r| !r.args.contains_key("arg"),
1345        };
1346        assert_eq!(Err(Error::InvalidValue("abcdef".to_string(), "oh noes".to_string())),
1347        args.parse(vec!["prog", "-a", "abcdef"]));
1348    });
1349
1350    test!(test_validation_fails_unbounded() {
1351        let args = args! {
1352            args: vec![arg! {
1353                name: "arg",
1354                short: Some("a"),
1355                num_values: NumValues::Any,
1356                validation: |v| {
1357                    let s: String = v.into();
1358                    if "abc" == s.as_str() {
1359                        Ok(())
1360                    } else {
1361                        Err("oh noes".to_string())
1362                    }
1363                },
1364            }],
1365            handler: |r| !r.args.contains_key("arg"),
1366        };
1367        assert_eq!(Err(Error::InvalidValue("abcdef".to_string(), "oh noes".to_string())),
1368        args.parse(vec!["prog", "-a", "abcdef"]));
1369    });
1370
1371    test!(test_fail_duplicate_arg() {
1372        let args: Args<()> = args! {
1373            args: vec![arg! {
1374                name: "arg",
1375                short: Some("a"),
1376                long: Some("arg"),
1377                num_values: NumValues::None,
1378            }],
1379            disable_overrides: true,
1380        };
1381        assert_eq!(Err(Error::Override("arg")), args.parse(vec!["prog", "-a", "--arg"]));
1382    });
1383
1384    test!(test_simple_and_filter() {
1385        let args: Args<()> = args! {
1386            args: vec![arg! {
1387                name: "arg",
1388                short: Some("a"),
1389                num_values: NumValues::None,
1390            }, arg! {
1391                name: "arg2",
1392                long: Some("arg"),
1393                num_values: NumValues::None,
1394            }],
1395            filters: Filters {
1396                filters: vec![Filter {
1397                    filter_type: FilterType::All,
1398                    inverse: false,
1399                    args: vec!["arg", "arg2"],
1400                }],
1401                ..Default::default()
1402            },
1403            disable_overrides: true,
1404        };
1405        assert_eq!(Ok(()), args.parse(vec!["prog", "-a", "--arg"]));
1406    });
1407
1408    test!(test_simple_and_filter_fails() {
1409        let args: Args<()> = args! {
1410            args: vec![arg! {
1411                name: "arg",
1412                short: Some("a"),
1413                num_values: NumValues::None,
1414            }, arg! {
1415                name: "arg2",
1416                long: Some("arg"),
1417                num_values: NumValues::None,
1418            }],
1419            filters: Filters {
1420                filters: vec![Filter {
1421                    filter_type: FilterType::All,
1422                    inverse: false,
1423                    args: vec!["arg", "arg2"],
1424                }],
1425                ..Default::default()
1426            },
1427            disable_overrides: true,
1428        };
1429        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "-a"]));
1430    });
1431
1432    test!(test_simple_or_filter() {
1433        let args: Args<()> = args! {
1434            args: vec![arg! {
1435                name: "arg",
1436                short: Some("a"),
1437                num_values: NumValues::None,
1438            }, arg! {
1439                name: "arg2",
1440                long: Some("arg"),
1441                num_values: NumValues::None,
1442            }],
1443            filters: Filters {
1444                filters: vec![Filter {
1445                    filter_type: FilterType::All,
1446                    inverse: true,
1447                    args: vec!["arg", "arg2"],
1448                }],
1449                ..Default::default()
1450            },
1451            disable_overrides: true,
1452        };
1453        assert_eq!(Ok(()), args.parse(vec!["prog", "-a"]));
1454    });
1455
1456    test!(test_simple_or_filter_fails() {
1457        let args: Args<()> = args! {
1458            args: vec![arg! {
1459                name: "arg",
1460                short: Some("a"),
1461                num_values: NumValues::None,
1462            }, arg! {
1463                name: "arg2",
1464                long: Some("arg"),
1465                num_values: NumValues::None,
1466            }],
1467            filters: Filters {
1468                filters: vec![Filter {
1469                    filter_type: FilterType::All,
1470                    inverse: true,
1471                    args: vec!["arg", "arg2"],
1472                }],
1473                ..Default::default()
1474            },
1475            disable_overrides: true,
1476        };
1477        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "-a", "--arg"]));
1478    });
1479
1480    test!(test_multiple_filters_any() {
1481        let args: Args<()> = args! {
1482            args: vec![arg! {
1483                name: "arg",
1484                short: Some("a"),
1485                num_values: NumValues::None,
1486            }, arg! {
1487                name: "arg2",
1488                long: Some("arg"),
1489                num_values: NumValues::None,
1490            }, arg! {
1491                name: "arg3",
1492                long: Some("arg3"),
1493                num_values: NumValues::None,
1494            }],
1495            filters: Filters {
1496                filters: vec![Filter {
1497                    filter_type: FilterType::All,
1498                    inverse: false,
1499                    args: vec!["arg", "arg2"],
1500                }, Filter {
1501                    filter_type: FilterType::All,
1502                    inverse: false,
1503                    args: vec!["arg", "arg3"],
1504                }],
1505                ..Default::default()
1506            },
1507            disable_overrides: true,
1508        };
1509        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "-a"]));
1510        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg"]));
1511        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg3"]));
1512        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg", "--arg3"]));
1513        assert_eq!(Ok(()), args.parse(vec!["prog", "-a", "--arg"]));
1514        assert_eq!(Ok(()), args.parse(vec!["prog", "-a", "--arg3"]));
1515        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg", "-a"]));
1516        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg3", "-a"]));
1517        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg3", "-a", "--arg"]));
1518    });
1519
1520    test!(test_multiple_filters_all() {
1521        let args: Args<()> = args! {
1522            args: vec![arg! {
1523                name: "arg",
1524                short: Some("a"),
1525                num_values: NumValues::None,
1526            }, arg! {
1527                name: "arg2",
1528                long: Some("arg"),
1529                num_values: NumValues::None,
1530            }, arg! {
1531                name: "arg3",
1532                long: Some("arg3"),
1533                num_values: NumValues::None,
1534            }],
1535            filters: Filters {
1536                filters: vec![Filter {
1537                    filter_type: FilterType::All,
1538                    inverse: false,
1539                    args: vec!["arg", "arg2"],
1540                }, Filter {
1541                    filter_type: FilterType::All,
1542                    inverse: false,
1543                    args: vec!["arg", "arg3"],
1544                }],
1545                filter_type: FilterType::All,
1546                ..Default::default()
1547            },
1548            disable_overrides: true,
1549        };
1550        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "-a"]));
1551        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg"]));
1552        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg3"]));
1553        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg", "--arg3"]));
1554        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "-a", "--arg"]));
1555        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "-a", "--arg3"]));
1556        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg", "-a"]));
1557        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg3", "-a"]));
1558        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg3", "-a", "--arg"]));
1559    });
1560
1561    test!(test_multiple_filters_all_not() {
1562        let args: Args<()> = args! {
1563            args: vec![arg! {
1564                name: "arg",
1565                short: Some("a"),
1566                num_values: NumValues::None,
1567            }, arg! {
1568                name: "arg2",
1569                long: Some("arg"),
1570                num_values: NumValues::None,
1571            }, arg! {
1572                name: "arg3",
1573                long: Some("arg3"),
1574                num_values: NumValues::None,
1575            }],
1576            filters: Filters {
1577                filters: vec![Filter {
1578                    filter_type: FilterType::All,
1579                    inverse: false,
1580                    args: vec!["arg", "arg2"],
1581                }, Filter {
1582                    filter_type: FilterType::All,
1583                    inverse: false,
1584                    args: vec!["arg", "arg3"],
1585                }],
1586                filter_type: FilterType::All,
1587                inverse: true
1588            },
1589            disable_overrides: true,
1590        };
1591        assert_eq!(Ok(()), args.parse(vec!["prog", "-a"]));
1592        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg"]));
1593        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg3"]));
1594        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg", "--arg3"]));
1595        assert_eq!(Ok(()), args.parse(vec!["prog", "-a", "--arg"]));
1596        assert_eq!(Ok(()), args.parse(vec!["prog", "-a", "--arg3"]));
1597        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg", "-a"]));
1598        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg3", "-a"]));
1599        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg3", "-a", "--arg"]));
1600    });
1601
1602    test!(test_multiple_filters_any_not() {
1603        let args: Args<()> = args! {
1604            args: vec![arg! {
1605                name: "arg",
1606                short: Some("a"),
1607                num_values: NumValues::None,
1608            }, arg! {
1609                name: "arg2",
1610                long: Some("arg"),
1611                num_values: NumValues::None,
1612            }, arg! {
1613                name: "arg3",
1614                long: Some("arg3"),
1615                num_values: NumValues::None,
1616            }],
1617            filters: Filters {
1618                filters: vec![Filter {
1619                    filter_type: FilterType::All,
1620                    inverse: false,
1621                    args: vec!["arg", "arg2"],
1622                }, Filter {
1623                    filter_type: FilterType::All,
1624                    inverse: false,
1625                    args: vec!["arg", "arg3"],
1626                }],
1627                filter_type: FilterType::Any,
1628                inverse: true
1629            },
1630            disable_overrides: true,
1631        };
1632        assert_eq!(Ok(()), args.parse(vec!["prog", "-a"]));
1633        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg"]));
1634        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg3"]));
1635        assert_eq!(Ok(()), args.parse(vec!["prog", "--arg", "--arg3"]));
1636        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "-a", "--arg"]));
1637        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "-a", "--arg3"]));
1638        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg", "-a"]));
1639        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg3", "-a"]));
1640        assert_eq!(Err(Error::BadInput), args.parse(vec!["prog", "--arg3", "-a", "--arg"]));
1641    });
1642
1643    test!(test_multiple_values_split_unsupported() {
1644        let args = args! {
1645            args: vec![arg! {
1646                name: "arg",
1647                long: Some("arg"),
1648                num_values: NumValues::Fixed(2),
1649            }],
1650            handler: |r| assert_has!(vec!["1", "2"], r, "arg"),
1651        };
1652        assert_eq!(Err(Error::WrongNumValues("arg", &NumValues::Fixed(2), Value::Array(vec![Value::String("1".to_string())]))),
1653            args.parse(vec!["prog", "--arg", "1", "--arg", "2"]));
1654    });
1655
1656    test!(test_fail_on_unknown_args() {
1657        let args = args! {
1658            args: vec![arg! {
1659                name: "arg",
1660                long: Some("arg"),
1661                num_values: NumValues::Fixed(2),
1662            }],
1663            fail_on_unknown_args: true,
1664            handler: |r| assert_has!(vec!["1", "2"], r, "arg"),
1665        };
1666        assert_eq!(Ok(1), args.parse(vec!["prog", "--arg", "1", "2"]));
1667        assert_eq!(Err(Error::UnknownArgs(vec!["-u".to_string()])), args.parse(vec!["prog", "--arg", "1", "2", "-u"]));
1668        assert_eq!(Err(Error::UnknownArgs(vec!["--u".to_string()])), args.parse(vec!["prog", "--arg", "1", "2", "--u"]));
1669        assert_eq!(Err(Error::UnknownArgs(vec!["-u".to_string(), "--u".to_string()])), args.parse(vec!["prog", "-u", "--arg", "1", "2", "--u"]));
1670    });
1671}