args.rs (4895B)
1 use super::{ 2 ExitCode, 3 io::{self, Write as _}, 4 }; 5 use std::ffi::OsString; 6 /// Unit tests. 7 #[cfg(test)] 8 mod tests; 9 /// Error from parsing CLI arguments. 10 #[cfg_attr(test, derive(Debug, PartialEq))] 11 pub(crate) enum E { 12 /// No arguments exist including the name of the program which is assumed to be the first argument. 13 NoArgs, 14 /// The contained string is not an argument that is supported. 15 UnknownArg(OsString), 16 } 17 impl E { 18 /// Writes `self` to `stderr`. 19 pub(crate) fn into_exit_code(self) -> ExitCode { 20 let mut stderr = io::stderr().lock(); 21 match self { 22 Self::NoArgs => writeln!( 23 stderr, 24 "No arguments were passed including the first argument which is assumed to be the name of the program. See lints help for more information." 25 ), 26 Self::UnknownArg(arg) => writeln!( 27 stderr, 28 "Unrecognized argument '{}' was passed. See lints help for more information.", 29 arg.display() 30 ), 31 }.map_or(ExitCode::FAILURE, |()| ExitCode::FAILURE) 32 } 33 } 34 /// The command passed. 35 #[cfg_attr(test, derive(Debug, PartialEq))] 36 #[derive(Clone, Copy)] 37 pub(crate) enum Cmd { 38 /// `"allow"` all lints. 39 Allow(Opts), 40 /// `"deny"`. 41 Deny(Opts), 42 /// `"help"`. 43 Help, 44 /// `"version"`. 45 Version, 46 } 47 /// Options to process lints. 48 #[cfg_attr(test, derive(Debug, PartialEq))] 49 #[derive(Clone, Copy, Default)] 50 pub(crate) struct Opts { 51 /// `--all` was passed. 52 pub all_lints: bool, 53 /// `--allow-undefined-lints` was passed. 54 pub allow_undefined_lints: bool, 55 /// `-` was passed. 56 pub read_stdin: bool, 57 } 58 impl Opts { 59 /// Add options from `opts` to `self`. 60 /// 61 /// Returns `None` iff successful; otherwise returns the problematic argument. 62 fn add<I: Iterator<Item = OsString>>( 63 &mut self, 64 mut opts: I, 65 arg: OsString, 66 ) -> Option<OsString> { 67 /// `b'-'`. 68 const DASH: u8 = b'-'; 69 /// `b"--all"`. 70 const ALL: &[u8] = b"--all".as_slice(); 71 /// `b"--allow-undefined-lints"`. 72 const ALLOW_UNDEFINED_LINTS: &[u8] = b"--allow-undefined-lints".as_slice(); 73 match arg.as_encoded_bytes() { 74 &[DASH] => { 75 if self.read_stdin { 76 Err(()) 77 } else { 78 self.read_stdin = true; 79 Ok(()) 80 } 81 } 82 ALL => { 83 if self.all_lints || self.read_stdin { 84 Err(()) 85 } else { 86 self.all_lints = true; 87 Ok(()) 88 } 89 } 90 ALLOW_UNDEFINED_LINTS => { 91 if self.allow_undefined_lints || self.read_stdin { 92 Err(()) 93 } else { 94 self.allow_undefined_lints = true; 95 Ok(()) 96 } 97 } 98 _ => Err(()), 99 } 100 .map_or(Some(arg), |()| { 101 opts.next().and_then(|opt| self.add(opts, opt)) 102 }) 103 } 104 } 105 impl Cmd { 106 /// Parses the CLI arguments. 107 pub(crate) fn try_from_args<I: IntoIterator<Item = OsString>>(iter: I) -> Result<Self, E> { 108 let mut args = iter.into_iter(); 109 args.next().ok_or(E::NoArgs).and_then(|_| { 110 args.next().map_or_else( 111 || Ok(Self::Deny(Opts::default())), 112 |cmd| match cmd.as_encoded_bytes() { 113 b"allow" => args.next().map_or_else( 114 || Ok(Self::Allow(Opts::default())), 115 |arg| { 116 let mut opts = Opts::default(); 117 opts.add(args, arg) 118 .map_or(Ok(Self::Allow(opts)), |opt| Err(E::UnknownArg(opt))) 119 }, 120 ), 121 b"deny" => args.next().map_or_else( 122 || Ok(Self::Deny(Opts::default())), 123 |arg| { 124 let mut opts = Opts::default(); 125 opts.add(args, arg) 126 .map_or(Ok(Self::Deny(opts)), |opt| Err(E::UnknownArg(opt))) 127 }, 128 ), 129 b"help" => args 130 .next() 131 .map_or(Ok(Self::Help), |opt| Err(E::UnknownArg(opt))), 132 b"version" => args 133 .next() 134 .map_or(Ok(Self::Version), |opt| Err(E::UnknownArg(opt))), 135 _ => { 136 let mut opts = Opts::default(); 137 opts.add(args, cmd) 138 .map_or(Ok(Self::Deny(opts)), |opt| Err(E::UnknownArg(opt))) 139 } 140 }, 141 ) 142 }) 143 } 144 }