diff --git a/src/args/flags.rs b/src/args/flags.rs index f7851b8..c85e1bb 100644 --- a/src/args/flags.rs +++ b/src/args/flags.rs @@ -28,21 +28,97 @@ pub enum Kind<'a> /// A single `--`. /// /// # Note - /// Shorthand for `Long(&[], false)` + /// Shorthand for `Long(&[], _)` Terminator, /// A single `-`. /// /// # Note - /// Shorthand for `Short(&[], false)` + /// Shorthand for `Short(&[], _)` TerminatorShort, /// An exact match with no prefix and flag for case-sensitivity. Exact(&'a str, bool), - /// Match everything, + /// Match everything. Any, } impl<'b> Kind<'b> { + /// Check if string `value` matches this `Kind`. + /// + /// Returns the matched part. + /// # Note + /// In this case of `Short` (or `Abrev` short match), return the rest of the argument string including the matched character + /// + /// # Short case-sensitivity + /// Single character matching case sensitivity can produce unexpected results if the lowercase transform of that character yields multiple characters. They are all checked for match. + pub fn is_match<'a, 'c>(&'a self, value: &'b str) -> Option<&'a str> + where 'c: 'a, + 'a: 'b + { + match self { + Self::Long(&[], _) | + Self::Abrev(&[], &[], _, _) | + Self::Terminator if value == "--" => Some("--"), + + Self::Short(&[], _) | + Self::TerminatorShort if value == "-" => Some("-"), + + Self::Abrev(any, _, true, _) | + Self::Long(any, true) if value.starts_with("--") => { + let value = &value[2..]; + + for &which in any.iter() { + if which == value { + return Some(which); + } + } + None + }, + Self::Abrev(any, _, false, _) | + Self::Long(any, false) if value.starts_with("--") => { + let value = value[2..].to_lowercase(); + + for (orig, which) in any.iter().map(|x| (x, x.to_lowercase())) { + if which == value { + return Some(orig); + } + } + None + }, + + Self::Abrev(_, any, _, true) | + Self::Short(any, true) if value.starts_with("-") => { + let mut value = value[1..].chars(); + let iter = &mut value; + for can_be in iter { + if any.contains(&(can_be,)) { + return Some(value.as_str()); + } + } + None + }, + + //Self::Abrev(_, any, _, false) | + Self::Short(any, false) if value.starts_with("-") => { + let mut value = value[1..].chars(); + let iter = &mut value; + let any: Vec<_> = any.iter().flat_map(|(chr,)| chr.to_lowercase()).collect(); + for can_be in iter.flat_map(|x| x.to_lowercase()) { + if any.contains(&can_be) { + return Some(value.as_str()); + } + } + None + }, + + Self::Exact(exact, false) if value.to_lowercase() == exact.to_lowercase() => Some(value), + Self::Exact(exact, true) if &value == exact => Some(value), + + Self::Any => Some(value), + _ => None, + } + } + /// Create a reference to this `Kind` with a lower lifetime. pub fn as_ref<'a>(&'a self) -> Kind<'a> { match &self { @@ -55,8 +131,18 @@ impl<'b> Kind<'b> Self::Any => Kind::Any, } } -} + /// Split `Kind::Abrev` into short and long + pub fn split_abrev(&self) -> Result<(Kind<'_>, Kind<'_>), &Self> + { + match self { + Self::Abrev(long, short, lcase, scase) => { + Ok((Kind::Long(long,*lcase), Kind::Short(short, *scase))) + }, + e => Err(e) + } + } +} /// Helper for `Cow` variants macro_rules! cow @@ -274,15 +360,18 @@ impl ArgState if *single { *chk = false; } - //TODO: Check Kind matches `arg`, check `value` matches `input` and get the values. - output.push(callback(&mut StateHandle{ - state: &mut self.user, - args: &mut input, - chk, - idx: i, - held: Either::None, //TODO: This will be for either one `=` or many `` from args as according to `value`. - }, i, &arg[..]) - .map_err(|err| Error{flag: kind.clone(), from: err, desc: Some(desc.clone().into_owned()), arg: (i, arg.clone())})?); + //TODO: When value is `Equals`, split at `=` and pass only the first part to `is_match`. + if let Some(kind_match) = kind.is_match(&arg[..]) { + //TODO: Check `value` matches `input` or `kind_match` + output.push(callback(&mut StateHandle{ + state: &mut self.user, + args: &mut input, + chk, + idx: i, + held: Either::None, //TODO: This will be for either one `=` or many `` from args as according to `value`. + }, i, kind_match) + .map_err(|err| Error{flag: kind.clone(), from: err, desc: Some(desc.clone().into_owned()), arg: (i, arg.clone())})?); + } } i+=1; }