Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>>
Expand Down
82 changes: 82 additions & 0 deletions examples/18_builder_macro.rs
Original file line number Diff line number Diff line change
@@ -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...
}
118 changes: 118 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)*}
}};
}