parser.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. #![allow(unused_variables, dead_code)]
  2. use std::ffi::{OsStr, OsString};
  3. pub type ShortArg = u8;
  4. pub type LongArg = &'static str;
  5. #[derive(PartialEq, Debug)]
  6. pub enum Flag {
  7. Short(ShortArg),
  8. Long(LongArg),
  9. }
  10. #[derive(PartialEq, Debug)]
  11. pub enum Strictness {
  12. ComplainAboutRedundantArguments,
  13. UseLastArguments,
  14. }
  15. #[derive(Copy, Clone, PartialEq, Debug)]
  16. pub enum TakesValue {
  17. Necessary,
  18. Forbidden,
  19. }
  20. #[derive(PartialEq, Debug)]
  21. pub struct Arg {
  22. short: Option<ShortArg>,
  23. long: LongArg,
  24. takes_value: TakesValue,
  25. }
  26. #[derive(PartialEq, Debug)]
  27. pub struct Args(&'static [Arg]);
  28. impl Args {
  29. fn lookup_short(&self, short: ShortArg) -> Option<&Arg> {
  30. self.0.into_iter().find(|arg| arg.short == Some(short))
  31. }
  32. fn lookup_long(&self, long: &OsStr) -> Option<&Arg> {
  33. self.0.into_iter().find(|arg| arg.long == long)
  34. }
  35. }
  36. #[derive(PartialEq, Debug)]
  37. pub struct Matches<'a> {
  38. /// Long and short arguments need to be kept in the same vector, because
  39. /// we usually want the one nearest the end to count.
  40. flags: Vec<(Flag, Option<&'a OsStr>)>,
  41. frees: Vec<&'a OsStr>,
  42. }
  43. #[derive(PartialEq, Debug)]
  44. pub enum ParseError<'a> {
  45. NeedsValue { flag: Flag },
  46. ForbiddenValue { flag: Flag },
  47. UnknownShortArgument { attempt: ShortArg },
  48. UnknownArgument { attempt: &'a OsStr },
  49. }
  50. fn parse<'a>(args: Args, inputs: &'a [OsString]) -> Result<Matches<'a>, ParseError<'a>> {
  51. use std::os::unix::ffi::OsStrExt;
  52. use self::TakesValue::*;
  53. let mut parsing = true;
  54. let mut results = Matches {
  55. flags: Vec::new(),
  56. frees: Vec::new(),
  57. };
  58. let mut iter = inputs.iter();
  59. while let Some(arg) = iter.next() {
  60. let bytes = arg.as_bytes();
  61. if !parsing {
  62. results.frees.push(arg)
  63. }
  64. else if arg == "--" {
  65. parsing = false;
  66. }
  67. else if bytes.starts_with(b"--") {
  68. let long_arg = OsStr::from_bytes(&bytes[2..]);
  69. if let Some((before, after)) = split_on_equals(long_arg) {
  70. if let Some(&Arg { short: _, long: long_arg_name, takes_value }) = args.lookup_long(before) {
  71. let flag = Flag::Long(long_arg_name);
  72. match takes_value {
  73. Necessary => results.flags.push((flag, Some(after))),
  74. Forbidden => return Err(ParseError::ForbiddenValue { flag })
  75. }
  76. }
  77. else {
  78. return Err(ParseError::UnknownArgument { attempt: before })
  79. }
  80. }
  81. else {
  82. if let Some(&Arg { short: _, long: long_arg_name, takes_value }) = args.lookup_long(long_arg) {
  83. let flag = Flag::Long(long_arg_name);
  84. match takes_value {
  85. Forbidden => results.flags.push((flag, None)),
  86. Necessary => {
  87. if let Some(next_arg) = iter.next() {
  88. results.flags.push((flag, Some(next_arg)));
  89. }
  90. else {
  91. return Err(ParseError::NeedsValue { flag })
  92. }
  93. }
  94. }
  95. }
  96. else {
  97. return Err(ParseError::UnknownArgument { attempt: long_arg })
  98. }
  99. }
  100. }
  101. else if bytes.starts_with(b"-") && arg != "-" {
  102. let short_arg = OsStr::from_bytes(&bytes[1..]);
  103. if let Some((before, after)) = split_on_equals(short_arg) {
  104. // TODO: remember to deal with the other bytes!
  105. if let Some(&Arg { short, long, takes_value }) = args.lookup_short(*before.as_bytes().last().unwrap()) {
  106. let flag = Flag::Short(short.unwrap());
  107. match takes_value {
  108. Necessary => results.flags.push((flag, Some(after))),
  109. Forbidden => return Err(ParseError::ForbiddenValue { flag })
  110. }
  111. }
  112. else {
  113. return Err(ParseError::UnknownArgument { attempt: before })
  114. }
  115. }
  116. else {
  117. for byte in &bytes[1..] {
  118. // TODO: gotta check that these don't take arguments
  119. // like -c4
  120. if let Some(&Arg { short, long, takes_value }) = args.lookup_short(*byte) {
  121. let flag = Flag::Short(*byte);
  122. match takes_value {
  123. Forbidden => results.flags.push((flag, None)),
  124. Necessary => {
  125. if let Some(next_arg) = iter.next() {
  126. results.flags.push((flag, Some(next_arg)));
  127. }
  128. else {
  129. return Err(ParseError::NeedsValue { flag })
  130. }
  131. }
  132. }
  133. }
  134. else {
  135. return Err(ParseError::UnknownShortArgument { attempt: *byte });
  136. }
  137. }
  138. }
  139. }
  140. else {
  141. results.frees.push(arg)
  142. }
  143. }
  144. Ok(results)
  145. }
  146. /// Splits a string on its `=` character, returning the two substrings on
  147. /// either side. Returns `None` if there’s no equals or a string is missing.
  148. fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
  149. use std::os::unix::ffi::OsStrExt;
  150. if let Some(index) = input.as_bytes().iter().position(|elem| *elem == b'=') {
  151. let (before, after) = input.as_bytes().split_at(index);
  152. // The after string contains the = that we need to remove.
  153. if before.len() >= 1 && after.len() >= 2 {
  154. return Some((OsStr::from_bytes(before),
  155. OsStr::from_bytes(&after[1..])))
  156. }
  157. }
  158. None
  159. }
  160. /// Creates an `OSString` (used in tests)
  161. #[cfg(test)]
  162. fn os(input: &'static str) -> OsString {
  163. let mut os = OsString::new();
  164. os.push(input);
  165. os
  166. }
  167. #[cfg(test)]
  168. mod split_test {
  169. use super::{split_on_equals, os};
  170. macro_rules! test_split {
  171. ($name:ident: $input:expr => None) => {
  172. #[test]
  173. fn $name() {
  174. assert_eq!(split_on_equals(&os($input)),
  175. None);
  176. }
  177. };
  178. ($name:ident: $input:expr => $before:expr, $after:expr) => {
  179. #[test]
  180. fn $name() {
  181. assert_eq!(split_on_equals(&os($input)),
  182. Some((&*os($before), &*os($after))));
  183. }
  184. };
  185. }
  186. test_split!(empty: "" => None);
  187. test_split!(letter: "a" => None);
  188. test_split!(just: "=" => None);
  189. test_split!(intro: "=bbb" => None);
  190. test_split!(denou: "aaa=" => None);
  191. test_split!(equals: "aaa=bbb" => "aaa", "bbb");
  192. test_split!(sort: "--sort=size" => "--sort", "size");
  193. test_split!(more: "this=that=other" => "this", "that=other");
  194. }
  195. #[cfg(test)]
  196. mod test {
  197. use super::*;
  198. use std::ffi::OsString;
  199. static TEST_ARGS: &'static [Arg] = &[
  200. Arg { short: Some(b'l'), long: "long", takes_value: TakesValue::Forbidden },
  201. Arg { short: Some(b'c'), long: "count", takes_value: TakesValue::Necessary }
  202. ];
  203. #[test]
  204. fn empty() {
  205. let bits = [ ];
  206. let results = parse(Args(TEST_ARGS), &bits);
  207. assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![] }))
  208. }
  209. #[test]
  210. fn filename() {
  211. let bits = [ os("exa") ];
  212. let results = parse(Args(TEST_ARGS), &bits);
  213. assert_eq!(results, Ok(Matches { frees: vec![ os("exa").as_os_str() ], flags: vec![] }))
  214. }
  215. #[test]
  216. fn the_dashes_do_nothing() {
  217. let bits = [ os("--") ];
  218. let results = parse(Args(TEST_ARGS), &bits);
  219. assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![] }))
  220. }
  221. #[test]
  222. fn but_just_one_does() {
  223. let bits = [ os("-") ];
  224. let results = parse(Args(TEST_ARGS), &bits);
  225. assert_eq!(results, Ok(Matches { frees: vec![ os("-").as_os_str() ], flags: vec![] }))
  226. }
  227. // ----- long args --------
  228. #[test]
  229. fn as_filename() {
  230. let bits = [ os("--"), os("--long") ];
  231. let results = parse(Args(TEST_ARGS), &bits);
  232. assert_eq!(results, Ok(Matches { frees: vec![os("--long").as_os_str() ], flags: vec![] }))
  233. }
  234. #[test]
  235. fn long() {
  236. let bits = [ os("--long") ];
  237. let results = parse(Args(TEST_ARGS), &bits);
  238. assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("long"), None) ] }))
  239. }
  240. #[test]
  241. fn long_equals() {
  242. let bits = [ os("--long=equals") ];
  243. let results = parse(Args(TEST_ARGS), &bits);
  244. assert_eq!(results, Err(ParseError::ForbiddenValue { flag: Flag::Long("long") }))
  245. }
  246. #[test]
  247. fn no_arg_separate() {
  248. let bits = [ os("--long"), os("4") ];
  249. let results = parse(Args(TEST_ARGS), &bits);
  250. assert_eq!(results, Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Long("long"), None) ] }))
  251. }
  252. #[test]
  253. fn no_arg_given() {
  254. let bits = [ os("--count") ];
  255. let results = parse(Args(TEST_ARGS), &bits);
  256. assert_eq!(results, Err(ParseError::NeedsValue { flag: Flag::Long("count") }))
  257. }
  258. #[test]
  259. fn arg_equals() {
  260. let bits = [ os("--count=4") ];
  261. let results = parse(Args(TEST_ARGS), &bits);
  262. assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }))
  263. }
  264. #[test]
  265. fn arg_separate() {
  266. let bits = [ os("--count"), os("4") ];
  267. let results = parse(Args(TEST_ARGS), &bits);
  268. assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }))
  269. }
  270. // ----- short args --------
  271. #[test]
  272. fn short_as_filename() {
  273. let bits = [ os("--"), os("-l") ];
  274. let results = parse(Args(TEST_ARGS), &bits);
  275. assert_eq!(results, Ok(Matches { frees: vec![os("-l").as_os_str() ], flags: vec![] }))
  276. }
  277. #[test]
  278. fn short_long() {
  279. let bits = [ os("-l") ];
  280. let results = parse(Args(TEST_ARGS), &bits);
  281. assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None) ] }))
  282. }
  283. #[test]
  284. fn short_long_equals() {
  285. let bits = [ os("-l=equals") ];
  286. let results = parse(Args(TEST_ARGS), &bits);
  287. assert_eq!(results, Err(ParseError::ForbiddenValue { flag: Flag::Short(b'l') }))
  288. }
  289. #[test]
  290. fn short_no_arg_separate() {
  291. let bits = [ os("-l"), os("4") ];
  292. let results = parse(Args(TEST_ARGS), &bits);
  293. assert_eq!(results, Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Short(b'l'), None) ] }))
  294. }
  295. #[test]
  296. fn short_no_arg_given() {
  297. let bits = [ os("-c") ];
  298. let results = parse(Args(TEST_ARGS), &bits);
  299. assert_eq!(results, Err(ParseError::NeedsValue { flag: Flag::Short(b'c') }))
  300. }
  301. #[test]
  302. fn short_arg_equals() {
  303. let bits = [ os("-c=4") ];
  304. let results = parse(Args(TEST_ARGS), &bits);
  305. assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }))
  306. }
  307. #[test]
  308. fn short_arg_separate() {
  309. let bits = [ os("-c"), os("4") ];
  310. let results = parse(Args(TEST_ARGS), &bits);
  311. assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }))
  312. }
  313. }