mcfunction_debugger/generator/parser/
command.rs1pub(crate) mod argument;
20pub mod resource_location;
21
22use crate::generator::parser::command::argument::{Argument, ArgumentParser};
23use serde::{Deserialize, Serialize};
24use std::{
25 collections::BTreeMap,
26 fmt::{Display, Write},
27 u32, usize,
28};
29
30pub struct CommandParser {
31 specs: BTreeMap<String, CommandSpec>,
32}
33
34impl CommandParser {
35 pub fn default() -> Result<CommandParser, serde_json::Error> {
36 let json = include_str!("commands.json");
37 CommandParser::from_str(json)
38 }
39
40 pub fn from_str(json: &str) -> serde_json::Result<CommandParser> {
41 let root_node: RootNode = serde_json::from_str(json)?;
42 Ok(CommandParser {
43 specs: root_node.children,
44 })
45 }
46
47 pub fn parse<'l>(&'l self, command: &'l str) -> CommandParserResult<'l> {
48 self.parse_from_specs(command, 0, &self.specs)
49 }
50
51 fn parse_from_specs<'l>(
52 &'l self,
53 command: &'l str,
54 index: usize,
55 specs: &'l BTreeMap<String, CommandSpec>,
56 ) -> CommandParserResult<'l> {
57 let parsed = Self::find_relevant_commands(command, index, specs)
58 .into_iter()
59 .map(|(name, spec)| (self.parse_from_single_spec(name, spec, command, index)))
60 .collect::<Vec<_>>();
61
62 let only_errors = parsed.iter().all(|parsed| parsed.error.is_some());
63 if only_errors {
64 parsed
66 .into_iter()
67 .max_by_key(|result| result.parsed_nodes.len())
68 .unwrap_or(CommandParserResult {
69 parsed_nodes: Vec::new(),
70 error: Some(CommandParserError {
71 message: "Incorrect argument for command".to_string(),
72 command,
73 index,
74 }),
75 })
76 } else {
77 parsed
79 .into_iter()
80 .filter(|parsed| parsed.error.is_none())
81 .next()
82 .unwrap()
83 }
84 }
85
86 fn find_relevant_commands<'l>(
88 command: &'l str,
89 index: usize,
90 specs: &'l BTreeMap<String, CommandSpec>,
91 ) -> Vec<(&'l String, &'l CommandSpec)> {
92 let string = &command[index..];
93 let literal_len = string.find(' ').unwrap_or(string.len());
94 let literal = &string[..literal_len];
95 if let Some((name, command)) = Self::find_literal_command(literal, specs) {
96 vec![(name, command)]
97 } else {
98 specs
99 .iter()
100 .filter(|(_name, spec)| matches!(spec, CommandSpec::Argument { .. }))
101 .collect::<Vec<_>>()
102 }
103 }
104
105 fn find_literal_command<'l>(
106 literal: &str,
107 specs: &'l BTreeMap<String, CommandSpec>,
108 ) -> Option<(&'l String, &'l CommandSpec)> {
109 specs
110 .iter()
111 .find(|(name, spec)| *name == literal && matches!(spec, CommandSpec::Literal { .. }))
112 }
113
114 fn parse_from_single_spec<'l>(
115 &'l self,
116 name: &'l str,
117 spec: &'l CommandSpec,
118 command: &'l str,
119 mut index: usize,
120 ) -> CommandParserResult<'l> {
121 let mut parsed_nodes = Vec::new();
122
123 macro_rules! Ok {
124 () => {
125 CommandParserResult {
126 parsed_nodes,
127 error: None,
128 }
129 };
130 }
131 macro_rules! Err {
132 ($message:expr) => {
133 CommandParserResult {
134 parsed_nodes,
135 error: Some(CommandParserError {
136 message: $message,
137 command,
138 index,
139 }),
140 }
141 };
142 }
143
144 let parsed_node = match spec.parse(name, command, index) {
145 Ok(parsed_node) => parsed_node,
146 Err(message) => return Err!(message),
147 };
148 index += parsed_node.len();
149 parsed_nodes.push(parsed_node);
150
151 if index >= command.len() {
152 if spec.executable() {
153 return Ok!();
154 } else {
155 return Err!("Incomplete command".to_string());
156 }
157 }
158
159 const SPACE: char = ' ';
160 if !command[index..].starts_with(SPACE) {
161 return Err!(
162 "Expected whitespace to end one argument, but found trailing data".to_string()
163 );
164 }
165 index += SPACE.len_utf8();
166
167 let redirect = match spec.redirect() {
169 Ok(ok) => ok,
170 Err(message) => return Err!(message),
171 };
172 let children = if let Some(redirect) = redirect {
173 if let Some(redirected) = self.specs.get(redirect) {
174 parsed_nodes.push(ParsedNode::Redirect(redirect));
175 redirected.children()
176 } else {
177 return Err!(format!("Failed to resolve redirect {}", redirect));
178 }
179 } else if spec.has_children() {
180 spec.children()
181 } else if !spec.executable() {
182 &self.specs
184 } else {
185 return Err!("Incorrect argument for command".to_string());
186 };
187 let mut result = self.parse_from_specs(command, index, children);
188 parsed_nodes.extend_from_slice(&result.parsed_nodes);
189 result.parsed_nodes = parsed_nodes;
190 result
191 }
192}
193
194#[derive(Clone, Debug, PartialEq)]
195pub struct CommandParserResult<'l> {
196 pub parsed_nodes: Vec<ParsedNode<'l>>,
197 pub error: Option<CommandParserError<'l>>,
198}
199
200#[derive(Clone, Debug, PartialEq)]
201pub struct CommandParserError<'l> {
202 pub message: String,
203 pub command: &'l str,
204 pub index: usize,
205}
206
207impl Display for CommandParserError<'_> {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 write!(f, "{}:\n{}\n", self.message, self.command)?;
210 for _ in 0..self.index {
211 f.write_char(' ')?;
212 }
213 f.write_char('^')
214 }
215}
216
217#[derive(Clone, Debug, PartialEq)]
218pub enum ParsedNode<'l> {
219 Redirect(&'l str),
220 Literal {
221 literal: &'l str,
222 index: usize,
223 },
224 Argument {
225 name: &'l str,
226 argument: Argument<'l>,
227 index: usize,
228 len: usize,
229 },
230}
231
232impl ParsedNode<'_> {
233 fn len(&self) -> usize {
234 match self {
235 ParsedNode::Redirect(_) => 0,
236 ParsedNode::Literal { literal, .. } => literal.len(),
237 ParsedNode::Argument { len, .. } => *len,
238 }
239 }
240}
241
242#[derive(Deserialize, Serialize, Debug)]
243#[serde(tag = "type", rename = "root")]
244struct RootNode {
245 children: BTreeMap<String, CommandSpec>,
246}
247
248#[derive(Debug, Deserialize, Serialize)]
249#[serde(tag = "type", rename_all = "snake_case")]
250pub enum CommandSpec {
251 Literal {
252 #[serde(flatten)]
253 node: Node,
254 },
255 Argument {
256 #[serde(flatten)]
257 node: Node,
258 #[serde(flatten)]
259 parser: ArgumentParser,
260 },
261}
262
263impl CommandSpec {
264 fn parse<'l>(
265 &self,
266 name: &'l str,
267 command: &'l str,
268 index: usize,
269 ) -> Result<ParsedNode<'l>, String> {
270 let string = &command[index..];
271 match self {
272 CommandSpec::Literal { .. } => {
273 let literal_len = string.find(' ').unwrap_or(string.len());
274 let literal = &string[..literal_len];
275 if literal == name {
276 Ok(ParsedNode::Literal { literal, index })
277 } else {
278 Err("Incorrect literal for command".to_string())
279 }
280 }
281 CommandSpec::Argument { parser, .. } => {
282 parser
283 .parse(string)
284 .map(|(argument, len)| ParsedNode::Argument {
285 name,
286 argument,
287 index,
288 len,
289 })
290 }
291 }
292 }
293}
294
295#[derive(Debug, Deserialize, Serialize)]
296pub struct Node {
297 #[serde(default)]
298 pub children: BTreeMap<String, CommandSpec>,
299 #[serde(default)]
300 pub executable: bool,
301 #[serde(default)]
302 pub redirect: Vec<String>,
303}
304
305impl CommandSpec {
306 pub fn has_children(&self) -> bool {
307 !self.children().is_empty()
308 }
309
310 pub fn children(&self) -> &BTreeMap<String, CommandSpec> {
311 match self {
312 CommandSpec::Literal { node, .. } => &node.children,
313 CommandSpec::Argument { node, .. } => &node.children,
314 }
315 }
316
317 pub fn executable(&self) -> bool {
318 match self {
319 CommandSpec::Literal { node, .. } => node.executable,
320 CommandSpec::Argument { node, .. } => node.executable,
321 }
322 }
323
324 pub fn redirect(&self) -> Result<Option<&String>, String> {
325 let redirect = match self {
326 CommandSpec::Literal { node, .. } => &node.redirect,
327 CommandSpec::Argument { node, .. } => &node.redirect,
328 };
329 if redirect.len() > 1 {
330 Err(format!("Multi redirect is not supported: {:?}", redirect))
331 } else {
332 Ok(redirect.first())
333 }
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn test_parse_json() {
343 let actual = &CommandParser::default().unwrap().specs;
345
346 assert!(
348 actual.contains_key("execute"),
349 "Expected actual to contain key 'execute': {:#?}",
350 actual
351 );
352 }
353
354 #[test]
355 fn test_serialize() {
356 let root = RootNode {
358 children: BTreeMap::new(),
359 };
360
361 let actual = serde_json::to_string(&root).unwrap();
362
363 assert_eq!(actual, r#"{"type":"root","children":{}}"#);
365 }
366}