parser.rs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. //! A general parser for command-line options.
  2. //!
  3. //! exa uses its own hand-rolled parser for command-line options. It supports
  4. //! the following syntax:
  5. //!
  6. //! - Long options: `--inode`, `--grid`
  7. //! - Long options with values: `--sort size`, `--level=4`
  8. //! - Short options: `-i`, `-G`
  9. //! - Short options with values: `-ssize`, `-L=4`
  10. //!
  11. //! These values can be mixed and matched: `exa -lssize --grid`. If you’ve used
  12. //! other command-line programs, then hopefully it’ll work much like them.
  13. //!
  14. //! Because exa already has its own files for the help text, shell completions,
  15. //! man page, and readme, so it can get away with having the options parser do
  16. //! very little: all it really needs to do is parse a slice of strings.
  17. //!
  18. //!
  19. //! ## UTF-8 and `OsStr`
  20. //!
  21. //! The parser uses `OsStr` as its string type. This is necessary for exa to
  22. //! list files that have invalid UTF-8 in their names: by treating file paths
  23. //! as bytes with no encoding, a file can be specified on the command-line and
  24. //! be looked up without having to be encoded into a `str` first.
  25. //!
  26. //! It also avoids the overhead of checking for invalid UTF-8 when parsing
  27. //! command-line options, as all the options and their values (such as
  28. //! `--sort size`) are guaranteed to just be 8-bit ASCII.
  29. use std::ffi::{OsStr, OsString};
  30. use std::fmt;
  31. use crate::options::error::{OptionsError, Choices};
  32. /// A **short argument** is a single ASCII character.
  33. pub type ShortArg = u8;
  34. /// A **long argument** is a string. This can be a UTF-8 string, even though
  35. /// the arguments will all be unchecked `OsString` values, because we don’t
  36. /// actually store the user’s input after it’s been matched to a flag, we just
  37. /// store which flag it was.
  38. pub type LongArg = &'static str;
  39. /// A **list of values** that an option can have, to be displayed when the
  40. /// user enters an invalid one or skips it.
  41. ///
  42. /// This is literally just help text, and won’t be used to validate a value to
  43. /// see if it’s correct.
  44. pub type Values = &'static [&'static str];
  45. /// A **flag** is either of the two argument types, because they have to
  46. /// be in the same array together.
  47. #[derive(PartialEq, Eq, Debug, Copy, Clone)]
  48. pub enum Flag {
  49. Short(ShortArg),
  50. Long(LongArg),
  51. }
  52. impl Flag {
  53. pub fn matches(&self, arg: &Arg) -> bool {
  54. match self {
  55. Self::Short(short) => arg.short == Some(*short),
  56. Self::Long(long) => arg.long == *long,
  57. }
  58. }
  59. }
  60. impl fmt::Display for Flag {
  61. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  62. match self {
  63. Self::Short(short) => write!(f, "-{}", *short as char),
  64. Self::Long(long) => write!(f, "--{long}"),
  65. }
  66. }
  67. }
  68. /// Whether redundant arguments should be considered a problem.
  69. #[derive(PartialEq, Eq, Debug, Copy, Clone)]
  70. pub enum Strictness {
  71. /// Throw an error when an argument doesn’t do anything, either because
  72. /// it requires another argument to be specified, or because two conflict.
  73. ComplainAboutRedundantArguments,
  74. /// Search the arguments list back-to-front, giving ones specified later
  75. /// in the list priority over earlier ones.
  76. UseLastArguments,
  77. }
  78. /// Whether a flag takes a value. This is applicable to both long and short
  79. /// arguments.
  80. #[derive(PartialEq, Eq, Debug, Copy, Clone)]
  81. pub enum TakesValue {
  82. /// This flag has to be followed by a value.
  83. /// If there’s a fixed set of possible values, they can be printed out
  84. /// with the error text.
  85. Necessary(Option<Values>),
  86. /// This flag will throw an error if there’s a value after it.
  87. Forbidden,
  88. /// This flag may be followed by a value to override its defaults
  89. Optional(Option<Values>),
  90. }
  91. /// An **argument** can be matched by one of the user’s input strings.
  92. #[derive(PartialEq, Eq, Debug, Copy, Clone)]
  93. pub struct Arg {
  94. /// The short argument that matches it, if any.
  95. pub short: Option<ShortArg>,
  96. /// The long argument that matches it. This is non-optional; all flags
  97. /// should at least have a descriptive long name.
  98. pub long: LongArg,
  99. /// Whether this flag takes a value or not.
  100. pub takes_value: TakesValue,
  101. }
  102. impl fmt::Display for Arg {
  103. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  104. write!(f, "--{}", self.long)?;
  105. if let Some(short) = self.short {
  106. write!(f, " (-{})", short as char)?;
  107. }
  108. Ok(())
  109. }
  110. }
  111. /// Literally just several args.
  112. #[derive(PartialEq, Eq, Debug)]
  113. pub struct Args(pub &'static [&'static Arg]);
  114. impl Args {
  115. /// Iterates over the given list of command-line arguments and parses
  116. /// them into a list of matched flags and free strings.
  117. pub fn parse<'args, I>(&self, inputs: I, strictness: Strictness) -> Result<Matches<'args>, ParseError>
  118. where I: IntoIterator<Item = &'args OsStr>
  119. {
  120. let mut parsing = true;
  121. // The results that get built up.
  122. let mut result_flags = Vec::new();
  123. let mut frees: Vec<&OsStr> = Vec::new();
  124. // Iterate over the inputs with “while let” because we need to advance
  125. // the iterator manually whenever an argument that takes a value
  126. // doesn’t have one in its string so it needs the next one.
  127. let mut inputs = inputs.into_iter();
  128. while let Some(arg) = inputs.next() {
  129. let bytes = os_str_to_bytes(arg);
  130. // Stop parsing if one of the arguments is the literal string “--”.
  131. // This allows a file named “--arg” to be specified by passing in
  132. // the pair “-- --arg”, without it getting matched as a flag that
  133. // doesn’t exist.
  134. if ! parsing {
  135. frees.push(arg)
  136. }
  137. else if arg == "--" {
  138. parsing = false;
  139. }
  140. // If the string starts with *two* dashes then it’s a long argument.
  141. else if bytes.starts_with(b"--") {
  142. let long_arg_name = bytes_to_os_str(&bytes[2..]);
  143. // If there’s an equals in it, then the string before the
  144. // equals will be the flag’s name, and the string after it
  145. // will be its value.
  146. if let Some((before, after)) = split_on_equals(long_arg_name) {
  147. let arg = self.lookup_long(before)?;
  148. let flag = Flag::Long(arg.long);
  149. match arg.takes_value {
  150. TakesValue::Necessary(_) |
  151. TakesValue::Optional(_) => result_flags.push((flag, Some(after))),
  152. TakesValue::Forbidden => return Err(ParseError::ForbiddenValue { flag }),
  153. }
  154. }
  155. // If there’s no equals, then the entire string (apart from
  156. // the dashes) is the argument name.
  157. else {
  158. let arg = self.lookup_long(long_arg_name)?;
  159. let flag = Flag::Long(arg.long);
  160. match arg.takes_value {
  161. TakesValue::Forbidden => {
  162. result_flags.push((flag, None))
  163. }
  164. TakesValue::Necessary(values) => {
  165. if let Some(next_arg) = inputs.next() {
  166. result_flags.push((flag, Some(next_arg)));
  167. }
  168. else {
  169. return Err(ParseError::NeedsValue { flag, values })
  170. }
  171. }
  172. TakesValue::Optional(_) => {
  173. if let Some(next_arg) = inputs.next() {
  174. result_flags.push((flag, Some(next_arg)));
  175. }
  176. else {
  177. result_flags.push((flag, None));
  178. }
  179. }
  180. }
  181. }
  182. }
  183. // If the string starts with *one* dash then it’s one or more
  184. // short arguments.
  185. else if bytes.starts_with(b"-") && arg != "-" {
  186. let short_arg = bytes_to_os_str(&bytes[1..]);
  187. // If there’s an equals in it, then the argument immediately
  188. // before the equals was the one that has the value, with the
  189. // others (if any) as value-less short ones.
  190. //
  191. // -x=abc => ‘x=abc’
  192. // -abcdx=fgh => ‘a’, ‘b’, ‘c’, ‘d’, ‘x=fgh’
  193. // -x= => error
  194. // -abcdx= => error
  195. //
  196. // There’s no way to give two values in a cluster like this:
  197. // it’s an error if any of the first set of arguments actually
  198. // takes a value.
  199. if let Some((before, after)) = split_on_equals(short_arg) {
  200. let (arg_with_value, other_args) = os_str_to_bytes(before).split_last().unwrap();
  201. // Process the characters immediately following the dash...
  202. for byte in other_args {
  203. let arg = self.lookup_short(*byte)?;
  204. let flag = Flag::Short(*byte);
  205. match arg.takes_value {
  206. TakesValue::Forbidden |
  207. TakesValue::Optional(_) => {
  208. result_flags.push((flag, None));
  209. }
  210. TakesValue::Necessary(values) => {
  211. return Err(ParseError::NeedsValue { flag, values });
  212. }
  213. }
  214. }
  215. // ...then the last one and the value after the equals.
  216. let arg = self.lookup_short(*arg_with_value)?;
  217. let flag = Flag::Short(arg.short.unwrap());
  218. match arg.takes_value {
  219. TakesValue::Necessary(_) |
  220. TakesValue::Optional(_) => {
  221. result_flags.push((flag, Some(after)));
  222. }
  223. TakesValue::Forbidden => {
  224. return Err(ParseError::ForbiddenValue { flag });
  225. }
  226. }
  227. }
  228. // If there’s no equals, then every character is parsed as
  229. // its own short argument. However, if any of the arguments
  230. // takes a value, then the *rest* of the string is used as
  231. // its value, and if there’s no rest of the string, then it
  232. // uses the next one in the iterator.
  233. //
  234. // -a => ‘a’
  235. // -abc => ‘a’, ‘b’, ‘c’
  236. // -abxdef => ‘a’, ‘b’, ‘x=def’
  237. // -abx def => ‘a’, ‘b’, ‘x=def’
  238. // -abx => error
  239. //
  240. else {
  241. for (index, byte) in bytes.iter().enumerate().skip(1) {
  242. let arg = self.lookup_short(*byte)?;
  243. let flag = Flag::Short(*byte);
  244. match arg.takes_value {
  245. TakesValue::Forbidden => {
  246. result_flags.push((flag, None))
  247. }
  248. TakesValue::Necessary(values) |
  249. TakesValue::Optional(values) => {
  250. if index < bytes.len() - 1 {
  251. let remnants = &bytes[index+1 ..];
  252. result_flags.push((flag, Some(bytes_to_os_str(remnants))));
  253. break;
  254. }
  255. else if let Some(next_arg) = inputs.next() {
  256. result_flags.push((flag, Some(next_arg)));
  257. }
  258. else {
  259. match arg.takes_value {
  260. TakesValue::Forbidden => {
  261. unreachable!()
  262. }
  263. TakesValue::Necessary(_) => {
  264. return Err(ParseError::NeedsValue { flag, values });
  265. }
  266. TakesValue::Optional(_) => {
  267. result_flags.push((flag, None));
  268. }
  269. }
  270. }
  271. }
  272. }
  273. }
  274. }
  275. }
  276. // Otherwise, it’s a free string, usually a file name.
  277. else {
  278. frees.push(arg);
  279. }
  280. }
  281. Ok(Matches { frees, flags: MatchedFlags { flags: result_flags, strictness } })
  282. }
  283. fn lookup_short(&self, short: ShortArg) -> Result<&Arg, ParseError> {
  284. match self.0.iter().find(|arg| arg.short == Some(short)) {
  285. Some(arg) => Ok(arg),
  286. None => Err(ParseError::UnknownShortArgument { attempt: short })
  287. }
  288. }
  289. fn lookup_long(&self, long: &OsStr) -> Result<&Arg, ParseError> {
  290. match self.0.iter().find(|arg| arg.long == long) {
  291. Some(arg) => Ok(arg),
  292. None => Err(ParseError::UnknownArgument { attempt: long.to_os_string() })
  293. }
  294. }
  295. }
  296. /// The **matches** are the result of parsing the user’s command-line strings.
  297. #[derive(PartialEq, Eq, Debug)]
  298. pub struct Matches<'args> {
  299. /// The flags that were parsed from the user’s input.
  300. pub flags: MatchedFlags<'args>,
  301. /// All the strings that weren’t matched as arguments, as well as anything
  302. /// after the special “--” string.
  303. pub frees: Vec<&'args OsStr>,
  304. }
  305. #[derive(PartialEq, Eq, Debug)]
  306. pub struct MatchedFlags<'args> {
  307. /// The individual flags from the user’s input, in the order they were
  308. /// originally given.
  309. ///
  310. /// Long and short arguments need to be kept in the same vector because
  311. /// we usually want the one nearest the end to count, and to know this,
  312. /// we need to know where they are in relation to one another.
  313. flags: Vec<(Flag, Option<&'args OsStr>)>,
  314. /// Whether to check for duplicate or redundant arguments.
  315. strictness: Strictness,
  316. }
  317. impl<'a> MatchedFlags<'a> {
  318. /// Whether the given argument was specified.
  319. /// Returns `true` if it was, `false` if it wasn’t, and an error in
  320. /// strict mode if it was specified more than once.
  321. pub fn has(&self, arg: &'static Arg) -> Result<bool, OptionsError> {
  322. self.has_where(|flag| flag.matches(arg))
  323. .map(|flag| flag.is_some())
  324. }
  325. /// Returns the first found argument that satisfies the predicate, or
  326. /// nothing if none is found, or an error in strict mode if multiple
  327. /// argument satisfy the predicate.
  328. ///
  329. /// You’ll have to test the resulting flag to see which argument it was.
  330. pub fn has_where<P>(&self, predicate: P) -> Result<Option<&Flag>, OptionsError>
  331. where P: Fn(&Flag) -> bool {
  332. if self.is_strict() {
  333. let all = self.flags.iter()
  334. .filter(|tuple| tuple.1.is_none() && predicate(&tuple.0))
  335. .collect::<Vec<_>>();
  336. if all.len() < 2 { Ok(all.first().map(|t| &t.0)) }
  337. else { Err(OptionsError::Duplicate(all[0].0, all[1].0)) }
  338. }
  339. else {
  340. Ok(self.has_where_any(predicate))
  341. }
  342. }
  343. /// Returns the first found argument that satisfies the predicate, or
  344. /// nothing if none is found, with strict mode having no effect.
  345. ///
  346. /// You’ll have to test the resulting flag to see which argument it was.
  347. pub fn has_where_any<P>(&self, predicate: P) -> Option<&Flag>
  348. where P: Fn(&Flag) -> bool {
  349. self.flags.iter().rev()
  350. .find(|tuple| tuple.1.is_none() && predicate(&tuple.0))
  351. .map(|tuple| &tuple.0)
  352. }
  353. // This code could probably be better.
  354. // Both ‘has’ and ‘get’ immediately begin with a conditional, which makes
  355. // me think the functionality could be moved to inside Strictness.
  356. /// Returns the value of the given argument if it was specified, nothing
  357. /// if it wasn’t, and an error in strict mode if it was specified more
  358. /// than once.
  359. pub fn get(&self, arg: &'static Arg) -> Result<Option<&OsStr>, OptionsError> {
  360. self.get_where(|flag| flag.matches(arg))
  361. }
  362. /// Returns the value of the argument that matches the predicate if it
  363. /// was specified, nothing if it wasn’t, and an error in strict mode if
  364. /// multiple arguments matched the predicate.
  365. ///
  366. /// It’s not possible to tell which flag the value belonged to from this.
  367. pub fn get_where<P>(&self, predicate: P) -> Result<Option<&OsStr>, OptionsError>
  368. where P: Fn(&Flag) -> bool {
  369. if self.is_strict() {
  370. let those = self.flags.iter()
  371. .filter(|tuple| tuple.1.is_some() && predicate(&tuple.0))
  372. .collect::<Vec<_>>();
  373. if those.len() < 2 { Ok(those.first().copied().map(|t| t.1.unwrap())) }
  374. else { Err(OptionsError::Duplicate(those[0].0, those[1].0)) }
  375. }
  376. else {
  377. let found = self.flags.iter().rev()
  378. .find(|tuple| tuple.1.is_some() && predicate(&tuple.0))
  379. .map(|tuple| tuple.1.unwrap());
  380. Ok(found)
  381. }
  382. }
  383. // It’s annoying that ‘has’ and ‘get’ won’t work when accidentally given
  384. // flags that do/don’t take values, but this should be caught by tests.
  385. /// Counts the number of occurrences of the given argument, even in
  386. /// strict mode.
  387. pub fn count(&self, arg: &Arg) -> usize {
  388. self.flags.iter()
  389. .filter(|tuple| tuple.0.matches(arg))
  390. .count()
  391. }
  392. /// Checks whether strict mode is on. This is usually done from within
  393. /// ‘has’ and ‘get’, but it’s available in an emergency.
  394. pub fn is_strict(&self) -> bool {
  395. self.strictness == Strictness::ComplainAboutRedundantArguments
  396. }
  397. }
  398. /// A problem with the user’s input that meant it couldn’t be parsed into a
  399. /// coherent list of arguments.
  400. #[derive(PartialEq, Eq, Debug)]
  401. pub enum ParseError {
  402. /// A flag that has to take a value was not given one.
  403. NeedsValue { flag: Flag, values: Option<Values> },
  404. /// A flag that can’t take a value *was* given one.
  405. ForbiddenValue { flag: Flag },
  406. /// A short argument, either alone or in a cluster, was not
  407. /// recognised by the program.
  408. UnknownShortArgument { attempt: ShortArg },
  409. /// A long argument was not recognised by the program.
  410. /// We don’t have a known &str version of the flag, so
  411. /// this may not be valid UTF-8.
  412. UnknownArgument { attempt: OsString },
  413. }
  414. impl fmt::Display for ParseError {
  415. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  416. match self {
  417. Self::NeedsValue { flag, values: None } => write!(f, "Flag {flag} needs a value"),
  418. Self::NeedsValue { flag, values: Some(cs) } => write!(f, "Flag {flag} needs a value ({})", Choices(cs)),
  419. Self::ForbiddenValue { flag } => write!(f, "Flag {flag} cannot take a value"),
  420. Self::UnknownShortArgument { attempt } => write!(f, "Unknown argument -{}", *attempt as char),
  421. Self::UnknownArgument { attempt } => write!(f, "Unknown argument --{}", attempt.to_string_lossy()),
  422. }
  423. }
  424. }
  425. #[cfg(unix)]
  426. fn os_str_to_bytes(s: &OsStr) -> &[u8]{
  427. use std::os::unix::ffi::OsStrExt;
  428. return s.as_bytes()
  429. }
  430. #[cfg(unix)]
  431. fn bytes_to_os_str(b: &[u8]) -> &OsStr{
  432. use std::os::unix::ffi::OsStrExt;
  433. return OsStr::from_bytes(b);
  434. }
  435. #[cfg(windows)]
  436. fn os_str_to_bytes<'b>(s: &'b OsStr) -> &'b [u8]{
  437. return s.to_str().unwrap().as_bytes()
  438. }
  439. #[cfg(windows)]
  440. fn bytes_to_os_str<'b>(b: &'b [u8]) -> &'b OsStr{
  441. use std::str;
  442. return OsStr::new(str::from_utf8(b).unwrap());
  443. }
  444. /// Splits a string on its `=` character, returning the two substrings on
  445. /// either side. Returns `None` if there’s no equals or a string is missing.
  446. fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
  447. if let Some(index) = os_str_to_bytes(input).iter().position(|elem| *elem == b'=') {
  448. let (before, after) = os_str_to_bytes(input).split_at(index);
  449. // The after string contains the = that we need to remove.
  450. if ! before.is_empty() && after.len() >= 2 {
  451. return Some((bytes_to_os_str(before),
  452. bytes_to_os_str(&after[1..])))
  453. }
  454. }
  455. None
  456. }
  457. #[cfg(test)]
  458. mod split_test {
  459. use super::split_on_equals;
  460. use std::ffi::{OsStr, OsString};
  461. macro_rules! test_split {
  462. ($name:ident: $input:expr => None) => {
  463. #[test]
  464. fn $name() {
  465. assert_eq!(split_on_equals(&OsString::from($input)),
  466. None);
  467. }
  468. };
  469. ($name:ident: $input:expr => $before:expr, $after:expr) => {
  470. #[test]
  471. fn $name() {
  472. assert_eq!(split_on_equals(&OsString::from($input)),
  473. Some((OsStr::new($before), OsStr::new($after))));
  474. }
  475. };
  476. }
  477. test_split!(empty: "" => None);
  478. test_split!(letter: "a" => None);
  479. test_split!(just: "=" => None);
  480. test_split!(intro: "=bbb" => None);
  481. test_split!(denou: "aaa=" => None);
  482. test_split!(equals: "aaa=bbb" => "aaa", "bbb");
  483. test_split!(sort: "--sort=size" => "--sort", "size");
  484. test_split!(more: "this=that=other" => "this", "that=other");
  485. }
  486. #[cfg(test)]
  487. mod parse_test {
  488. use super::*;
  489. macro_rules! test {
  490. ($name:ident: $inputs:expr => frees: $frees:expr, flags: $flags:expr) => {
  491. #[test]
  492. fn $name() {
  493. let inputs: &[&'static str] = $inputs.as_ref();
  494. let inputs = inputs.iter().map(OsStr::new);
  495. let frees: &[&'static str] = $frees.as_ref();
  496. let frees = frees.iter().map(OsStr::new).collect();
  497. let flags = <[_]>::into_vec(Box::new($flags));
  498. let strictness = Strictness::UseLastArguments; // this isn’t even used
  499. let got = Args(TEST_ARGS).parse(inputs, strictness);
  500. let flags = MatchedFlags { flags, strictness };
  501. let expected = Ok(Matches { frees, flags });
  502. assert_eq!(got, expected);
  503. }
  504. };
  505. ($name:ident: $inputs:expr => error $error:expr) => {
  506. #[test]
  507. fn $name() {
  508. use self::ParseError::*;
  509. let inputs = $inputs.iter().map(OsStr::new);
  510. let strictness = Strictness::UseLastArguments; // this isn’t even used
  511. let got = Args(TEST_ARGS).parse(inputs, strictness);
  512. assert_eq!(got, Err($error));
  513. }
  514. };
  515. }
  516. const SUGGESTIONS: Values = &[ "example" ];
  517. static TEST_ARGS: &[&Arg] = &[
  518. &Arg { short: Some(b'l'), long: "long", takes_value: TakesValue::Forbidden },
  519. &Arg { short: Some(b'v'), long: "verbose", takes_value: TakesValue::Forbidden },
  520. &Arg { short: Some(b'c'), long: "count", takes_value: TakesValue::Necessary(None) },
  521. &Arg { short: Some(b't'), long: "type", takes_value: TakesValue::Necessary(Some(SUGGESTIONS)) }
  522. ];
  523. // Just filenames
  524. test!(empty: [] => frees: [], flags: []);
  525. test!(one_arg: ["exa"] => frees: [ "exa" ], flags: []);
  526. // Dashes and double dashes
  527. test!(one_dash: ["-"] => frees: [ "-" ], flags: []);
  528. test!(two_dashes: ["--"] => frees: [], flags: []);
  529. test!(two_file: ["--", "file"] => frees: [ "file" ], flags: []);
  530. test!(two_arg_l: ["--", "--long"] => frees: [ "--long" ], flags: []);
  531. test!(two_arg_s: ["--", "-l"] => frees: [ "-l" ], flags: []);
  532. // Long args
  533. test!(long: ["--long"] => frees: [], flags: [ (Flag::Long("long"), None) ]);
  534. test!(long_then: ["--long", "4"] => frees: [ "4" ], flags: [ (Flag::Long("long"), None) ]);
  535. test!(long_two: ["--long", "--verbose"] => frees: [], flags: [ (Flag::Long("long"), None), (Flag::Long("verbose"), None) ]);
  536. // Long args with values
  537. test!(bad_equals: ["--long=equals"] => error ForbiddenValue { flag: Flag::Long("long") });
  538. test!(no_arg: ["--count"] => error NeedsValue { flag: Flag::Long("count"), values: None });
  539. test!(arg_equals: ["--count=4"] => frees: [], flags: [ (Flag::Long("count"), Some(OsStr::new("4"))) ]);
  540. test!(arg_then: ["--count", "4"] => frees: [], flags: [ (Flag::Long("count"), Some(OsStr::new("4"))) ]);
  541. // Long args with values and suggestions
  542. test!(no_arg_s: ["--type"] => error NeedsValue { flag: Flag::Long("type"), values: Some(SUGGESTIONS) });
  543. test!(arg_equals_s: ["--type=exa"] => frees: [], flags: [ (Flag::Long("type"), Some(OsStr::new("exa"))) ]);
  544. test!(arg_then_s: ["--type", "exa"] => frees: [], flags: [ (Flag::Long("type"), Some(OsStr::new("exa"))) ]);
  545. // Short args
  546. test!(short: ["-l"] => frees: [], flags: [ (Flag::Short(b'l'), None) ]);
  547. test!(short_then: ["-l", "4"] => frees: [ "4" ], flags: [ (Flag::Short(b'l'), None) ]);
  548. test!(short_two: ["-lv"] => frees: [], flags: [ (Flag::Short(b'l'), None), (Flag::Short(b'v'), None) ]);
  549. test!(mixed: ["-v", "--long"] => frees: [], flags: [ (Flag::Short(b'v'), None), (Flag::Long("long"), None) ]);
  550. // Short args with values
  551. test!(bad_short: ["-l=equals"] => error ForbiddenValue { flag: Flag::Short(b'l') });
  552. test!(short_none: ["-c"] => error NeedsValue { flag: Flag::Short(b'c'), values: None });
  553. test!(short_arg_eq: ["-c=4"] => frees: [], flags: [(Flag::Short(b'c'), Some(OsStr::new("4"))) ]);
  554. test!(short_arg_then: ["-c", "4"] => frees: [], flags: [(Flag::Short(b'c'), Some(OsStr::new("4"))) ]);
  555. test!(short_two_together: ["-lctwo"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]);
  556. test!(short_two_equals: ["-lc=two"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]);
  557. test!(short_two_next: ["-lc", "two"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]);
  558. // Short args with values and suggestions
  559. test!(short_none_s: ["-t"] => error NeedsValue { flag: Flag::Short(b't'), values: Some(SUGGESTIONS) });
  560. test!(short_two_together_s: ["-texa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]);
  561. test!(short_two_equals_s: ["-t=exa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]);
  562. test!(short_two_next_s: ["-t", "exa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]);
  563. // Unknown args
  564. test!(unknown_long: ["--quiet"] => error UnknownArgument { attempt: OsString::from("quiet") });
  565. test!(unknown_long_eq: ["--quiet=shhh"] => error UnknownArgument { attempt: OsString::from("quiet") });
  566. test!(unknown_short: ["-q"] => error UnknownShortArgument { attempt: b'q' });
  567. test!(unknown_short_2nd: ["-lq"] => error UnknownShortArgument { attempt: b'q' });
  568. test!(unknown_short_eq: ["-q=shhh"] => error UnknownShortArgument { attempt: b'q' });
  569. test!(unknown_short_2nd_eq: ["-lq=shhh"] => error UnknownShortArgument { attempt: b'q' });
  570. }
  571. #[cfg(test)]
  572. mod matches_test {
  573. use super::*;
  574. macro_rules! test {
  575. ($name:ident: $input:expr, has $param:expr => $result:expr) => {
  576. #[test]
  577. fn $name() {
  578. let flags = MatchedFlags {
  579. flags: $input.to_vec(),
  580. strictness: Strictness::UseLastArguments,
  581. };
  582. assert_eq!(flags.has(&$param), Ok($result));
  583. }
  584. };
  585. }
  586. static VERBOSE: Arg = Arg { short: Some(b'v'), long: "verbose", takes_value: TakesValue::Forbidden };
  587. static COUNT: Arg = Arg { short: Some(b'c'), long: "count", takes_value: TakesValue::Necessary(None) };
  588. test!(short_never: [], has VERBOSE => false);
  589. test!(short_once: [(Flag::Short(b'v'), None)], has VERBOSE => true);
  590. test!(short_twice: [(Flag::Short(b'v'), None), (Flag::Short(b'v'), None)], has VERBOSE => true);
  591. test!(long_once: [(Flag::Long("verbose"), None)], has VERBOSE => true);
  592. test!(long_twice: [(Flag::Long("verbose"), None), (Flag::Long("verbose"), None)], has VERBOSE => true);
  593. test!(long_mixed: [(Flag::Long("verbose"), None), (Flag::Short(b'v'), None)], has VERBOSE => true);
  594. #[test]
  595. fn only_count() {
  596. let everything = OsString::from("everything");
  597. let flags = MatchedFlags {
  598. flags: vec![ (Flag::Short(b'c'), Some(&*everything)) ],
  599. strictness: Strictness::UseLastArguments,
  600. };
  601. assert_eq!(flags.get(&COUNT), Ok(Some(&*everything)));
  602. }
  603. #[test]
  604. fn rightmost_count() {
  605. let everything = OsString::from("everything");
  606. let nothing = OsString::from("nothing");
  607. let flags = MatchedFlags {
  608. flags: vec![ (Flag::Short(b'c'), Some(&*everything)),
  609. (Flag::Short(b'c'), Some(&*nothing)) ],
  610. strictness: Strictness::UseLastArguments,
  611. };
  612. assert_eq!(flags.get(&COUNT), Ok(Some(&*nothing)));
  613. }
  614. #[test]
  615. fn no_count() {
  616. let flags = MatchedFlags { flags: Vec::new(), strictness: Strictness::UseLastArguments };
  617. assert!(!flags.has(&COUNT).unwrap());
  618. }
  619. }