summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--argparse/.gitignore13
-rw-r--r--argparse/.travis.yml34
-rw-r--r--argparse/Cargo.toml12
-rw-r--r--argparse/LICENSE19
-rw-r--r--argparse/README.rst341
-rw-r--r--argparse/bulk.yaml11
-rw-r--r--argparse/examples/greeting.rs26
-rw-r--r--argparse/examples/structure.rs38
-rw-r--r--argparse/examples/subcommands.rs88
-rw-r--r--argparse/src/action.rs33
-rw-r--r--argparse/src/bool.rs22
-rw-r--r--argparse/src/custom.rs95
-rw-r--r--argparse/src/from_cli.rs100
-rw-r--r--argparse/src/generic.rs133
-rw-r--r--argparse/src/help.rs93
-rw-r--r--argparse/src/lib.rs64
-rw-r--r--argparse/src/num.rs58
-rw-r--r--argparse/src/parser.rs967
-rw-r--r--argparse/src/print.rs13
-rw-r--r--argparse/src/test_bool.rs98
-rw-r--r--argparse/src/test_const.rs28
-rw-r--r--argparse/src/test_enum.rs51
-rw-r--r--argparse/src/test_env.rs43
-rw-r--r--argparse/src/test_float.rs50
-rw-r--r--argparse/src/test_help.rs154
-rw-r--r--argparse/src/test_int.rs107
-rw-r--r--argparse/src/test_many.rs95
-rw-r--r--argparse/src/test_optional.rs54
-rw-r--r--argparse/src/test_parser.rs64
-rw-r--r--argparse/src/test_path.rs30
-rw-r--r--argparse/src/test_pos.rs196
-rw-r--r--argparse/src/test_str.rs28
-rw-r--r--argparse/src/test_usage.rs57
-rw-r--r--argparse/vagga.yaml59
-rw-r--r--nitrocli/CHANGELOG.md1
-rw-r--r--nitrocli/Cargo.lock5
-rw-r--r--nitrocli/Cargo.toml4
-rw-r--r--nitrocli/ci/gitlab-ci.yml2
38 files changed, 3285 insertions, 1 deletions
diff --git a/argparse/.gitignore b/argparse/.gitignore
new file mode 100644
index 0000000..6ae6389
--- /dev/null
+++ b/argparse/.gitignore
@@ -0,0 +1,13 @@
+/.vagga
+*.o
+*.so
+*.rlib
+/argparse_test
+/target
+# examples
+/greeting
+/structure
+/subcommands
+
+# Cargo files
+Cargo.lock
diff --git a/argparse/.travis.yml b/argparse/.travis.yml
new file mode 100644
index 0000000..7ccbf43
--- /dev/null
+++ b/argparse/.travis.yml
@@ -0,0 +1,34 @@
+sudo: false
+dist: trusty
+language: rust
+
+cache:
+- cargo
+
+before_cache:
+- rm -r $TRAVIS_BUILD_DIR/target/debug
+
+jobs:
+ include:
+ - os: linux
+ rust: stable
+ - os: linux
+ rust: beta
+ - os: linux
+ rust: nightly
+
+ # deploy
+ - stage: publish
+ os: linux
+ rust: stable
+ env:
+ # CARGO_TOKEN
+ - secure: "tk6bJEv46YfZwAKYzxn9+afzEb6nGym9lo/YJgjYIolv2qsNyMLlmC8ptRSRTHwOQPd3c54Y9XYP+61miMmWjppQSjJ4yvkUqnyiYzzdxzVM5dNIbXcqO6GbTgE2rIx9BOH0c/qrmw1KW2iz8TChxgQu/vv8pmDL1kmyawVy3EE="
+ install: true
+ script: true
+
+ deploy:
+ - provider: script
+ script: 'cargo publish --verbose --token=$CARGO_TOKEN'
+ on:
+ tags: true
diff --git a/argparse/Cargo.toml b/argparse/Cargo.toml
new file mode 100644
index 0000000..7552373
--- /dev/null
+++ b/argparse/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+
+name = "argparse"
+description = "Powerful command-line argument parsing library"
+license = "MIT"
+readme = "README.rst"
+keywords = ["command-line", "cli", "command", "argument"]
+categories = ["command-line-interface"]
+homepage = "http://github.com/tailhook/rust-argparse"
+version = "0.2.2"
+authors = ["Paul Colomiets <paul@colomiets.name>"]
+
diff --git a/argparse/LICENSE b/argparse/LICENSE
new file mode 100644
index 0000000..3e00de4
--- /dev/null
+++ b/argparse/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014-2015 Paul Colomiets
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/argparse/README.rst b/argparse/README.rst
new file mode 100644
index 0000000..8a3fca0
--- /dev/null
+++ b/argparse/README.rst
@@ -0,0 +1,341 @@
+========
+Argparse
+========
+
+The ``rust-argparse`` is command-line parsing module for rust. It's inspired
+by python's ``argparse`` module.
+
+Features:
+
+* Supports standard (GNU) option conventions
+* Properly typed values
+* Automatically generated help and usage messages
+
+Importing
+=========
+Edit your Cargo.toml to add ``rust-argparse`` to your project.
+
+.. code-block:: rust
+
+ [dependencies]
+ argparse = "0.2.2"
+
+
+Example
+=======
+
+The following code is a simple Rust program with command-line arguments:
+
+.. code-block:: rust
+
+ extern crate argparse;
+
+ use argparse::{ArgumentParser, StoreTrue, Store};
+
+ fn main() {
+ let mut verbose = false;
+ let mut name = "World".to_string();
+ { // this block limits scope of borrows by ap.refer() method
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Greet somebody.");
+ ap.refer(&mut verbose)
+ .add_option(&["-v", "--verbose"], StoreTrue,
+ "Be verbose");
+ ap.refer(&mut name)
+ .add_option(&["--name"], Store,
+ "Name for the greeting");
+ ap.parse_args_or_exit();
+ }
+
+ if verbose {
+ println!("name is {}", name);
+ }
+ println!("Hello {}!", name);
+ }
+
+Assuming the Rust code above is saved into a file ``greeting.rs``, let's see
+what we have now::
+
+ $ rustc greeting.rs
+ $ ./greeting -h
+ Usage:
+ ./greeting [OPTIONS]
+
+ Greet somebody.
+
+ Optional arguments:
+ -h, --help Show this help message and exit
+ -v, --verbose
+ Be verbose
+ --name NAME Name for the greeting
+ $ ./greeting
+ Hello World!
+ $ ./greeting --name Bob
+ Hello Bob!
+ $ ./greeting -v --name Alice
+ name is Alice
+ Hello Alice!
+
+
+Basic Workflow
+==============
+
+
+Create ArgumentParser
+---------------------
+
+The argument parser is created empty and is built incrementally. So we create
+a mutable variable::
+
+ extern crate argparse;
+ use argparse::ArgumentParser;
+
+ let mut parser = ArgumentParser::new();
+
+
+Customize
+---------
+
+There are optional customization methods. The most important one is::
+
+ parser.set_description("My command-line utility")
+
+The descripion is rewrapped to fit 80 column string nicely. Just like option
+descriptions.
+
+Add Options
+-----------
+
+The ``refer`` method creates a cell variable, which the result will be written
+to::
+
+ let mut verbose = false;
+ parser.refer(&mut verbose);
+
+Next we add an options which control the variable:
+For example::
+
+ parser.refer(&mut verbose)
+ .add_option(&["-v", "--verbose"], StoreTrue,
+ "Be verbose");
+
+You may add multiple options for the same variable::
+
+ parser.refer(&mut verbose)
+ .add_option(&["-v", "--verbose"], StoreTrue,
+ "Be verbose")
+ .add_option(&["-q", "--quiet"], StoreFalse,
+ "Be verbose");
+
+Similarly positional arguments are added::
+
+ let mut command = String;
+ parser.refer(&mut command)
+ .add_argument("command", Store,
+ "Command to run");
+
+
+
+Organizing Options
+------------------
+
+It's often useful to organize options into some kind of structure. You can
+easily borrow variables from the structure into option parser. For example::
+
+ struct Options {
+ verbose: bool,
+ }
+ ...
+ let mut options = Options { verbose: false };
+ parser.refer(&mut options.verbose)
+ .add_option(&["-v"], StoreTrue,
+ "Be verbose");
+
+
+Parsing Arguments
+-----------------
+
+All the complex work is done in ``parser.parse_args()``. But there is
+a simpler option::
+
+ parser.parse_args_or_exit()
+
+In case you don't want argparse to exit itself, you might use the
+``parse_args`` function directly::
+
+ use std::process::exit;
+
+ match parser.parse_args() {
+ Ok(()) => {}
+ Err(x) => {
+ std::process::exit(x);
+ }
+ }
+
+
+ArgumentParser Methods
+======================
+
+``parser.refer<T>(var: &mut T) -> Ref``
+ Attach the variable to argument parser. The options are added to the
+ returned ``Ref`` object and modify a variable passed to the method.
+
+``parser.add_option(names: &[&str], action: TypedAction, help: &str)``
+ Add a single option which has no parameters. Most options must be added
+ by ``refer(..)`` and methods on ``Ref`` object (see below).
+
+ Example::
+
+ ap.add_option(&["-V", "--version"],
+ Print(env!("CARGO_PKG_VERSION").to_string()), "Show version");
+
+``parser.set_description(descr: &str)``
+ Set description that is at the top of help message.
+
+``parser.stop_on_first_argument(val: bool)``
+ If called with ``true``, parser will stop searching for options when first
+ non-option (the one doesn't start with ``-``) argument is encountered. This
+ is useful if you want to parse following options with another argparser or
+ external program.
+
+``parser.silence_double_dash(val: bool)``
+ If called with ``true`` (default), parser will not treat *first* double
+ dash ``--`` as positional argument. Use ``false`` if you need to add some
+ meaning to the ``--`` marker.
+
+``parser.print_usage(name: &str, writer: &mut Write)``
+ Prints usage string to stderr.
+
+``parser.print_help(name: &str, writer: &mut Write)``
+ Writes help to ``writer``, used by ``--help`` option internally.
+
+``parser.parse_args()``
+ Method that does all the dirty work. And returns ``Result``
+
+``parser.parse_args_or_exit()``
+ Method that does all the dirty work. And in case of failure just ``exit()``
+
+
+Variable Reference Methods
+==========================
+
+The ``argparse::Ref`` object is returned from ``parser.refer()``.
+The following methods are used to add and customize arguments:
+
+``option.add_option(names: &[&str], action: TypedAction, help: &str)``
+ Add an option. All items in names should be either in format ``-X`` or
+ ``--long-option`` (i.e. one dash and one char or two dashes and long name).
+ How this option will be interpreted and whether it will have an argument
+ dependes on the action. See below list of actions.
+
+``option.add_argument(name: &str, action: TypedAction, help: &str)``
+ Add a positional argument
+
+``option.metavar(var: &str)``
+ A name of the argument in usage messages (for options having argument).
+
+``option.envvar(var: &str)``
+ A name of the environment variable to get option value from. The value
+ would be parsed with ``FromStr::from_str``, just like an option having
+ ``Store`` action.
+
+``option.required()``
+ The option or argument is required (it's optional by default). If multiple
+ options or multiple arguments are defined for this reference at least one
+ of them is required.
+
+
+Actions
+=======
+
+The following actions are available out of the box. They may be used in either
+``add_option`` or ``add_argument``:
+
+``Store``
+ An option has single argument. Stores a value from command-line in a
+ variable. Any type that has the ``FromStr`` and ``Clone`` traits implemented
+ may be used.
+
+``StoreOption``
+ As ``Store``, but wrap value with ``Some`` for use with ``Option``. For
+ example:
+
+ let mut x: Option<i32> = None;
+ ap.refer(&mut x).add_option(&["-x"], StoreOption, "Set var x");
+
+``StoreConst(value)``
+ An option has no arguments. Store a hard-coded ``value`` into variable,
+ when specified. Any type with the ``Clone`` trait implemented may be used.
+
+``PushConst(value)``
+ An option has no arguments. Push a hard-coded ``value`` into variable,
+ when specified. Any type which has the ``Clone`` type implemented may be
+ used. Option might used for a list of operations to perform, when ``required``
+ is set for this variable, at least one operation is required.
+
+``StoreTrue``
+ Stores boolean ``true`` value in a variable.
+ (shortcut for ``StoreConst(true)``)
+
+``StoreFalse``
+ Stores boolean ``false`` value in a variable.
+ (shortcut for ``StoreConst(false)``)
+
+
+``IncrBy(num)``
+ An option has no arguments. Increments the value stored in a variable by a
+ value ``num``. Any type which has the ``Add`` and ``Clone`` traits may be used.
+
+``DecrBy(nym)``
+ Decrements the value stored in a variable by a value ``num``. Any type
+ which has the ``Add`` and ``Clone`` traits may be used.
+
+``Collect``
+ When used for an ``--option``, requires single argument. When used for a
+ positional argument consumes all remaining arguments. Parsed options are
+ added to the list. I.e. a ``Collect`` action requires a
+ ``Vec<int>`` variable. Parses arguments using ``FromStr`` trait.
+
+``List``
+ When used for positional argument, works the same as ``List``. When used
+ as an option, consumes all remaining arguments.
+
+ Note the usage of ``List`` is strongly discouraged, because of complex
+ rules below. Use ``Collect`` and positional options if possible. But usage
+ of ``List`` action may be useful if you need shell expansion of anything
+ other than last positional argument.
+
+ Let's learn rules by example. For the next options::
+
+ ap.refer(&mut lst1).add_option(&["-X", "--xx"], List, "List1");
+ ap.refer(&mut lst2).add_argument("yy", List, "List2");
+
+ The following command line::
+
+ ./run 1 2 3 -X 4 5 6
+
+ Will return ``[1, 2, 3]`` in the ``lst1`` and the ``[4,5,6]`` in the
+ ``lst2``.
+
+ Note that using when using ``=`` or equivalent short option mode, the
+ 'consume all' mode is not enabled. I.e. in the following command-line::
+
+ ./run 1 2 -X3 4 --xx=5 6
+
+ The ``lst1`` has ``[3, 5]`` and ``lst2`` has ``[1, 2, 4, 6]``.
+ The argument consuming also stops on ``--`` or the next option::
+
+ ./run: -X 1 2 3 -- 4 5 6
+ ./run: -X 1 2 --xx=3 4 5 6
+
+ Both of the above parse ``[4, 5, 6]`` as ``lst1`` and
+ the ``[1, 2, 3]`` as the ``lst2``.
+
+``Print(value)``
+ Print the text and exit (with status ``0``). Useful for ``--version``
+ option::
+
+ ap.add_option(&["-V", "--version"],
+ Print(env!("CARGO_PKG_VERSION").to_string()), "Show version");
+
+
diff --git a/argparse/bulk.yaml b/argparse/bulk.yaml
new file mode 100644
index 0000000..73a7d02
--- /dev/null
+++ b/argparse/bulk.yaml
@@ -0,0 +1,11 @@
+minimum-bulk: v0.4.5
+
+versions:
+
+- file: Cargo.toml
+ block-start: ^\[package\]
+ block-end: ^\[.*\]
+ regex: ^version\s*=\s*"(\S+)"
+
+- file: README.rst
+ regex: ^\s*argparse\s*=\s*"(\S+)"
diff --git a/argparse/examples/greeting.rs b/argparse/examples/greeting.rs
new file mode 100644
index 0000000..77e719c
--- /dev/null
+++ b/argparse/examples/greeting.rs
@@ -0,0 +1,26 @@
+extern crate argparse;
+
+use argparse::{ArgumentParser, StoreTrue, Store, Print};
+
+fn main() {
+ let mut verbose = false;
+ let mut name = "World".to_string();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Greet somebody.");
+ ap.add_option(&["-V", "--version"],
+ Print(env!("CARGO_PKG_VERSION").to_string()), "Show version");
+ ap.refer(&mut verbose)
+ .add_option(&["-v", "--verbose"], StoreTrue,
+ "Be verbose");
+ ap.refer(&mut name)
+ .add_option(&["--name"], Store,
+ "Name for the greeting");
+ ap.parse_args_or_exit();
+ }
+
+ if verbose {
+ println!("name is {}", name);
+ }
+ println!("Hello {}!", name);
+}
diff --git a/argparse/examples/structure.rs b/argparse/examples/structure.rs
new file mode 100644
index 0000000..59b9345
--- /dev/null
+++ b/argparse/examples/structure.rs
@@ -0,0 +1,38 @@
+extern crate argparse;
+
+use std::process::exit;
+
+use argparse::{ArgumentParser, StoreTrue, Store};
+
+struct Options {
+ verbose: bool,
+ name: String,
+}
+
+fn main() {
+ let mut options = Options {
+ verbose: false,
+ name: "World".to_string(),
+ };
+ {
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Greet somebody.");
+ ap.refer(&mut options.verbose)
+ .add_option(&["-v", "--verbose"], StoreTrue,
+ "Be verbose");
+ ap.refer(&mut options.name)
+ .add_option(&["--name"], Store,
+ "Name for the greeting");
+ match ap.parse_args() {
+ Ok(()) => {}
+ Err(x) => {
+ exit(x);
+ }
+ }
+ }
+
+ if options.verbose {
+ println!("name is {}", options.name);
+ }
+ println!("Hello {}!", options.name);
+}
diff --git a/argparse/examples/subcommands.rs b/argparse/examples/subcommands.rs
new file mode 100644
index 0000000..8fb061a
--- /dev/null
+++ b/argparse/examples/subcommands.rs
@@ -0,0 +1,88 @@
+use std::str::FromStr;
+use std::io::{stdout, stderr};
+extern crate argparse;
+
+use argparse::{ArgumentParser, StoreTrue, Store, List};
+
+#[allow(non_camel_case_types)]
+#[derive(Debug)]
+enum Command {
+ play,
+ record,
+}
+
+impl FromStr for Command {
+ type Err = ();
+ fn from_str(src: &str) -> Result<Command, ()> {
+ return match src {
+ "play" => Ok(Command::play),
+ "record" => Ok(Command::record),
+ _ => Err(()),
+ };
+ }
+}
+
+
+
+fn play_command(verbose: bool, args: Vec<String>) {
+ let mut output = "".to_string();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Plays a sound");
+ ap.refer(&mut output)
+ .add_option(&["--output"], Store,
+ r#"Output sink to play to"#);
+ match ap.parse(args, &mut stdout(), &mut stderr()) {
+ Ok(()) => {}
+ Err(x) => {
+ std::process::exit(x);
+ }
+ }
+ }
+ println!("Verbosity: {}, Output: {}", verbose, output);
+}
+
+fn record_command(verbose: bool, args: Vec<String>) {
+ let mut input = "".to_string();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Records a sound");
+ ap.refer(&mut input)
+ .add_option(&["--input"], Store,
+ r#"Output source to record from"#);
+ match ap.parse(args, &mut stdout(), &mut stderr()) {
+ Ok(()) => {}
+ Err(x) => {
+ std::process::exit(x);
+ }
+ }
+ }
+ println!("Verbosity: {}, Input: {}", verbose, input);
+}
+
+fn main() {
+ let mut verbose = false;
+ let mut subcommand = Command::play;
+ let mut args = vec!();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Plays or records sound");
+ ap.refer(&mut verbose)
+ .add_option(&["-v", "--verbose"], StoreTrue,
+ "Be verbose");
+ ap.refer(&mut subcommand).required()
+ .add_argument("command", Store,
+ r#"Command to run (either "play" or "record")"#);
+ ap.refer(&mut args)
+ .add_argument("arguments", List,
+ r#"Arguments for command"#);
+ ap.stop_on_first_argument(true);
+ ap.parse_args_or_exit();
+ }
+
+ args.insert(0, format!("subcommand {:?}", subcommand));
+ match subcommand {
+ Command::play => play_command(verbose, args),
+ Command::record => record_command(verbose, args),
+ }
+}
diff --git a/argparse/src/action.rs b/argparse/src/action.rs
new file mode 100644
index 0000000..c3a0aff
--- /dev/null
+++ b/argparse/src/action.rs
@@ -0,0 +1,33 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+
+pub enum ParseResult {
+ Parsed,
+ Help,
+ Exit,
+ Error(String),
+}
+
+
+pub enum Action<'a> {
+ Flag(Box<IFlagAction + 'a>),
+ Single(Box<IArgAction + 'a>),
+ Push(Box<IArgsAction + 'a>),
+ Many(Box<IArgsAction + 'a>),
+}
+
+pub trait TypedAction<T> {
+ fn bind<'x>(&self, Rc<RefCell<&'x mut T>>) -> Action<'x>;
+}
+
+pub trait IFlagAction {
+ fn parse_flag(&self) -> ParseResult;
+}
+
+pub trait IArgAction {
+ fn parse_arg(&self, arg: &str) -> ParseResult;
+}
+
+pub trait IArgsAction {
+ fn parse_args(&self, args: &[&str]) -> ParseResult;
+}
diff --git a/argparse/src/bool.rs b/argparse/src/bool.rs
new file mode 100644
index 0000000..6ada458
--- /dev/null
+++ b/argparse/src/bool.rs
@@ -0,0 +1,22 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use super::action::Action;
+use super::action::TypedAction;
+use super::action::Action::Flag;
+use super::generic::StoreConstAction;
+use super::{StoreTrue, StoreFalse};
+
+
+impl TypedAction<bool> for StoreTrue {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut bool>>) -> Action<'x> {
+ return Flag(Box::new(StoreConstAction { cell: cell, value: true }));
+ }
+}
+
+impl TypedAction<bool> for StoreFalse {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut bool>>) -> Action<'x> {
+ return Flag(Box::new(StoreConstAction { cell: cell, value: false }));
+ }
+}
+
diff --git a/argparse/src/custom.rs b/argparse/src/custom.rs
new file mode 100644
index 0000000..d002ffe
--- /dev/null
+++ b/argparse/src/custom.rs
@@ -0,0 +1,95 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use super::{Parse, ParseOption, ParseList, ParseCollect, FromCommandLine};
+use super::action::Action;
+use super::action::{TypedAction, IArgAction, IArgsAction};
+use super::action::ParseResult;
+use super::action::ParseResult::{Parsed, Error};
+use super::action::Action::{Single, Push, Many};
+
+pub struct ParseAction<'a, T: 'a> {
+ pub cell: Rc<RefCell<&'a mut T>>,
+}
+
+pub struct ParseOptionAction<'a, T: 'a> {
+ cell: Rc<RefCell<&'a mut Option<T>>>,
+}
+
+pub struct ParseListAction<'a, T: 'a> {
+ cell: Rc<RefCell<&'a mut Vec<T>>>,
+}
+
+impl<T: 'static + FromCommandLine> TypedAction<T> for Parse {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut T>>) -> Action<'x> {
+ return Single(Box::new(ParseAction { cell: cell }));
+ }
+}
+
+impl<T: 'static + FromCommandLine> TypedAction<Option<T>> for ParseOption {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut Option<T>>>) -> Action<'x> {
+ return Single(Box::new(ParseOptionAction { cell: cell }));
+ }
+}
+
+impl<T: 'static + FromCommandLine + Clone> TypedAction<Vec<T>> for ParseList {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut Vec<T>>>) -> Action<'x> {
+ return Many(Box::new(ParseListAction { cell: cell }));
+ }
+}
+
+impl<T> TypedAction<Vec<T>> for ParseCollect
+ where T: 'static + FromCommandLine + Clone
+{
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut Vec<T>>>) -> Action<'x> {
+ return Push(Box::new(ParseListAction { cell: cell }))
+ }
+}
+
+impl<'a, T: FromCommandLine> IArgAction for ParseAction<'a, T> {
+ fn parse_arg(&self, arg: &str) -> ParseResult {
+ match FromCommandLine::from_argument(arg) {
+ Ok(x) => {
+ **self.cell.borrow_mut() = x;
+ return Parsed;
+ }
+ Err(error) => {
+ return Error(format!("Bad value {:?}: {}", arg, error));
+ }
+ }
+ }
+}
+
+impl<'a, T: FromCommandLine> IArgAction for ParseOptionAction<'a, T> {
+ fn parse_arg(&self, arg: &str) -> ParseResult {
+ match FromCommandLine::from_argument(arg) {
+ Ok(x) => {
+ **self.cell.borrow_mut() = Some(x);
+ return Parsed;
+ }
+ Err(error) => {
+ return Error(format!("Bad value {:?}: {}", arg, error));
+ }
+ }
+ }
+}
+
+impl<'a, T: FromCommandLine + Clone> IArgsAction for ParseListAction<'a, T> {
+ fn parse_args(&self, args: &[&str]) -> ParseResult {
+ let mut result = vec!();
+ for arg in args.iter() {
+ match FromCommandLine::from_argument(*arg) {
+ Ok(x) => {
+ result.push(x);
+ }
+ Err(error) => {
+ return Error(format!("Bad value {:?}: {}", arg, error));
+ }
+ }
+ }
+ **self.cell.borrow_mut() = result;
+ return Parsed;
+ }
+}
+
+
diff --git a/argparse/src/from_cli.rs b/argparse/src/from_cli.rs
new file mode 100644
index 0000000..7ea6d58
--- /dev/null
+++ b/argparse/src/from_cli.rs
@@ -0,0 +1,100 @@
+use std::str::FromStr;
+use std::path::PathBuf;
+use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
+
+use super::FromCommandLine;
+
+
+impl FromCommandLine for PathBuf {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ Ok(From::from(s))
+ }
+}
+
+impl FromCommandLine for f32 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for f64 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+
+// TODO(tailhook) implement various radices for integer values
+impl FromCommandLine for isize {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for i8 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for i16 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for i32 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for i64 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for usize {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for u8 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for u16 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for u32 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for u64 {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for bool {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for String {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|_| unreachable!())
+ }
+}
+impl FromCommandLine for Ipv4Addr {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for Ipv6Addr {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
+impl FromCommandLine for SocketAddr {
+ fn from_argument(s: &str) -> Result<Self, String> {
+ FromStr::from_str(s).map_err(|e| format!("{:?}", e))
+ }
+}
diff --git a/argparse/src/generic.rs b/argparse/src/generic.rs
new file mode 100644
index 0000000..1b1b7dc
--- /dev/null
+++ b/argparse/src/generic.rs
@@ -0,0 +1,133 @@
+use std::cell::RefCell;
+use std::str::FromStr;
+use std::rc::Rc;
+
+use super::{StoreConst, Store, StoreOption, List, Collect, PushConst};
+use super::action::Action;
+use super::action::{TypedAction, IFlagAction, IArgAction, IArgsAction};
+use super::action::ParseResult;
+use super::action::ParseResult::{Parsed, Error};
+use super::action::Action::{Flag, Single, Push, Many};
+
+pub struct StoreConstAction<'a, T: 'a> {
+ pub value: T,
+ pub cell: Rc<RefCell<&'a mut T>>,
+}
+
+pub struct PushConstAction<'a, T: 'a> {
+ pub value: T,
+ pub cell: Rc<RefCell<&'a mut Vec<T>>>,
+}
+
+pub struct StoreAction<'a, T: 'a> {
+ pub cell: Rc<RefCell<&'a mut T>>,
+}
+
+pub struct StoreOptionAction<'a, T: 'a> {
+ cell: Rc<RefCell<&'a mut Option<T>>>,
+}
+
+pub struct ListAction<'a, T: 'a> {
+ cell: Rc<RefCell<&'a mut Vec<T>>>,
+}
+
+impl<T: 'static + Clone> TypedAction<T> for StoreConst<T> {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut T>>) -> Action<'x> {
+ let StoreConst(ref val) = *self;
+ return Flag(Box::new(StoreConstAction { cell: cell, value: val.clone() }));
+ }
+}
+
+impl<T: 'static + Clone> TypedAction<Vec<T>> for PushConst<T> {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut Vec<T>>>) -> Action<'x> {
+ let PushConst(ref val) = *self;
+ return Flag(Box::new(PushConstAction { cell: cell, value: val.clone() }));
+ }
+}
+
+impl<T: 'static + FromStr> TypedAction<T> for Store {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut T>>) -> Action<'x> {
+ return Single(Box::new(StoreAction { cell: cell }));
+ }
+}
+
+impl<T: 'static + FromStr> TypedAction<Option<T>> for StoreOption {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut Option<T>>>) -> Action<'x> {
+ return Single(Box::new(StoreOptionAction { cell: cell }));
+ }
+}
+
+impl<T: 'static + FromStr + Clone> TypedAction<Vec<T>> for List {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut Vec<T>>>) -> Action<'x> {
+ return Many(Box::new(ListAction { cell: cell }));
+ }
+}
+
+impl<T: 'static + FromStr + Clone> TypedAction<Vec<T>> for Collect {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut Vec<T>>>) -> Action<'x> {
+ return Push(Box::new(ListAction { cell: cell }));
+ }
+}
+
+impl<'a, T: Clone> IFlagAction for StoreConstAction<'a, T> {
+ fn parse_flag(&self) -> ParseResult {
+ let mut targ = self.cell.borrow_mut();
+ **targ = self.value.clone();
+ return Parsed;
+ }
+}
+
+impl<'a, T: Clone> IFlagAction for PushConstAction<'a, T> {
+ fn parse_flag(&self) -> ParseResult {
+ let mut targ = self.cell.borrow_mut();
+ targ.push(self.value.clone());
+ return Parsed;
+ }
+}
+
+impl<'a, T: FromStr> IArgAction for StoreAction<'a, T> {
+ fn parse_arg(&self, arg: &str) -> ParseResult {
+ match FromStr::from_str(arg) {
+ Ok(x) => {
+ **self.cell.borrow_mut() = x;
+ return Parsed;
+ }
+ Err(_) => {
+ return Error(format!("Bad value {}", arg));
+ }
+ }
+ }
+}
+
+impl<'a, T: FromStr> IArgAction for StoreOptionAction<'a, T> {
+ fn parse_arg(&self, arg: &str) -> ParseResult {
+ match FromStr::from_str(arg) {
+ Ok(x) => {
+ **self.cell.borrow_mut() = Some(x);
+ return Parsed;
+ }
+ Err(_) => {
+ return Error(format!("Bad value {}", arg));
+ }
+ }
+ }
+}
+
+impl<'a, T: FromStr + Clone> IArgsAction for ListAction<'a, T> {
+ fn parse_args(&self, args: &[&str]) -> ParseResult {
+ let mut result = vec!();
+ for arg in args.iter() {
+ match FromStr::from_str(*arg) {
+ Ok(x) => {
+ result.push(x);
+ }
+ Err(_) => {
+ return Error(format!("Bad value {}", arg));
+ }
+ }
+ }
+ **self.cell.borrow_mut() = result;
+ return Parsed;
+ }
+}
+
diff --git a/argparse/src/help.rs b/argparse/src/help.rs
new file mode 100644
index 0000000..c7145be
--- /dev/null
+++ b/argparse/src/help.rs
@@ -0,0 +1,93 @@
+use std::str::CharIndices;
+use std::io::Result as IoResult;
+use std::io::Write;
+
+use super::action::{IFlagAction, ParseResult};
+use super::action::ParseResult::Help;
+
+pub struct HelpAction;
+
+impl IFlagAction for HelpAction {
+ fn parse_flag(&self) -> ParseResult {
+ return Help;
+ }
+}
+
+
+struct WordsIter<'a> {
+ data: &'a str,
+ iter: CharIndices<'a>,
+}
+
+impl<'a> WordsIter<'a> {
+ fn new(data: &'a str) -> WordsIter<'a> {
+ return WordsIter {
+ data: data,
+ iter: data.char_indices(),
+ };
+ }
+}
+
+impl<'a> Iterator for WordsIter<'a> {
+ type Item = &'a str;
+ fn next(&mut self) -> Option<&'a str> {
+ let word_start;
+ loop {
+ let (idx, ch) = match self.iter.next() {
+ None => return None,
+ Some((idx, ch)) => ((idx, ch)),
+ };
+ match ch {
+ ' ' | '\t' | '\r' | '\n' => continue,
+ _ => {
+ word_start = idx;
+ break;
+ }
+ }
+ }
+ loop {
+ let (idx, ch) = match self.iter.next() {
+ None => break,
+ Some((idx, ch)) => ((idx, ch)),
+ };
+ match ch {
+ ' ' | '\t' | '\r' | '\n' => {
+ return Some(&self.data[word_start..idx]);
+ }
+ _ => continue,
+ }
+ }
+ return Some(&self.data[word_start..self.data.len()]);
+ }
+}
+
+pub fn wrap_text(buf: &mut Write, data: &str, width: usize, indent: usize)
+ -> IoResult<()>
+{
+ let mut witer = WordsIter::new(data);
+ let mut off = indent;
+ match witer.next() {
+ None => {
+ return Ok(());
+ }
+ Some(word) => {
+ try!(buf.write(word.as_bytes()));
+ off += word.len();
+ }
+ }
+ for word in witer {
+ if off + word.len() + 1 > width {
+ try!(buf.write(b"\n"));
+ for _ in 0..indent {
+ try!(buf.write(b" "));
+ }
+ off = indent;
+ } else {
+ try!(buf.write(b" "));
+ off += 1;
+ }
+ try!(buf.write(word.as_bytes()));
+ off += word.len();
+ }
+ return Ok(());
+}
diff --git a/argparse/src/lib.rs b/argparse/src/lib.rs
new file mode 100644
index 0000000..62b0cf9
--- /dev/null
+++ b/argparse/src/lib.rs
@@ -0,0 +1,64 @@
+#![crate_name = "argparse"]
+#![crate_type = "lib"]
+
+pub use self::parser::{ArgumentParser, Ref};
+
+pub mod action;
+pub mod parser;
+mod generic;
+mod custom;
+mod help;
+mod print;
+
+mod bool;
+mod num;
+mod from_cli;
+
+pub trait FromCommandLine: Sized {
+ fn from_argument(s: &str) -> Result<Self, String>;
+}
+
+// TODO(tailhook) make consts
+pub struct StoreTrue;
+pub struct StoreFalse;
+
+pub struct StoreConst<T>(pub T);
+
+pub struct PushConst<T>(pub T);
+
+pub struct Store;
+pub struct Parse;
+
+pub struct StoreOption;
+pub struct ParseOption;
+
+pub struct List;
+pub struct ParseList;
+
+pub struct Collect;
+pub struct ParseCollect;
+
+/// Print string and exit with status 0
+///
+/// Particularly useful for `--version` option and similar
+pub struct Print(pub String);
+
+pub struct IncrBy<T>(pub T);
+
+pub struct DecrBy<T>(pub T);
+
+
+#[cfg(test)] mod test_parser;
+#[cfg(test)] mod test_bool;
+#[cfg(test)] mod test_int;
+#[cfg(test)] mod test_float;
+#[cfg(test)] mod test_str;
+#[cfg(test)] mod test_enum;
+#[cfg(test)] mod test_pos;
+#[cfg(test)] mod test_many;
+#[cfg(test)] mod test_optional;
+#[cfg(test)] mod test_usage;
+#[cfg(test)] mod test_help;
+#[cfg(test)] mod test_env;
+#[cfg(test)] mod test_const;
+#[cfg(test)] mod test_path;
diff --git a/argparse/src/num.rs b/argparse/src/num.rs
new file mode 100644
index 0000000..9f34de7
--- /dev/null
+++ b/argparse/src/num.rs
@@ -0,0 +1,58 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+use std::ops::{Add, Sub};
+
+use super::{IncrBy, DecrBy};
+use super::action::{TypedAction, Action, ParseResult};
+use super::action::ParseResult::Parsed;
+use super::action::IFlagAction;
+use super::action::Action::Flag;
+
+pub struct IncrByAction<'a, T: 'a> {
+ delta: T,
+ cell: Rc<RefCell<&'a mut T>>,
+}
+
+pub struct DecrByAction<'a, T: 'a> {
+ delta: T,
+ cell: Rc<RefCell<&'a mut T>>,
+}
+
+impl<T: 'static + Add<Output = T> + Clone> TypedAction<T> for IncrBy<T> {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut T>>) -> Action<'x> {
+ let IncrBy(ref delta) = *self;
+ return Flag(Box::new(IncrByAction { cell: cell, delta: delta.clone() }));
+ }
+}
+
+impl<T: 'static + Sub<Output = T> + Clone> TypedAction<T> for DecrBy<T> {
+ fn bind<'x>(&self, cell: Rc<RefCell<&'x mut T>>) -> Action<'x> {
+ let DecrBy(ref delta) = *self;
+ return Flag(Box::new(DecrByAction { cell: cell, delta: delta.clone() }));
+ }
+}
+
+impl<'a, T: Add<Output = T> + Clone> IFlagAction for IncrByAction<'a, T> {
+ fn parse_flag(&self) -> ParseResult {
+ let oldval = {
+ let targ = self.cell.borrow();
+ targ.clone()
+ };
+ let mut targ = self.cell.borrow_mut();
+ **targ = oldval + self.delta.clone();
+ return Parsed;
+ }
+}
+
+impl<'a, T: Sub<Output = T> + Clone> IFlagAction for DecrByAction<'a, T> {
+ fn parse_flag(&self) -> ParseResult {
+ let oldval = {
+ let targ = self.cell.borrow();
+ targ.clone()
+ };
+ let mut targ = self.cell.borrow_mut();
+ **targ = oldval - self.delta.clone();
+ return Parsed;
+ }
+}
+
diff --git a/argparse/src/parser.rs b/argparse/src/parser.rs
new file mode 100644
index 0000000..ac7e3da
--- /dev/null
+++ b/argparse/src/parser.rs
@@ -0,0 +1,967 @@
+use std::env;
+use std::io::{Write};
+use std::io::Result as IoResult;
+use std::io::{stdout, stderr};
+use std::rc::Rc;
+use std::cell::RefCell;
+use std::iter::Peekable;
+use std::slice::Iter;
+use std::hash::Hash;
+use std::hash::Hasher;
+use std::str::FromStr;
+use std::process::exit;
+
+#[allow(unused_imports)] #[allow(deprecated)]
+use std::ascii::AsciiExt;
+
+use std::collections::HashMap;
+use std::collections::hash_map::Entry;
+use std::collections::HashSet;
+
+use super::action::{Action, ParseResult};
+use super::action::ParseResult::{Parsed, Help, Exit, Error};
+use super::action::TypedAction;
+use super::action::Action::{Flag, Single, Push, Many};
+use super::action::IArgAction;
+use super::generic::StoreAction;
+use super::help::{HelpAction, wrap_text};
+use action::IFlagAction;
+
+use self::ArgumentKind::{Positional, ShortOption, LongOption, Delimiter};
+
+
+static OPTION_WIDTH: usize = 24;
+static TOTAL_WIDTH: usize = 79;
+
+
+enum ArgumentKind {
+ Positional,
+ ShortOption,
+ LongOption,
+ Delimiter, // Barely "--"
+}
+
+
+impl ArgumentKind {
+ fn check(name: &str) -> ArgumentKind {
+ let mut iter = name.chars();
+ let char1 = iter.next();
+ let char2 = iter.next();
+ let char3 = iter.next();
+ return match char1 {
+ Some('-') => match char2 {
+ Some('-') => match char3 {
+ Some(_) => LongOption, // --opt
+ None => Delimiter, // just --
+ },
+ Some(_) => ShortOption, // -opts
+ None => Positional, // single dash
+ },
+ Some(_) | None => Positional,
+ }
+ }
+}
+
+struct GenericArgument<'parser> {
+ id: usize,
+ varid: usize,
+ name: &'parser str,
+ help: &'parser str,
+ action: Action<'parser>,
+}
+
+struct GenericOption<'parser> {
+ id: usize,
+ varid: Option<usize>,
+ names: Vec<&'parser str>,
+ help: &'parser str,
+ action: Action<'parser>,
+}
+
+struct EnvVar<'parser> {
+ varid: usize,
+ name: &'parser str,
+ action: Box<IArgAction + 'parser>,
+}
+
+impl<'a> Hash for GenericOption<'a> {
+ fn hash<H>(&self, state: &mut H) where H: Hasher {
+ self.id.hash(state);
+ }
+}
+
+impl<'a> PartialEq for GenericOption<'a> {
+ fn eq(&self, other: &GenericOption<'a>) -> bool {
+ return self.id == other.id;
+ }
+}
+
+impl<'a> Eq for GenericOption<'a> {}
+
+impl<'a> Hash for GenericArgument<'a> {
+ fn hash<H>(&self, state: &mut H) where H: Hasher {
+ self.id.hash(state);
+ }
+}
+
+impl<'a> PartialEq for GenericArgument<'a> {
+ fn eq(&self, other: &GenericArgument<'a>) -> bool {
+ return self.id == other.id;
+ }
+}
+
+impl<'a> Eq for GenericArgument<'a> {}
+
+pub struct Var {
+ id: usize,
+ metavar: String,
+ required: bool,
+}
+
+impl Hash for Var {
+ fn hash<H>(&self, state: &mut H) where H: Hasher {
+ self.id.hash(state);
+ }
+}
+
+impl PartialEq for Var {
+ fn eq(&self, other: &Var) -> bool {
+ return self.id == other.id;
+ }
+}
+
+impl Eq for Var {}
+
+struct Context<'ctx, 'parser: 'ctx> {
+ parser: &'ctx ArgumentParser<'parser>,
+ set_vars: HashSet<usize>,
+ list_options: HashMap<Rc<GenericOption<'parser>>, Vec<&'ctx str>>,
+ list_arguments: HashMap<Rc<GenericArgument<'parser>>, Vec<&'ctx str>>,
+ arguments: Vec<&'ctx str>,
+ iter: Peekable<Iter<'ctx, String>>,
+ stderr: &'ctx mut (Write + 'ctx),
+}
+
+impl<'a, 'b> Context<'a, 'b> {
+
+ fn parse_option(&mut self, opt: Rc<GenericOption<'b>>,
+ optarg: Option<&'a str>)
+ -> ParseResult
+ {
+ let value = match optarg {
+ Some(value) => value,
+ None => match self.iter.next() {
+ Some(value) => {
+ &value[..]
+ }
+ None => {
+ return match opt.action {
+ Many(_) => Parsed,
+ _ => Error(format!(
+ // TODO(tailhook) is {:?} ok?
+ "Option {:?} requires an argument", opt.names)),
+ };
+ }
+ },
+ };
+ match opt.varid {
+ Some(varid) => { self.set_vars.insert(varid); }
+ None => {}
+ }
+ match opt.action {
+ Single(ref action) => {
+ return action.parse_arg(value);
+ }
+ Push(_) => {
+ (match self.list_options.entry(opt.clone()) {
+ Entry::Occupied(occ) => occ.into_mut(),
+ Entry::Vacant(vac) => vac.insert(Vec::new()),
+ }).push(value);
+ return Parsed;
+ }
+ Many(_) => {
+ let vec = match self.list_options.entry(opt.clone()) {
+ Entry::Occupied(occ) => occ.into_mut(),
+ Entry::Vacant(vac) => vac.insert(Vec::new()),
+ };
+ vec.push(value);
+ match optarg {
+ Some(_) => return Parsed,
+ _ => {}
+ }
+ loop {
+ match self.iter.peek() {
+ None => { break; }
+ Some(arg) if arg.starts_with("-") => {
+ break;
+ }
+ Some(value) => {
+ vec.push(&value[..]);
+ }
+ }
+ self.iter.next();
+ }
+ return Parsed;
+ }
+ _ => panic!(),
+ };
+ }
+
+ fn parse_long_option(&mut self, arg: &'a str) -> ParseResult {
+ let mut equals_iter = arg.splitn(2, '=');
+ let optname = equals_iter.next().unwrap();
+ let valueref = equals_iter.next();
+ let opt = self.parser.long_options.get(&optname.to_string());
+ match opt {
+ Some(opt) => {
+ match opt.action {
+ Flag(ref action) => {
+ match valueref {
+ Some(_) => {
+ return Error(format!(
+ "Option {} does not accept an argument",
+ optname));
+ }
+ None => {
+ match opt.varid {
+ Some(varid) => {
+ self.set_vars.insert(varid);
+ }
+ None => {}
+ }
+ return action.parse_flag();
+ }
+ }
+ }
+ Single(_) | Push(_) | Many(_) => {
+ return self.parse_option(opt.clone(), valueref);
+ }
+ }
+ }
+ None => {
+ return Error(format!("Unknown option {}", arg));
+ }
+ }
+ }
+
+ fn parse_short_options<'x>(&'x mut self, arg: &'a str) -> ParseResult {
+ let mut iter = arg.char_indices();
+ iter.next();
+ for (idx, ch) in iter {
+ let opt = match self.parser.short_options.get(&ch) {
+ Some(opt) => { opt }
+ None => {
+ return Error(format!("Unknown short option \"{}\"", ch));
+ }
+ };
+ let res = match opt.action {
+ Flag(ref action) => {
+ match opt.varid {
+ Some(varid) => { self.set_vars.insert(varid); }
+ None => {}
+ }
+ action.parse_flag()
+ }
+ Single(_) | Push(_) | Many(_) => {
+ let value;
+ if idx + 1 < arg.len() {
+ value = Some(&arg[idx+1..arg.len()]);
+ } else {
+ value = None;
+ }
+ return self.parse_option(opt.clone(), value);
+ }
+ };
+ match res {
+ Parsed => { continue; }
+ x => { return x; }
+ }
+ }
+ return Parsed;
+ }
+
+ fn postpone_argument(&mut self, arg: &'a str) {
+ self.arguments.push(arg);
+ }
+
+ fn parse_options(&mut self) -> ParseResult {
+ self.iter.next(); // Command name
+ loop {
+ let next = self.iter.next();
+ let arg = match next {
+ Some(arg) => { arg }
+ None => { break; }
+ };
+ let res = match ArgumentKind::check(&arg[..]) {
+ Positional => {
+ self.postpone_argument(&arg[..]);
+ if self.parser.stop_on_first_argument {
+ break;
+ }
+ continue;
+ }
+ LongOption => self.parse_long_option(&arg[..]),
+ ShortOption => self.parse_short_options(&arg[..]),
+ Delimiter => {
+ if !self.parser.silence_double_dash {
+ self.postpone_argument("--");
+ }
+ break;
+ }
+ };
+ match res {
+ Parsed => continue,
+ _ => return res,
+ }
+ }
+
+ loop {
+ match self.iter.next() {
+ None => break,
+ Some(arg) => self.postpone_argument(&arg[..]),
+ }
+ }
+ return Parsed;
+ }
+
+ fn parse_arguments(&mut self) -> ParseResult {
+ let mut pargs = self.parser.arguments.iter();
+ for arg in self.arguments.iter() {
+ let opt;
+ loop {
+ match pargs.next() {
+ Some(option) => {
+ if self.set_vars.contains(&option.varid) {
+ continue;
+ }
+ opt = option;
+ break;
+ }
+ None => match self.parser.catchall_argument {
+ Some(ref option) => {
+ opt = option;
+ break;
+ }
+ None => return Error(format!(
+ "Unexpected argument {}", arg)),
+ }
+ };
+ }
+ let res = match opt.action {
+ Single(ref act) => {
+ self.set_vars.insert(opt.varid);
+ act.parse_arg(*arg)
+ },
+ Many(_) | Push(_) => {
+ (match self.list_arguments.entry(opt.clone()) {
+ Entry::Occupied(occ) => occ.into_mut(),
+ Entry::Vacant(vac) => vac.insert(Vec::new()),
+ }).push(*arg);
+ Parsed
+ },
+ _ => unreachable!(),
+ };
+ match res {
+ Parsed => continue,
+ _ => return res,
+ }
+ }
+ return Parsed;
+ }
+
+ fn parse_list_vars(&mut self) -> ParseResult {
+ for (opt, lst) in self.list_options.iter() {
+ match opt.action {
+ Push(ref act) | Many(ref act) => {
+ let res = act.parse_args(&lst[..]);
+ match res {
+ Parsed => continue,
+ _ => return res,
+ }
+ }
+ _ => panic!(),
+ }
+ }
+ for (opt, lst) in self.list_arguments.iter() {
+ match opt.action {
+ Push(ref act) | Many(ref act) => {
+ let res = act.parse_args(&lst[..]);
+ match res {
+ Parsed => continue,
+ _ => return res,
+ }
+ }
+ _ => panic!(),
+ }
+ }
+ return Parsed;
+ }
+
+ fn parse_env_vars(&mut self) -> ParseResult {
+ for evar in self.parser.env_vars.iter() {
+ match env::var(evar.name) {
+ Ok(val) => {
+ match evar.action.parse_arg(&val[..]) {
+ Parsed => {
+ self.set_vars.insert(evar.varid);
+ continue;
+ }
+ Error(err) => {
+ write!(self.stderr,
+ "WARNING: Environment variable {}: {}\n",
+ evar.name, err).ok();
+ }
+ _ => unreachable!(),
+ }
+ }
+ Err(_) => {}
+ }
+ }
+ return Parsed;
+ }
+
+ fn check_required(&mut self) -> ParseResult {
+ // Check for required arguments
+ for var in self.parser.vars.iter() {
+ if var.required && !self.set_vars.contains(&var.id) {
+ // First try positional arguments
+ for opt in self.parser.arguments.iter() {
+ if opt.varid == var.id {
+ return Error(format!(
+ "Argument {} is required", opt.name));
+ }
+ }
+ // Then options
+ let mut all_options = vec!();
+ for opt in self.parser.options.iter() {
+ match opt.varid {
+ Some(varid) if varid == var.id => {}
+ _ => { continue }
+ }
+ all_options.extend(opt.names.clone().into_iter());
+ }
+ if all_options.len() > 1 {
+ return Error(format!(
+ "One of the options {:?} is required", all_options));
+ } else if all_options.len() == 1 {
+ return Error(format!(
+ "Option {:?} is required", all_options));
+ }
+ // Then envvars
+ for envvar in self.parser.env_vars.iter() {
+ if envvar.varid == var.id {
+ return Error(format!(
+ "Environment var {} is required", envvar.name));
+ }
+ }
+ }
+ }
+ return Parsed;
+ }
+
+ fn parse(parser: &ArgumentParser, args: &Vec<String>, stderr: &mut Write)
+ -> ParseResult
+ {
+ let mut ctx = Context {
+ parser: parser,
+ iter: args.iter().peekable(),
+ set_vars: HashSet::new(),
+ list_options: HashMap::new(),
+ list_arguments: HashMap::new(),
+ arguments: Vec::new(),
+ stderr: stderr,
+ };
+
+ match ctx.parse_env_vars() {
+ Parsed => {}
+ x => { return x; }
+ }
+
+ match ctx.parse_options() {
+ Parsed => {}
+ x => { return x; }
+ }
+
+ match ctx.parse_arguments() {
+ Parsed => {}
+ x => { return x; }
+ }
+
+ match ctx.parse_list_vars() {
+ Parsed => {}
+ x => { return x; }
+ }
+
+ match ctx.check_required() {
+ Parsed => {}
+ x => { return x; }
+ }
+
+ return Parsed;
+ }
+}
+
+pub struct Ref<'parser:'refer, 'refer, T: 'parser> {
+ cell: Rc<RefCell<&'parser mut T>>,
+ varid: usize,
+ parser: &'refer mut ArgumentParser<'parser>,
+}
+
+impl<'parser, 'refer, T> Ref<'parser, 'refer, T> {
+
+ pub fn add_option<'x, A: TypedAction<T>>(&'x mut self,
+ names: &[&'parser str], action: A, help: &'parser str)
+ -> &'x mut Ref<'parser, 'refer, T>
+ {
+ {
+ let var = &mut self.parser.vars[self.varid];
+ if var.metavar.len() == 0 {
+ let mut longest_name = names[0];
+ let mut llen = longest_name.len();
+ for name in names.iter() {
+ if name.len() > llen {
+ longest_name = *name;
+ llen = longest_name.len();
+ }
+ }
+ if llen > 2 {
+ var.metavar = longest_name[2..llen]
+ .to_ascii_uppercase().replace("-", "_");
+ }
+ }
+ }
+ self.parser.add_option_for(Some(self.varid), names,
+ action.bind(self.cell.clone()),
+ help);
+ return self;
+ }
+
+ pub fn add_argument<'x, A: TypedAction<T>>(&'x mut self,
+ name: &'parser str, action: A, help: &'parser str)
+ -> &'x mut Ref<'parser, 'refer, T>
+ {
+ let act = action.bind(self.cell.clone());
+ let opt = Rc::new(GenericArgument {
+ id: self.parser.arguments.len(),
+ varid: self.varid,
+ name: name,
+ help: help,
+ action: act,
+ });
+ match opt.action {
+ Flag(_) => panic!("Flag arguments can't be positional"),
+ Many(_) | Push(_) => {
+ match self.parser.catchall_argument {
+ Some(ref y) => panic!(format!(
+ "Option {} conflicts with option {}",
+ name, y.name)),
+ None => {},
+ }
+ self.parser.catchall_argument = Some(opt);
+ }
+ Single(_) => {
+ self.parser.arguments.push(opt);
+ }
+ }
+ {
+ let var = &mut self.parser.vars[self.varid];
+ if var.metavar.len() == 0 {
+ var.metavar = name.to_string();
+ }
+ }
+ return self;
+ }
+
+ pub fn metavar<'x>(&'x mut self, name: &str)
+ -> &'x mut Ref<'parser, 'refer, T>
+ {
+ {
+ let var = &mut self.parser.vars[self.varid];
+ var.metavar = name.to_string();
+ }
+ return self;
+ }
+
+ pub fn required<'x>(&'x mut self)
+ -> &'x mut Ref<'parser, 'refer, T>
+ {
+ {
+ let var = &mut self.parser.vars[self.varid];
+ var.required = true;
+ }
+ return self;
+ }
+}
+
+impl<'parser, 'refer, T: 'static + FromStr> Ref<'parser, 'refer, T> {
+ pub fn envvar<'x>(&'x mut self, varname: &'parser str)
+ -> &'x mut Ref<'parser, 'refer, T>
+ {
+ self.parser.env_vars.push(Rc::new(EnvVar {
+ varid: self.varid,
+ name: varname,
+ action: Box::new(StoreAction { cell: self.cell.clone() }),
+ }));
+ return self;
+ }
+}
+
+/// The main argument parser class
+pub struct ArgumentParser<'parser> {
+ description: &'parser str,
+ vars: Vec<Box<Var>>,
+ options: Vec<Rc<GenericOption<'parser>>>,
+ arguments: Vec<Rc<GenericArgument<'parser>>>,
+ env_vars: Vec<Rc<EnvVar<'parser>>>,
+ catchall_argument: Option<Rc<GenericArgument<'parser>>>,
+ short_options: HashMap<char, Rc<GenericOption<'parser>>>,
+ long_options: HashMap<String, Rc<GenericOption<'parser>>>,
+ stop_on_first_argument: bool,
+ silence_double_dash: bool,
+}
+
+
+
+impl<'parser> ArgumentParser<'parser> {
+
+ /// Create an empty argument parser
+ pub fn new() -> ArgumentParser<'parser> {
+
+ let mut ap = ArgumentParser {
+ description: "",
+ vars: Vec::new(),
+ env_vars: Vec::new(),
+ arguments: Vec::new(),
+ catchall_argument: None,
+ options: Vec::new(),
+ short_options: HashMap::new(),
+ long_options: HashMap::new(),
+ stop_on_first_argument: false,
+ silence_double_dash: true,
+ };
+ ap.add_option_for(None, &["-h", "--help"], Flag(Box::new(HelpAction)),
+ "Show this help message and exit");
+ return ap;
+ }
+
+ /// Borrow mutable variable for an argument
+ ///
+ /// This returns `Ref` object which should be used configure the option
+ pub fn refer<'x, T>(&'x mut self, val: &'parser mut T)
+ -> Box<Ref<'parser, 'x, T>>
+ {
+ let cell = Rc::new(RefCell::new(val));
+ let id = self.vars.len();
+ self.vars.push(Box::new(Var {
+ id: id,
+ required: false,
+ metavar: "".to_string(),
+ }));
+ return Box::new(Ref {
+ cell: cell.clone(),
+ varid: id,
+ parser: self,
+ });
+ }
+
+ /// Add option to argument parser
+ ///
+ /// This is only useful for options that don't store value. For
+ /// example `Print(...)`
+ pub fn add_option<F:IFlagAction + 'parser>(&mut self,
+ names: &[&'parser str], action: F, help: &'parser str)
+ {
+ self.add_option_for(None, names, Flag(Box::new(action)), help);
+ }
+
+ /// Set description of the command
+ pub fn set_description(&mut self, descr: &'parser str) {
+ self.description = descr;
+ }
+
+ fn add_option_for(&mut self, var: Option<usize>,
+ names: &[&'parser str],
+ action: Action<'parser>, help: &'parser str)
+ {
+ let opt = Rc::new(GenericOption {
+ id: self.options.len(),
+ varid: var,
+ names: names.to_vec(),
+ help: help,
+ action: action,
+ });
+
+ if names.len() < 1 {
+ panic!("At least one name for option must be specified");
+ }
+ for nameptr in names.iter() {
+ let name = *nameptr;
+ match ArgumentKind::check(name) {
+ Positional|Delimiter => {
+ panic!("Bad argument name {}", name);
+ }
+ LongOption => {
+ self.long_options.insert(
+ name.to_string(), opt.clone());
+ }
+ ShortOption => {
+ if name.len() > 2 {
+ panic!("Bad short argument {}", name);
+ }
+ self.short_options.insert(
+ name.as_bytes()[1] as char, opt.clone());
+ }
+ }
+ }
+ self.options.push(opt);
+ }
+
+ /// Print help
+ ///
+ /// Usually command-line option is used for printing help,
+ /// this is here for any awkward cases
+ pub fn print_help(&self, name: &str, writer: &mut Write) -> IoResult<()> {
+ return HelpFormatter::print_help(self, name, writer);
+ }
+
+ /// Print usage
+ ///
+ /// Usually printed into stderr on error of command-line parsing
+ pub fn print_usage(&self, name: &str, writer: &mut Write) -> IoResult<()>
+ {
+ return HelpFormatter::print_usage(self, name, writer);
+ }
+
+ /// Parse arguments
+ ///
+ /// This is most powerful method. Usually you need `parse_args`
+ /// or `parse_args_or_exit` instead
+ pub fn parse(&self, args: Vec<String>,
+ stdout: &mut Write, stderr: &mut Write)
+ -> Result<(), i32>
+ {
+ let name = if args.len() > 0 { &args[0][..] } else { "unknown" };
+ match Context::parse(self, &args, stderr) {
+ Parsed => return Ok(()),
+ Exit => return Err(0),
+ Help => {
+ self.print_help(name, stdout).unwrap();
+ return Err(0);
+ }
+ Error(message) => {
+ self.error(&name[..], &message[..], stderr);
+ return Err(2);
+ }
+ }
+ }
+
+ /// Write an error similar to one produced by the library itself
+ ///
+ /// Only needed if you like to do some argument validation that is out
+ /// of scope of the argparse
+ pub fn error(&self, command: &str, message: &str, writer: &mut Write) {
+ self.print_usage(command, writer).unwrap();
+ write!(writer, "{}: {}\n", command, message).ok();
+ }
+
+ /// Configure parser to ignore options when first non-option argument is
+ /// encountered.
+ ///
+ /// Useful for commands that want to pass following options to the
+ /// subcommand or subprocess, but need some options to be set before
+ /// command is specified.
+ pub fn stop_on_first_argument(&mut self, want_stop: bool) {
+ self.stop_on_first_argument = want_stop;
+ }
+
+ /// Do not put double-dash (bare `--`) into argument
+ ///
+ /// The double-dash is used to stop parsing options and treat all the
+ /// following tokens as the arguments regardless of whether they start
+ /// with dash (minus) or not.
+ ///
+ /// The method only useful for `List` arguments. On by default. The method
+ /// allows to set option to `false` so that `cmd xx -- yy` will get
+ /// ``xx -- yy`` as arguments instead of ``xx yy`` by default. This is
+ /// useful if your ``--`` argument is meaningful. Only first double-dash
+ /// is ignored by default.
+ pub fn silence_double_dash(&mut self, silence: bool) {
+ self.silence_double_dash = silence;
+ }
+
+ /// Convenience method to parse arguments
+ ///
+ /// On error returns error code that is supposed to be returned by
+ /// an application. (i.e. zero on `--help` and `2` on argument error)
+ pub fn parse_args(&self) -> Result<(), i32> {
+ // TODO(tailhook) can we get rid of collect?
+ return self.parse(env::args().collect(),
+ &mut stdout(), &mut stderr());
+ }
+
+ /// The simplest conveninece method
+ ///
+ /// The method returns only in case of successful parsing or exits with
+ /// appropriate code (including successful on `--help`) otherwise.
+ pub fn parse_args_or_exit(&self) {
+ // TODO(tailhook) can we get rid of collect?
+ self.parse(env::args().collect(), &mut stdout(), &mut stderr())
+ .map_err(|c| exit(c))
+ .ok();
+ }
+}
+
+struct HelpFormatter<'a, 'b: 'a> {
+ name: &'a str,
+ parser: &'a ArgumentParser<'b>,
+ buf: &'a mut (Write + 'a),
+}
+
+impl<'a, 'b> HelpFormatter<'a, 'b> {
+ pub fn print_usage(parser: &ArgumentParser, name: &str, writer: &mut Write)
+ -> IoResult<()>
+ {
+ return HelpFormatter { parser: parser, name: name, buf: writer }
+ .write_usage();
+ }
+
+ pub fn print_help(parser: &ArgumentParser, name: &str, writer: &mut Write)
+ -> IoResult<()>
+ {
+ return HelpFormatter { parser: parser, name: name, buf: writer }
+ .write_help();
+ }
+
+ pub fn print_argument(&mut self, arg: &GenericArgument<'b>)
+ -> IoResult<()>
+ {
+ let mut num = 2;
+ try!(write!(self.buf, " {}", arg.name));
+ num += arg.name.len();
+ if num >= OPTION_WIDTH {
+ try!(write!(self.buf, "\n"));
+ for _ in 0..OPTION_WIDTH {
+ try!(write!(self.buf, " "));
+ }
+ } else {
+ for _ in num..OPTION_WIDTH {
+ try!(write!(self.buf, " "));
+ }
+ }
+ try!(wrap_text(self.buf, arg.help, TOTAL_WIDTH, OPTION_WIDTH));
+ try!(write!(self.buf, "\n"));
+ return Ok(());
+ }
+
+ pub fn print_option(&mut self, opt: &GenericOption<'b>) -> IoResult<()> {
+ let mut num = 2;
+ try!(write!(self.buf, " "));
+ let mut niter = opt.names.iter();
+ let name = niter.next().unwrap();
+ try!(write!(self.buf, "{}", name));
+ num += name.len();
+ for name in niter {
+ try!(write!(self.buf, ","));
+ try!(write!(self.buf, "{}", name));
+ num += name.len() + 1;
+ }
+ match opt.action {
+ Flag(_) => {}
+ Single(_) | Push(_) | Many(_) => {
+ try!(write!(self.buf, " "));
+ let var = &self.parser.vars[opt.varid.unwrap()];
+ try!(write!(self.buf, "{}", &var.metavar[..]));
+ num += var.metavar.len() + 1;
+ }
+ }
+ if num >= OPTION_WIDTH {
+ try!(write!(self.buf, "\n"));
+ for _ in 0..OPTION_WIDTH {
+ try!(write!(self.buf, " "));
+ }
+ } else {
+ for _ in num..OPTION_WIDTH {
+ try!(write!(self.buf, " "));
+ }
+ }
+ try!(wrap_text(self.buf, opt.help, TOTAL_WIDTH, OPTION_WIDTH));
+ try!(write!(self.buf, "\n"));
+ return Ok(());
+ }
+
+ fn write_help(&mut self) -> IoResult<()> {
+ try!(self.write_usage());
+ try!(write!(self.buf, "\n"));
+ if self.parser.description.len() > 0 {
+ try!(wrap_text(self.buf, self.parser.description,TOTAL_WIDTH, 0));
+ try!(write!(self.buf, "\n"));
+ }
+ if self.parser.arguments.len() > 0
+ || self.parser.catchall_argument.is_some()
+ {
+ try!(write!(self.buf, "\nPositional arguments:\n"));
+ for arg in self.parser.arguments.iter() {
+ try!(self.print_argument(&**arg));
+ }
+ match self.parser.catchall_argument {
+ Some(ref opt) => {
+ try!(self.print_argument(&**opt));
+ }
+ None => {}
+ }
+ }
+ if self.parser.short_options.len() > 0
+ || self.parser.long_options.len() > 0
+ {
+ try!(write!(self.buf, "\nOptional arguments:\n"));
+ for opt in self.parser.options.iter() {
+ try!(self.print_option(&**opt));
+ }
+ }
+ return Ok(());
+ }
+
+ fn write_usage(&mut self) -> IoResult<()> {
+ try!(write!(self.buf, "Usage:\n "));
+ try!(write!(self.buf, "{}", self.name));
+ if self.parser.options.len() != 0 {
+ if self.parser.short_options.len() > 1
+ || self.parser.long_options.len() > 1
+ {
+ try!(write!(self.buf, " [OPTIONS]"));
+ }
+ for opt in self.parser.arguments.iter() {
+ let var = &self.parser.vars[opt.varid];
+ try!(write!(self.buf, " "));
+ if !var.required {
+ try!(write!(self.buf, "["));
+ }
+ try!(write!(self.buf, "{}",
+ &opt.name.to_ascii_uppercase()[..]));
+ if !var.required {
+ try!(write!(self.buf, "]"));
+ }
+ }
+ match self.parser.catchall_argument {
+ Some(ref opt) => {
+ let var = &self.parser.vars[opt.varid];
+ try!(write!(self.buf, " "));
+ if !var.required {
+ try!(write!(self.buf, "["));
+ }
+ try!(write!(self.buf, "{}",
+ &opt.name.to_ascii_uppercase()[..]));
+ if !var.required {
+ try!(write!(self.buf, " ...]"));
+ } else {
+ try!(write!(self.buf, " [...]"));
+ }
+ }
+ None => {}
+ }
+ }
+ try!(write!(self.buf, "\n"));
+ return Ok(());
+ }
+
+}
diff --git a/argparse/src/print.rs b/argparse/src/print.rs
new file mode 100644
index 0000000..d0335c5
--- /dev/null
+++ b/argparse/src/print.rs
@@ -0,0 +1,13 @@
+use Print;
+use action::{IFlagAction, ParseResult};
+
+impl IFlagAction for Print {
+ fn parse_flag(&self) -> ParseResult {
+ if self.0.ends_with("\n") {
+ print!("{}", self.0);
+ } else {
+ println!("{}", self.0);
+ }
+ return ParseResult::Exit;
+ }
+}
diff --git a/argparse/src/test_bool.rs b/argparse/src/test_bool.rs
new file mode 100644
index 0000000..a30cf3c
--- /dev/null
+++ b/argparse/src/test_bool.rs
@@ -0,0 +1,98 @@
+use parser::ArgumentParser;
+use super::Store;
+use super::{StoreTrue, StoreFalse};
+use test_parser::{check_ok};
+
+fn store_true(args: &[&str]) -> bool {
+ let mut verbose = false;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut verbose)
+ .add_option(&["-t", "--true"], StoreTrue,
+ "Store true action");
+ check_ok(&ap, args);
+ }
+ return verbose;
+}
+
+#[test]
+fn test_store_true() {
+ assert!(!store_true(&["./argparse_test"]));
+ assert!(store_true(&["./argparse_test", "--true"]));
+}
+
+fn store_false(args: &[&str]) -> bool {
+ let mut verbose = true;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut verbose)
+ .add_option(&["-f", "--false"], StoreFalse,
+ "Store false action");
+ check_ok(&ap, args);
+ }
+ return verbose;
+}
+#[test]
+fn test_store_false() {
+ assert!(store_false(&["./argparse_test"]));
+ assert!(!store_false(&["./argparse_test", "--false"]));
+}
+
+fn store_bool(args: &[&str]) -> bool {
+ let mut verbose = false;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut verbose)
+ .add_option(&["-f", "--false"], StoreFalse,
+ "Store false action")
+ .add_option(&["-t", "--true"], StoreTrue,
+ "Store false action");
+ check_ok(&ap, args);
+ }
+ return verbose;
+}
+
+#[test]
+fn test_bool() {
+ assert!(!store_bool(&["./argparse_test"]));
+ assert!(store_bool(&["./argparse_test", "-t"]));
+ assert!(!store_bool(&["./argparse_test", "-f"]));
+ assert!(store_bool(&["./argparse_test", "-fft"]));
+ assert!(!store_bool(&["./argparse_test", "-fffft", "-f"]));
+ assert!(store_bool(&["./argparse_test", "--false", "-fffft", "-f",
+ "--true"]));
+}
+
+fn set_bool(args: &[&str]) -> bool {
+ let mut verbose = false;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut verbose)
+ .add_option(&["-s", "--set"], Store,
+ "Set boolean value");
+ check_ok(&ap, args);
+ }
+ return verbose;
+}
+
+
+#[test]
+fn test_set_bool() {
+ assert!(!set_bool(&["./argparse_test"]));
+ assert!(set_bool(&["./argparse_test", "-strue"]));
+ assert!(!set_bool(&["./argparse_test", "-sfalse"]));
+
+ // Unfortunately other values do not work
+}
+
+#[test]
+#[should_panic(expected="Bad value yes")]
+fn test_bad_bools1() {
+ assert!(!set_bool(&["./argparse_test", "-syes"]));
+}
+
+#[test]
+#[should_panic(expected="Bad value no")]
+fn test_bad_bools2() {
+ assert!(!set_bool(&["./argparse_test", "-sno"]));
+}
diff --git a/argparse/src/test_const.rs b/argparse/src/test_const.rs
new file mode 100644
index 0000000..b12e3d8
--- /dev/null
+++ b/argparse/src/test_const.rs
@@ -0,0 +1,28 @@
+use parser::ArgumentParser;
+use super::{PushConst};
+use test_parser::{check_ok};
+
+
+fn push_const(args: &[&str]) -> Vec<u32> {
+ let mut res = vec!();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut res)
+ .add_option(&["-o", "--one"], PushConst(1),
+ "Add one to the list")
+ .add_option(&["-t", "--two"], PushConst(2),
+ "Add two to the list")
+ .add_option(&["-3", "--three"], PushConst(3),
+ "Add three to the list");
+ check_ok(&ap, args);
+ }
+ return res;
+}
+
+#[test]
+fn test_push() {
+ assert_eq!(push_const(&["./argparse_test"]), vec!());
+ assert_eq!(push_const(&["./argparse_test", "--one"]), vec!(1));
+ assert_eq!(push_const(&["./argparse_test", "-3"]), vec!(3));
+ assert_eq!(push_const(&["./argparse_test", "-oo3tt"]), vec!(1, 1, 3, 2, 2));
+}
diff --git a/argparse/src/test_enum.rs b/argparse/src/test_enum.rs
new file mode 100644
index 0000000..5205738
--- /dev/null
+++ b/argparse/src/test_enum.rs
@@ -0,0 +1,51 @@
+use std::str::FromStr;
+
+use parser::ArgumentParser;
+use super::Store;
+use test_parser::{check_ok};
+
+use self::Greeting::{Hello, Hi, NoGreeting};
+
+
+#[derive(PartialEq, Eq, Debug)]
+enum Greeting {
+ Hello,
+ Hi,
+ NoGreeting,
+}
+
+impl FromStr for Greeting {
+ type Err = ();
+ fn from_str(src: &str) -> Result<Greeting, ()> {
+ return match src {
+ "hello" => Ok(Hello),
+ "hi" => Ok(Hi),
+ _ => Err(()),
+ };
+ }
+}
+
+fn parse_enum(args: &[&str]) -> Greeting {
+ let mut val = NoGreeting;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-g"], Store,
+ "Greeting");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_parse_enum() {
+ assert_eq!(parse_enum(&["./argparse_test"]), NoGreeting);
+ assert_eq!(parse_enum(&["./argparse_test", "-ghello"]), Hello);
+ assert_eq!(parse_enum(&["./argparse_test", "-ghi"]), Hi);
+}
+
+#[test]
+#[should_panic]
+fn test_parse_error() {
+ parse_enum(&["./argparse_test", "-ghell"]);
+}
diff --git a/argparse/src/test_env.rs b/argparse/src/test_env.rs
new file mode 100644
index 0000000..2e1b649
--- /dev/null
+++ b/argparse/src/test_env.rs
@@ -0,0 +1,43 @@
+use std::env;
+
+use parser::ArgumentParser;
+use super::Store;
+use test_parser::{check_ok};
+
+
+fn required(args: &[&str]) -> (isize, isize) {
+ let mut val1 = 1isize;
+ let mut val2 = 2isize;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val1)
+ .add_option(&["--v1"], Store, "The value 1")
+ .add_argument("v1", Store, "The value 1")
+ .envvar("TEST_ENV_REQUIRED_V1")
+ .required();
+ ap.refer(&mut val2)
+ .add_argument("v2", Store, "The value 2");
+ check_ok(&ap, args);
+ }
+ return (val1, val2)
+}
+
+#[test]
+#[should_panic]
+fn test_required() {
+ env::set_var("TEST_ENV_REQUIRED_V1", "some_crap");
+ required(&["./argparse_test"]);
+ env::remove_var("TEST_ENV_REQUIRED_V1");
+}
+
+#[test]
+fn test_req() {
+ env::set_var("TEST_ENV_REQUIRED_V1", "some_crap");
+ assert_eq!(required(&["./argparse_test", "10"]), (10, 2));
+ assert_eq!(required(&["./argparse_test", "11", "21"]), (11, 21));
+ assert_eq!(required(&["./argparse_test", "--v1=7"]), (7, 2));
+ env::set_var("TEST_ENV_REQUIRED_V1", "9");
+ assert_eq!(required(&["./argparse_test", "10"]), (9, 10));
+ assert_eq!(required(&["./argparse_test", "7", "--v1=15"]), (15, 7));
+ env::remove_var("TEST_ENV_REQUIRED_V1");
+}
diff --git a/argparse/src/test_float.rs b/argparse/src/test_float.rs
new file mode 100644
index 0000000..9ef0f56
--- /dev/null
+++ b/argparse/src/test_float.rs
@@ -0,0 +1,50 @@
+use parser::ArgumentParser;
+use super::{IncrBy,DecrBy};
+use super::Store;
+use test_parser::{check_ok};
+
+fn incr_decr(args: &[&str]) -> f32 {
+ let mut val = 0f32;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-d", "--decr"], DecrBy(0.25f32),
+ "Decrement value")
+ .add_option(&["-i", "--incr"], IncrBy(0.5f32),
+ "Increment value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_incr_decr() {
+ assert_eq!(incr_decr(&["./argparse_test",
+ "--incr", "-iii"]), 2.0);
+ assert_eq!(incr_decr(&["./argparse_test",
+ "-iiddd", "--incr", "-iii"]), 2.25);
+}
+
+#[test]
+fn test_float() {
+ let mut val = 0.1;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-s", "--set"], Store,
+ "Set float value");
+ check_ok(&ap, &["./argparse_test", "-s", "15.125"]);
+ }
+ assert_eq!(val, 15.125);
+}
+
+#[test]
+#[should_panic]
+fn test_fail() {
+ let mut val = 0.1;
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-s", "--set"], Store,
+ "Set float value");
+ check_ok(&ap, &["./argparse_test", "-s", "test"]);
+}
diff --git a/argparse/src/test_help.rs b/argparse/src/test_help.rs
new file mode 100644
index 0000000..f23b14b
--- /dev/null
+++ b/argparse/src/test_help.rs
@@ -0,0 +1,154 @@
+use std::str::from_utf8;
+
+use parser::ArgumentParser;
+use super::{Store, List};
+
+#[test]
+fn test_empty() {
+ let mut ap = ArgumentParser::new();
+ let mut buf = Vec::<u8>::new();
+ ap.set_description("Test program");
+ assert!(ap.print_help("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n".to_string()
+ + " ./argparse_test\n"
+ + "\n"
+ + "Test program\n"
+ + "\n"
+ + "Optional arguments:\n"
+ + " -h,--help Show this help message and exit\n"
+ , from_utf8(&buf[..]).unwrap().to_string());
+}
+
+#[test]
+fn test_options() {
+ let mut val = 0;
+ let mut val2 = 0;
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Test program. The description of the program is ought
+ to be very long, because we want to test how word wrapping works for
+ it. So some more text would be ok for the test");
+ ap.refer(&mut val)
+ .add_option(&["--value"], Store,
+ "Set integer value");
+ ap.refer(&mut val2)
+ .add_option(&["-L", "--long-option"], Store,
+ "Long option value");
+ let mut buf = Vec::<u8>::new();
+ assert!(ap.print_help("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n".to_string()
+ + " ./argparse_test [OPTIONS]
+
+Test program. The description of the program is ought to be very long, because
+we want to test how word wrapping works for it. So some more text would be ok
+for the test\n"
+ + "\n"
+ + "Optional arguments:\n"
+ + " -h,--help Show this help message and exit\n"
+ + " --value VALUE Set integer value\n"
+ + " -L,--long-option LONG_OPTION\n"
+ + " Long option value\n"
+ , from_utf8(&buf[..]).unwrap().to_string());
+}
+
+#[test]
+fn test_argument() {
+ let mut val = 0;
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Test program");
+ ap.refer(&mut val)
+ .add_argument("value", Store,
+ "Integer value");
+ let mut buf = Vec::<u8>::new();
+ assert!(ap.print_help("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n".to_string()
+ + " ./argparse_test [VALUE]\n"
+ + "\n"
+ + "Test program\n"
+ + "\n"
+ + "Positional arguments:\n"
+ + " value Integer value\n"
+ + "\n"
+ + "Optional arguments:\n"
+ + " -h,--help Show this help message and exit\n"
+ , from_utf8(&buf[..]).unwrap().to_string());
+}
+
+#[test]
+fn test_arguments() {
+ let mut v1 = 0;
+ let mut v2 = Vec::<u32>::new();
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Test program");
+ ap.refer(&mut v1)
+ .add_argument("v1", Store,
+ "Integer value 1");
+ ap.refer(&mut v2)
+ .add_argument("v2", List,
+ "More values");
+ let mut buf = Vec::<u8>::new();
+ assert!(ap.print_help("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n".to_string()
+ + " ./argparse_test [V1] [V2 ...]\n"
+ + "\n"
+ + "Test program\n"
+ + "\n"
+ + "Positional arguments:\n"
+ + " v1 Integer value 1\n"
+ + " v2 More values\n"
+ + "\n"
+ + "Optional arguments:\n"
+ + " -h,--help Show this help message and exit\n"
+ , from_utf8(&buf[..]).unwrap().to_string());
+}
+
+#[test]
+fn test_req_arguments() {
+ let mut v1 = 0;
+ let mut v2 = Vec::<u32>::new();
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Test program");
+ ap.refer(&mut v1)
+ .add_argument("v1", Store,
+ "Integer value 1")
+ .required();
+ ap.refer(&mut v2)
+ .add_argument("v2", List,
+ "More values")
+ .required();
+ let mut buf = Vec::<u8>::new();
+ assert!(ap.print_help("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n".to_string()
+ + " ./argparse_test V1 V2 [...]\n"
+ + "\n"
+ + "Test program\n"
+ + "\n"
+ + "Positional arguments:\n"
+ + " v1 Integer value 1\n"
+ + " v2 More values\n"
+ + "\n"
+ + "Optional arguments:\n"
+ + " -h,--help Show this help message and exit\n"
+ , from_utf8(&buf[..]).unwrap().to_string());
+}
+
+#[test]
+fn test_metavar() {
+ let mut val2 = 0;
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Test program.");
+ ap.refer(&mut val2)
+ .add_option(&["-L", "--long-option"], Store,
+ "Long option value")
+ .metavar("VAL");
+ let mut buf = Vec::<u8>::new();
+ assert!(ap.print_help("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n".to_string()
+ + " ./argparse_test [OPTIONS]\n"
+ + "\n"
+ + "Test program.\n"
+ + "\n"
+ + "Optional arguments:\n"
+ + " -h,--help Show this help message and exit\n"
+ + " -L,--long-option VAL Long option value\n"
+ , from_utf8(&buf[..]).unwrap().to_string());
+}
diff --git a/argparse/src/test_int.rs b/argparse/src/test_int.rs
new file mode 100644
index 0000000..1826eaf
--- /dev/null
+++ b/argparse/src/test_int.rs
@@ -0,0 +1,107 @@
+use parser::ArgumentParser;
+use super::{IncrBy,DecrBy};
+use super::Store;
+use test_parser::{check_ok};
+
+fn incr_int(args: &[&str]) -> usize {
+ let mut val = 0;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-i", "--incr"], IncrBy(1usize),
+ "Increment value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_incr_int() {
+ assert_eq!(incr_int(&["./argparse_test"]), 0);
+ assert_eq!(incr_int(&["./argparse_test", "--incr"]), 1);
+ assert_eq!(incr_int(&["./argparse_test", "-iiiii"]), 5);
+}
+
+fn decr_int(args: &[&str]) -> isize {
+ let mut val = 5;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-d", "--decr"], DecrBy(1isize),
+ "Decrement value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_decr_int() {
+ assert_eq!(decr_int(&["./argparse_test"]), 5);
+ assert_eq!(decr_int(&["./argparse_test", "--decr"]), 4);
+ assert_eq!(decr_int(&["./argparse_test", "-dddddd"]), -1);
+}
+
+#[test]
+fn test_incr_decr() {
+ let mut val = 0;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-d", "--decr"], DecrBy(1isize),
+ "Decrement value")
+ .add_option(&["-i", "--incr"], IncrBy(1isize),
+ "Increment value");
+ check_ok(&ap, &["./argparse_test",
+ "-iiddd", "--incr", "-iii"]);
+ }
+ assert_eq!(val, 3);
+}
+
+fn set_int(args: &[&str]) -> isize {
+ let mut val = 0;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-s", "--set"], Store,
+ "Set integer value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_set_int() {
+ assert_eq!(set_int(&["./argparse_test", "-s", "10"]), 10);
+ assert_eq!(set_int(&["./argparse_test", "--set", "99"]), 99);
+ assert_eq!(set_int(&["./argparse_test", "-s", "7", "-s77"]), 77);
+ assert_eq!(set_int(&["./argparse_test", "-s333", "--set=123"]), 123);
+}
+
+#[test]
+#[should_panic(expected="Bad value 1.5")]
+fn test_set_int_bad() {
+ set_int(&["./argparse_test", "-s1.5"]);
+}
+
+fn set_i16(args: &[&str]) -> i16 {
+ let mut val = 0;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-s", "--set"], Store,
+ "Set integer value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_i16() {
+ assert_eq!(set_i16(&["./argparse_test", "-s", "124"]), 124);
+}
+
+#[test]
+#[should_panic(expected="Bad value 1000000")]
+fn test_i16_big() {
+ set_i16(&["./argparse_test", "-s", "1000000"]);
+}
diff --git a/argparse/src/test_many.rs b/argparse/src/test_many.rs
new file mode 100644
index 0000000..68e2cec
--- /dev/null
+++ b/argparse/src/test_many.rs
@@ -0,0 +1,95 @@
+use parser::ArgumentParser;
+use super::{List, Store, Collect};
+use test_parser::{check_ok};
+
+fn pos_list(args: &[&str]) -> (isize, Vec<isize>) {
+ let mut val1 = 1;
+ let mut val2 = Vec::new();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val1).add_argument("v1", Store, "The value 1");
+ ap.refer(&mut val2).add_argument("v2", List, "The list of vals");
+ check_ok(&ap, args);
+ }
+ return (val1, val2);
+}
+
+#[test]
+fn test_pos_list() {
+ assert_eq!(pos_list(&["./argparse_test", "10"]), (10, vec!()));
+ assert_eq!(pos_list(&["./argparse_test", "11", "21"]), (11, vec!(21)));
+ assert_eq!(pos_list(&["./argparse_test", "10", "20", "30"]),
+ (10, vec!(20, 30)));
+}
+
+fn pos_collect(args: &[&str]) -> Vec<isize> {
+ let mut lst = Vec::new();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut lst)
+ .add_argument("v", Collect, "The list of vals");
+ check_ok(&ap, args);
+ }
+ return lst;
+}
+
+#[test]
+fn test_pos_collect() {
+ assert_eq!(pos_collect(&["./argparse_test", "10"]), vec!(10));
+ assert_eq!(pos_collect(&["./argparse_test", "11", "21"]), vec!(11, 21));
+ assert_eq!(pos_collect(&["./argparse_test", "10", "20", "30"]),
+ vec!(10, 20, 30));
+}
+
+#[test]
+#[should_panic]
+fn wrong_type() {
+ pos_collect(&["./argparse_test", "10", "20", "test"]);
+}
+
+fn collect(args: &[&str]) -> Vec<isize> {
+ let mut lst = Vec::new();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut lst).add_option(&["-a", "--add"], Collect,
+ "The list of vals");
+ check_ok(&ap, args);
+ }
+ return lst;
+}
+
+#[test]
+fn test_collect() {
+ assert_eq!(collect(&["./argparse_test", "-a10"]), vec!(10));
+ assert_eq!(collect(&["./argparse_test", "--add=11", "-a", "21"]),
+ vec!(11, 21));
+ assert_eq!(collect(&["./argparse_test",
+ "-a", "10", "--add=20", "--add", "30"]), vec!(10, 20, 30));
+}
+
+#[test]
+#[should_panic]
+fn test_extra() {
+ collect(&["./argparse_test", "-a", "10", "20", "30"]);
+}
+
+fn list(args: &[&str]) -> Vec<isize> {
+ let mut vec = Vec::new();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut vec).add_option(&["-a", "--add"], List,
+ "The list of vals");
+ check_ok(&ap, args);
+ }
+ return vec;
+}
+
+#[test]
+#[should_panic]
+fn test_list() {
+ assert_eq!(list(&["./argparse_test", "-a10"]), vec!(10));
+ assert_eq!(list(&["./argparse_test", "--add", "11", "21"]), vec!(11, 21));
+ assert_eq!(list(&["./argparse_test", "-a", "10", "20", "30"]),
+ vec!(10, 20, 30));
+ assert_eq!(list(&["./argparse_test", "10", "20", "30"]), vec!(10, 20, 30));
+}
diff --git a/argparse/src/test_optional.rs b/argparse/src/test_optional.rs
new file mode 100644
index 0000000..5f4c35e
--- /dev/null
+++ b/argparse/src/test_optional.rs
@@ -0,0 +1,54 @@
+use parser::ArgumentParser;
+use super::StoreOption;
+use test_parser::{check_ok};
+
+fn opt(args: &[&str]) -> Option<isize> {
+ let mut val = None;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-s", "--set"], StoreOption,
+ "Set int value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_opt() {
+ assert_eq!(opt(&["./argparse_test"]), None);
+ assert_eq!(opt(&["./argparse_test", "-s", "10"]), Some(10));
+ assert_eq!(opt(&["./argparse_test", "--set", "11"]), Some(11));
+}
+
+#[test]
+#[should_panic]
+fn test_opt_no_arg() {
+ opt(&["./argparse_test", "--set"]);
+}
+
+fn optstr(args: &[&str]) -> Option<String> {
+ let mut val = None;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-s", "--set"], StoreOption,
+ "Set string value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_str() {
+ assert_eq!(optstr(&["./argparse_test"]), None);
+ assert_eq!(optstr(&["./argparse_test", "-s", "10"]), Some(10.to_string()));
+ assert_eq!(optstr(&["./argparse_test", "--set", "11"]),
+ Some(11.to_string()));
+}
+
+#[test]
+#[should_panic]
+fn test_str_no_art() {
+ optstr(&["./argparse_test", "--set"]);
+}
diff --git a/argparse/src/test_parser.rs b/argparse/src/test_parser.rs
new file mode 100644
index 0000000..f60aa64
--- /dev/null
+++ b/argparse/src/test_parser.rs
@@ -0,0 +1,64 @@
+
+use parser::ArgumentParser;
+
+pub fn check_ok(ap: &ArgumentParser, args: &[&str]) {
+ let mut stdout = Vec::<u8>::new();
+ let mut stderr = Vec::<u8>::new();
+ let mut owned_args = Vec::new();
+ for x in args.iter() {
+ owned_args.push(x.to_string());
+ }
+ let res = ap.parse(owned_args, &mut stdout, &mut stderr);
+ match res {
+ Ok(()) => return,
+ Err(x) => panic!(
+ String::from_utf8(stderr).unwrap() +
+ &format!("Expected ok, but found Exit({})", x)[..]),
+ }
+}
+
+pub fn check_exit(ap: &ArgumentParser, args: &[&str]) {
+ let mut stdout = Vec::<u8>::new();
+ let mut stderr = Vec::<u8>::new();
+ let mut owned_args = Vec::new();
+ for x in args.iter() {
+ owned_args.push(x.to_string());
+ }
+ let res = ap.parse(owned_args, &mut stdout, &mut stderr);
+ match res {
+ Err(0) => return,
+ Err(x) => panic!(format!("Expected code {} got {}", 0usize, x)),
+ Ok(()) => panic!(format!("Expected failure, got success")),
+ }
+}
+
+pub fn check_err(ap: &ArgumentParser, args: &[&str]) {
+ let mut stdout = Vec::<u8>::new();
+ let mut stderr = Vec::<u8>::new();
+ let mut owned_args = Vec::new();
+ for x in args.iter() {
+ owned_args.push(x.to_string());
+ }
+ let res = ap.parse(owned_args, &mut stdout, &mut stderr);
+ match res {
+ Err(2) => return,
+ Err(x) => panic!(format!("Expected code {} got {}", 2usize, x)),
+ Ok(()) => panic!(format!("Expected failure, got success")),
+ }
+}
+
+#[test]
+fn test_no_arg() {
+ let ap = ArgumentParser::new();
+ check_ok(&ap, &["./argparse_test"]);
+ check_err(&ap, &["./argparse_test", "a"]);
+ check_err(&ap, &["./argparse_test", "-a"]);
+ check_err(&ap, &["./argparse_test", "--an-option"]);
+}
+
+#[test]
+fn test_help() {
+ let ap = ArgumentParser::new();
+ check_ok(&ap, &["./argparse_test"]);
+ check_exit(&ap, &["./argparse_test", "--help"]);
+}
diff --git a/argparse/src/test_path.rs b/argparse/src/test_path.rs
new file mode 100644
index 0000000..868ae8c
--- /dev/null
+++ b/argparse/src/test_path.rs
@@ -0,0 +1,30 @@
+use std::path::PathBuf;
+use parser::ArgumentParser;
+use super::Parse;
+use test_parser::{check_ok};
+
+fn parse_str(args: &[&str]) -> PathBuf {
+ let mut val: PathBuf = From::from("");
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-s", "--set"], Parse,
+ "Set path value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_path() {
+ assert_eq!(parse_str(&["./argparse_test", "-s", "/hello"]),
+ PathBuf::from("/hello"));
+ assert_eq!(parse_str(&["./argparse_test", "--set", "a///b/../c"]),
+ PathBuf::from("a/b/../c"));
+}
+
+#[test]
+#[should_panic]
+fn test_err() {
+ parse_str(&["./argparse_test", "--set"]);
+}
diff --git a/argparse/src/test_pos.rs b/argparse/src/test_pos.rs
new file mode 100644
index 0000000..2542050
--- /dev/null
+++ b/argparse/src/test_pos.rs
@@ -0,0 +1,196 @@
+use parser::ArgumentParser;
+use super::{Store, List};
+use test_parser::{check_ok};
+
+fn parse_pos(args: &[&str]) -> isize {
+ let mut val = 0;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_argument("value", Store, "The value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+
+#[test]
+fn test_argument() {
+ assert_eq!(parse_pos(&["./argparse_test", "10"]), 10);
+}
+
+#[test]
+#[should_panic]
+fn too_much_args() {
+ parse_pos(&["./argparse_test", "10", "20"]);
+}
+
+#[test]
+#[should_panic]
+fn wrong_value() {
+ parse_pos(&["./argparse_test", "test", "20"]);
+}
+
+#[test]
+#[should_panic]
+fn float_value() {
+ parse_pos(&["./argparse_test", "1.5"]);
+}
+
+fn parse_two(args: &[&str]) -> (isize, isize) {
+ let mut val1 = 1;
+ let mut val2 = 2;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val1)
+ .add_argument("v1", Store, "The value 1");
+ ap.refer(&mut val2)
+ .add_argument("v2", Store, "The value 2");
+ check_ok(&ap, args);
+ }
+ return (val1, val2);
+}
+
+#[test]
+fn test_two() {
+ assert_eq!(parse_two(&["./argparse_test", "10"]), (10, 2));
+ assert_eq!(parse_two(&["./argparse_test", "11", "21"]), (11, 21));
+}
+
+#[test]
+#[should_panic]
+fn test_two_fail_many() {
+ parse_two(&["./argparse_test", "10", "20", "30"]);
+}
+
+#[test]
+#[should_panic]
+fn test_two_fail_value() {
+ parse_two(&["./argparse_test", "test", "20"]);
+}
+
+#[test]
+#[should_panic]
+fn test_two_fail_float() {
+ parse_two(&["./argparse_test", "1.5"]);
+}
+
+fn parse_pos_opt(args: &[&str]) -> (isize, isize) {
+ let mut val1 = 1;
+ let mut val2 = 2;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val1)
+ .add_option(&["--v1"], Store, "The value 1")
+ .add_argument("v1", Store, "The value 1");
+ ap.refer(&mut val2)
+ .add_argument("v2", Store, "The value 2");
+ check_ok(&ap, args);
+ }
+ return (val1, val2);
+}
+
+#[test]
+fn test_positional_optional() {
+ assert_eq!(parse_pos_opt(&["./argparse_test", "10"]), (10, 2));
+ assert_eq!(parse_pos_opt(&["./argparse_test", "11", "21"]), (11, 21));
+ assert_eq!(parse_pos_opt(&["./argparse_test", "--v1=7", "8"]), (7, 8));
+ assert_eq!(parse_pos_opt(&["./argparse_test", "10", "--v1=9"]), (9, 10));
+}
+
+#[test]
+#[should_panic]
+fn test_pos_opt_err() {
+ parse_pos_opt(&["./argparse_test", "--v1=10", "20", "30"]);
+}
+
+fn parse_pos_req(args: &[&str]) -> (isize, isize) {
+ let mut val1 = 1;
+ let mut val2 = 2;
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val1)
+ .add_option(&["--v1"], Store, "The value 1")
+ .add_argument("v1", Store, "The value 1")
+ .required();
+ ap.refer(&mut val2)
+ .add_argument("v2", Store, "The value 2");
+ check_ok(&ap, args);
+ }
+ return (val1, val2);
+}
+
+#[test]
+fn test_positional_required() {
+ assert_eq!(parse_pos_req(&["./argparse_test", "10"]), (10, 2));
+ assert_eq!(parse_pos_req(&["./argparse_test", "11", "21"]), (11, 21));
+ assert_eq!(parse_pos_req(&["./argparse_test", "--v1=7"]), (7, 2));
+ assert_eq!(parse_pos_req(&["./argparse_test", "10", "--v1=9"]), (9, 10));
+}
+
+#[test]
+#[should_panic]
+fn test_pos_extra() {
+ parse_pos_req(&["./argparse_test", "--v1=10", "20", "30"]);
+}
+
+#[test]
+#[should_panic]
+fn test_pos_no_req() {
+ parse_pos_req(&["./argparse_test"]);
+}
+
+fn pos_stop(args: &[&str]) -> (isize, Vec<String>) {
+ let mut val1 = 1;
+ let mut val2 = Vec::new();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val1)
+ .add_option(&["--v1"], Store, "The value 1")
+ .add_argument("v1", Store, "The value 1")
+ .required();
+ ap.refer(&mut val2)
+ .add_argument("v2", List, "The value 2");
+ ap.stop_on_first_argument(true);
+ check_ok(&ap, args);
+ }
+ return (val1, val2);
+}
+
+#[test]
+fn test_pos_stop() {
+ assert_eq!(pos_stop(&["./argparse_test", "10"]), (10, vec!()));
+ assert_eq!(pos_stop(&["./argparse_test", "11", "21"]),
+ (11, vec!("21".to_string())));
+ assert_eq!(pos_stop(&["./argparse_test", "--v1=7"]), (7, vec!()));
+ assert_eq!(pos_stop(&["./argparse_test", "10", "--v1=9", "--whatever"]),
+ (10, vec!("--v1=9".to_string(), "--whatever".to_string())));
+}
+
+#[test]
+#[should_panic]
+fn test_test() {
+ pos_stop(&["./argparse_test"]);
+}
+
+fn pos_dash(args: &[&str], dash: bool) -> Vec<String> {
+ let mut val = Vec::new();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_argument("v1", List, "The value");
+ ap.silence_double_dash(dash);
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_pos_dash() {
+ assert_eq!(pos_dash(&["./argparse_test", "1"], true),
+ vec!("1".to_string()));
+ assert_eq!(pos_dash(&["./argparse_test", "--", "1"], true),
+ vec!("1".to_string()));
+ assert_eq!(pos_dash(&["./argparse_test", "--", "1"], false),
+ vec!("--".to_string(), "1".to_string()));
+}
diff --git a/argparse/src/test_str.rs b/argparse/src/test_str.rs
new file mode 100644
index 0000000..42db286
--- /dev/null
+++ b/argparse/src/test_str.rs
@@ -0,0 +1,28 @@
+use parser::ArgumentParser;
+use super::Store;
+use test_parser::{check_ok};
+
+fn parse_str(args: &[&str]) -> String {
+ let mut val: String = "".to_string();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["-s", "--set"], Store,
+ "Set string value");
+ check_ok(&ap, args);
+ }
+ return val;
+}
+
+#[test]
+fn test_str() {
+ assert_eq!(parse_str(&["./argparse_test", "-s", "10"]), "10".to_string());
+ assert_eq!(parse_str(&["./argparse_test", "--set", "value"]),
+ "value".to_string());
+}
+
+#[test]
+#[should_panic]
+fn test_err() {
+ parse_str(&["./argparse_test", "--set"]);
+}
diff --git a/argparse/src/test_usage.rs b/argparse/src/test_usage.rs
new file mode 100644
index 0000000..efe89fe
--- /dev/null
+++ b/argparse/src/test_usage.rs
@@ -0,0 +1,57 @@
+use std::str::from_utf8;
+
+use parser::ArgumentParser;
+use super::{Store, List};
+
+#[test]
+fn test_empty() {
+ let ap = ArgumentParser::new();
+ let mut buf = Vec::<u8>::new();
+ assert!(ap.print_usage("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n ./argparse_test\n", from_utf8(&buf[..]).unwrap());
+}
+
+#[test]
+fn test_options() {
+ let mut val = 0;
+ let mut buf = Vec::<u8>::new();
+ {
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_option(&["--value"], Store,
+ "Set integer value");
+ assert!(ap.print_usage("./argparse_test", &mut buf).is_ok());
+ }
+ assert_eq!("Usage:\n ./argparse_test [OPTIONS]\n",
+ from_utf8(&buf[..]).unwrap());
+}
+
+#[test]
+fn test_argument() {
+ let mut val = 0;
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut val)
+ .add_argument("value", Store,
+ "Integer value");
+ let mut buf = Vec::<u8>::new();
+ assert!(ap.print_usage("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n ./argparse_test [VALUE]\n",
+ from_utf8(&buf[..]).unwrap());
+}
+
+#[test]
+fn test_arguments() {
+ let mut v1 = 0;
+ let mut v2 = Vec::<u32>::new();
+ let mut ap = ArgumentParser::new();
+ ap.refer(&mut v1)
+ .add_argument("v1", Store,
+ "Integer value 1");
+ ap.refer(&mut v2)
+ .add_argument("v2", List,
+ "More values");
+ let mut buf = Vec::<u8>::new();
+ assert!(ap.print_usage("./argparse_test", &mut buf).is_ok());
+ assert_eq!("Usage:\n ./argparse_test [V1] [V2 ...]\n",
+ from_utf8(&buf[..]).unwrap());
+}
diff --git a/argparse/vagga.yaml b/argparse/vagga.yaml
new file mode 100644
index 0000000..c6e0c52
--- /dev/null
+++ b/argparse/vagga.yaml
@@ -0,0 +1,59 @@
+containers:
+
+ build:
+ setup:
+ - !Ubuntu bionic
+ - !Install [build-essential, ca-certificates, vim]
+ - !TarInstall
+ url: https://static.rust-lang.org/dist/rust-1.28.0-x86_64-unknown-linux-gnu.tar.gz
+ # We install rustc and cargo, but skip rust-docs
+ script: "./install.sh --prefix=/usr \
+ --components=rustc,rust-std-x86_64-unknown-linux-gnu,cargo"
+ - &bulk !Tar
+ url: "https://github.com/tailhook/bulk/releases/download/v0.4.12/bulk-v0.4.12.tar.gz"
+ sha256: 7deeb4895b3909afea46194ef01bafdeb30ff89fc4a7b6497172ba117734040e
+ path: /
+ environ:
+ HOME: /work/run
+ volumes:
+ /tmp: !Tmpfs { size: 100Mi }
+
+
+commands:
+
+ make: !Command
+ description: Build the library
+ container: build
+ run: [cargo, build]
+
+ test: !Command
+ description: Run the tests
+ container: build
+ run: [cargo, test]
+
+ cargo: !Command
+ container: build
+ run: [cargo]
+
+ example-greeting: !Command
+ description: Build and run "greeting" example
+ container: build
+ accepts-arguments: true
+ run: [cargo, run, --example, greeting, "--"]
+
+ example-structure: !Command
+ description: Build and run "structure" example
+ container: build
+ accepts-arguments: true
+ run: [cargo, run, --example, structure, "--"]
+
+ example-subcommands: !Command
+ description: Build and run "subcommands" example
+ container: build
+ accepts-arguments: true
+ run: [cargo, run, --example, subcommands, "--"]
+
+ _bulk: !Command
+ description: Run `bulk` command (for version bookkeeping)
+ container: build
+ run: [bulk]
diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md
index 81102a9..1beabf2 100644
--- a/nitrocli/CHANGELOG.md
+++ b/nitrocli/CHANGELOG.md
@@ -12,6 +12,7 @@ Unreleased
version of the crate, and minimum version of `rustc` required
- Fixed wrong messages in the pinentry dialog that were caused by unescaped
spaces in a string
+- Added `argparse` dependency in version `0.2.2`
0.1.3
diff --git a/nitrocli/Cargo.lock b/nitrocli/Cargo.lock
index 4f6a80e..f500246 100644
--- a/nitrocli/Cargo.lock
+++ b/nitrocli/Cargo.lock
@@ -1,4 +1,8 @@
[[package]]
+name = "argparse"
+version = "0.2.2"
+
+[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -41,6 +45,7 @@ replace = "libc 0.2.45"
name = "nitrocli"
version = "0.1.3"
dependencies = [
+ "argparse 0.2.2",
"nitrokey 0.2.1",
]
diff --git a/nitrocli/Cargo.toml b/nitrocli/Cargo.toml
index 68b3b80..42a411f 100644
--- a/nitrocli/Cargo.toml
+++ b/nitrocli/Cargo.toml
@@ -35,6 +35,10 @@ A command line tool for interacting with the Nitrokey Storage device.
[badges]
gitlab = { repository = "d-e-s-o/nitrocli", branch = "master" }
+[dependencies.argparse]
+version = "0.2.2"
+path = "../argparse"
+
[dependencies.nitrokey]
version = "0.2.1"
path = "../nitrokey"
diff --git a/nitrocli/ci/gitlab-ci.yml b/nitrocli/ci/gitlab-ci.yml
index c51a6bd..b4ec21a 100644
--- a/nitrocli/ci/gitlab-ci.yml
+++ b/nitrocli/ci/gitlab-ci.yml
@@ -24,7 +24,7 @@ lint:clippy:
# necessary because consumed dependencies may emit errors otherwise.
- cd nitrocli
- cargo clippy --all-targets --all-features --
- -A warnings -A clippy::float_cmp -A clippy::cast_ptr_alignment
+ -A warnings -A clippy::float_cmp -A clippy::cast_ptr_alignment -A clippy::unused_io_amount
- cargo clean --package=nitrocli
- cargo clippy --all-targets --all-features -- -D warnings