diff --git a/Cargo.toml b/Cargo.toml index d9dae347..325cabb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,10 @@ optional = true version = "~0.6.3" optional = true +[dependencies.yaml-rust] +version = "~0.2.1" +optional = true + #[dependencies.clippy] #version = "~0.0.12" #optional = true @@ -27,6 +31,7 @@ optional = true default=["suggestions", "color"] suggestions=["strsim"] color = ["ansi_term"] +yaml = ["yaml-rust"] #lints = ["clippy", "nightly"] # for building with nightly and unstable features diff --git a/src/app.rs b/src/app/app.rs similarity index 94% rename from src/app.rs rename to src/app/app.rs index 8ee354e3..81f1ef8f 100644 --- a/src/app.rs +++ b/src/app/app.rs @@ -7,192 +7,14 @@ use args::{ArgMatches, Arg, SubCommand, MatchedArg}; use args::{FlagBuilder, OptBuilder, PosBuilder}; use args::ArgGroup; use fmt::Format; +use super::settings::AppSettings; + +use super::suggestions::{DidYouMeanMessageStyle, did_you_mean}; -#[cfg(feature = "suggestions")] -use strsim; const INTERNAL_ERROR_MSG: &'static str = "Internal Error: Failed to write string. Please \ consider filing a bug report!"; -/// Produces a string from a given list of possible values which is similar to -/// the passed in value `v` with a certain confidence. -/// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield -/// `Some("foo")`, whereas "blark" would yield `None`. -#[cfg(feature = "suggestions")] -#[cfg_attr(feature = "lints", allow(needless_lifetimes))] -fn did_you_mean<'a, T, I>(v: &str, possible_values: I) -> Option<&'a str> - where T: AsRef + 'a, - I: IntoIterator { - - let mut candidate: Option<(f64, &str)> = None; - for pv in possible_values.into_iter() { - let confidence = strsim::jaro_winkler(v, pv.as_ref()); - if confidence > 0.8 && (candidate.is_none() || - (candidate.as_ref().unwrap().0 < confidence)) { - candidate = Some((confidence, pv.as_ref())); - } - } - match candidate { - None => None, - Some((_, candidate)) => Some(candidate), - } -} - -#[cfg(not(feature = "suggestions"))] -fn did_you_mean<'a, T, I>(_: &str, _: I) -> Option<&'a str> - where T: AsRef + 'a, - I: IntoIterator { - None -} - -/// A helper to determine message formatting -enum DidYouMeanMessageStyle { - /// Suggested value is a long flag - LongFlag, - /// Suggested value is one of various possible values - EnumValue, -} - -/// Application level settings, which affect how `App` operates -pub enum AppSettings { - /// Allows subcommands to override all requirements of the parent (this command). For example - /// if you had a subcommand or even top level application which had a required arguments that - /// are only required as long as there is no subcommand present. - /// - /// **NOTE:** This defaults to false (using subcommand does *not* negate requirements) - /// - /// # Example - /// - /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::SubcommandsNegateReqs) - /// # ; - /// ``` - SubcommandsNegateReqs, - /// Allows specifying that if no subcommand is present at runtime, error and exit gracefully - /// - /// **NOTE:** This defaults to false (subcommands do *not* need to be present) - /// - /// # Example - /// - /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::SubcommandRequired) - /// # ; - /// ``` - SubcommandRequired, - /// Specifies that the help text sould be displayed (and then exit gracefully), if no - /// arguments are present at runtime (i.e. an empty run such as, `$ myprog`. - /// - /// **NOTE:** Subcommands count as arguments - /// - /// # Example - /// - /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::ArgRequiredElseHelp) - /// # ; - /// ``` - ArgRequiredElseHelp, - /// Uses version of the current command for all subcommands. (Defaults to false; subcommands - /// have independant version strings) - /// - /// **NOTE:** The version for the current command and this setting must be set **prior** to - /// adding any subcommands - /// - /// # Example - /// - /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; - /// App::new("myprog") - /// .version("v1.1") - /// .setting(AppSettings::GlobalVersion) - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches(); - /// // running `myprog test --version` will display - /// // "myprog-test v1.1" - /// ``` - GlobalVersion, - /// Disables `-V` and `--version` for all subcommands (Defaults to false; subcommands have - /// version flags) - /// - /// **NOTE:** This setting must be set **prior** adding any subcommands - /// - /// **NOTE:** Do not set this value to false, it will have undesired results! - /// - /// # Example - /// - /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; - /// App::new("myprog") - /// .version("v1.1") - /// .setting(AppSettings::VersionlessSubcommands) - /// .subcommand(SubCommand::with_name("test")) - /// .get_matches(); - /// // running `myprog test --version` will display unknown argument error - /// ``` - VersionlessSubcommands, - /// By default the auto-generated help message groups flags, options, and positional arguments - /// separately. This setting disable that and groups flags and options together presenting a - /// more unified help message (a la getopts or docopt style). - /// - /// **NOTE:** This setting is cosmetic only and does not affect any functionality. - /// - /// # Example - /// - /// ```no_run - /// # use clap::{App, Arg, SubCommand, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::UnifiedHelpMessage) - /// .get_matches(); - /// // running `myprog --help` will display a unified "docopt" or "getopts" style help message - /// ``` - UnifiedHelpMessage, - /// Will display a message "Press [ENTER]/[RETURN] to continue..." and wait user before - /// exiting - /// - /// This is most useful when writing an application which is run from a GUI shortcut, or on - /// Windows where a user tries to open the binary by double-clicking instead of using the - /// command line (i.e. set `.arg_required_else_help(true)` and `.wait_on_error(true)` to - /// display the help in such a case). - /// - /// **NOTE:** This setting is **not** recursive with subcommands, meaning if you wish this - /// behavior for all subcommands, you must set this on each command (needing this is extremely - /// rare) - /// - /// # Example - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::WaitOnError) - /// # ; - /// ``` - WaitOnError, - /// Specifies that the help text sould be displayed (and then exit gracefully), if no - /// subcommands are present at runtime (i.e. an empty run such as, `$ myprog`. - /// - /// **NOTE:** This should *not* be used with `.subcommand_required()` as they do the same - /// thing, except one prints the help text, and one prints an error. - /// - /// **NOTE:** If the user specifies arguments at runtime, but no subcommand the help text will - /// still be displayed and exit. If this is *not* the desired result, consider using - /// `.arg_required_else_help()` - /// - /// # Example - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::SubcommandRequiredElseHelp) - /// # ; - /// ``` - SubcommandRequiredElseHelp, -} - /// Used to create a representation of a command line program and all possible command line /// arguments. /// @@ -323,6 +145,56 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } + /// Creates a new instace of `App` from a .yml (YAML) file. + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// let prog = App::from_yaml(include!("my_app.yml")); + /// ``` + #[cfg(feature = "yaml")] + pub fn from_yaml(n: &'ar str) -> Self { + + App { + name: n.to_owned(), + name_slice: n, + author: None, + about: None, + more_help: None, + version: None, + flags: BTreeMap::new(), + opts: BTreeMap::new(), + positionals_idx: BTreeMap::new(), + positionals_name: HashMap::new(), + subcommands: BTreeMap::new(), + needs_long_version: true, + needs_long_help: true, + needs_subcmd_help: true, + help_short: None, + version_short: None, + required: vec![], + short_list: vec![], + long_list: vec![], + usage_str: None, + usage: None, + blacklist: vec![], + bin_name: None, + groups: HashMap::new(), + subcmds_neg_reqs: false, + global_args: vec![], + no_sc_error: false, + help_str: None, + wait_on_error: false, + help_on_no_args: false, + help_on_no_sc: false, + global_ver: false, + versionless_scs: None, + unified_help: false, + overrides: vec![] + } + } + /// Sets a string of author(s) and will be displayed to the user when they request the help /// information with `--help` or `-h`. /// diff --git a/src/app/mod.rs b/src/app/mod.rs new file mode 100644 index 00000000..4e5dff05 --- /dev/null +++ b/src/app/mod.rs @@ -0,0 +1,6 @@ +mod settings; +mod app; +mod suggestions; + +pub use self::settings::AppSettings; +pub use self::app::App; \ No newline at end of file diff --git a/src/app/settings.rs b/src/app/settings.rs new file mode 100644 index 00000000..445d3e46 --- /dev/null +++ b/src/app/settings.rs @@ -0,0 +1,139 @@ +/// Application level settings, which affect how `App` operates +pub enum AppSettings { + /// Allows subcommands to override all requirements of the parent (this command). For example + /// if you had a subcommand or even top level application which had a required arguments that + /// are only required as long as there is no subcommand present. + /// + /// **NOTE:** This defaults to false (using subcommand does *not* negate requirements) + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, AppSettings}; + /// App::new("myprog") + /// .setting(AppSettings::SubcommandsNegateReqs) + /// # ; + /// ``` + SubcommandsNegateReqs, + /// Allows specifying that if no subcommand is present at runtime, error and exit gracefully + /// + /// **NOTE:** This defaults to false (subcommands do *not* need to be present) + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, AppSettings}; + /// App::new("myprog") + /// .setting(AppSettings::SubcommandRequired) + /// # ; + /// ``` + SubcommandRequired, + /// Specifies that the help text sould be displayed (and then exit gracefully), if no + /// arguments are present at runtime (i.e. an empty run such as, `$ myprog`. + /// + /// **NOTE:** Subcommands count as arguments + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, AppSettings}; + /// App::new("myprog") + /// .setting(AppSettings::ArgRequiredElseHelp) + /// # ; + /// ``` + ArgRequiredElseHelp, + /// Uses version of the current command for all subcommands. (Defaults to false; subcommands + /// have independant version strings) + /// + /// **NOTE:** The version for the current command and this setting must be set **prior** to + /// adding any subcommands + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// App::new("myprog") + /// .version("v1.1") + /// .setting(AppSettings::GlobalVersion) + /// .subcommand(SubCommand::with_name("test")) + /// .get_matches(); + /// // running `myprog test --version` will display + /// // "myprog-test v1.1" + /// ``` + GlobalVersion, + /// Disables `-V` and `--version` for all subcommands (Defaults to false; subcommands have + /// version flags) + /// + /// **NOTE:** This setting must be set **prior** adding any subcommands + /// + /// **NOTE:** Do not set this value to false, it will have undesired results! + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// App::new("myprog") + /// .version("v1.1") + /// .setting(AppSettings::VersionlessSubcommands) + /// .subcommand(SubCommand::with_name("test")) + /// .get_matches(); + /// // running `myprog test --version` will display unknown argument error + /// ``` + VersionlessSubcommands, + /// By default the auto-generated help message groups flags, options, and positional arguments + /// separately. This setting disable that and groups flags and options together presenting a + /// more unified help message (a la getopts or docopt style). + /// + /// **NOTE:** This setting is cosmetic only and does not affect any functionality. + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// App::new("myprog") + /// .setting(AppSettings::UnifiedHelpMessage) + /// .get_matches(); + /// // running `myprog --help` will display a unified "docopt" or "getopts" style help message + /// ``` + UnifiedHelpMessage, + /// Will display a message "Press [ENTER]/[RETURN] to continue..." and wait user before + /// exiting + /// + /// This is most useful when writing an application which is run from a GUI shortcut, or on + /// Windows where a user tries to open the binary by double-clicking instead of using the + /// command line (i.e. set `.arg_required_else_help(true)` and `.wait_on_error(true)` to + /// display the help in such a case). + /// + /// **NOTE:** This setting is **not** recursive with subcommands, meaning if you wish this + /// behavior for all subcommands, you must set this on each command (needing this is extremely + /// rare) + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg, AppSettings}; + /// App::new("myprog") + /// .setting(AppSettings::WaitOnError) + /// # ; + /// ``` + WaitOnError, + /// Specifies that the help text sould be displayed (and then exit gracefully), if no + /// subcommands are present at runtime (i.e. an empty run such as, `$ myprog`. + /// + /// **NOTE:** This should *not* be used with `.subcommand_required()` as they do the same + /// thing, except one prints the help text, and one prints an error. + /// + /// **NOTE:** If the user specifies arguments at runtime, but no subcommand the help text will + /// still be displayed and exit. If this is *not* the desired result, consider using + /// `.arg_required_else_help()` + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg, AppSettings}; + /// App::new("myprog") + /// .setting(AppSettings::SubcommandRequiredElseHelp) + /// # ; + /// ``` + SubcommandRequiredElseHelp, +} \ No newline at end of file diff --git a/src/app/suggestions.rs b/src/app/suggestions.rs new file mode 100644 index 00000000..2530e71d --- /dev/null +++ b/src/app/suggestions.rs @@ -0,0 +1,41 @@ +#[cfg(feature = "suggestions")] +use strsim; + +/// Produces a string from a given list of possible values which is similar to +/// the passed in value `v` with a certain confidence. +/// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield +/// `Some("foo")`, whereas "blark" would yield `None`. +#[cfg(feature = "suggestions")] +#[cfg_attr(feature = "lints", allow(needless_lifetimes))] +pub fn did_you_mean<'a, T, I>(v: &str, possible_values: I) -> Option<&'a str> + where T: AsRef + 'a, + I: IntoIterator { + + let mut candidate: Option<(f64, &str)> = None; + for pv in possible_values.into_iter() { + let confidence = strsim::jaro_winkler(v, pv.as_ref()); + if confidence > 0.8 && (candidate.is_none() || + (candidate.as_ref().unwrap().0 < confidence)) { + candidate = Some((confidence, pv.as_ref())); + } + } + match candidate { + None => None, + Some((_, candidate)) => Some(candidate), + } +} + +#[cfg(not(feature = "suggestions"))] +pub fn did_you_mean<'a, T, I>(_: &str, _: I) -> Option<&'a str> + where T: AsRef + 'a, + I: IntoIterator { + None +} + +/// A helper to determine message formatting +pub enum DidYouMeanMessageStyle { + /// Suggested value is a long flag + LongFlag, + /// Suggested value is one of various possible values + EnumValue, +} \ No newline at end of file