mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 06:12:40 +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::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
use std::vec::IntoIter;
|
use std::vec::IntoIter;
|
||||||
|
|
||||||
use argmatches::ArgMatches;
|
use argmatches::ArgMatches;
|
||||||
|
@ -60,6 +61,8 @@ pub struct App {
|
||||||
short_list: HashSet<char>,
|
short_list: HashSet<char>,
|
||||||
long_list: HashSet<&'static str>,
|
long_list: HashSet<&'static str>,
|
||||||
blacklist: 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
|
/// 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.
|
/// to the user when they print version or help and usage information.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use clap::{App, Arg};
|
/// # use clap::{App, Arg};
|
||||||
|
@ -94,13 +97,15 @@ impl App {
|
||||||
arg_list: HashSet::new(),
|
arg_list: HashSet::new(),
|
||||||
short_list: HashSet::new(),
|
short_list: HashSet::new(),
|
||||||
long_list: HashSet::new(),
|
long_list: HashSet::new(),
|
||||||
|
usage_str: None,
|
||||||
blacklist: HashSet::new(),
|
blacklist: HashSet::new(),
|
||||||
|
bin_name: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a string of author(s)
|
/// Sets a string of author(s)
|
||||||
///
|
///
|
||||||
/// Example:
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use clap::{App, Arg};
|
/// # use clap::{App, Arg};
|
||||||
|
@ -115,7 +120,7 @@ impl App {
|
||||||
|
|
||||||
/// Sets a string briefly describing what the program does
|
/// Sets a string briefly describing what the program does
|
||||||
///
|
///
|
||||||
/// Example:
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use clap::{App, Arg};
|
/// # use clap::{App, Arg};
|
||||||
|
@ -130,7 +135,7 @@ impl App {
|
||||||
|
|
||||||
/// Sets a string of the version number
|
/// Sets a string of the version number
|
||||||
///
|
///
|
||||||
/// Example:
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use clap::{App, Arg};
|
/// # use clap::{App, Arg};
|
||||||
|
@ -138,14 +143,34 @@ impl App {
|
||||||
/// .version("v0.1.24")
|
/// .version("v0.1.24")
|
||||||
/// # .get_matches();
|
/// # .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.version = Some(v);
|
||||||
self
|
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
|
/// Adds an argument to the list of valid possibilties
|
||||||
///
|
///
|
||||||
/// Example:
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use clap::{App, Arg};
|
/// # use clap::{App, Arg};
|
||||||
|
@ -239,9 +264,9 @@ impl App {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds arguments to the list of valid possibilties
|
/// Adds multiple arguments to the list of valid possibilties
|
||||||
///
|
///
|
||||||
/// Example:
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # use clap::{App, Arg};
|
/// # use clap::{App, Arg};
|
||||||
|
@ -257,12 +282,44 @@ impl App {
|
||||||
self
|
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 {
|
pub fn subcommand(mut self, subcmd: App) -> App {
|
||||||
if subcmd.name == "help" { self.needs_subcmd_help = false; }
|
if subcmd.name == "help" { self.needs_subcmd_help = false; }
|
||||||
self.subcommands.insert(subcmd.name, Box::new(subcmd));
|
self.subcommands.insert(subcmd.name, Box::new(subcmd));
|
||||||
self
|
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 {
|
pub fn subcommands(mut self, subcmds: Vec<App>) -> App {
|
||||||
for subcmd in subcmds.into_iter() {
|
for subcmd in subcmds.into_iter() {
|
||||||
self = self.subcommand(subcmd);
|
self = self.subcommand(subcmd);
|
||||||
|
@ -277,16 +334,50 @@ impl App {
|
||||||
|
|
||||||
fn report_error(&self, msg: String, help: bool, quit: bool) {
|
fn report_error(&self, msg: String, help: bool, quit: bool) {
|
||||||
println!("{}", msg);
|
println!("{}", msg);
|
||||||
if help { self.print_help(); }
|
if help { self.print_usage(true); }
|
||||||
if quit { env::set_exit_status(1); self.exit(); }
|
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) {
|
fn print_help(&self) {
|
||||||
self.print_version(false);
|
self.print_version(false);
|
||||||
let mut flags = false;
|
let flags = ! self.flags.is_empty();
|
||||||
let mut pos = false;
|
let pos = ! self.positionals_idx.is_empty();
|
||||||
let mut opts = false;
|
let opts = ! self.opts.is_empty();
|
||||||
let mut subcmds = false;
|
let subcmds = ! self.subcommands.is_empty();
|
||||||
|
|
||||||
if let Some(author) = self.author {
|
if let Some(author) = self.author {
|
||||||
println!("{}", author);
|
println!("{}", author);
|
||||||
|
@ -295,12 +386,7 @@ impl App {
|
||||||
println!("{}", about);
|
println!("{}", about);
|
||||||
}
|
}
|
||||||
println!("");
|
println!("");
|
||||||
println!("USAGE:");
|
self.print_usage(false);
|
||||||
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 {""});
|
|
||||||
if flags || opts || pos || subcmds {
|
if flags || opts || pos || subcmds {
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
|
@ -318,11 +404,12 @@ impl App {
|
||||||
println!("");
|
println!("");
|
||||||
println!("OPTIONS:");
|
println!("OPTIONS:");
|
||||||
for v in self.opts.values() {
|
for v in self.opts.values() {
|
||||||
println!("\t{}{}{}\t\t{}",
|
let mut needs_tab = false;
|
||||||
if let Some(ref s) = v.short{format!("-{}",s)}else{format!(" ")},
|
println!("\t{}{}{}\t{}",
|
||||||
if let Some(ref l) = v.long {format!(",--{}",l)}else {format!(" ")},
|
if let Some(ref s) = v.short{format!("-{} ",s)}else{format!(" ")},
|
||||||
format!(" <{}>", v.name),
|
if let Some(ref l) = v.long {format!(",--{}=",l)}else {needs_tab = true; format!(" ")},
|
||||||
if let Some(ref h) = v.help {*h} else {" "} );
|
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 {
|
if pos {
|
||||||
|
@ -381,7 +468,7 @@ impl App {
|
||||||
if *l == arg {
|
if *l == arg {
|
||||||
if self.blacklist.contains(k) {
|
if self.blacklist.contains(k) {
|
||||||
self.report_error(format!("The argument --{} is mutually exclusive with one or more other arguments", arg),
|
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{
|
matches.opts.insert(k, OptArg{
|
||||||
name: v.name,
|
name: v.name,
|
||||||
|
@ -413,7 +500,7 @@ impl App {
|
||||||
if ! multi {
|
if ! multi {
|
||||||
if self.blacklist.contains(k) {
|
if self.blacklist.contains(k) {
|
||||||
self.report_error(format!("The argument --{} is mutually exclusive with one or more other arguments", arg),
|
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{
|
matches.flags.insert(k, FlagArg{
|
||||||
name: v.name,
|
name: v.name,
|
||||||
|
@ -453,7 +540,7 @@ impl App {
|
||||||
if ! found {
|
if ! found {
|
||||||
self.report_error(
|
self.report_error(
|
||||||
format!("Argument --{} isn't valid", arg),
|
format!("Argument --{} isn't valid", arg),
|
||||||
false, true);
|
true, true);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -467,7 +554,7 @@ impl App {
|
||||||
if ! self.parse_single_short_flag(matches, c) {
|
if ! self.parse_single_short_flag(matches, c) {
|
||||||
self.report_error(
|
self.report_error(
|
||||||
format!("Argument -{} isn't valid",c),
|
format!("Argument -{} isn't valid",c),
|
||||||
false, true);
|
true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -486,7 +573,7 @@ impl App {
|
||||||
|
|
||||||
self.report_error(
|
self.report_error(
|
||||||
format!("Argument -{} isn't valid",arg_c),
|
format!("Argument -{} isn't valid",arg_c),
|
||||||
false, true);
|
true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -556,7 +643,7 @@ impl App {
|
||||||
} else {
|
} else {
|
||||||
format!("\"{}\"", v.name)
|
format!("\"{}\"", v.name)
|
||||||
}),
|
}),
|
||||||
false, true);
|
true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (k, v) in matches.opts.iter() {
|
for (k, v) in matches.opts.iter() {
|
||||||
|
@ -569,7 +656,7 @@ impl App {
|
||||||
} else {
|
} else {
|
||||||
format!("\"{}\"", v.name)
|
format!("\"{}\"", v.name)
|
||||||
}),
|
}),
|
||||||
false, true);
|
true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (k, v) in matches.positionals.iter() {
|
for (k, v) in matches.positionals.iter() {
|
||||||
|
@ -631,7 +718,7 @@ impl App {
|
||||||
format!("--{}",long)
|
format!("--{}",long)
|
||||||
}else{
|
}else{
|
||||||
format!("-{}",opt.short.unwrap())
|
format!("-{}",opt.short.unwrap())
|
||||||
}),false, true);
|
}),true, true);
|
||||||
}
|
}
|
||||||
matches.opts.insert(nvo, OptArg{
|
matches.opts.insert(nvo, OptArg{
|
||||||
name: opt.name,
|
name: opt.name,
|
||||||
|
@ -689,12 +776,12 @@ impl App {
|
||||||
if self.positionals_idx.is_empty() { // || self.positionals_name.is_empty() {
|
if self.positionals_idx.is_empty() { // || self.positionals_name.is_empty() {
|
||||||
self.report_error(
|
self.report_error(
|
||||||
format!("Found positional argument {}, but {} doesn't accept any", arg, self.name),
|
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 let Some(ref p) = self.positionals_idx.get(&pos_counter) {
|
||||||
if self.blacklist.contains(p.name) {
|
if self.blacklist.contains(p.name) {
|
||||||
self.report_error(format!("The argument \"{}\" is mutually exclusive with one or more other arguments", arg),
|
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{
|
matches.positionals.insert(p.name, PosArg{
|
||||||
name: p.name,
|
name: p.name,
|
||||||
|
@ -727,7 +814,7 @@ impl App {
|
||||||
}
|
}
|
||||||
pos_counter += 1;
|
pos_counter += 1;
|
||||||
} else {
|
} 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) => {
|
Some(ref a) => {
|
||||||
self.report_error(
|
self.report_error(
|
||||||
format!("Argument \"{}\" requires a value but none was supplied", a),
|
format!("Argument \"{}\" requires a value but none was supplied", a),
|
||||||
false, true);
|
true, true);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
if ! self.required.is_empty() {
|
if ! self.required.is_empty() {
|
||||||
self.report_error("One or more required arguments were not supplied".to_string(),
|
self.report_error("One or more required arguments were not supplied".to_string(),
|
||||||
false, true);
|
true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.validate_blacklist(&matches);
|
self.validate_blacklist(&matches);
|
||||||
|
@ -762,8 +849,17 @@ impl App {
|
||||||
let mut matches = ArgMatches::new(self.name);
|
let mut matches = ArgMatches::new(self.name);
|
||||||
|
|
||||||
let args = env::args().collect::<Vec<_>>();
|
let args = env::args().collect::<Vec<_>>();
|
||||||
|
let mut it = args.into_iter();
|
||||||
self.get_matches_from(&mut matches, &mut 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
|
matches
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue