1use std::collections::{BTreeMap, HashMap};
2
3use crate::types::{
4 CommandDispatchResult, CommandParseResult, DispatchFunction, ParseState, SpannedWord,
5};
6
7pub struct RootDispatchNode {
10 pub(crate) literals: HashMap<&'static str, DispatchNode>,
11 pub(crate) aliases: HashMap<&'static str, &'static str>,
12}
13
14impl RootDispatchNode {
15 pub fn dispatch(&self, input: &str) -> CommandDispatchResult {
16 let parse_state = ParseState::new(input);
17 self.dispatch_with(parse_state)
18 }
19
20 pub fn dispatch_with(&self, mut parse_state: ParseState) -> CommandDispatchResult {
21 if let Some(spanned_word) = parse_state.pop_input() {
22 if let Some(aliased) = self.aliases.get(spanned_word.word) {
23 let literal = self
26 .literals
27 .get(*aliased)
28 .expect("literal must exist if it has an alias");
29
30 literal.dispatch(&mut parse_state)
31 } else {
32 let literal = self.literals.get(spanned_word.word);
35
36 if let Some(literal) = literal {
37 literal.dispatch(&mut parse_state)
38 } else {
39 CommandDispatchResult::UnknownCommand
40 }
41 }
42 } else {
43 CommandDispatchResult::IncompleteCommand
44 }
45 }
46}
47
48pub(crate) struct DispatchNode {
49 pub(crate) literals: BTreeMap<&'static str, DispatchNode>,
50 pub(crate) aliases: BTreeMap<&'static str, &'static str>,
51 pub(crate) parsers: Vec<ArgumentNode>,
52 pub(crate) executor: Option<DispatchFunction>,
53}
54
55impl DispatchNode {
56 fn dispatch(&self, remaining: &mut ParseState) -> CommandDispatchResult {
57 if let Some(next_word) = remaining.pop_input() {
58 if let Some(aliased) = self.aliases.get(next_word.word) {
61 let literal = self
63 .literals
64 .get(*aliased)
65 .expect("literal must exist if it has an alias");
66
67 literal.dispatch(remaining)
68 } else if let Some(literal) = self.literals.get(next_word.word) {
69 literal.dispatch(remaining)
71 } else {
72 let mut result: Option<CommandDispatchResult> = None;
74 for arg in &self.parsers {
75 let prev_cursor = remaining.cursor();
76
77 let parse_result = arg.parse(next_word, remaining);
78 match parse_result {
79 CommandDispatchResult::ParseError {
80 span: _,
81 errmsg: _,
82 continue_parsing,
83 } => {
84 if continue_parsing {
85 if result.is_none() {
86 result = Some(parse_result);
87 }
88 } else {
89 return parse_result;
90 }
91 }
92 _ => return parse_result,
93 }
94
95 debug_assert!(
98 remaining.cursor() == prev_cursor,
99 "cursor was updated by an argument node that failed"
100 );
101 }
102 match result {
103 Some(dispatch_result) => dispatch_result,
104 None => CommandDispatchResult::TooManyArguments,
105 }
106 }
107 } else {
108 if let Some(executor) = self.executor {
111 let (arguments, spans) = remaining.get_arguments();
113 executor(arguments, spans)
114 } else {
115 CommandDispatchResult::IncompleteCommand
117 }
118 }
119 }
120}
121
122pub(crate) struct ArgumentNode {
125 pub(crate) parse: fn(SpannedWord, &mut ParseState) -> CommandParseResult,
126 pub(crate) dispatch_node: DispatchNode,
127}
128
129impl ArgumentNode {
130 fn parse(&self, word: SpannedWord, remaining: &mut ParseState) -> CommandDispatchResult {
131 let parse_result = (self.parse)(word, remaining);
133
134 match parse_result {
135 CommandParseResult::Ok => {
136 self.dispatch_node.dispatch(remaining)
138 }
139 CommandParseResult::Err {
140 span,
141 errmsg,
142 continue_parsing,
143 } => {
144 CommandDispatchResult::ParseError {
146 span,
147 errmsg,
148 continue_parsing,
149 }
150 }
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use std::collections::{BTreeMap, HashMap};
158
159 use maplit::hashmap;
160
161 use crate::dispatcher::{ArgumentNode, DispatchNode, RootDispatchNode};
162 use crate::types::ParseState;
163 use crate::types::{CommandDispatchResult, CommandParseResult, Span, SpannedWord};
164
165 #[test]
166 pub fn dispatch_with_parse() {
167 static mut DISPATCH_EXECUTED: bool = false;
168
169 fn hello_world(data: &[u8], spans: &[Span]) -> CommandDispatchResult {
170 #[repr(C)]
171 struct Data(u8, &'static str, u16);
172
173 debug_assert_eq!(spans.len(), 3);
174 debug_assert_eq!(data.len(), std::mem::size_of::<Data>());
175 let data: &Data = unsafe { &*(data as *const _ as *const Data) };
176
177 assert_eq!(data.0, 100);
178 assert_eq!(data.1, "my_string");
179 assert_eq!(data.2, 8372);
180 unsafe { DISPATCH_EXECUTED = true };
181
182 CommandDispatchResult::Success(Ok(()))
183 }
184
185 let root = RootDispatchNode {
186 literals: hashmap!(
187 "hello" => DispatchNode {
188 literals: BTreeMap::new(),
189 aliases: BTreeMap::new(),
190 parsers: vec![
191 ArgumentNode {
192 parse: parse_u8,
193 dispatch_node: DispatchNode {
194 literals: BTreeMap::new(),
195 aliases: BTreeMap::new(),
196 parsers: vec![
197 ArgumentNode {
198 parse: parse_str,
199 dispatch_node: DispatchNode {
200 literals: BTreeMap::new(),
201 aliases: BTreeMap::new(),
202 parsers: vec![
203 ArgumentNode {
204 parse: parse_u16,
205 dispatch_node: DispatchNode {
206 literals: BTreeMap::new(),
207 aliases: BTreeMap::new(),
208 parsers: vec![],
209 executor: Some(hello_world)
210 }
211 }
212 ],
213 executor: None,
214 }
215 }
216 ],
217 executor: None,
218 }
219 }
220 ],
221 executor: None,
222 }
223 ),
224 aliases: HashMap::new(),
225 };
226
227 root.dispatch("hello 100 my_string 8372");
228
229 assert!(unsafe { DISPATCH_EXECUTED });
230 }
231
232 #[test]
233 pub fn dispatch_with_context() {
234 static mut DISPATCH_EXECUTED: bool = false;
235
236 struct MyStruct(u32);
237
238 fn my_command(data: &[u8], spans: &[Span]) -> CommandDispatchResult {
239 #[repr(C)]
240 struct Data(&'static MyStruct);
241
242 debug_assert_eq!(spans.len(), 1);
243 debug_assert_eq!(data.len(), std::mem::size_of::<Data>());
244 let data: &Data = unsafe { &*(data as *const _ as *const Data) };
245
246 assert_eq!(data.0 .0, 873183);
247 unsafe { DISPATCH_EXECUTED = true };
248
249 CommandDispatchResult::Success(Ok(()))
250 }
251
252 let root = RootDispatchNode {
253 literals: hashmap!(
254 "execute" => DispatchNode {
255 literals: BTreeMap::new(),
256 aliases: BTreeMap::new(),
257 parsers: vec![],
258 executor: Some(my_command)
259 }
260 ),
261 aliases: HashMap::new(),
262 };
263
264 let my_struct = MyStruct(873183);
265 let mut parse_state = ParseState::new("execute");
266 parse_state.push_ref(&my_struct, parse_state.full_span);
267 root.dispatch_with(parse_state);
268
269 assert!(unsafe { DISPATCH_EXECUTED });
270 }
271
272 fn parse_u8(input: SpannedWord, state: &mut ParseState) -> CommandParseResult {
275 match input.word.parse::<u8>() {
276 Ok(parsed) => {
277 state.push_arg(parsed, input.span);
278 CommandParseResult::Ok
279 }
280 Err(_) => CommandParseResult::Err {
281 span: input.span,
282 errmsg: "failed to parse u8".into(),
283 continue_parsing: true,
284 },
285 }
286 }
287
288 fn parse_u16(input: SpannedWord, state: &mut ParseState) -> CommandParseResult {
289 match input.word.parse::<u16>() {
290 Ok(parsed) => {
291 state.push_arg(parsed, input.span);
292 CommandParseResult::Ok
293 }
294 Err(_) => CommandParseResult::Err {
295 span: input.span,
296 errmsg: "failed to parse u8".into(),
297 continue_parsing: true,
298 },
299 }
300 }
301
302 fn parse_str(input: SpannedWord, state: &mut ParseState) -> CommandParseResult {
303 state.push_str(input.word, input.span);
304 CommandParseResult::Ok
305 }
306}