1#[derive(Debug, PartialEq)]
2pub enum CommandFlag {
6 Force,
9}
10
11#[derive(Debug, Default, PartialEq)]
12pub struct ParsedCommand {
16 pub commands: Vec<String>,
18 pub args: Vec<String>,
20 pub flags: Vec<CommandFlag>,
22}
23
24impl ParsedCommand {
25 #[must_use]
26 pub fn is_forced(&self) -> bool {
31 self.flags.contains(&CommandFlag::Force)
32 }
33}
34
35#[must_use]
36pub fn parse(commands: &[&str], input: &str) -> Option<ParsedCommand> {
45 let (flags, input) = parse_flags(input);
46 let mut parts = input.splitn(2, ' ');
47 let input = parts.next()?;
48 let args = parts
49 .next()
50 .map(|s| s.split(' ').map(ToString::to_string).collect())
51 .unwrap_or_default();
52 let commands = parse_commands(commands, input);
53
54 if commands.is_empty() {
55 return None;
56 }
57
58 Some(ParsedCommand {
59 commands,
60 args,
61 flags,
62 })
63}
64
65fn parse_flags(input: &str) -> (Vec<CommandFlag>, &str) {
73 if let Some(input) = input.strip_suffix("!") {
74 (vec![CommandFlag::Force], input)
75 } else {
76 (vec![], input)
77 }
78}
79
80fn parse_commands(commands: &[&str], input: &str) -> Vec<String> {
89 for command in commands {
90 if &input == command {
91 return vec![(*command).to_string()];
92 }
93 }
94
95 let mut result = Vec::new();
96 for c in input.chars() {
97 if let Some(command) = commands.iter().find(|cmd| cmd.starts_with(c)) {
98 result.push((*command).to_string());
99 }
100 }
101
102 result
103}
104
105#[cfg(test)]
106mod test {
107 use super::*;
108
109 #[test]
110 fn test_parse() {
111 let commands = ["quit", "write"];
112 assert_eq!(
113 parse(&commands, "quit"),
114 Some(ParsedCommand {
115 commands: vec!["quit".to_string()],
116 flags: vec![],
117 ..Default::default()
118 })
119 );
120 assert_eq!(
121 parse(&commands, "q"),
122 Some(ParsedCommand {
123 commands: vec!["quit".to_string()],
124 flags: vec![],
125 ..Default::default()
126 })
127 );
128 assert_eq!(
129 parse(&commands, "q!"),
130 Some(ParsedCommand {
131 commands: vec!["quit".to_string()],
132 flags: vec![CommandFlag::Force],
133 ..Default::default()
134 })
135 );
136 assert_eq!(
137 parse(&commands, "wq"),
138 Some(ParsedCommand {
139 commands: vec!["write".to_string(), "quit".to_string()],
140 flags: vec![],
141 ..Default::default()
142 })
143 );
144 assert_eq!(
145 parse(&commands, "wq!"),
146 Some(ParsedCommand {
147 commands: vec!["write".to_string(), "quit".to_string()],
148 flags: vec![CommandFlag::Force],
149 ..Default::default()
150 })
151 );
152 }
153
154 #[test]
155 fn test_parse_command() {
156 let commands = ["quit", "write"];
157 assert_eq!(parse_commands(&commands, "quit"), vec!["quit"]);
158 assert_eq!(parse_commands(&commands, "q"), vec!["quit"]);
159 assert_eq!(parse_commands(&commands, "w"), vec!["write"]);
160 assert_eq!(parse_commands(&commands, "wq"), vec!["write", "quit"]);
161 }
162
163 #[test]
164 fn test_parse_flags() {
165 assert_eq!(parse_flags("q"), (vec![], "q"));
166 assert_eq!(parse_flags("q!"), (vec![CommandFlag::Force], "q"));
167 }
168}