mirror of
https://github.com/clap-rs/clap
synced 2024-12-12 22:02:35 +00:00
Added custom usage strings
This commit is contained in:
parent
b77920304c
commit
1814d87192
1 changed files with 135 additions and 39 deletions
174
src/app.rs
174
src/app.rs
|
@ -4,6 +4,7 @@ use std::collections::BTreeMap;
|
|||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::vec::IntoIter;
|
||||
|
||||
use argmatches::ArgMatches;
|
||||
|
@ -60,6 +61,8 @@ pub struct App {
|
|||
short_list: HashSet<char>,
|
||||
long_list: HashSet<&'static str>,
|
||||
blacklist: HashSet<&'static str>,
|
||||
usage_str: Option<&'static str>,
|
||||
bin_name: Option<String>
|
||||
|
||||
}
|
||||
|
||||
|
@ -67,7 +70,7 @@ impl App {
|
|||
/// Creates a new instance of an application requiring a name (such as the binary). Will be displayed
|
||||
/// to the user when they print version or help and usage information.
|
||||
///
|
||||
/// Example:
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
|
@ -94,13 +97,15 @@ impl App {
|
|||
arg_list: HashSet::new(),
|
||||
short_list: HashSet::new(),
|
||||
long_list: HashSet::new(),
|
||||
usage_str: None,
|
||||
blacklist: HashSet::new(),
|
||||
bin_name: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a string of author(s)
|
||||
///
|
||||
/// Example:
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
|
@ -115,7 +120,7 @@ impl App {
|
|||
|
||||
/// Sets a string briefly describing what the program does
|
||||
///
|
||||
/// Example:
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
|
@ -130,7 +135,7 @@ impl App {
|
|||
|
||||
/// Sets a string of the version number
|
||||
///
|
||||
/// Example:
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
|
@ -138,14 +143,34 @@ impl App {
|
|||
/// .version("v0.1.24")
|
||||
/// # .get_matches();
|
||||
/// ```
|
||||
pub fn version(mut self, v: &'static str)-> App {
|
||||
pub fn version(mut self, v: &'static str) -> App {
|
||||
self.version = Some(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a custom usage string to over-ride the one auto-generated by `clap`
|
||||
/// *NOTE:* You do not need to specify the "USAGE: " portion, as that will
|
||||
/// still be applied by `clap`, you only need to specify the portion starting
|
||||
/// with the binary name.
|
||||
/// *NOTE:* This will not replace the entire help message, only the portion
|
||||
/// showing the usage.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// # let app = App::new("myprog")
|
||||
/// .usage("myapp [-clDas] <some_file>")
|
||||
/// # .get_matches();
|
||||
/// ```
|
||||
pub fn usage(mut self, u: &'static str) -> App {
|
||||
self.usage_str = Some(u);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an argument to the list of valid possibilties
|
||||
///
|
||||
/// Example:
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
|
@ -239,9 +264,9 @@ impl App {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds arguments to the list of valid possibilties
|
||||
/// Adds multiple arguments to the list of valid possibilties
|
||||
///
|
||||
/// Example:
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
|
@ -257,12 +282,44 @@ impl App {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds a subcommand to the list of valid possibilties. Subcommands
|
||||
/// are effectively sub apps, because they can contain their own arguments
|
||||
/// and subcommands. They also function just like apps, in that they get their
|
||||
/// own auto generated help and version switches.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg, SubCommand};
|
||||
/// # let app = App::new("myprog")
|
||||
/// .subcommand(SubCommand::new("config")
|
||||
/// .about("Controls configuration features")
|
||||
/// .arg(Arg::new("config_file")
|
||||
/// .index(1)
|
||||
/// .help("Configuration file to use")))
|
||||
/// // Additional subcommand configuration goes here, such as arguments...
|
||||
/// )
|
||||
/// # .get_matches();
|
||||
/// ```
|
||||
pub fn subcommand(mut self, subcmd: App) -> App {
|
||||
if subcmd.name == "help" { self.needs_subcmd_help = false; }
|
||||
self.subcommands.insert(subcmd.name, Box::new(subcmd));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds multiple subcommands to the list of valid possibilties
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg, SubCommand};
|
||||
/// # let app = App::new("myprog")
|
||||
/// .subcommands( vec![
|
||||
/// SubCommand::new("config").about("Controls configuration functionality")
|
||||
/// .arg(Arg::new("config_file").index(1)),
|
||||
/// SubCommand::new("debug").about("Controls debug functionality")])
|
||||
/// # .get_matches();
|
||||
/// ```
|
||||
pub fn subcommands(mut self, subcmds: Vec<App>) -> App {
|
||||
for subcmd in subcmds.into_iter() {
|
||||
self = self.subcommand(subcmd);
|
||||
|
@ -277,16 +334,50 @@ impl App {
|
|||
|
||||
fn report_error(&self, msg: String, help: bool, quit: bool) {
|
||||
println!("{}", msg);
|
||||
if help { self.print_help(); }
|
||||
if help { self.print_usage(true); }
|
||||
if quit { env::set_exit_status(1); self.exit(); }
|
||||
}
|
||||
|
||||
fn print_usage(&self, more_info: bool) {
|
||||
println!("USAGE:");
|
||||
if let Some(u) = self.usage_str {
|
||||
println!("\t{}",u);
|
||||
} else {
|
||||
let flags = ! self.flags.is_empty();
|
||||
let pos = ! self.positionals_idx.is_empty();
|
||||
let req_pos = self.positionals_idx.values().filter_map(|ref x| if x.required { Some(x.name) } else {None})
|
||||
.fold(String::new(), |acc, ref name| acc + &format!("<{}> ", name.to_uppercase())[..]);
|
||||
let req_opts = self.opts.values().filter(|ref x| x.required)
|
||||
.fold(String::new(), |acc, ref o| acc + &format!("{}{} ",if let Some(s) = o.short {
|
||||
format!("-{} ", s)
|
||||
} else {
|
||||
format!("--{}=",o.long.unwrap())
|
||||
},o.name.to_uppercase()));
|
||||
let opts = ! self.opts.is_empty();
|
||||
let subcmds = ! self.subcommands.is_empty();
|
||||
|
||||
print!("\t{} {} {} {} {}", if let Some(ref name) = self.bin_name { &name[..] } else { self.name },
|
||||
if flags {"[FLAGS]"} else {""},
|
||||
if opts {
|
||||
if req_opts.is_empty() { "[OPTIONS]" } else { &req_opts[..] }
|
||||
} else { "" },
|
||||
if pos {
|
||||
if req_pos.is_empty() { "[POSITIONAL]"} else { &req_pos[..] }
|
||||
} else {""},
|
||||
if subcmds {"[SUBCOMMANDS]"} else {""});
|
||||
}
|
||||
|
||||
if more_info {
|
||||
println!("\nFor more information try --help");
|
||||
}
|
||||
}
|
||||
|
||||
fn print_help(&self) {
|
||||
self.print_version(false);
|
||||
let mut flags = false;
|
||||
let mut pos = false;
|
||||
let mut opts = false;
|
||||
let mut subcmds = false;
|
||||
let flags = ! self.flags.is_empty();
|
||||
let pos = ! self.positionals_idx.is_empty();
|
||||
let opts = ! self.opts.is_empty();
|
||||
let subcmds = ! self.subcommands.is_empty();
|
||||
|
||||
if let Some(author) = self.author {
|
||||
println!("{}", author);
|
||||
|
@ -295,12 +386,7 @@ impl App {
|
|||
println!("{}", about);
|
||||
}
|
||||
println!("");
|
||||
println!("USAGE:");
|
||||
print!("\t{} {} {} {} {}", self.name,
|
||||
if ! self.subcommands.is_empty() {subcmds = true; "[SUBCOMMANDS]"} else {""},
|
||||
if ! self.flags.is_empty() {flags = true; "[FLAGS]"} else {""},
|
||||
if ! self.opts.is_empty() {opts = true; "[OPTIONS]"} else {""},
|
||||
if ! self.positionals_idx.is_empty() {pos = true; "[POSITIONAL]"} else {""});
|
||||
self.print_usage(false);
|
||||
if flags || opts || pos || subcmds {
|
||||
println!("");
|
||||
}
|
||||
|
@ -318,11 +404,12 @@ impl App {
|
|||
println!("");
|
||||
println!("OPTIONS:");
|
||||
for v in self.opts.values() {
|
||||
println!("\t{}{}{}\t\t{}",
|
||||
if let Some(ref s) = v.short{format!("-{}",s)}else{format!(" ")},
|
||||
if let Some(ref l) = v.long {format!(",--{}",l)}else {format!(" ")},
|
||||
format!(" <{}>", v.name),
|
||||
if let Some(ref h) = v.help {*h} else {" "} );
|
||||
let mut needs_tab = false;
|
||||
println!("\t{}{}{}\t{}",
|
||||
if let Some(ref s) = v.short{format!("-{} ",s)}else{format!(" ")},
|
||||
if let Some(ref l) = v.long {format!(",--{}=",l)}else {needs_tab = true; format!(" ")},
|
||||
format!("{}", v.name.to_uppercase()),
|
||||
if let Some(ref h) = v.help {if needs_tab {format!("\t{}", *h)} else { format!("{}", *h) } } else {format!(" ")} );
|
||||
}
|
||||
}
|
||||
if pos {
|
||||
|
@ -381,7 +468,7 @@ impl App {
|
|||
if *l == arg {
|
||||
if self.blacklist.contains(k) {
|
||||
self.report_error(format!("The argument --{} is mutually exclusive with one or more other arguments", arg),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
matches.opts.insert(k, OptArg{
|
||||
name: v.name,
|
||||
|
@ -413,7 +500,7 @@ impl App {
|
|||
if ! multi {
|
||||
if self.blacklist.contains(k) {
|
||||
self.report_error(format!("The argument --{} is mutually exclusive with one or more other arguments", arg),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
matches.flags.insert(k, FlagArg{
|
||||
name: v.name,
|
||||
|
@ -453,7 +540,7 @@ impl App {
|
|||
if ! found {
|
||||
self.report_error(
|
||||
format!("Argument --{} isn't valid", arg),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -467,7 +554,7 @@ impl App {
|
|||
if ! self.parse_single_short_flag(matches, c) {
|
||||
self.report_error(
|
||||
format!("Argument -{} isn't valid",c),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -486,7 +573,7 @@ impl App {
|
|||
|
||||
self.report_error(
|
||||
format!("Argument -{} isn't valid",arg_c),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -556,7 +643,7 @@ impl App {
|
|||
} else {
|
||||
format!("\"{}\"", v.name)
|
||||
}),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
}
|
||||
for (k, v) in matches.opts.iter() {
|
||||
|
@ -569,7 +656,7 @@ impl App {
|
|||
} else {
|
||||
format!("\"{}\"", v.name)
|
||||
}),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
}
|
||||
for (k, v) in matches.positionals.iter() {
|
||||
|
@ -631,7 +718,7 @@ impl App {
|
|||
format!("--{}",long)
|
||||
}else{
|
||||
format!("-{}",opt.short.unwrap())
|
||||
}),false, true);
|
||||
}),true, true);
|
||||
}
|
||||
matches.opts.insert(nvo, OptArg{
|
||||
name: opt.name,
|
||||
|
@ -689,12 +776,12 @@ impl App {
|
|||
if self.positionals_idx.is_empty() { // || self.positionals_name.is_empty() {
|
||||
self.report_error(
|
||||
format!("Found positional argument {}, but {} doesn't accept any", arg, self.name),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
if let Some(ref p) = self.positionals_idx.get(&pos_counter) {
|
||||
if self.blacklist.contains(p.name) {
|
||||
self.report_error(format!("The argument \"{}\" is mutually exclusive with one or more other arguments", arg),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
matches.positionals.insert(p.name, PosArg{
|
||||
name: p.name,
|
||||
|
@ -727,7 +814,7 @@ impl App {
|
|||
}
|
||||
pos_counter += 1;
|
||||
} else {
|
||||
self.report_error(format!("Positional argument \"{}\" was found, but {} wasn't expecting any", arg, self.name), false, true);
|
||||
self.report_error(format!("Positional argument \"{}\" was found, but {} wasn't expecting any", arg, self.name), true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -736,13 +823,13 @@ impl App {
|
|||
Some(ref a) => {
|
||||
self.report_error(
|
||||
format!("Argument \"{}\" requires a value but none was supplied", a),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if ! self.required.is_empty() {
|
||||
self.report_error("One or more required arguments were not supplied".to_string(),
|
||||
false, true);
|
||||
true, true);
|
||||
}
|
||||
|
||||
self.validate_blacklist(&matches);
|
||||
|
@ -762,8 +849,17 @@ impl App {
|
|||
let mut matches = ArgMatches::new(self.name);
|
||||
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
|
||||
self.get_matches_from(&mut matches, &mut args.into_iter());
|
||||
let mut it = args.into_iter();
|
||||
if let Some(name) = it.next() {
|
||||
let p = Path::new(&name[..]);
|
||||
if let Some(f) = p.file_name() {
|
||||
match f.to_os_string().into_string() {
|
||||
Ok(s) => self.bin_name = Some(s),
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.get_matches_from(&mut matches, &mut it );
|
||||
|
||||
matches
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue