// Internal use crate::build::{Arg, ArgSettings}; use crate::util::{Key, VecMap}; use crate::INTERNAL_ERROR_MSG; #[derive(PartialEq, Debug)] enum UsageToken { Name, ValName, Short, Long, Help, Multiple, Unknown, Default, } #[derive(Debug)] pub(crate) struct UsageParser<'a> { usage: &'a str, pos: usize, start: usize, prev: UsageToken, explicit_name_set: bool, } impl<'a> UsageParser<'a> { fn new(usage: &'a str) -> Self { debugln!("UsageParser::new: usage={:?}", usage); UsageParser { usage, pos: 0, start: 0, prev: UsageToken::Unknown, explicit_name_set: false, } } pub(crate) fn from_usage(usage: &'a str) -> Self { debugln!("UsageParser::from_usage;"); UsageParser::new(usage) } pub(crate) fn parse(mut self) -> Arg<'a> { debugln!("UsageParser::parse;"); let mut arg = Arg::default(); arg.disp_ord = 999; arg.unified_ord = 999; loop { debugln!("UsageParser::parse:iter: pos={};", self.pos); self.stop_at(token); if let Some(&c) = self.usage.as_bytes().get(self.pos) { match c { b'-' => self.short_or_long(&mut arg), b'.' => self.multiple(&mut arg), b'@' => self.default(&mut arg), b'\'' => self.help(&mut arg), _ => self.name(&mut arg), } } else { break; } } arg.num_vals = match arg.val_names { Some(ref v) if v.len() >= 2 => Some(v.len() as u64), _ => None, }; if !arg.has_switch() && arg.is_set(ArgSettings::MultipleOccurrences) { // We had a positional and need to set mult vals too arg.setb(ArgSettings::MultipleValues); } debugln!("UsageParser::parse: vals...{:?}", arg.val_names); arg } fn name(&mut self, arg: &mut Arg<'a>) { debugln!("UsageParser::name;"); if *self .usage .as_bytes() .get(self.pos) .expect(INTERNAL_ERROR_MSG) == b'<' && !self.explicit_name_set { arg.setb(ArgSettings::Required); } self.pos += 1; self.stop_at(name_end); let name = &self.usage[self.start..self.pos]; if self.prev == UsageToken::Unknown { debugln!("UsageParser::name: setting name...{}", name); arg.id = name.key(); arg.name = name; if arg.long.is_none() && arg.short.is_none() { debugln!("UsageParser::name: explicit name set..."); self.explicit_name_set = true; self.prev = UsageToken::Name; } } else { debugln!("UsageParser::name: setting val name...{}", name); if let Some(ref mut v) = arg.val_names { let len = v.len(); v.insert(len, name); } else { let mut v = VecMap::new(); v.insert(0, name); arg.val_names = Some(v); arg.setb(ArgSettings::TakesValue); } self.prev = UsageToken::ValName; } } fn stop_at(&mut self, f: F) where F: Fn(u8) -> bool, { debugln!("UsageParser::stop_at;"); self.start = self.pos; self.pos += self.usage[self.start..] .bytes() .take_while(|&b| f(b)) .count(); } fn short_or_long(&mut self, arg: &mut Arg<'a>) { debugln!("UsageParser::short_or_long;"); self.pos += 1; if *self .usage .as_bytes() .get(self.pos) .expect(INTERNAL_ERROR_MSG) == b'-' { self.pos += 1; self.long(arg); return; } self.short(arg) } fn long(&mut self, arg: &mut Arg<'a>) { debugln!("UsageParser::long;"); self.stop_at(long_end); let name = &self.usage[self.start..self.pos]; if !self.explicit_name_set { debugln!("UsageParser::long: setting name...{}", name); arg.id = name.key(); arg.name = name; } debugln!("UsageParser::long: setting long...{}", name); arg.long = Some(name); self.prev = UsageToken::Long; } fn short(&mut self, arg: &mut Arg<'a>) { debugln!("UsageParser::short;"); let start = &self.usage[self.pos..]; let short = start.chars().next().expect(INTERNAL_ERROR_MSG); debugln!("UsageParser::short: setting short...{}", short); arg.short = Some(short); if arg.name.is_empty() { // --long takes precedence but doesn't set self.explicit_name_set let name = &start[..short.len_utf8()]; debugln!("UsageParser::short: setting name...{}", name); arg.id = name.key(); arg.name = name; } self.prev = UsageToken::Short; } // "something..." fn multiple(&mut self, arg: &mut Arg) { debugln!("UsageParser::multiple;"); let mut dot_counter = 1; let start = self.pos; let mut bytes = self.usage[start..].bytes(); while bytes.next() == Some(b'.') { dot_counter += 1; self.pos += 1; if dot_counter == 3 { debugln!("UsageParser::multiple: setting multiple"); if arg.is_set(ArgSettings::TakesValue) { arg.setb(ArgSettings::MultipleValues); } arg.setb(ArgSettings::MultipleOccurrences); self.prev = UsageToken::Multiple; self.pos += 1; break; } } } fn help(&mut self, arg: &mut Arg<'a>) { debugln!("UsageParser::help;"); self.stop_at(help_start); self.start = self.pos + 1; self.pos = self.usage.len() - 1; debugln!( "UsageParser::help: setting help...{}", &self.usage[self.start..self.pos] ); arg.help = Some(&self.usage[self.start..self.pos]); self.pos += 1; // Move to next byte to keep from thinking ending ' is a start self.prev = UsageToken::Help; } fn default(&mut self, arg: &mut Arg<'a>) { debugln!( "UsageParser::default; from: \"{}\"", &self.usage[self.pos..self.usage.len()] ); self.pos += 1; // Skip @ self.stop_at(default_value_end); // Find first space after value debugln!( "UsageParser::default: setting default...\"{}\"", &self.usage[self.start..self.pos] ); arg.setb(ArgSettings::TakesValue); arg.default_vals = Some(vec![std::ffi::OsStr::new( &self.usage[self.start..self.pos], )]); self.prev = UsageToken::Default; } } #[inline] fn name_end(b: u8) -> bool { b != b']' && b != b'>' } #[inline] fn token(b: u8) -> bool { b != b'\'' && b != b'.' && b != b'<' && b != b'[' && b != b'-' && b != b'@' } #[inline] fn long_end(b: u8) -> bool { b != b'\'' && b != b'.' && b != b'<' && b != b'[' && b != b'=' && b != b' ' } #[inline] fn help_start(b: u8) -> bool { b != b'\'' } #[inline] fn default_value_end(b: u8) -> bool { b != b' ' } #[cfg(test)] mod test { use crate::build::{Arg, ArgSettings}; #[allow(clippy::cognitive_complexity)] #[test] fn create_flag_usage() { let a = Arg::from("[flag] -f 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("[flag] --flag 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.long.unwrap(), "flag"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("--flag 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.long.unwrap(), "flag"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("[flag] -f --flag 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.short.unwrap(), 'f'); assert_eq!(a.long.unwrap(), "flag"); assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("[flag] -f... 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("[flag] -f --flag... 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.long.unwrap(), "flag"); assert_eq!(a.short.unwrap(), 'f'); assert_eq!(a.help.unwrap(), "some help info"); assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("-f --flag... 'some help info'"); assert_eq!(a.name, "flag"); assert_eq!(a.long.unwrap(), "flag"); assert_eq!(a.short.unwrap(), 'f'); assert_eq!(a.help.unwrap(), "some help info"); assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("--flags"); assert_eq!(a.name, "flags"); assert_eq!(a.long.unwrap(), "flags"); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("--flags..."); assert_eq!(a.name, "flags"); assert_eq!(a.long.unwrap(), "flags"); assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("[flags] -f"); assert_eq!(a.name, "flags"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("[flags] -f..."); assert_eq!(a.name, "flags"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("-f 'some help info'"); assert_eq!(a.name, "f"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("-f"); assert_eq!(a.name, "f"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); let a = Arg::from("-f..."); assert_eq!(a.name, "f"); assert_eq!(a.short.unwrap(), 'f'); assert!(a.is_set(ArgSettings::MultipleOccurrences)); assert!(a.val_names.is_none()); assert!(a.num_vals.is_none()); } #[test] fn create_option_usage0() { // Short only let a = Arg::from("[option] -o [opt] 'some help info'"); assert_eq!(a.name, "option"); assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(!a.is_set(ArgSettings::MultipleValues)); assert!(a.is_set(ArgSettings::TakesValue)); assert!(!a.is_set(ArgSettings::Required)); assert_eq!(a.val_names.unwrap().values().collect::>(), [&"opt"]); assert!(a.num_vals.is_none()); } #[test] fn create_option_usage1() { let a = Arg::from("-o [opt] 'some help info'"); assert_eq!(a.name, "o"); assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); assert!(!a.is_set(ArgSettings::MultipleOccurrences)); assert!(!a.is_set(ArgSettings::MultipleValues)); assert!(a.is_set(ArgSettings::TakesValue)); assert!(!a.is_set(ArgSettings::Required)); assert_eq!(a.val_names.unwrap().values().collect::>(), [&"opt"]); assert!(a.num_vals.is_none()); } #[test] fn create_option_usage2() { let a = Arg::from("