From de36cd18c07aa3868f9319424116acdbfd297631 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Mon, 18 Feb 2019 20:37:53 -0500 Subject: [PATCH] wip --- Cargo.toml | 1 - rustfmt.toml | 2 + src/args_map.rs | 272 ------------------ src/build/{app/mod.rs => app.rs} | 133 ++++----- src/build/{arg/mod.rs => arg.rs} | 213 +++++--------- src/build/arg/help_message.rs | 21 +- src/build/arg/{key/mod.rs => key.rs} | 14 +- src/build/arg/key/position.rs | 6 +- src/build/arg/key/short.rs | 4 +- src/build/arg/occurrence.rs | 31 +- src/build/arg/validation_rules.rs | 60 +++- src/build/arg/validation_rules/conditions.rs | 43 +++ src/build/arg/validation_rules/rules.rs | 52 ++++ src/build/arg/{value/mod.rs => value.rs} | 13 + .../arg/value/{ => filter}/possible_values.rs | 0 .../arg/value/{ => filter}/terminator.rs | 0 src/build/arg/value/filter/validators.rs | 4 + src/build/arg/value/value_name.rs | 2 + src/build/args.rs | 113 ++++++++ src/build/mod.rs | 1 + src/lib.rs | 1 - src/util/fnv.rs | 13 +- 22 files changed, 483 insertions(+), 516 deletions(-) delete mode 100644 src/args_map.rs rename src/build/{app/mod.rs => app.rs} (96%) rename src/build/{arg/mod.rs => arg.rs} (97%) rename src/build/arg/{key/mod.rs => key.rs} (71%) create mode 100644 src/build/arg/validation_rules/conditions.rs create mode 100644 src/build/arg/validation_rules/rules.rs rename src/build/arg/{value/mod.rs => value.rs} (50%) rename src/build/arg/value/{ => filter}/possible_values.rs (100%) rename src/build/arg/value/{ => filter}/terminator.rs (100%) create mode 100644 src/build/arg/value/filter/validators.rs create mode 100644 src/build/args.rs diff --git a/Cargo.toml b/Cargo.toml index 923d719d..36b2ea1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ [package] - name = "clap" version = "3.0.0-beta.1" authors = ["Kevin K. "] diff --git a/rustfmt.toml b/rustfmt.toml index 0d83eda4..aeabaa48 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1,4 @@ +type_punctuation_density = "Compressed" format_strings = false fn_single_line = true +where_pred_indent = "Visual" \ No newline at end of file diff --git a/src/args_map.rs b/src/args_map.rs deleted file mode 100644 index 7e361a09..00000000 --- a/src/args_map.rs +++ /dev/null @@ -1,272 +0,0 @@ -use std::collections::HashMap; -use std::collections::hash_map::Keys; -use std::ffi::{OsStr, OsString}; -use std::slice::{Iter, IterMut}; - -use build::{ArgSettings, Arg}; -use util::hash; - -// LE -fn short_to_bytes(s: char) -> [u8; 8] { - let bytes = ['-', s]; - let h = '-' as u32; - let c = s as u32; - - let b1 : u8 = ((h >> 24) & 0xff) as u8; - let b2 : u8 = ((h >> 16) & 0xff) as u8; - let b3 : u8 = ((h >> 8) & 0xff) as u8; - let b4 : u8 = (h & 0xff) as u8; - let b5 : u8 = ((c >> 24) & 0xff) as u8; - let b6 : u8 = ((c >> 16) & 0xff) as u8; - let b7 : u8 = ((c >> 8) & 0xff) as u8; - let b8 : u8 = (c & 0xff) as u8; - - [b1, b1, b3, b4, b5, b6, b7, b8] -} - -// LE -fn u64_to_bytes(u: u64) -> [u8; 8] { - let b1 : u8 = ((u >> 56) & 0xff) as u8; - let b2 : u8 = ((u >> 48) & 0xff) as u8; - let b3 : u8 = ((u >> 40) & 0xff) as u8; - let b4 : u8 = ((u >> 32) & 0xff) as u8; - let b5 : u8 = ((u >> 24) & 0xff) as u8; - let b6 : u8 = ((u >> 16) & 0xff) as u8; - let b7 : u8 = ((u >> 8) & 0xff) as u8; - let b8 : u8 = (u & 0xff) as u8; - - [b1, b1, b3, b4, b5, b6, b7, b8] -} - -#[derive(Default, PartialEq, Debug, Clone)] -pub struct ArgsMap<'help> { - pub index_map: HashMap, - pub args: Vec>, - built: bool, // mutation isn't possible after being built -} - -impl<'help> ArgsMap<'help> { - pub fn new() -> Self { ArgsMap::default() } - //TODO ::from(x), ::with_capacity(n) etc - - pub fn contains_long(&self, l: &str) -> bool { self.index_map.get(&hash(l.as_bytes())).is_some() } - - pub fn contains_short(&self, c: char) -> bool { self.index_map.get(&hash(short_to_bytes(c))).is_some() } - - pub fn insert(&mut self, arg: Arg<'help>) -> usize { - assert!(!self.built, "Cannot add Args to the map after the map is built"); - - let index = self.args.len(); - self.insert_keys(&arg, index); - self.args.push(arg); - index - } - - pub fn push(&mut self, arg: Arg<'help>) -> usize { - assert!(!self.built, "Cannot add Args to the map after the map is built"); - - let index = self.args.len(); - self.args.push(arg); - index - } - - //TODO ::push_many([x, y]) - - pub fn insert_short_key(&mut self, key: char, index: usize) { - self.index_map.insert(hash(short_to_bytes(key)), index); - } - - pub fn insert_long_key(&mut self, key: &str, index: usize) { - self.index_map.insert(hash(key.as_bytes()), index); - } - - pub fn insert_positional_key(&mut self, key: u64, index: usize) { - self.index_map.insert(hash(u64_to_bytes(key)), index); - } - - pub fn get_by_id(&self, key: u64) -> Option<&Arg<'help>> { - self.args.get(*self.index_map.get(&key)?) - } - pub fn get_by_index(&self, key: usize) -> Option<&Arg<'help>> { - self.args.get(key) - } - pub fn get_by_short(&self, key: char) -> Option<&Arg<'help>> { - self.args.get(*self.index_map.get(&hash(short_to_bytes(key)))?) - } - // &[u8] better? - pub fn get_by_short_with_hyphen(&self, key: [u8; 8]) -> Option<&Arg<'help>> { - self.args.get(*self.index_map.get(&hash(&key))?) - } - pub fn get_by_long(&self, key: &str) -> Option<&Arg<'help>> { - self.args.get(*self.index_map.get(&hash(key.as_bytes()))?) - } - pub fn get_by_long_with_hyphen(&self, key: &[u8]) -> Option<&Arg<'help>> { - self.args.get(*self.index_map.get(&hash(key))?) - } - pub fn get_by_positional(&self, key: u64) -> Option<&Arg<'help>> { - self.args.get(*self.index_map.get(&hash(u64_to_bytes(key)))?) - } - - pub fn get_mut_by_id(&mut self, key: u64) -> Option<&mut Arg<'help>> { - self.args.get_mut(*self.index_map.get(&key)?) - } - pub fn get_mut_by_index(&mut self, key: usize) -> Option<&mut Arg<'help>> { - self.args.get_mut(key) - } - pub fn get_mut_by_short(&mut self, key: char) -> Option<&mut Arg<'help>> { - self.args.get_mut(*self.index_map.get(&hash(short_to_bytes(key)))?) - } - // &[u8] better? - pub fn get_mut_by_short_with_hyphen(&mut self, key: [u8; 8]) -> Option<&mut Arg<'help>> { - self.args.get_mut(*self.index_map.get(&hash(&key))?) - } - pub fn get_mut_by_long(&mut self, key: &str) -> Option<&mut Arg<'help>> { - self.args.get_mut(*self.index_map.get(&hash(key.as_bytes()))?) - } - pub fn get_mut_by_long_with_hyphen(&mut self, key: &[u8]) -> Option<&mut Arg<'help>> { - self.args.get_mut(*self.index_map.get(&hash(key))?) - } - pub fn get_mut_by_positional(&mut self, key: u64) -> Option<&mut Arg<'help>> { - self.args.get_mut(*self.index_map.get(&hash(u64_to_bytes(key)))?) - } - - pub fn is_empty(&self) -> bool { self.args.is_empty() } - - // Remove Key - pub fn remove_id_key(&mut self, key: u64) -> Option { - self.index_map.remove(&key) - } - pub fn remove_short_key(&mut self, key: char) -> Option { - self.index_map.remove(&hash(short_to_bytes(key))) - } - pub fn remove_short_key_with_hyphen(&mut self, key: [u8; 8]) -> Option { - self.index_map.remove(&hash(&key)) - } - pub fn remove_long_key(&mut self, key: &str) -> Option { - self.index_map.remove(&hash(key.as_bytes())) - } - pub fn remove_long_key_with_hyphen(&mut self, key: &[u8]) -> Option { - self.index_map.remove(&hash(key)) - } - pub fn remove_positional_key(&mut self, key: u64) -> Option { - self.index_map.remove(&hash(u64_to_bytes(key))) - } - - pub fn remove_by_id(&mut self, id: u64) -> Option { - assert!(!self.built, "Cannot remove args once built"); - let i = self.index_map.remove(&id)?; - Some(self.args.swap_remove(i)) - } - - //TODO ::remove_keys([KeyA, KeyB]) - - fn insert_keys(&mut self, arg: &Arg, index: usize) { - self.index_map.insert(arg.id, index); - if let Some(p) = arg.index { - self.index_map.insert(hash(u64_to_bytes(p)), index); - } else { - if let Some(s) = arg.short { - self.index_map.insert(hash(short_to_bytes(s)), index); - } - if let Some(l) = arg.long { - self.index_map.insert(hash(l.as_bytes()), index); - } - } - } - - pub fn _build(&mut self) { - self.built = true; - - for (i, arg) in self.args.iter().enumerate() { - self.insert_keys(arg, i); - } - } - - pub fn find_by_name(&mut self, name: u64) -> usize { - self.args - .iter() - .position(|x| x.id == name) - .expect("No such name found") - } -} - - -// Iter getters -impl<'help> ArgsMap<'help> { - pub fn iter_args(&self) -> Iter> { - self.args.iter() - } - - pub fn iter_args_mut(&mut self) -> IterMut> { - self.args.iter_mut() - } - - pub fn flags(&self) -> impl Iterator> { - self.args.iter().filter(|x| !x.is_set(ArgSettings::TakesValue) && x.has_switch()) - } - - pub fn opts(&self) -> impl Iterator> { - self.args.iter().filter(|x| x.is_set(ArgSettings::TakesValue) && x.has_switch()) - } - - pub fn positionals(&self) -> impl Iterator> { - self.args.iter().filter(|x| x.index.is_some()) - } - - pub fn global_args(&self) -> impl Iterator> { - self.args.iter().filter(|a| a.is_set(ArgSettings::Global)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use util::hash; - - #[test] - fn get_some_value() { - let mut map: ArgsMap = ArgsMap::new(); - - map.insert(Arg::new("Value1").long("value")); - - assert_eq!( - map.get_by_long_with_hyphen("--value".as_bytes()).unwrap().id, - hash("Value1") - ); - } - - #[test] - fn get_none_value() { - let mut map: ArgsMap = ArgsMap::new(); - - map.insert(Arg::new("Value1").long("value")); - - assert_eq!(map.get_by_long("none"), None); - } - - #[test] - fn insert_multiple_keys() { - let mut map: ArgsMap = ArgsMap::new(); - let index = map.insert(Arg::new("Value1").long("value")); - - map.insert_long_key("other", index); - - assert_eq!( - map.get_by_long("value"), - map.get_by_long("other"), - ); - assert_eq!(map.args.len(), 1); - } - - #[test] - fn remove_key() { - let mut map: ArgsMap = ArgsMap::new(); - let index = map.insert(Arg::new("Value1").long("value")); - map.insert_long_key("other", index); - - map.remove_long_key("other"); - - assert_eq!(map.index_map.len(), 1); - assert_eq!(map.args.len(), 1); - } -} diff --git a/src/build/app/mod.rs b/src/build/app.rs similarity index 96% rename from src/build/app/mod.rs rename to src/build/app.rs index 372bb38a..78a2f8d9 100644 --- a/src/build/app/mod.rs +++ b/src/build/app.rs @@ -17,13 +17,14 @@ use std::process; use yaml_rust::Yaml; // Internal -use build::{Arg, ArgGroup, ArgSettings, Terminal, HelpMsg, VersionMsg, Aliases}; -use args_map::ArgsMap; -use output::fmt::ColorWhen; -use output::{Help, Usage}; -use parse::errors::Result as ClapResult; -use parse::{ArgMatcher, ArgMatches, Parser}; -use util::hash; +use crate::build::{Arg, ArgGroup, ArgSettings, Terminal, HelpMsg, VersionMsg, Aliases}; +use crate::build::args::Args; +use crate::output::fmt::ColorWhen; +use crate::output::{Help, Usage}; +use crate::parse::errors::Result as ClapResult; +use crate::parse::{ArgMatcher, ArgMatches, Parser}; +use crate::util::hash; +use crate::build::args::Find; use INTERNAL_ERROR_MSG; #[doc(hidden)] @@ -91,7 +92,7 @@ pub struct App<'help> { pub g_settings: AppFlags, // The list of valid arguments #[doc(hidden)] - pub args: ArgsMap<'help>, + pub args: Args<'help>, // A list of valid subcommands #[doc(hidden)] pub subcommands: Vec>, @@ -127,6 +128,40 @@ impl<'help> App<'help> { } } + /// Preallocate `args` number of Arguments + pub fn with_args>(n: S, args: usize) -> Self where S: 'help { + let name = n.as_ref(); + App { + id: hash(name), + name, + args: Args::with_capacity(args), + ..Default::default() + } + } + + /// Preallocate `scs` number of SubCommands + pub fn with_subcommands>(n: S, scs: usize) -> Self where S: 'help { + let name = n.as_ref(); + App { + id: hash(name), + name, + subcommands: Vec::with_capacity(scs), + ..Default::default() + } + } + + /// Preallocate `args` number of Arguments and `scs` number of SubCommands (not recursive) + pub fn with_args_and_subcommands>(n: S, args: usize, scs: usize) -> Self where S: 'help { + let name = n.as_ref(); + App { + id: hash(name), + name, + args: Args::with_capacity(args), + subcommands: Vec::with_capacity(scs), + ..Default::default() + } + } + /// Get the name of the app pub fn get_name(&self) -> &str { &self.name } @@ -962,7 +997,7 @@ impl<'help> App<'help> { { let a = self .args - .remove_by_id(hash(arg)) + .remove(hash(arg)) .unwrap_or_else(|| Arg::new(arg)); self.args.push(f(a)); @@ -1365,8 +1400,7 @@ impl<'a, 'help> App<'help> { let global_arg_vec: Vec = self .args - .args - .iter() + .args() .filter(|a| a.is_set(ArgSettings::Global)) .map(|ga| ga.id) .collect(); @@ -1401,8 +1435,7 @@ impl<'a, 'help> App<'help> { true }); - let mut pos_counter = 1; - for a in self.args.args.iter_mut() { + for a in self.args.args_mut() { // Figure out implied settings if a.is_set(ArgSettings::Last) { // if an arg has `Last` set, we need to imply DontCollapseArgsInUsage so that args @@ -1411,10 +1444,6 @@ impl<'a, 'help> App<'help> { self.set(AppSettings::ContainsLast); } a._build(); - if a.short.is_none() && a.long.is_none() && a.index.is_none() { - a.index = Some(pos_counter); - pos_counter += 1; - } } debug_assert!(self._app_debug_asserts()); @@ -1425,8 +1454,8 @@ impl<'a, 'help> App<'help> { // Perform some expensive assertions on the Parser itself fn _app_debug_asserts(&mut self) -> bool { debugln!("App::_app_debug_asserts;"); - for id in self.args.args.iter().map(|x| x.id) { - if self.args.args.iter().filter(|x| x.id == id).count() > 1 { + for id in self.args.args.args().map(|x| x.id) { + if self.args.args.args().filter(|x| x.id == id).count() > 1 { panic!("Arg names must be unique"); } } @@ -1471,7 +1500,7 @@ impl<'a, 'help> App<'help> { pub(crate) fn _create_help_and_version(&mut self) { debugln!("App::_create_help_and_version;"); // @TODO @perf hardcode common hashes? - if !(self.args.get_by_long("help").is_some() || self.args.get_by_id(hash("help")).is_some()) + if !(self.args.find("help").is_some() || self.args.get_by_id(hash("help")).is_some()) { debugln!("App::_create_help_and_version: Building --help"); let mut help = Arg::new("help") @@ -1541,31 +1570,27 @@ impl<'a, 'help> App<'help> { debugln!("App::_arg_debug_asserts:{}", a.name); // Long conflicts - if let Some(l) = a.long { + for l in a.longs() { assert!( - self.args.args.iter().filter(|x| x.long == Some(l)).count() < 2, + self.args.args().filter(|x| x.uses_long(l)).count() < 2, "Argument long must be unique\n\n\t--{} is already in use", l ); } // Short conflicts - if let Some(s) = a.short { + if let Some(s) = a.get_short() { assert!( - self.args.args.iter().filter(|x| x.short == Some(s)).count() < 2, + self.args.args().filter(|x| x.uses_short(s)).count() < 2, "Argument short must be unique\n\n\t-{} is already in use", s ); } - if let Some(idx) = a.index { + if let Some(idx) = a.get_position() { // No index conflicts assert!( - self.args.positionals().fold(0, |acc, p| if p.index == Some(idx as u64) { - acc + 1 - } else { - acc - }) < 2, + self.args.positionals().filter(|x| x.uses_position(idx)).count() < 2, "Argument '{}' has the same index as another positional \ argument\n\n\tUse Arg::setting(ArgSettings::MultipleValues) to allow one \ positional argument to take multiple values", @@ -1574,14 +1599,8 @@ impl<'a, 'help> App<'help> { } if a.is_set(ArgSettings::Last) { assert!( - a.long.is_none(), - "Flags or Options may not have last(true) set. {} has both a long and \ - last(true) set.", - a.id - ); - assert!( - a.short.is_none(), - "Flags or Options may not have last(true) set. {} has both a short and \ + a.has_switch(), + "Flags or Options may not have last(true) set. {} has either a long or short and \ last(true) set.", a.id ); @@ -1690,20 +1709,6 @@ impl<'help> App<'help> { } } - pub(crate) fn contains_long(&self, l: &str) -> bool { - if !self.is_set(AppSettings::Propagated) { - panic!("If App::_build hasn't been called, manually search through Arg longs"); - } - self.args.contains_long(l) - } - - pub(crate) fn contains_short(&self, s: char) -> bool { - if !self.is_set(AppSettings::Propagated) { - panic!("If App::_build hasn't been called, manually search through Arg shorts"); - } - self.args.contains_short(s) - } - pub fn is_set(&self, s: AppSettings) -> bool { self.settings.is_set(s) || self.g_settings.is_set(s) } @@ -1718,28 +1723,6 @@ impl<'help> App<'help> { pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() } - pub fn has_args(&self) -> bool { !self.args.is_empty() } - - pub fn has_opts(&self) -> bool { self.args.has_opts() } - - pub fn has_flags(&self) -> bool { self.args.has_flags() } - - pub fn has_positionals(&self) -> bool { self.args.has_positionals() } - - pub fn has_visible_opts(&self) -> bool { self.args.opts().any(|o| !o.is_set(ArgSettings::Hidden)) } - - pub fn has_visible_flags(&self) -> bool { self.args.flags().any(|o| !o.is_set(ArgSettings::Hidden)) } - - pub fn has_visible_positionals(&self) -> bool { - self.args.positionals().any(|o| !o.is_set(ArgSettings::Hidden)) - } - - pub fn has_visible_subcommands(&self) -> bool { - self.subcommands.iter() - .filter(|sc| sc.name != "help") - .any(|sc| !sc.is_set(AppSettings::Hidden)) - } - pub(crate) fn unroll_args_in_group(&self, group: u64) -> Vec { let mut g_vec = vec![group]; let mut args = vec![]; @@ -1754,7 +1737,7 @@ impl<'help> App<'help> { .iter() { if !args.contains(n) { - if self.find(*n).is_some() { + if self.args.find(*n).is_some() { args.push(*n) } else { g_vec.push(*n); diff --git a/src/build/arg/mod.rs b/src/build/arg.rs similarity index 97% rename from src/build/arg/mod.rs rename to src/build/arg.rs index b7d9e521..09e6766c 100644 --- a/src/build/arg/mod.rs +++ b/src/build/arg.rs @@ -1,37 +1,31 @@ -// @TODO @p2 @docs remove Arg::setting(foo) in examples, we are sticking with Arg::foo(true) isntead - +// @TODO @p2 @docs remove Arg::setting(foo) in examples, we are sticking with Arg::foo(true) instead mod settings; mod key; -mod short; -mod long; -pub use self::settings::{ArgFlags, ArgSettings}; -// Std -#[cfg(any(target_os = "windows", target_arch = "wasm32"))] -use osstringext::OsStrExt3; use std::borrow::Cow; use std::env; use std::ffi::{OsStr, OsString}; use std::fmt::{self, Display, Formatter}; +use std::hash::Hash; #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] use std::os::unix::ffi::OsStrExt; use std::rc::Rc; use std::str; -use std::hash::Hash; -// Third Party -use util::VecMap; #[cfg(feature = "yaml")] use yaml_rust; -// Internal use build::UsageParser; use INTERNAL_ERROR_MSG; +#[cfg(any(target_os = "windows", target_arch = "wasm32"))] +use osstringext::OsStrExt3; use util::hash; -use self::key::Key; +use util::VecMap; -type Validator = Rc Result<(), String>>; -type ValidatorOs = Rc Result<(), String>>; +pub use self::key::{Key, Position, Short, Long}; +pub use self::settings::{ArgFlags, ArgSettings}; + +pub type ArgId = u64; /// The abstract representation of a command line argument. Used to set all the options and /// relationships that define a valid argument for the program. @@ -59,13 +53,16 @@ type ValidatorOs = Rc Result<(), String>>; #[derive(Default, Clone)] pub struct Arg<'help> { #[doc(hidden)] - pub id: u64, + pub id: ArgId, #[doc(hidden)] key: Key<'help>, #[doc(hidden)] - pub settings: ArgFlags, - #[doc(hidden)] - pub env: Option<(&'help OsStr, Option)>, + settings: ArgFlags, + value: Option, + help: HelpMessage, + occurrence: Occurrence, + validation: ValidationRules<'help>, + } impl<'help> Arg<'help> { @@ -86,35 +83,15 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`Arg`]: ./struct.Arg.html - pub fn new(id: T) -> Self where T: Hash { + pub fn new(id: T) -> Self where T: Hash{ Arg { id: hash(id), - disp_ord: 999, - unified_ord: 999, - help: None, - long_help: None, - blacklist: None, settings: ArgFlags::default(), - r_unless: None, - overrides: None, - requires: None, key: Key::new(), - possible_vals: None, - val_names: None, - num_vals: None, - num_vals_per_occ: None, - max_vals: None, - min_vals: None, - validator: None, - validator_os: None, - val_delim: None, - default_val: None, - default_vals_ifs: None, - env: None, - terminator: None, - index: None, - r_ifs: None, - help_heading: None, + value: None, + help: HelpMessage::new(), + occurrence: Occurrence::new(), + validation: ValidationRules::new(), } } @@ -351,7 +328,7 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::long_help`]: ./struct.Arg.html#method.long_help pub fn help(mut self, h: &'help str) -> Self { - self.help = Some(h); + self.help.short_message(h); self } @@ -422,7 +399,7 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::help`]: ./struct.Arg.html#method.help pub fn long_help(mut self, h: &'help str) -> Self { - self.long_help = Some(h); + self.help.long_message(h); self } @@ -484,11 +461,10 @@ impl<'help> Arg<'help> { /// [`Arg::required_unless(name)`]: ./struct.Arg.html#method.required_unless pub fn required_unless(mut self, other: T) -> Self where T: Hash { let id = hash(other); - if let Some(ref mut vec) = self.r_unless { - vec.push(id); - } else { - self.r_unless = Some(vec![id]); - } + self.validation.self_required_rule( + Rule::new() + .rule_modifier(RuleModifier::Unless) + .condition(Condition::new(id))); self } @@ -556,14 +532,13 @@ impl<'help> Arg<'help> { /// [`Arg::required_unless_one`]: ./struct.Arg.html#method.required_unless_one /// [`Arg::required_unless_all(names)`]: ./struct.Arg.html#method.required_unless_all pub fn required_unless_all(mut self, others: &[T]) -> Self where T: Hash { - if let Some(ref mut vec) = self.r_unless { - for s in others { - vec.push(hash(s)); - } - } else { - self.r_unless = Some(others.iter().map(hash).collect()); - } - self.setting(ArgSettings::RequiredUnlessAll) + self.validation.self_required_rule( + Rule::new() + .rule_modifier(RuleModifier::Unless) + .conditions_modifier(ConditionsModifier::All) + .conditions( + others.iter().map(|x| Condition::new(hash(x))))); + self } /// Sets args that override this arg's [required] setting. (i.e. this arg will be required @@ -631,13 +606,11 @@ impl<'help> Arg<'help> { /// [`Arg::required_unless_one(names)`]: ./struct.Arg.html#method.required_unless_one /// [`Arg::required_unless_all`]: ./struct.Arg.html#method.required_unless_all pub fn required_unless_one(mut self, others: &[T]) -> Self where T: Hash { - if let Some(ref mut vec) = self.r_unless { - for s in others { - vec.push(hash(s)); - } - } else { - self.r_unless = Some(others.iter().map(hash).collect()); - } + self.validation.self_required_rule( + Rule::new() + .rule_modifier(RuleModifier::Unless) + .conditions( + others.iter().map(|x| Condition::new(hash(x))))); self } @@ -680,11 +653,9 @@ impl<'help> Arg<'help> { /// ``` pub fn conflicts_with(mut self, other: T) -> Self where T: Hash { let id = hash(other); - if let Some(ref mut vec) = self.blacklist { - vec.push(id); - } else { - self.blacklist = Some(vec![id]); - } + self.validation.conflicts_rule( + Rule::new() + .condition(Condition::new(id))); self } @@ -730,13 +701,10 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::conflicts_with`]: ./struct.Arg.html#method.conflicts_with pub fn conflicts_with_all(mut self, others: &[T]) -> Self where T: Hash { - if let Some(ref mut vec) = self.blacklist { - for s in others { - vec.push(hash(s)); - } - } else { - self.blacklist = Some(others.iter().map(hash).collect()); - } + self.validation.conflicts_rule( + Rule::new() + .conditions_modifier(ConditionsModifier::All) + .conditions(otheres.iter().map(|x| Condition::new(hash(x))))); self } @@ -842,12 +810,9 @@ impl<'help> Arg<'help> { /// [`Multiple*`]: ./enum.ArgSettings.html#variant.MultipleValues /// [`UseValueDelimiter`]: ./enum.ArgSettings.html#variant.UseValueDelimiter pub fn overrides_with(mut self, other: T) -> Self where T: Hash { - let id = hash(other); - if let Some(ref mut vec) = self.overrides { - vec.push(id); - } else { - self.overrides = Some(vec![id]); - } + self.validation.overrides_rule( + Rule::new() + .condition(Condition::new(hash(other)))); self } @@ -878,14 +843,11 @@ impl<'help> Arg<'help> { /// assert!(!m.is_present("debug")); /// assert!(!m.is_present("flag")); /// ``` - pub fn overrides_with_all(mut self, others: &[T]) -> Self where T: Hash{ - if let Some(ref mut vec) = self.overrides { - for s in others { - vec.push(hash(s)); - } - } else { - self.overrides = Some(others.iter().map(hash).collect()); - } + pub fn overrides_with_all(mut self, others: &[T]) -> Self where T: Hash { + self.validation.overrides_rule( + Rule::new() + .conditions_modifier(ConditionsModifier::All) + .conditions(others.iter().map(|x| Condition::new(hash(x))))); self } @@ -945,14 +907,9 @@ impl<'help> Arg<'help> { /// [Conflicting]: ./struct.Arg.html#method.conflicts_with /// [override]: ./struct.Arg.html#method.overrides_with pub fn requires(mut self, other: T) -> Self where T: Hash { - let id = hash(other); - if let Some(ref mut vec) = self.requires { - vec.push((None, id)); - } else { - let mut vec = vec![]; - vec.push((None, id)); - self.requires = Some(vec); - } + self.validation.requirements_rule( + Rule::new() + .condition(Condition::new(hash(other)))); self } @@ -1016,12 +973,10 @@ impl<'help> Arg<'help> { /// [Conflicting]: ./struct.Arg.html#method.conflicts_with /// [override]: ./struct.Arg.html#method.overrides_with pub fn requires_if(mut self, val: &'help str, other: T) -> Self where T: Hash { - let id = hash(other); - if let Some(ref mut vec) = self.requires { - vec.push((Some(val), id)); - } else { - self.requires = Some(vec![(Some(val), id)]); - } + // need self val and other val...have to re-think + self.validation.requirements_rule( + Rule::new() + .condition(Condition::new(hash(other)))); self } @@ -1384,7 +1339,7 @@ impl<'help> Arg<'help> { /// [`App`]: ./struct.App.html /// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html pub fn index(mut self, idx: u64) -> Self { - self.index = Some(idx); + self.key.index(idx); self } @@ -1434,8 +1389,7 @@ impl<'help> Arg<'help> { /// [`number_of_values`]: ./struct.Arg.html#method.number_of_values /// [`max_values`]: ./struct.Arg.html#method.max_values pub fn value_terminator(mut self, term: &'help str) -> Self { - self.setb(ArgSettings::TakesValue); - self.terminator = Some(term); + self.value.terminator(term); self } @@ -1487,7 +1441,6 @@ impl<'help> Arg<'help> { /// [options]: ./struct.Arg.html#method.takes_value /// [positional arguments]: ./struct.Arg.html#method.index pub fn possible_values(mut self, values: &[&'help str]) -> Self { - self.setb(ArgSettings::TakesValue); if let Some(ref mut vec) = self.possible_vals { for s in values { vec.push(s); @@ -1552,7 +1505,6 @@ impl<'help> Arg<'help> { /// [options]: ./struct.Arg.html#method.takes_value /// [positional arguments]: ./struct.Arg.html#method.index pub fn possible_value(mut self, value: &'help str) -> Self { - self.setb(ArgSettings::TakesValue); if let Some(ref mut vec) = self.possible_vals { vec.push(value); } else { @@ -1598,7 +1550,6 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple pub fn number_of_values(mut self, qty: u64) -> Self { - self.setb(ArgSettings::TakesValue); self.num_vals = Some(qty); self } @@ -1639,7 +1590,6 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple pub fn number_of_values_per_occurrence(mut self, qty: u64) -> Self { - self.setb(ArgSettings::TakesValue); self.num_vals_per_occ = Some(qty); self } @@ -1681,9 +1631,9 @@ impl<'help> Arg<'help> { /// [`Err(String)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err /// [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html pub fn validator(mut self, f: F) -> Self - where - F: Fn(String) -> Result + 'static, - E: ToString, + where + F: Fn(String) -> Result + 'static, + E: ToString, { self.validator = Some(Rc::new(move |s| { f(s).map(|_| ()).map_err(|e| e.to_string()) @@ -1722,8 +1672,8 @@ impl<'help> Arg<'help> { /// [`Err(String)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err /// [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html pub fn validator_os(mut self, f: F) -> Self - where - F: Fn(&OsStr) -> Result + 'static, + where + F: Fn(&OsStr) -> Result + 'static, { self.validator_os = Some(Rc::new(move |s| f(s).map(|_| ()))); self @@ -1786,8 +1736,6 @@ impl<'help> Arg<'help> { /// ``` /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple pub fn max_values(mut self, qty: u64) -> Self { - self.setb(ArgSettings::TakesValue); - self.setb(ArgSettings::MultipleValues); self.max_vals = Some(qty); self } @@ -1851,7 +1799,7 @@ impl<'help> Arg<'help> { /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple pub fn min_values(mut self, qty: u64) -> Self { self.min_vals = Some(qty); - self.setting(ArgSettings::TakesValue) + self } /// Specifies the separator to use when values are clumped together, defaults to `,` (comma). @@ -1878,9 +1826,6 @@ impl<'help> Arg<'help> { /// [`Arg::use_delimiter(true)`]: ./struct.Arg.html#method.use_delimiter /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value pub fn value_delimiter(mut self, d: &str) -> Self { - self.unsetb(ArgSettings::ValueDelimiterNotSet); - self.setb(ArgSettings::TakesValue); - self.setb(ArgSettings::UseValueDelimiter); self.val_delim = Some( d.chars() .nth(0) @@ -1949,11 +1894,6 @@ impl<'help> Arg<'help> { /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`Arg::multiple(true)`]: ./struct.Arg.html#method.multiple pub fn value_names(mut self, names: &[&'help str]) -> Self { - self.setb(ArgSettings::TakesValue); - if self.is_set(ArgSettings::ValueDelimiterNotSet) { - self.unsetb(ArgSettings::ValueDelimiterNotSet); - self.setb(ArgSettings::UseValueDelimiter); - } if let Some(ref mut vals) = self.val_names { let mut l = vals.len(); for s in names { @@ -2017,7 +1957,6 @@ impl<'help> Arg<'help> { /// [positional]: ./struct.Arg.html#method.index /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value pub fn value_name(mut self, name: &'help str) -> Self { - self.setb(ArgSettings::TakesValue); if let Some(ref mut vals) = self.val_names { let l = vals.len(); vals.insert(l, name); @@ -2101,7 +2040,6 @@ impl<'help> Arg<'help> { /// [`Arg::default_value`]: ./struct.Arg.html#method.default_value /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html pub fn default_value_os(mut self, val: &'help OsStr) -> Self { - self.setb(ArgSettings::TakesValue); self.default_val = Some(val); self } @@ -2221,7 +2159,6 @@ impl<'help> Arg<'help> { default: &'help OsStr, ) -> Self where T: Hash { let id = hash(arg); - self.setb(ArgSettings::TakesValue); if let Some(ref mut vm) = self.default_vals_ifs { let l = vm.len(); vm.insert(l, (id, val, default)); @@ -2333,7 +2270,7 @@ impl<'help> Arg<'help> { /// [`Arg::default_value_ifs`]: ./struct.Arg.html#method.default_value_ifs /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html #[cfg_attr(feature = "lints", allow(explicit_counter_loop))] - pub fn default_value_ifs_os(mut self, ifs: &[(T, Option<&'help OsStr>, &'help OsStr)]) -> Self where T: Hash{ + pub fn default_value_ifs_os(mut self, ifs: &[(T, Option<&'help OsStr>, &'help OsStr)]) -> Self where T: Hash { for &(arg, val, default) in ifs { self = self.default_value_if_os(arg, val, default); } @@ -2445,8 +2382,6 @@ impl<'help> Arg<'help> { /// from the environment if available in the exact same manner as [`Arg::env`] only using /// [`OsStr`]s instead. pub fn env_os(mut self, name: &'help OsStr) -> Self { - self.setb(ArgSettings::TakesValue); - self.env = Some((name, env::var_os(name))); self } @@ -3831,7 +3766,7 @@ impl<'help> Arg<'help> { #[doc(hidden)] pub fn _build(&mut self) { self.set_default_delimiter(); - if self.key.is_positional() { + if self.is_positional() { if self.max_vals.is_some() || self.min_vals.is_some() || (self.num_vals.is_some() && self.num_vals.unwrap() > 1) @@ -3926,7 +3861,7 @@ impl<'help> Display for Arg<'help> { } if self.settings.is_set(ArgSettings::MultipleValues) && (self.val_names.is_none() - || (self.val_names.is_some() && self.val_names.as_ref().unwrap().len() == 1)) + || (self.val_names.is_some() && self.val_names.as_ref().unwrap().len() == 1)) { write!(f, "...")?; } @@ -4137,16 +4072,16 @@ impl<'help> From<&'help yaml_rust::Yaml> for Arg<'help> { a } - } // Flags #[cfg(test)] mod test { - use super::Arg; use build::ArgSettings; use util::VecMap; + use super::Arg; + #[test] fn flag_display() { let mut f = Arg::new("flg"); diff --git a/src/build/arg/help_message.rs b/src/build/arg/help_message.rs index 0c5691ad..fd5f4081 100644 --- a/src/build/arg/help_message.rs +++ b/src/build/arg/help_message.rs @@ -1,8 +1,25 @@ +#[derive(Default)] pub struct HelpMessage<'help> { - short_message: &'help str, - long_message: &'help str, + short: Option<&'help str>, + long: Option<&'help str>, value_names: VecMap, display_order: DisplayOrder, unified_order: DisplayOrder, // @TODO remove? heading: &'help str, // @TODO multiple? +} + +impl<'help> HelpMessage<'help> { + pub fn new() -> Self { + HelpMessage::default() + } + + #[inline(always)] + pub fn short_message(&mut self, m: &'help str) { + self.short = Some(m); + } + + #[inline(always)] + pub fn long_message(&mut self, m: &'help str) { + self.long = Some(m); + } } \ No newline at end of file diff --git a/src/build/arg/key/mod.rs b/src/build/arg/key.rs similarity index 71% rename from src/build/arg/key/mod.rs rename to src/build/arg/key.rs index a78f96d7..5dd6715e 100644 --- a/src/build/arg/key/mod.rs +++ b/src/build/arg/key.rs @@ -1,4 +1,10 @@ -use build::arg::{Short, Long, Position}; +mod long; +mod short; +mod position; + +pub use self::long::Long; +pub use self::short::Short; +pub use self::position::Position; pub struct Key<'help> { short: Option, @@ -20,12 +26,16 @@ impl<'help> Key<'help> { } pub fn short(&mut self, short: char) { - self.short.replace(short); + self.short.replace(Short(short)); } pub fn long(&mut self, l: &'help str) { self.long.add_long(l); } + pub fn index(&mut self, i: u64) { + self.index = Some(Position::at(i)); + } + pub fn hidden_long(&mut self, l: &'help str) { self.long.add_hidden_long(l); } diff --git a/src/build/arg/key/position.rs b/src/build/arg/key/position.rs index 15474daf..daa888c1 100644 --- a/src/build/arg/key/position.rs +++ b/src/build/arg/key/position.rs @@ -7,7 +7,11 @@ impl Default for Position { } impl Position { - fn new() -> Self { + pub fn new() -> Self { Position(1) } + pub fn at(i: u64) -> Self { + assert!(i>0, "Positional Index cannot be less than 1"); + Position(i) + } } \ No newline at end of file diff --git a/src/build/arg/key/short.rs b/src/build/arg/key/short.rs index 48061d85..6f4eff90 100644 --- a/src/build/arg/key/short.rs +++ b/src/build/arg/key/short.rs @@ -1,3 +1 @@ -pub struct Short { - short: char -} \ No newline at end of file +pub struct Short(char); diff --git a/src/build/arg/occurrence.rs b/src/build/arg/occurrence.rs index 697ede10..f8b5ca6c 100644 --- a/src/build/arg/occurrence.rs +++ b/src/build/arg/occurrence.rs @@ -1,5 +1,34 @@ -#[derive(Default, Copy, Clone, PartialEq, Eq)] +use std::u64; + +#[derive(Copy, Clone, PartialEq, Eq)] pub struct Occurrence { min: u64, max: u64 +} + +impl Default for Occurrence { + fn default() -> Self { + Occurrence { + min: 1, + max: u64::MAX + } + } +} + +impl Occurrence { + #[inline(always)] + pub fn min(&mut self, num: u64) { + self.min = num; + } + + #[inline(always)] + pub fn max(&mut self, num: u64) { + self.max = num; + } + + #[inline] + pub fn exact(&mut self, num: u64) { + self.max = num; + self.min = num; + } } \ No newline at end of file diff --git a/src/build/arg/validation_rules.rs b/src/build/arg/validation_rules.rs index e6b2db63..692e2385 100644 --- a/src/build/arg/validation_rules.rs +++ b/src/build/arg/validation_rules.rs @@ -1,17 +1,49 @@ -pub struct Rule<'help> { - other_arg: u64, - self_value: Option<&'help str>, - other_value: Option<&'help str>, -} - -pub struct Rules<'help> { - rules: Vec>, -} - +#[derive(Default)] pub struct ValidationRules<'help> { occurrence: Occurrence, - conflicts: Rules<'help>, - requirements: Rules<'help>, - overrides: Rules<'help>, - requirements_unless: Rules<'help>, + conflicts: Vec>, + requirements: Vec>, + self_required: Vec>, + overrides: Vec>, +} + +impl<'help> ValidationRules<'help> { + pub fn new() -> Self { + ValidationRules::default() + } + + #[inline(always)] + pub fn requirement_rule(&mut self, r: Rule) { + self.requirements.push(r); + } + + #[inline(always)] + pub fn self_required_rule(&mut self, r: Rule) { + self.self_required.push(r); + } + + #[inline(always)] + pub fn conflicts_rule(&mut self, r: Rule) { + self.conflicts.push(r); + } + + #[inline(always)] + pub fn overrides_rule(&mut self, r: Rule) { + self.overrides.push(r); + } + + #[inline(always)] + pub fn max_occurs(&mut self, num: usize) { + self.occurrence.max(num); + } + + #[inline(always)] + pub fn min_occurs(&mut self, num: usize) { + self.occurrence.min(num); + } + + #[inline] + pub fn exact_occurs(&mut self, num: usize) { + self.occurrence.exact(num); + } } diff --git a/src/build/arg/validation_rules/conditions.rs b/src/build/arg/validation_rules/conditions.rs new file mode 100644 index 00000000..bc2ffe48 --- /dev/null +++ b/src/build/arg/validation_rules/conditions.rs @@ -0,0 +1,43 @@ +pub enum ConditionsModifier { + All, + Any, + None, +} + +impl Default for ConditionsModifer { + fn default() -> Self { + ConditionsModifier::Any + } +} + +pub struct Condition<'help> { + arg: u64, + arg_value: Option<&'help str>, + other_value: Option<&'help str>, +} + +impl<'help> Condition<'help> { + pub fn new(id: ArgId) -> Self { + Condition { + arg: id, + arg_value: None, + other_value: None, + } + + } + + pub fn arg(mut self, id: ArgId) -> Self { + self.arg = id; + self + } + + pub fn arg_value(mut self, val: &'help str) -> Self { + self.arg_value = Some(val); + self + } + + pub fn other_value(mut self, val: &'help str) -> Self { + self.other_value = Some(val); + self + } +} \ No newline at end of file diff --git a/src/build/arg/validation_rules/rules.rs b/src/build/arg/validation_rules/rules.rs new file mode 100644 index 00000000..5f96ef18 --- /dev/null +++ b/src/build/arg/validation_rules/rules.rs @@ -0,0 +1,52 @@ +use super::ConditionsModifier; + +pub enum RuleModifer { + Unless, + With, +} + +impl Default for RuleModifer { + fn default() -> Self { + RuleModifer::With + } +} + +#[derive(Default)] +pub struct Rule<'help> { + rule_mod: RuleModifer, + conditions_mod: ConditionsModifier, + conditions: Vec>, +} + +impl<'help> Rule<'help> { + pub fn new() -> Self { + Rule::default() + } + + pub fn rule_modifier(mut self, rm: RuleModifer) -> Self { + self.rule_mod = rm; + self + } + + pub fn conditions_mod(mut self, cm: ConditionsModifier) -> Self { + self.conditions_mod = cm; + self + } + + pub fn condition(mut self, c: Condition) -> Self { + self.conditions.push(c); + self + } + + pub fn conditions(mut self, conds: &dyn Iterator) -> Self { + for c in conds { + self.conditions.push(cond); + } + self + } + + pub fn clear_conditions(&mut self) { + self.conditions.clear() + } +} + diff --git a/src/build/arg/value/mod.rs b/src/build/arg/value.rs similarity index 50% rename from src/build/arg/value/mod.rs rename to src/build/arg/value.rs index e7df2950..bbf2c762 100644 --- a/src/build/arg/value/mod.rs +++ b/src/build/arg/value.rs @@ -1,7 +1,20 @@ +mod default_values; +mod delimiter; +mod filter; +mod possible_values; +mod terminator; +mod value_name; + pub struct Value<'help> { defaults: Option, filter: Filter, occurrence: Occurrence, requires_equals: bool, delimiter: Delimiter, +} + +impl<'help> Value<'help> { + fn new() -> Self { + + } } \ No newline at end of file diff --git a/src/build/arg/value/possible_values.rs b/src/build/arg/value/filter/possible_values.rs similarity index 100% rename from src/build/arg/value/possible_values.rs rename to src/build/arg/value/filter/possible_values.rs diff --git a/src/build/arg/value/terminator.rs b/src/build/arg/value/filter/terminator.rs similarity index 100% rename from src/build/arg/value/terminator.rs rename to src/build/arg/value/filter/terminator.rs diff --git a/src/build/arg/value/filter/validators.rs b/src/build/arg/value/filter/validators.rs new file mode 100644 index 00000000..6176cdd6 --- /dev/null +++ b/src/build/arg/value/filter/validators.rs @@ -0,0 +1,4 @@ +pub type Validator = Rc Result<(), String>>; +pub type ValidatorOs = Rc Result<(), String>>; + + diff --git a/src/build/arg/value/value_name.rs b/src/build/arg/value/value_name.rs index 7e21581f..0d7ebd8f 100644 --- a/src/build/arg/value/value_name.rs +++ b/src/build/arg/value/value_name.rs @@ -1,3 +1,5 @@ +// Maybe move to help message? Or keep here in case we integrate into Value at some point? + use std::io; pub struct ValueName<'help>(&'help str); diff --git a/src/build/args.rs b/src/build/args.rs new file mode 100644 index 00000000..37d1fad8 --- /dev/null +++ b/src/build/args.rs @@ -0,0 +1,113 @@ +use std::ops::Index; + +use crate::Arg; +use crate::build::arg::{ArgId, Position}; + +pub trait Find { + type Output; + fn find(&self, by: By) -> Option<>::Output>; +} + +pub trait FindMut { + type Output; + fn find(&mut self, by: By) -> Option<>::Output>; +} + +pub struct Args<'help> { + inner: Vec>, +} + +impl<'help> Args<'help> { + pub fn contains_short(&self, s: char) -> bool { + self.args().any(|x| x.uses_short(c)) + } + pub fn contains_long(&self, l: &str) -> bool { + self.args().any(|x| x.uses_long(l)) + } + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + pub fn has_flags(&self) -> bool { + self.flags().any(|x| x.is_flag()) + } + pub fn has_options(&self) -> bool { + self.options().any(|x| x.is_option()) + } + pub fn has_positionals(&self) -> bool { + self.positionals().any(|x| x.is_positional()) + } + pub fn len(&self) -> usize { + self.inner.len() + } + pub fn push(&mut self, arg: Arg<'help>) { + self.inner.push(arg) + } + pub fn remove(&mut self, id: ArgId) -> Option> { + let opt_i = self.args().enumerate().find(|(x, i)| x.id == id).map(|x| x.0); + if let Some(i) = opt_i { + Some(self.inner.swap_remove(i)) + } else { + None + } + } + pub fn find(&self, id: ArgId) -> Option<&Arg<'help>> { + self.args().find(|x| x.id == id) + } + pub fn find_short(&self, s: char) -> Option<&Arg<'help>> { + self.args().find(|x| x.uses_short(s)) + } + pub fn find_long(&self, l: &str) -> Option<&Arg<'help>> { + self.args().find(|x| x.uses_long(l)) + } + pub fn find_position(&self, p: Position) -> Option<&Arg<'help>> { + self.positionals().find(|x| x.uses_position(p)) + } + pub fn find_mut(&mut self, id: ArgId) -> Option<&mut Arg<'help>> { + self.args_mut().find(|x| x.id == id) + } + pub fn find_short_mut(&mut self, s: char) -> Option<&mut Arg<'help>> { + self.args_mut().find(|x| x.uses_short(s)) + } + pub fn find_long_mut(&mut self, l: &str) -> Option<&mut Arg<'help>> { + self.args_mut().find(|x| x.uses_long(l)) + } + pub fn find_position_mut(&mut self, p: Position) -> Option<&mut Arg<'help>> { + self.positionals_mut().find(|x| x.uses_position(p)) + } +} + +// Iterator Getters +impl<'help> Args<'help> { + pub fn args(&self) -> impl Iterator { + self.inner.iter() + } + pub fn flags(&self) -> impl Iterator { + self.args().filter(|x| x.is_flat()) + } + pub fn options(&self) -> impl Iterator { + self.args().filter(|x| x.is_option()) + } + pub fn positionals(&self) -> impl Iterator { + self.args().filter(|x| x.is_positional()) + } + pub fn args_mut(&mut self) -> impl Iterator { + self.inner.iter_mut() + } + pub fn flags_mut(&mut self) -> impl Iterator { + self.args_mut().filter(|x| x.is_flat()) + } + pub fn options_mut(&mut self) -> impl Iterator { + self.args_mut().filter(|x| x.is_option()) + } + pub fn positionals_mut(&mut self) -> impl Iterator { + self.args_mut().filter(|x| x.is_positional()) + } +} + +impl<'a, 'help> Index for Args<'help> { + type Output = &'a Arg<'help>; + + fn index(&'a self, index: usize) -> &'a Arg<'help> { + self.inner[index] + } +} diff --git a/src/build/mod.rs b/src/build/mod.rs index c6ecb29b..7133e81d 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -7,6 +7,7 @@ mod aliases; pub mod app; pub mod arg; +pub mod args; mod arg_group; mod usage_parser; diff --git a/src/lib.rs b/src/lib.rs index f88f6e48..01526370 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -578,7 +578,6 @@ use std::result::Result as StdResult; mod macros; mod build; -mod args_map; mod output; mod parse; mod util; diff --git a/src/util/fnv.rs b/src/util/fnv.rs index 7a95950e..25acc368 100644 --- a/src/util/fnv.rs +++ b/src/util/fnv.rs @@ -1,7 +1,12 @@ use std::hash::{Hash, Hasher}; +const MAGIC_INIT: u64 = 0x811C9DC5; + #[inline] -pub(crate) fn hash(t: T) -> u64 where T: Hash { +pub(crate) fn hash(t: T) -> u64 + where + T: Hash, +{ let mut hasher = FnvHasher::new(); t.hash(&mut hasher); hasher.finish() @@ -10,9 +15,7 @@ pub(crate) fn hash(t: T) -> u64 where T: Hash { pub(crate) struct FnvHasher(u64); impl FnvHasher { - pub(crate) fn new() -> Self { - FnvHasher(0x811C9DC5) - } + pub(crate) fn new() -> Self { FnvHasher(MAGIC_INIT) } } impl Hasher for FnvHasher { @@ -27,4 +30,4 @@ impl Hasher for FnvHasher { *self = FnvHasher(hash); } -} \ No newline at end of file +}