Merge branch 'master' into issue-231

This commit is contained in:
Kevin K. 2015-09-08 22:49:07 -04:00
commit c0c1613188
3 changed files with 201 additions and 0 deletions

View file

@ -4,6 +4,7 @@ The following is a list of contributors to the [clap](https://github.com/kbknapp
* [Ivan Dmitrievsky](https://github.com/idmit) <<ivan.dmitrievsky@gmail.com>>
* [J/A](https://github.com/archer884)
* [Jacob Helwig](https://github.com/jhelwig) <<jacob@technosorcery.net>>
* [James McGlashan](https://github.com/james-darkfox)
* [Kevin K.](https://github.com/kbknapp) <<kbknapp@gmail.com>>
* [Markus Unterwaditzer](https://github.com/untitaker) <<markus@Unterwaditzer.net>>
* [Sebastian Thiel](https://github.com/Byron) <<byronimo@gmail.com>>

View file

@ -0,0 +1,82 @@
#[macro_use] extern crate clap;
// No use imports from clap. #[macro_use] gives us `clap_app!` which internally uses `$crate::`
fn main() {
// Validation example testing that a file exists
let file_exists = |path| {
if std::fs::metadata(path).is_ok() {
Ok(())
} else {
Err(String::from("File doesn't exist"))
}
};
// External module may contain this subcommand. If this exists in another module, a function is
// required to access it. Recommend `fn clap() -> Clap::SubCommand`.
let external_sub_command = clap_app!( @subcommand foo =>
(@arg bar: -b "Bar")
);
let matches = clap_app!(MyApp =>
(@setting SubcommandRequiredElseHelp)
(version: "1.0")
(author: "Alice")
(about: "Does awesome things")
(@arg config: -c --config <conf> #{1, 2} {file_exists} "Sets a custom config file")
(@arg input: * "Input file")
(@group test =>
(@attributes +required)
(@arg output: "Sets an optional output file")
(@arg debug: -d ... "Turn debugging information on")
)
(subcommand: external_sub_command)
(@subcommand test =>
(about: "does testing things")
(version: "2.5")
(@arg list: -l "Lists test values")
(@arg test_req: -r requires[list] "Tests requirement for listing")
(@arg aaaa: --aaaa +takes_value {
|a| if a.contains("a") {
Ok(())
} else {
Err(String::from("string does not contain at least one a"))
}
} "Test if the argument contains an a")
)
).get_matches();
// You can check the value provided by positional arguments, or option arguments
if let Some(o) = matches.value_of("output") {
println!("Value for output: {}", o);
}
if let Some(c) = matches.value_of("config") {
println!("Value for config: {}", c);
}
// You can see how many times a particular flag or argument occurred
// Note, only flags can have multiple occurrences
match matches.occurrences_of("debug") {
0 => println!("Debug mode is off"),
1 => println!("Debug mode is kind of on"),
2 => println!("Debug mode is on"),
3 | _ => println!("Don't be crazy"),
}
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level app
if let Some(ref matches) = matches.subcommand_matches("test") {
// "$ myapp test" was run
if matches.is_present("list") {
// "$ myapp test -l" was run
println!("Printing testing lists...");
} else {
println!("Not printing testing lists...");
}
}
// Continued program logic goes here...
}

View file

@ -739,3 +739,121 @@ macro_rules! crate_version {
option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
}
}
/// App, Arg, SubCommand and Group builder macro (Usage-string like input)
#[macro_export]
macro_rules! clap_app {
(@app ($builder:expr)) => { $builder };
(@app ($builder:expr) (@arg $name:ident: $($tail:tt)*) $($tt:tt)*) => {
clap_app!{ @app
($builder.arg(clap_app!{ @arg ($crate::Arg::with_name(stringify!($name))) (-) $($tail)* }))
$($tt)*
}
};
(@app ($builder:expr) (@setting $setting:ident) $($tt:tt)*) => {
clap_app!{ @app
($builder.setting($crate::AppSettings::$setting))
$($tt)*
}
};
// Treat the application builder as an argument to set it's attributes
(@app ($builder:expr) (@attributes $($attr:tt)*) $($tt:tt)*) => {
clap_app!{ @app (clap_app!{ @arg ($builder) $($attr)* }) $($tt)* }
};
(@app ($builder:expr) (@group $name:ident => $($tail:tt)*) $($tt:tt)*) => {
clap_app!{ @app
(clap_app!{ @group ($builder, $crate::ArgGroup::with_name(stringify!($name))) $($tail)* })
$($tt)*
}
};
// Handle subcommand creation
(@app ($builder:expr) (@subcommand $name:ident => $($tail:tt)*) $($tt:tt)*) => {
clap_app!{ @app
($builder.subcommand(
clap_app!{ @app ($crate::SubCommand::with_name(stringify!($name))) $($tail)* }
))
$($tt)*
}
};
// Yaml like function calls - used for setting varous meta directly against the app
(@app ($builder:expr) ($ident:ident: $($v:expr),*) $($tt:tt)*) => {
clap_app!{ @app ($builder.$ident($($v),*)) $($tt)* }
};
// Add members to group and continue argument handling with the parent builder
(@group ($builder:expr, $group:expr)) => { $builder.arg_group($group) };
(@group ($builder:expr, $group:expr) (@attributes $($attr:tt)*) $($tt:tt)*) => {
clap_app!{ @group ($builder, clap_app!{ @arg ($group) (-) $($attr)* }) $($tt)* }
};
(@group ($builder:expr, $group:expr) (@arg $name:ident: $($tail:tt)*) $($tt:tt)*) => {
clap_app!{ @group
(clap_app!{ @app ($builder) (@arg $name: $($tail)*) },
$group.add(stringify!($name)))
$($tt)*
}
};
// No more tokens to munch
(@arg ($arg:expr) $modes:tt) => { $arg };
// Shorthand tokens influenced by the usage_string
(@arg ($arg:expr) $modes:tt --$long:ident $($tail:tt)*) => {
clap_app!{ @arg ($arg.long(stringify!($long))) $modes $($tail)* }
};
(@arg ($arg:expr) $modes:tt -$short:ident $($tail:tt)*) => {
clap_app!{ @arg ($arg.short(stringify!($short))) $modes $($tail)* }
};
(@arg ($arg:expr) (-) <$var:ident> $($tail:tt)*) => {
clap_app!{ @arg ($arg.value_name(stringify!($var))) (+) +takes_value +required $($tail)* }
};
(@arg ($arg:expr) (+) <$var:ident> $($tail:tt)*) => {
clap_app!{ @arg ($arg.value_name(stringify!($var))) (+) $($tail)* }
};
(@arg ($arg:expr) (-) [$var:ident] $($tail:tt)*) => {
clap_app!{ @arg ($arg.value_name(stringify!($var))) +takes_value (+) $($tail)* }
};
(@arg ($arg:expr) (+) [$var:ident] $($tail:tt)*) => {
clap_app!{ @arg ($arg.value_name(stringify!($var))) (+) $($tail)* }
};
(@arg ($arg:expr) $modes:tt ... $($tail:tt)*) => {
clap_app!{ @arg ($arg) $modes +multiple $($tail)* }
};
// Shorthand magic
(@arg ($arg:expr) $modes:tt #{$n:expr, $m:expr} $($tail:tt)*) => {
clap_app!{ @arg ($arg) $modes min_values($n) max_values($m) $($tail)* }
};
(@arg ($arg:expr) $modes:tt * $($tail:tt)*) => {
clap_app!{ @arg ($arg) $modes +required $($tail)* }
};
// !foo -> .foo(false)
(@arg ($arg:expr) $modes:tt !$ident $($tail:tt)*) => {
clap_app!{ @arg ($arg.$ident(false)) $modes $($tail)* }
};
// foo -> .foo(true)
(@arg ($arg:expr) $modes:tt +$ident:ident $($tail:tt)*) => {
clap_app!{ @arg ($arg.$ident(true)) $modes $($tail)* }
};
// Validator
(@arg ($arg:expr) $modes:tt {$fn_:expr} $($tail:tt)*) => {
clap_app!{ @arg ($arg.validator($fn_)) $modes $($tail)* }
};
(@as_expr $expr:expr) => { $expr };
// Help
(@arg ($arg:expr) $modes:tt $desc:tt) => { $arg.help(clap_app!{ @as_expr $desc }) };
// Handle functions that need to be called multiple times for each argument
(@arg ($arg:expr) $modes:tt $ident:ident[$($target:ident)*] $($tail:tt)*) => {
clap_app!{ @arg ($arg $( .$ident(stringify!($target)) )*) $modes $($tail)* }
};
// Inherit builder's functions
(@arg ($arg:expr) $modes:tt $ident:ident($($expr:expr)*) $($tail:tt)*) => {
clap_app!{ @arg ($arg.$ident($($expr)*)) $modes $($tail)* }
};
// Build a subcommand outside of an app.
(@subcommand $name:ident => $($tail:tt)*) => {
clap_app!{ @app ($crate::SubCommand::with_name(stringify!($name))) $($tail)* }
};
// Start the magic
($name:ident => $($tail:tt)*) => {{
clap_app!{ @app ($crate::App::new(stringify!($name))) $($tail)*}
}};
}