graphite_command/
dispatcher.rs

1use std::collections::{BTreeMap, HashMap};
2
3use crate::types::{
4    CommandDispatchResult, CommandParseResult, DispatchFunction, ParseState, SpannedWord,
5};
6
7// Node implemenatations
8
9pub 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                // Aliased literal
24
25                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                // Non-aliased
33
34                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            // There is some input remaining
59
60            if let Some(aliased) = self.aliases.get(next_word.word) {
61                // Literal match via alias, dispatch to there
62                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 match, dispatch to there
70                literal.dispatch(remaining)
71            } else {
72                // No literal match, try to parse the input
73                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                    // Parse failed, try next parser
96                    // Also debug assert that the cursor didn't change
97                    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            // There is no input remaining, see if this node is an executor
109
110            if let Some(executor) = self.executor {
111                // This node is an executor, lets execute!
112                let (arguments, spans) = remaining.get_arguments();
113                executor(arguments, spans)
114            } else {
115                // Node isn't an executor, input *should* have had more remaining
116                CommandDispatchResult::IncompleteCommand
117            }
118        }
119    }
120}
121
122// Argument node
123
124pub(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        // Try to parse a value
132        let parse_result = (self.parse)(word, remaining);
133
134        match parse_result {
135            CommandParseResult::Ok => {
136                // Parse succeeded, continue dispatching
137                self.dispatch_node.dispatch(remaining)
138            }
139            CommandParseResult::Err {
140                span,
141                errmsg,
142                continue_parsing,
143            } => {
144                // Parse failed, bubble up ParseError
145                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    // Parser functions
273
274    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}