rags_rs/lib.rs
1//! # Introduction
2//!
3//! `rags` is an easy to use argument parsing library that provides pretty help-printing.
4//!
5//! The library allows defining arguments in the same tree-like manner that users
6//! and developers expect. This leads to efficient parsing as we can efficiently
7//! eliminate work based on the state of the parsing. Once an argument has been
8//! matched it will never be inspected again.
9//!
10//! `rags` also makes liberal use of the `From<String>` trait so that arguments
11//! can be parsed into any complex type. This means, for example, that an argument
12//! naming a file can be constructed directly into a struct wrapping `std::fs::File`.
13//! This leads to re-usable code between subcommands and developers can spend less time
14//! and effort inspecting args.
15//!
16//! Arguments in the same level (it's tree-like) are parsed in the order in which
17//! they are defined. This means that global args are easy and provides both
18//! argument and semantic isolation between subcommands. Once a branch in the parse
19//! tree is taken (subcommand), the parser will not consider arguments defined "after"
20//! that branch in a higher scope. Because nested subcommands always lead to a lower
21//! scope, all arguments along that parse path are considered. This leads to 2 basic
22//! rules of usage:
23//!
24//! 1. global arguments should always be declared first
25//! 2. positional arguments should be defined within a subcommand scope even if shared
26//!    betwwen subcommands
27//!
28//!
29//!
30//! # Example Usage
31//!
32//! Below is an example of usage that tries to capture most features an concepts.
33//! While this had to be edited to match Rust's doctest requirements, the examples
34//! directory contains examples which follow best practices in a "real" application
35//! such as defining descriptions as static, not returning errors from `main`, etc.
36//!
37//! ```rust
38//! extern crate rags_rs as rags;
39//!
40//! #[derive(Debug)]
41//! pub struct Options {
42//!     debug: bool,
43//!     verbosity: usize,
44//!
45//!     subcmds: Vec<String>,
46//!
47//!     build_release: bool,
48//!     build_link: Vec<String>,
49//!     package: String,
50//!
51//!     dry_run: bool,
52//!
53//!     initial_file: String,
54//!     additional_files: Vec<String>,
55//! }
56//! impl Default for Options {
57//!     fn default() -> Options {
58//!         Options {
59//!             debug: false,
60//!             verbosity: 0,
61//!
62//!             subcmds: vec!(),
63//!
64//!             build_release: false,
65//!             build_link: vec!(),
66//!             package: "main".to_string(),
67//!
68//!             dry_run: false,
69//!
70//!             initial_file: "".to_string(),
71//!             additional_files: vec!(),
72//!         }
73//!     }
74//! }
75//!
76//! fn main() -> Result<(), rags::Error> {
77//!     let long_desc: &'static str =
78//!     "This example aims to show beginner to intermediate options on the parser
79//!     as well as good practices.
80//!
81//!     As such, the usefulness of the binary is minimal but it will show you how
82//!     an application should be structured, options passed, errors handled, and
83//!     using parser state to control execution flow (print_help+exit, subcommands, etc).";
84//!
85//!
86//!     let mut opts = Options::default();
87//!     let mut parser = rags::Parser::from_args();
88//!     parser
89//!         .app_desc("example using most rags features")
90//!         .app_long_desc(long_desc)
91//!         .group("logging", "adjust logging output")?
92//!             .flag('D', "debug", "enter debug mode", &mut opts.debug, false)?
93//!             .count('v', "verbose", "increase vebosity (can be given multiple times)",
94//!                 &mut opts.verbosity, 1)?
95//!             .done()?
96//!         .subcommand("build", "build a target", &mut opts.subcmds, None)?
97//!             .arg('p', "package", "rename the package", &mut opts.package, Some("PKG"), true)?
98//!             .list('l', "lib", "libraries to link", &mut opts.build_link, Some("LIB"), false)?
99//!             .long_flag("release", "do a release build", &mut opts.build_release, false)?
100//!             .positional("file", "file to build", &mut opts.initial_file, true)?
101//!             .positional_list("files", "additional files to build",
102//!                 &mut opts.additional_files, false)?
103//!             .done()?
104//!         .subcommand("clean", "clean all build artifacts", &mut opts.subcmds, None)?
105//!             .flag('p', "print-only", "print what files would be cleaned, but do not clean",
106//!                 &mut opts.dry_run, false)?
107//!             .done()?
108//!     ;
109//!
110//!     if parser.wants_help() {
111//!         parser.print_help();
112//!     } else {
113//!         println!("final config: {:?}", opts);
114//!     }
115//!
116//!     Ok(())
117//! }
118//! ```
119//!
120//!
121//!
122//! # Example Help Dialog
123//!
124//! The above example prints the following under various help requests:
125//!
126//! ### Root Command Help
127//!
128//! ```ignore
129//! $ rags --help
130//! rags - 0.1.0 - example using most rags features
131//!
132//! usage: rags {subcommand} [-Dv]
133//!
134//! This example aims to show beginner to intermediate options on the parser
135//! as well as good practices.
136//!
137//! As such, the usefulness of the binary is minimal but it will show you how
138//! an application should be structured, options passed, errors handled, and
139//! using parser state to control execution flow (print_help+exit, subcommands, etc).
140//!
141//! subcommands:
142//!     build                build a target
143//!     clean                clean all build artifacts
144//!
145//! logging:                 adjust logging output
146//!     -D, --debug          enter debug mode [default: false]
147//!     -v, --verbose        increase vebosity (can be given multiple times) [default: 0]
148//!
149//! ```
150//!
151//!
152//!
153//! ### Subcommand  Help
154//!
155//! Notice that in the subcommand help we still see the global arguments.
156//!
157//! ```ignore
158//! $ rags build --help
159//! rags build - 0.1.0 - build a target
160//!
161//! usage: rags build [-Dv -l LIB --release] -p PKG file [files...]
162//!
163//! logging:                     adjust logging output
164//!     -D, --debug              enter debug mode [default: false]
165//!     -v, --verbose            increase vebosity (can be given multiple times) [default: 0]
166//!
167//! options:
168//!     -p, --package PKG        rename the package [required, default: main]
169//!     -l, --lib LIB            libraries to link
170//!         --release            do a release build [default: false]
171//!
172//! positionals:
173//!     file                     file to build [required]
174//!     files...                 additional files to build
175//!
176//! ```
177
178use std::env;
179use std::str::FromStr;
180use std::string::ToString;
181use std::collections::BTreeMap;
182
183extern crate bit_set;
184
185pub mod errors;
186pub use errors::*;
187
188mod printer;
189use printer::arg_string;
190
191type MatchResult = Result<Option<FoundMatch>, Error>;
192
193#[cfg(test)] mod test_args;
194#[cfg(test)] mod test_flags;
195#[cfg(test)] mod test_count;
196#[cfg(test)] mod test_lists;
197#[cfg(test)] mod test_positionals;
198#[cfg(test)] mod test_subcmds;
199#[cfg(test)] mod test_unused;
200
201/// Helper macro to populate the application name, version, and description
202/// from the Cargo manifest. Metadata setter functions can be called multiple
203/// times if only some of this information is specified in the manifest.
204///
205/// If called with no arguments, this is the same as
206/// [Parser::from_args](struct.Parser.html#method.from_args).
207/// Providing one or more args passes the args to
208/// [Parser::from_strings](struct.Parser.html#method.from_strings).
209#[macro_export]
210macro_rules! argparse {
211    () => {{
212        let mut p = $crate::Parser::from_args();
213        argparse!(p, true)
214    }};
215    ($args:ident) => {{
216        let mut p = $crate::Parser::from_strings($args);
217        argparse!(p, true)
218    }};
219    ($args:expr) => {{
220        let mut p = $crate::Parser::from_strings($args);
221        argparse!(p, true)
222    }};
223    ($p:ident, true) => {{
224        $p.app_name(env!("CARGO_PKG_NAME"))
225            .app_version(env!("CARGO_PKG_VERSION"))
226            .app_desc(env!("CARGO_PKG_DESCRIPTION"));
227        $p
228    }}
229}
230
231
232enum ItemType {
233    Argument,
234    Subcommand,
235    Group,
236}
237
238/// Defines where the value (if any) associated with a given argument is located.
239#[derive(Debug)]
240enum ValueLocation {
241    Unknown,
242    HasEqual(usize),
243    TakesNext,
244}
245
246/// FoundMatch is emitted when we match an argument. This carries all necessary
247/// metadata about the argument to be parsed.
248struct FoundMatch {
249    index: usize,
250    run_count: usize,
251    value: ValueLocation,
252}
253impl FoundMatch {
254    pub fn new(idx: usize, runs: usize, loc: ValueLocation) -> FoundMatch {
255        FoundMatch {
256            index: idx,
257            run_count: runs,
258            value: loc,
259        }
260    }
261}
262
263
264/// Defines the types of arguments we can handle, and when matched, our best
265/// guess as to what kind of arg that is until we can verify with more context.
266#[derive(PartialEq)]
267pub enum LooksLike {
268    ShortArg,
269    LongArg,
270    Positional,
271}
272impl std::fmt::Display for LooksLike {
273    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
274        match self {
275            LooksLike::ShortArg => {
276                write!(f, "short-arg")
277            }
278            LooksLike::LongArg => {
279                write!(f, "long-arg")
280            }
281            LooksLike::Positional => {
282                write!(f, "positional")
283            }
284        }
285    }
286}
287
288/// Unused carries information about arguments which go unmatched.
289/// Used both in delineating short-code runs as well as passing back
290/// all unmatched arguments to the user (when requested via
291/// [Parser::unused](struct.Parser.html#method.unused)).
292pub struct Unused {
293    pub arg: String,
294    pub looks_like: LooksLike,
295}
296impl Unused {
297    pub fn new(value: String) -> Unused {
298        let arg_0 = value.chars().nth(0).or(Some('\0')).unwrap();
299        let arg_1 = value.chars().nth(1).or(Some('\0')).unwrap();
300
301        let looks_like = if (arg_0 == '-') && (arg_1 == '-') {
302            LooksLike::LongArg
303        } else if (arg_0 == '-') && (arg_1 != '-') {
304            LooksLike::ShortArg
305        } else {
306            LooksLike::Positional
307        };
308
309        Unused {
310            arg: value,
311            looks_like: looks_like,
312        }
313    }
314}
315impl std::fmt::Display for Unused {
316    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
317        match self.looks_like {
318            LooksLike::ShortArg | LooksLike::LongArg => {
319                write!(f, "unused or unknown argument: {}", self.arg)
320            }
321            LooksLike::Positional => {
322                write!(f, "unused positional or arg-value: {}", self.arg)
323            }
324        }
325    }
326}
327
328/// Parser holds the state required for parsing. The methods provided here
329/// define how arguments should be treated as well as where they are constructed.
330///
331/// Arguments used for help (-h/--help) are already registered, and the boolean
332/// for this can be accessed via [Parser::wants_help](#method.wants_help).
333///
334/// Memory usage of this structure is bounded by the arguments passed as well
335/// as a bitset mapping which args have been matched. Rust does not provide
336/// an `O(1)` access to the args iterator, thus we store it. This also keeps
337/// implementation consistent when using
338/// [Parser::from_strings](#method.from_strings)
339///
340/// This structure can be dropped after handling of args/help are complete.
341pub struct Parser {
342    args: Vec<String>,
343    mask: bit_set::BitSet,
344    run_masks: BTreeMap<usize, bit_set::BitSet>,
345
346    walk_depth: usize,
347    commit_depth: usize,
348    max_depth: usize,
349    parse_done: bool,
350    curr_group: Option<&'static str>,
351
352    help: bool,
353    has_variadic: bool,
354    argstop: Option<usize>,
355    printer: printer::Printer,
356}
357impl Parser {
358    /// Creates a new parser for the arg strings given.
359    pub fn from_strings(input: Vec<String>) -> Parser {
360        let argstop = match input.iter().enumerate().find(|(_, a)| a.as_str() == "--") {
361            Some((i, _)) => { Some(i) }
362            None => { None }
363        };
364        let count = argstop.unwrap_or(input.len());
365
366        let mut bits = bit_set::BitSet::with_capacity(count);
367        // TODO: PR with BitSet::set_all() -- or an inverse iter that iterates all unset
368        for i in 1..count {
369            bits.insert(i);
370        }
371
372        let mut p = Parser{
373            args: input,
374            mask: bits,
375            run_masks: BTreeMap::new(),
376            walk_depth: 0,
377            commit_depth: 0,
378            max_depth: 0,
379            parse_done: false,
380            curr_group: None,
381
382            help: false,
383            has_variadic: false,
384            argstop,
385            printer: printer::Printer::new(printer::App::empty()),
386        };
387
388        let mut wants_help = false;
389        p.flag('h', "help", "print this help dialog", &mut wants_help, false)
390            .expect("could not handle help flag");
391        p.help = wants_help;
392
393        p
394    }
395
396    /// Collects the arguments given on the command line and defers to
397    /// [Parser::from_strings](#method.from_strings).
398    pub fn from_args() -> Parser {
399        let args = env::args().collect::<Vec<String>>();
400        Parser::from_strings(args)
401    }
402
403    /// Unused returns all unmatched args. The [Unused](struct.Unused.html) struct
404    /// contains the necessary information to call out unrecognized args or typos in
405    /// passed arguments.
406    ///
407    /// If there is an unused character in a run of shortcodes (e.g. `-abcd`, with `b` unused)
408    /// the argument within the [Unused](struct.Unused.html) struct will be prefixed with a dash.
409    pub fn unused(&self) -> Vec<Unused> {
410        let mut result = vec!();
411        for i in self.mask.iter() {
412            match self.run_masks.get(&i) {
413                None => {}
414                Some(mask) => {
415                    for m in mask.iter() {
416                        let s = format!("-{}", self.args[i].chars().nth(m+1).unwrap());
417                        result.push(Unused{
418                            arg: s,
419                            looks_like: LooksLike::ShortArg,
420                        });
421                    }
422                    continue;
423                }
424            }
425
426            result.push(Unused::new(self.args[i].clone()));
427        }
428
429        result
430    }
431
432
433    //----------------------------------------------------------------
434    // help setup
435    //----------------------------------------------------------------
436
437    /// Sets the name of the application to be printed in the help dialog.
438    /// Printed on the first line of the dialog.
439    pub fn app_name<'a>(&'a mut self, name: &'static str) -> &'a mut Parser {
440        self.printer.set_name(name);
441        self
442    }
443
444    /// Sets the description of the application to be printed in the help dialog.
445    /// Printed on the first line of the dialog.
446    pub fn app_desc<'a>(&'a mut self, desc: &'static str) -> &'a mut Parser {
447        self.printer.set_short_desc(desc);
448        self
449    }
450
451    /// Sets the long-form description of the application to be printed in the
452    /// help dialog. Printed after the base application info and usage lines.
453    pub fn app_long_desc<'a>(&'a mut self, desc: &'static str) -> &'a mut Parser {
454        self.printer.set_long_desc(desc);
455        self
456    }
457
458    /// Sets the version of the application to be printed in the help dialog.
459    /// Printed on the first line of the dialog.
460    pub fn app_version<'a>(&'a mut self, vers: &'static str) -> &'a mut Parser {
461        self.printer.set_version(vers);
462        self
463    }
464
465    /// Returns whether the help argument was given and help should be printed.
466    /// The help dialog can be printed using [Parser::print_help](#method.print_help).
467    pub fn wants_help(&self) -> bool {
468        self.help
469    }
470
471    /// Prints the help information. If subcommands are provided, the help for
472    /// the leaf subcommand is printed.
473    pub fn print_help(&self) {
474        self.printer.print();
475    }
476
477
478    //----------------------------------------------------------------
479    // parse helpers
480    //----------------------------------------------------------------
481
482    /// Closes a context opened by calling [Parser::group](#method.group) or
483    /// [Parser::subcommand](#method.subcommand).
484    pub fn done(&mut self) -> Result<&mut Parser, Error> {
485        if self.curr_group.is_some() {
486            self.curr_group = None;
487            return Ok(self);
488        }
489
490        if self.walk_depth == 0 {
491            return Err(Error::InvalidState("call to done() at top-level"));
492        }
493
494        if (self.walk_depth == self.commit_depth) && ( self.commit_depth == self.max_depth) {
495            self.parse_done = true;
496        }
497        self.walk_depth -= 1;
498
499        Ok(self)
500    }
501
502    fn should_ignore(&self, item: ItemType) -> bool {
503        if self.parse_done {
504            return true;
505        }
506        match item {
507            ItemType::Argument => {
508                self.walk_depth != self.max_depth
509            }
510            ItemType::Subcommand => {
511                self.walk_depth != (self.commit_depth + 1)
512            }
513            ItemType::Group => {
514                // never ignore a group as there is no side-effect, and needs to be registered for the ensuing done()
515                false
516            }
517        }
518
519    }
520
521    fn commit_next_level(&mut self) {
522        self.commit_depth += 1;
523        self.max_depth = std::cmp::max(self.commit_depth, self.max_depth);
524    }
525
526    fn walk_next_level(&mut self) {
527        self.walk_depth += 1;
528    }
529
530
531    fn handle_run(&mut self, idx: usize, short: char, expect_value: bool) -> MatchResult {
532        let arg = &self.args[idx];
533        if expect_value && !arg.ends_with(short) {
534            return Err(Error::ValuedArgInRun(short, arg.clone()));
535        }
536
537        let matches = arg.match_indices(short).map(|(i,_)| i).collect::<Vec<usize>>();
538        if matches.is_empty() {
539            // no matches here
540            return Ok(None);
541        }
542
543        // fetch the current mask for this run, or insert a new one
544        let runmask = match self.run_masks.get_mut(&idx) {
545            Some(mutref) => {
546                mutref
547            }
548            None => {
549                let mut bits = bit_set::BitSet::with_capacity(arg.len());
550                for i in 1..arg.len() { // skip 0, because we want to skip the leading '-'
551                    bits.insert(i);
552                }
553                self.run_masks.insert(idx, bits);
554                self.run_masks.get_mut(&idx).expect("failed to insert run mask")
555            }
556        };
557        if runmask.is_empty() {
558            return Ok(None);
559        }
560
561        let mut count: usize = 0;
562        for i in matches.iter() {
563            if runmask.contains(*i) == false { continue; }
564
565            runmask.remove(*i);
566            count += 1;
567        }
568        if count == 0 {
569            return Ok(None);
570        }
571
572        // when we empty a runmask, we set the "parent" index to be fully used
573        if runmask.is_empty() {
574            self.mask.remove(idx);
575        }
576
577        Ok(Some(FoundMatch::new(idx, count,
578            if expect_value {
579                ValueLocation::TakesNext
580            } else {
581                ValueLocation::Unknown
582            }
583        )))
584    }
585
586    fn matches_short(&mut self, idx: usize, short: char, expect_value: bool) -> MatchResult {
587        if short == '\0' { return Ok(None); } // no match
588
589        let arg = &self.args[idx];
590        if arg.len() < 2 {
591            return Ok(None);
592        }
593
594        if self.run_masks.contains_key(&idx) {
595            return self.handle_run(idx, short, expect_value);
596        }
597
598
599        let mut chars = arg.chars();
600        let arg_0 = chars.next().or(Some('\0')).unwrap();
601        let arg_1 = chars.next().or(Some('\0')).unwrap();
602        let arg_2 = chars.next().or(Some('\0')).unwrap();
603
604        // expect arg[0] to be '-'  -- otherwise, looks like a positional
605        // also expect arg[1] NOT to be '-'  -- otherwise, looks like a long
606        if arg_0 != '-' {
607            return Ok(None);
608        } else if arg_1 == '-' {
609            return Ok(None);
610        }
611
612        // expect arg[1] to be the character we are looking for (so not a long)
613        if arg_1 != short {
614            // if it is not, but we have something that looks like a run, try that
615            if arg.len() > 2 && arg_1 != '-' && arg_2 != '=' {
616                return self.handle_run(idx, short, expect_value);
617            }
618            return Ok(None)
619        }
620
621        // if we got here, and the length is 2, we have the base case so just return
622        if arg.len() == 2 {
623            let has_next = self.mask.contains(idx + 1);
624            return if expect_value && has_next {
625                Ok(Some(FoundMatch::new(idx, 0, ValueLocation::TakesNext)))
626            } else {
627                Ok(Some(FoundMatch::new(idx, 0, ValueLocation::Unknown)))
628            };
629        }
630
631        // if the arg has >2 characters, and arg[2] == '=', then we match and
632        // return the '=' offset
633        if arg_2 == '=' {
634            // return HasEqual regardless of expect_value because errors should be handled there
635            // rather than this lower context
636            return Ok(Some(FoundMatch::new(idx, 0, ValueLocation::HasEqual(2))));
637        }
638
639        // we know the arg has len>=3, arg[2] != '=', so it must be a run
640        self.handle_run(idx, short, expect_value)
641    }
642
643    fn matches_long(&self, idx: usize, long: &'static str, expect_value: bool) -> MatchResult {
644        if long.is_empty() { return Ok(None); }
645
646        let arg = self.args[idx].as_str();
647        let end_of_arg = 2 + long.len();
648
649        // not enough string to match
650        if arg.len() < end_of_arg {
651            return Ok(None);
652        }
653
654        // not a long arg
655        if &arg[..2] != "--" {
656            return Ok(None);
657        }
658
659        if &arg[2..end_of_arg] != long {
660            return Ok(None);
661        }
662
663        // we got exactly what we were looking for, so return
664        if arg.len() == end_of_arg {
665            let has_next = self.mask.contains(idx + 1);
666            return Ok(Some(FoundMatch::new(
667                idx, 0,
668                if expect_value && has_next {
669                    ValueLocation::TakesNext
670                } else {
671                    ValueLocation::Unknown
672                }
673            )));
674        }
675
676        // we got here, so the string is longer than we expect
677        // so check for a '=' trailing and return as such
678        if let Some(c) = arg.chars().nth(end_of_arg) {
679            if c == '=' {
680                // return HasEqual regardless of expect_value because errors should be handled
681                // there rather than this lower context
682                return Ok(Some(FoundMatch::new(idx, 0, ValueLocation::HasEqual(end_of_arg))));
683            }
684        }
685
686        // otherwise, no match
687        Ok(None)
688    }
689
690    fn find_match(&mut self, short: char, long: &'static str, expect_value: bool)
691        -> MatchResult
692    {
693        let mask_view = self.mask.iter().collect::<Vec<usize>>();
694        for i in mask_view.iter() {
695            match self.matches_short(*i, short, expect_value) {
696                Ok(Some(mat)) => {
697                    return Ok(Some(mat));
698                }
699                Ok(None) => {} // no match, so ignore
700                Err(e) => { return Err(e); }
701            }
702
703            match self.matches_long(*i, long, expect_value) {
704                Ok(Some(mat)) => {
705                    return Ok(Some(mat));
706                }
707                Ok(None) => {} // no match, so ignore
708                Err(e) => { return Err(e); }
709            }
710        }
711        Ok(None)
712    }
713
714    fn find_subcommand(&self, name: &'static str) -> Option<FoundMatch> {
715        for i in self.mask.iter() {
716            let arg = &self.args[i];
717            if arg == name {
718                return Some(FoundMatch::new(i, 0, ValueLocation::Unknown));
719            }
720        }
721        None
722    }
723
724    // takes index of the arg that matched, not the value to be constructed
725    fn construct_arg<T: FromStr>(&mut self,
726        info: &FoundMatch,
727        short: char, long: &'static str,
728        into: &mut T
729    ) -> Result<(), Error>
730        where <T as FromStr>::Err: std::fmt::Display
731    {
732        match info.value {
733            ValueLocation::Unknown => {
734                Err(Error::MissingArgValue(short, long))
735            }
736            ValueLocation::TakesNext => {
737                if self.mask.contains(info.index + 1) == false {
738                    return Err(Error::MissingArgValue(short, long));
739                }
740                self.mask.remove(info.index + 1); // mark the argument index as having been used/claimed
741                let val = &self.args[info.index + 1];
742                *into = T::from_str(val.as_str())
743                    .map_err(|e| Error::ConstructionError(short, long, format!("{}", e)))?;
744                Ok(())
745            }
746            ValueLocation::HasEqual(off) => {
747                let val = &self.args[info.index][(off+1)..];
748                // TODO: val.len() > 1 check + error
749                *into = T::from_str(val)
750                    .map_err(|e| Error::ConstructionError(short, long, format!("{}", e)))?;
751                Ok(())
752            }
753        }
754    }
755
756
757    //----------------------------------------------------------------
758    // arg(s)
759    //----------------------------------------------------------------
760
761    /// Registers a long and short code which are expected to be followed by a value.
762    /// The associated value can be separated by either a space or an equal sign
763    /// (e.g. `--foo=7` or `--foo 7`).
764    ///
765    /// The type you wish to be parse the arg value into must implement `From<String>`
766    /// for construction as well as `ToString` for printing defaults in the help dialog.
767    ///
768    /// You may provide a label to display next to the argument in the help dialog
769    /// (e.g. `-f, --file FILE` where the label here is `FILE`).
770    ///
771    /// Arguments may additionally be marked as required. If the argument is not provided
772    /// when marked as required, this method will return an error which will propogate up
773    /// the call stack without parsing further args (fail fast).
774    pub fn arg<'a, T: FromStr+ToString>(&'a mut self,
775        short: char, long: &'static str, desc: &'static str,
776        into: &mut T, label: Option<&'static str>, required: bool
777    ) -> Result<&'a mut Parser, Error>
778        where <T as FromStr>::Err: std::fmt::Display
779    {
780
781        if self.should_ignore(ItemType::Argument) { return Ok(self); }
782
783        // only add help if it is wanted
784        if self.wants_help() {
785            self.printer.add_arg(
786                printer::Argument::new(
787                    short, long, desc,
788                    label, Some(into.to_string()), required
789                ),
790                self.curr_group
791            )?;
792            return Ok(self);
793        }
794
795        let found_opt = self.find_match(short, long, true)?;
796        if found_opt.is_none() {
797            // only required if !help
798            if required  && !self.wants_help() {
799                return Err(Error::MissingArgument(arg_string(short, long, false)));
800            }
801            return Ok(self);
802        }
803
804        let found = found_opt.unwrap();
805        self.mask.remove(found.index);
806        self.construct_arg(&found, short, long, into)?;
807
808        Ok(self)
809    }
810
811    /// Convenience method for declaring a [Parser::arg](#method.arg) without a long code.
812    pub fn short_arg<'a, T: FromStr+ToString>(&'a mut self,
813        short: char, desc: &'static str, into: &mut T, label: Option<&'static str>,
814        required: bool
815    ) -> Result<&'a mut Parser, Error>
816        where <T as FromStr>::Err: std::fmt::Display
817    {
818        self.arg(short, "", desc, into, label, required)
819    }
820
821    /// Convenience method for declaring a [Parser::arg](#method.arg) without a short code.
822    pub fn long_arg<'a, T: FromStr+ToString>(&'a mut self,
823        long: &'static str, desc: &'static str, into: &mut T, label: Option<&'static str>,
824        required: bool
825    ) -> Result<&'a mut Parser, Error>
826        where <T as FromStr>::Err: std::fmt::Display
827    {
828        self.arg('\0', long, desc, into, label, required)
829    }
830
831
832
833    //----------------------------------------------------------------
834    // flag(s)
835    //----------------------------------------------------------------
836
837    // TODO: the help flags should be stored on `self` which is why this is
838    // a member function. once the flag(s) are configurable we will store them
839    // on the parser for this case
840    fn is_help_flags(&self, short: char, long: &'static str) -> bool  {
841        (short == 'h') || (long == "help")
842    }
843
844    /// Flag defines an argument that takes no value, but instead sets a boolean.
845    /// Typically when a flag is given the backing bool is set to `true`, however,
846    /// the `invert` argument here allows "negative-flags" which instead turn an
847    /// option off.
848    pub fn flag<'a>(&'a mut self,
849        short: char, long: &'static str, desc: &'static str,
850        into: &mut bool, invert: bool
851    ) -> Result<&'a mut Parser, Error>
852    {
853
854        if self.should_ignore(ItemType::Argument) { return Ok(self); }
855
856        if self.wants_help() {
857            self.printer.add_arg(
858                printer::Argument::new(short, long, desc, None, Some(into.to_string()), false),
859                self.curr_group
860            )?;
861
862            if !self.is_help_flags(short, long) {
863                return Ok(self);
864            }
865        }
866
867        let found_opt = self.find_match(short, long, false)?;
868        if found_opt.is_none() {
869            return Ok(self);
870        }
871
872        let found = found_opt.unwrap();
873        self.mask.remove(found.index);
874
875        match found.value {
876            ValueLocation::Unknown => {
877                *into = !invert;
878            }
879            ValueLocation::TakesNext => {
880                return Err(Error::InvalidInput(short, long, "flag should not have a value"));
881            }
882            ValueLocation::HasEqual(_) => {
883                return Err(Error::InvalidInput(short, long, "flag should not have a value"));
884            }
885        }
886
887        Ok(self)
888    }
889
890    /// Convenience method for declaring a [Parser::flag](#method.flag) without a long code.
891    pub fn short_flag<'a>(&'a mut self,
892        short: char, desc: &'static str,
893        into: &mut bool, invert: bool
894    ) -> Result<&'a mut Parser, Error>
895    {
896        self.flag(short, "", desc, into, invert)
897    }
898
899    /// Convenience method for declaring a [Parser::flag](#method.flag) without a short code.
900    pub fn long_flag<'a>(&'a mut self,
901        long: &'static str, desc: &'static str,
902        into: &'a mut bool, invert: bool
903    ) -> Result<&'a mut Parser, Error>
904    {
905        self.flag('\0', long, desc, into, invert)
906    }
907
908
909    //----------------------------------------------------------------
910    // count(s)
911    //----------------------------------------------------------------
912
913    /// Count (inc|dec)rements a backing numeric type every time the argument is provided.
914    /// The classic case is increasing verbosity (e.g. `-v` is 1, `-vvvv` is 4).
915    ///
916    /// The `step` argument to this method defines what should be added to the target value
917    /// every time the arg is seen. You may provide negative numbers to decrement.
918    ///
919    /// Floating point numeric types are supported, but are atypical.
920    pub fn count<'a, T: std::ops::AddAssign + ToString + Clone>(&'a mut self,
921        short: char, long: &'static str, desc: &'static str,
922        into: &mut T, step: T
923    ) -> Result<&'a mut Parser, Error>
924    {
925
926        if self.should_ignore(ItemType::Argument) { return Ok(self); }
927
928        if self.wants_help() {
929            self.printer.add_arg(
930                printer::Argument::new(short, long, desc, None, Some(into.to_string()), false),
931                self.curr_group
932            )?;
933            return Ok(self);
934        }
935
936        loop { // loop until we get no results back
937            let found_opt = self.find_match(short, long, false)?;
938            if found_opt.is_none() {
939                return Ok(self);
940            }
941
942            let found = found_opt.unwrap();
943            if found.run_count == 0 { // was not part of a run, remove eniter index
944                self.mask.remove(found.index);
945            }
946
947            match found.value {
948                ValueLocation::Unknown => {
949                    if found.run_count == 0 {
950                        into.add_assign(step.clone());
951                    } else {
952                        for _ in 0..found.run_count {
953                            into.add_assign(step.clone());
954                        }
955                    }
956                }
957                ValueLocation::TakesNext => {
958                    return Err(Error::InvalidInput(short, long, "count should not have a value"));
959                }
960                ValueLocation::HasEqual(_) => {
961                    return Err(Error::InvalidInput(short, long, "count should not have a value"));
962                }
963            }
964        }
965    }
966
967    /// Convenience method for declaring a [Parser::count](#method.count) without a long code.
968    pub fn short_count<'a, T: std::ops::AddAssign + ToString + Clone>(&'a mut self,
969        short: char, desc: &'static str,
970        into: &mut T, step: T
971    ) -> Result<&'a mut Parser, Error>
972    {
973        self.count(short, "", desc, into, step)
974    }
975
976    /// Convenience method for declaring a [Parser::count](#method.count) without a short code.
977    pub fn long_count<'a, T: std::ops::AddAssign + ToString + Clone>(&'a mut self,
978        long: &'static str, desc: &'static str,
979        into: &mut T, step: T
980    ) -> Result<&'a mut Parser, Error>
981    {
982        self.count('\0', long, desc, into, step)
983    }
984
985
986    //----------------------------------------------------------------
987    // list(s)
988    //----------------------------------------------------------------
989
990    /// List collects values from args and appends them to a vector of the target type.
991    ///
992    /// Follows the same parsing semantics as [Parser::arg](#method.arg), but appends to
993    /// a collection rather a single value. Just as with an arg, the target type must
994    /// implement `From<String>` as well as `ToString`. Likewise, the `label` and `required`
995    /// arguments to this method work the same.
996    pub fn list<'a, T: FromStr + ToString>(&'a mut self,
997        short: char, long: &'static str, desc: &'static str,
998        into: &mut Vec<T>, label: Option<&'static str>, required: bool
999    ) -> Result<&'a mut Parser, Error>
1000        where <T as FromStr>::Err: std::fmt::Display
1001    {
1002
1003        if self.should_ignore(ItemType::Argument) { return Ok(self); }
1004
1005        if self.wants_help() {
1006            self.printer.add_arg(
1007                printer::Argument::new(short, long, desc, label, None, required),
1008                self.curr_group
1009            )?;
1010            return Ok(self);
1011        }
1012
1013        let mut found_count = 0;
1014        loop { // loop until we get no results back
1015            let found_opt = self.find_match(short, long, true)?;
1016            if found_opt.is_none() { // TODO: required count -- does this make sense?
1017                // only requried when !help
1018                if required && (found_count == 0) && !self.wants_help() {
1019                    return Err(Error::MissingArgument(arg_string(short, long, false)));
1020                }
1021                return Ok(self);
1022            }
1023            found_count += 1;
1024
1025            let found = found_opt.unwrap();
1026            self.mask.remove(found.index);
1027
1028            let ctor_result = match found.value {
1029                ValueLocation::Unknown => {
1030                    return Err(Error::MissingArgValue(short, long));
1031                }
1032                ValueLocation::TakesNext => {
1033                    self.mask.remove(found.index + 1);
1034                    let str_val = &self.args[found.index + 1];
1035                    T::from_str(str_val)
1036                }
1037                ValueLocation::HasEqual(eq_idx) => {
1038                    // index already removed
1039                    let str_val = &self.args[found.index][eq_idx + 1..];
1040                    T::from_str(str_val)
1041                }
1042            };
1043
1044            into.push(
1045                ctor_result
1046                    .map_err(|e| Error::ConstructionError(short, long, format!("{}", e)))?
1047            );
1048        }
1049    }
1050
1051    /// Convenience method for declaring a [Parser::list](#method.list) without a long code.
1052    pub fn short_list<'a, T: FromStr + ToString>(&'a mut self,
1053        short: char, desc: &'static str,
1054        into: &mut Vec<T>, label: Option<&'static str>, required: bool
1055    ) -> Result<&'a mut Parser, Error>
1056        where <T as FromStr>::Err: std::fmt::Display
1057    {
1058        self.list(short, "", desc, into, label, required)
1059    }
1060
1061    /// Convenience method for declaring a [Parser::list](#method.list) without a short code.
1062    pub fn long_list<'a, T: FromStr + ToString>(&'a mut self,
1063        long: &'static str, desc: &'static str,
1064        into: &mut Vec<T>, label: Option<&'static str>, required: bool
1065    ) -> Result<&'a mut Parser, Error>
1066        where <T as FromStr>::Err: std::fmt::Display
1067    {
1068        self.list('\0', long, desc, into, label, required)
1069    }
1070
1071
1072
1073    //----------------------------------------------------------------
1074    // subcommand(s)
1075    //----------------------------------------------------------------
1076
1077    /// Subcommands provide information about what the application should do as well
1078    /// as giving scope to arguments. This method creates a new context (zero cost)
1079    /// for which arguments can be defined. By creating a new context we allow for
1080    /// subcommands to share argument codes with differing meanings. You must close
1081    /// this context/scope using [Parser::done](#method.done).
1082    ///
1083    /// When a subcommand is matched it is appended to a vector. The application is
1084    /// expected to iterate that vector to determine the correct internal function(s)
1085    /// to call.
1086    ///
1087    /// An optional long description specific to this command can be provided.
1088    /// The application's long description is not printed in the help dialog when a
1089    /// subcommand is matched.
1090    ///
1091    /// Because subcommands are indistinguishable from positional arguments, all
1092    /// definitions for positional arguments should be done after defining all subcommands.
1093    pub fn subcommand<'a, T: FromStr + ToString>(&'a mut self,
1094        name: &'static str, desc: &'static str, into: &mut Vec<T>,
1095        long_desc: Option<&'static str>
1096    ) -> Result<&'a mut Parser, Error>
1097        where <T as FromStr>::Err: std::fmt::Display
1098    {
1099        // even if we do not match this subcommand, all parsing until the
1100        // associated ::done() call happens within the next level so we
1101        // must move into it unconditionally
1102        self.walk_next_level();
1103
1104        if self.should_ignore(ItemType::Subcommand) {
1105            return Ok(self);
1106        }
1107
1108        if self.wants_help() {
1109            self.printer.add_subcommand(printer::Subcommand::new(name, desc));
1110            // do not return, subcommands need to continue parsing to set levels
1111            // and help appropriately
1112        }
1113
1114        if name.is_empty() {
1115            return Err(Error::InvalidState("subcommand(...) given empty name"));
1116        }
1117
1118        if let Some(info) = self.find_subcommand(name) {
1119            self.mask.remove(info.index);
1120            let arg = &self.args[info.index];
1121            into.push(
1122                T::from_str(arg)
1123                    .map_err(|e| Error::SubConstructionError(name, format!("{}", e)))?
1124            );
1125
1126            self.commit_next_level();
1127            self.printer.new_level(
1128                name, desc,
1129                if let Some(d) = long_desc { d } else { "" }
1130            );
1131        }
1132
1133        Ok(self)
1134    }
1135
1136    //----------------------------------------------------------------
1137    // group(s)
1138    //----------------------------------------------------------------
1139
1140    /// Group can be used to group various arguments into a named section within
1141    /// the help dialog. When a help argument is not provided, this is a no-op.
1142    ///
1143    /// This method opens a new scope/context which must be closed using
1144    /// [Parser::done](#method.done). However, no masking of arguments occurs in
1145    /// this created scope. The only effect a group has is on the printing of args.
1146    pub fn group<'a>(&'a mut self, name: &'static str, desc: &'static str)
1147        -> Result<&'a mut Parser, Error>
1148    {
1149        if let Some(orig) = self.curr_group {
1150            return Err(Error::NestedGroup(orig, name));
1151        }
1152
1153        if self.should_ignore(ItemType::Group) { return Ok(self); }
1154
1155        self.curr_group = Some(name);
1156        if self.wants_help() {
1157            self.printer.add_group(name, desc)?;
1158        }
1159        Ok(self)
1160    }
1161
1162
1163    //----------------------------------------------------------------
1164    // positional(s)
1165    //----------------------------------------------------------------
1166
1167    /// Creates a named positional argument. Positionals are taken on an in-order basis
1168    /// meaning when multiple positionals are defined, the values are constructed in the
1169    /// order they are provided by the user. This method does not parse anything after
1170    /// the arg-stop setinel (`--`); see [Parser::positional_list](#method.positional_list).
1171    ///
1172    /// You may define as many named positionals as required, but if you simply wish to
1173    /// capture all positionals, see [Parser::positional_list](#method.positional_list).
1174    ///
1175    /// Because positionals are indistinguishable from subcommands, all positionals should
1176    /// be defined after all subcommands. You can, however, safely define positionals within
1177    /// a leaf subcommand scope.
1178    ///
1179    /// Just as in the base [Parser::arg](#method.arg) case, the target type must implement
1180    /// both `From<String>` and `ToString`.
1181    pub fn positional<'a, T: ToString + FromStr>(&'a mut self,
1182        name: &'static str, desc: &'static str,
1183        into: &mut T, required: bool
1184    ) -> Result<&'a mut Parser, Error>
1185        where <T as FromStr>::Err: std::fmt::Display
1186    {
1187        if self.should_ignore(ItemType::Argument) { return Ok(self); }
1188
1189        if self.has_variadic {
1190            return Err(Error::UnorderedPositionals(name));
1191        }
1192
1193        if self.wants_help() {
1194            let def = into.to_string();
1195            self.printer.add_positional(printer::Positional::new(
1196                name, desc, if def.is_empty() { None } else { Some(def) },
1197                required, false
1198            ))?;
1199            return Ok(self);
1200        }
1201
1202        let idx = match self.mask.iter().next() {
1203            Some(i) => { i }
1204            None => {
1205                if required {
1206                    return Err(Error::MissingPositional(name.to_string()));
1207                } else {
1208                    return Ok(self);
1209                }
1210            }
1211        };
1212        let val = &self.args[idx];
1213        *into = T::from_str(val)
1214            .map_err(|e| Error::PositionalConstructionError(name, format!("{}", e)))?;
1215
1216        self.mask.remove(idx);
1217
1218        Ok(self)
1219    }
1220
1221    /// Gathers all unused arguments which are assumed to be positionals. Unused here
1222    /// does not include short code runs. Unrecognized arguments will also be returned
1223    /// here as there is mass complexity in determining the difference. For instance,
1224    /// `-9` is a valid short code flag but also has meaning as a positional.
1225    ///
1226    /// All arguments provided after the arg-stop setinel (`--`) will be gathered here.
1227    /// For example, in `my_app list --foo=7 -- list --help` the trailing `list --help`
1228    /// will not be parsed as arguments by this parser but instead will be considered
1229    /// positionals.
1230    ///
1231    /// Just as [Parser::list](#method.list) is a vector of [Parser::arg](#method.arg),
1232    /// this method is a vector of [Parser::positional](#method.positional) sharing a
1233    /// single name for the set.
1234    ///
1235    /// This method may only be called once, or an error will be returned.
1236    pub fn positional_list<'a, T: ToString + FromStr>(&'a mut self,
1237        name: &'static str, desc: &'static str,
1238        into: &mut Vec<T>, required: bool
1239    ) -> Result<&'a mut Parser, Error>
1240        where <T as FromStr>::Err: std::fmt::Display
1241    {
1242        if self.should_ignore(ItemType::Argument) { return Ok(self); }
1243
1244        if self.has_variadic {
1245            return Err(Error::MultipleVariadic(name));
1246        } else {
1247            self.has_variadic = true;
1248        }
1249
1250        // TODO: should we print defaults of lists?
1251        if self.wants_help() {
1252            self.printer.add_positional(printer::Positional::new(
1253                name, desc, None, required, true
1254            ))?;
1255            return Ok(self);
1256        }
1257
1258        let mut found_count: usize = 0;
1259        // TODO: I hate this, but self.mask.iter() is immut and mask mod is mut....
1260        let mut found_idxs: Vec<usize> = vec!();
1261        for i in self.mask.iter() {
1262            let val = &self.args[i];
1263            into.push(
1264                T::from_str(val).map_err(|e|
1265                    Error::PositionalConstructionError(name, format!("{}", e))
1266                )?
1267            );
1268
1269            found_count += 1;
1270            found_idxs.push(i);
1271        }
1272        for i in found_idxs.iter() {
1273            self.mask.remove(*i);
1274        }
1275
1276        if let Some(stop) = self.argstop {
1277            for i in (stop+1)..self.args.len() {
1278                let val = &self.args[i];
1279                into.push(
1280                    T::from_str(val).map_err(|e|
1281                        Error::PositionalConstructionError(name, format!("{}", e))
1282                    )?
1283                );
1284
1285                found_count += 1;
1286                found_idxs.push(i);
1287            }
1288        }
1289
1290        if required && (found_count == 0) {
1291            Err(Error::MissingPositional(format!("{}...", name)))
1292        } else {
1293            Ok(self)
1294        }
1295    }
1296}
1297
1298#[cfg(test)]
1299#[macro_use]
1300pub mod test_helpers {
1301    #[macro_export]
1302    macro_rules! string_vec {
1303        ( $($x:expr),* ) => {
1304            vec!( $(($x.to_string()),)* )
1305        }
1306    }
1307}
1308
1309#[cfg(test)]
1310mod handle_args {
1311    use super::*;
1312
1313    #[test]
1314    fn as_string_vec() {
1315        let mut verbosity = 0;
1316        let test_args = string_vec!("a", "b", "c");
1317        assert!(test_args.len() == 3);
1318        Parser::from_strings(test_args)
1319            .arg('v', "verbose", "increase verbosity with each given", &mut verbosity, None, false)
1320                .expect("failed to handle verbose argument(s)")
1321        ;
1322    }
1323
1324    #[test]
1325    fn as_args_iter() {
1326        let mut verbosity: u64 = 0;
1327        Parser::from_args()
1328            .arg('v', "verbose", "increase verbosity with each given", &mut verbosity, None, false)
1329                .expect("failed to handle verbose argument(s)")
1330        ;
1331    }
1332}