azalea_brigadier/tree/
mod.rs

1use std::{
2    collections::{BTreeMap, HashMap},
3    fmt::Debug,
4    hash::Hash,
5    ptr,
6    sync::Arc,
7};
8
9use parking_lot::RwLock;
10
11use crate::{
12    builder::{
13        argument_builder::ArgumentBuilderType, literal_argument_builder::Literal,
14        required_argument_builder::Argument,
15    },
16    context::{CommandContext, CommandContextBuilder, ParsedArgument, StringRange},
17    exceptions::{BuiltInExceptions, CommandSyntaxException},
18    modifier::RedirectModifier,
19    string_reader::StringReader,
20    suggestion::{Suggestions, SuggestionsBuilder},
21};
22
23pub type Command<S> = Option<Arc<dyn Fn(&CommandContext<S>) -> i32 + Send + Sync>>;
24
25/// An ArgumentBuilder that has been built.
26#[non_exhaustive]
27pub struct CommandNode<S> {
28    pub value: ArgumentBuilderType,
29
30    // this is a BTreeMap because children need to be ordered when getting command suggestions
31    pub children: BTreeMap<String, Arc<RwLock<CommandNode<S>>>>,
32    pub literals: HashMap<String, Arc<RwLock<CommandNode<S>>>>,
33    pub arguments: HashMap<String, Arc<RwLock<CommandNode<S>>>>,
34
35    pub command: Command<S>,
36    pub requirement: Arc<dyn Fn(&S) -> bool + Send + Sync>,
37    pub redirect: Option<Arc<RwLock<CommandNode<S>>>>,
38    pub forks: bool,
39    pub modifier: Option<Arc<RedirectModifier<S>>>,
40}
41
42impl<S> Clone for CommandNode<S> {
43    fn clone(&self) -> Self {
44        Self {
45            value: self.value.clone(),
46            children: self.children.clone(),
47            literals: self.literals.clone(),
48            arguments: self.arguments.clone(),
49            command: self.command.clone(),
50            requirement: self.requirement.clone(),
51            redirect: self.redirect.clone(),
52            forks: self.forks,
53            modifier: self.modifier.clone(),
54        }
55    }
56}
57
58impl<S> CommandNode<S> {
59    /// Gets the literal, or panics. You should use match if you're not certain
60    /// about the type.
61    pub fn literal(&self) -> &Literal {
62        match self.value {
63            ArgumentBuilderType::Literal(ref literal) => literal,
64            _ => panic!("CommandNode::literal() called on non-literal node"),
65        }
66    }
67    /// Gets the argument, or panics. You should use match if you're not certain
68    /// about the type.
69    pub fn argument(&self) -> &Argument {
70        match self.value {
71            ArgumentBuilderType::Argument(ref argument) => argument,
72            _ => panic!("CommandNode::argument() called on non-argument node"),
73        }
74    }
75
76    pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec<Arc<RwLock<CommandNode<S>>>> {
77        let literals = &self.literals;
78
79        if literals.is_empty() {
80            self.arguments.values().cloned().collect()
81        } else {
82            let cursor = input.cursor();
83            while input.can_read() && input.peek() != ' ' {
84                input.skip();
85            }
86            let text: String = input
87                .string()
88                .chars()
89                .skip(cursor)
90                .take(input.cursor() - cursor)
91                .collect();
92            input.cursor = cursor;
93            let literal = literals.get(&text);
94            if let Some(literal) = literal {
95                vec![literal.clone()]
96            } else {
97                self.arguments.values().cloned().collect()
98            }
99        }
100    }
101
102    pub fn can_use(&self, source: &S) -> bool {
103        (self.requirement)(source)
104    }
105
106    pub fn add_child(&mut self, node: &Arc<RwLock<CommandNode<S>>>) {
107        let child = self.children.get(node.read().name());
108        if let Some(child) = child {
109            // We've found something to merge onto
110            if let Some(command) = &node.read().command {
111                child.write().command = Some(command.clone());
112            }
113            for grandchild in node.read().children.values() {
114                child.write().add_child(grandchild);
115            }
116        } else {
117            self.children
118                .insert(node.read().name().to_string(), node.clone());
119            match &node.read().value {
120                ArgumentBuilderType::Literal(literal) => {
121                    self.literals.insert(literal.value.clone(), node.clone());
122                }
123                ArgumentBuilderType::Argument(argument) => {
124                    self.arguments.insert(argument.name.clone(), node.clone());
125                }
126            }
127        }
128    }
129
130    pub fn name(&self) -> &str {
131        match &self.value {
132            ArgumentBuilderType::Argument(argument) => &argument.name,
133            ArgumentBuilderType::Literal(literal) => &literal.value,
134        }
135    }
136
137    pub fn usage_text(&self) -> String {
138        match &self.value {
139            ArgumentBuilderType::Argument(argument) => format!("<{}>", argument.name),
140            ArgumentBuilderType::Literal(literal) => literal.value.to_owned(),
141        }
142    }
143
144    pub fn child(&self, name: &str) -> Option<Arc<RwLock<CommandNode<S>>>> {
145        self.children.get(name).cloned()
146    }
147
148    pub fn parse_with_context(
149        &self,
150        reader: &mut StringReader,
151        context_builder: &mut CommandContextBuilder<S>,
152    ) -> Result<(), CommandSyntaxException> {
153        match self.value {
154            ArgumentBuilderType::Argument(ref argument) => {
155                let start = reader.cursor();
156                let result = argument.parse(reader)?;
157                let parsed = ParsedArgument {
158                    range: StringRange::between(start, reader.cursor()),
159                    result,
160                };
161
162                context_builder.with_argument(&argument.name, parsed.clone());
163                context_builder.with_node(Arc::new(RwLock::new(self.clone())), parsed.range);
164
165                Ok(())
166            }
167            ArgumentBuilderType::Literal(ref literal) => {
168                let start = reader.cursor();
169                let end = self.parse(reader);
170
171                if let Some(end) = end {
172                    context_builder.with_node(
173                        Arc::new(RwLock::new(self.clone())),
174                        StringRange::between(start, end),
175                    );
176                    return Ok(());
177                }
178
179                Err(BuiltInExceptions::LiteralIncorrect {
180                    expected: literal.value.clone(),
181                }
182                .create_with_context(reader))
183            }
184        }
185    }
186
187    fn parse(&self, reader: &mut StringReader) -> Option<usize> {
188        match self.value {
189            ArgumentBuilderType::Argument(_) => {
190                panic!("Can't parse argument.")
191            }
192            ArgumentBuilderType::Literal(ref literal) => {
193                let start = reader.cursor();
194                if reader.can_read_length(literal.value.len()) {
195                    let end = start + literal.value.len();
196                    if reader
197                        .string()
198                        .get(start..end)
199                        .expect("Couldn't slice reader correctly?")
200                        == literal.value
201                    {
202                        reader.cursor = end;
203                        if !reader.can_read() || reader.peek() == ' ' {
204                            return Some(end);
205                        } else {
206                            reader.cursor = start;
207                        }
208                    }
209                }
210            }
211        }
212        None
213    }
214
215    pub fn list_suggestions(
216        &self,
217        // context is here because that's how it is in mojang's brigadier, but we haven't
218        // implemented custom suggestions yet so this is unused rn
219        _context: CommandContext<S>,
220        builder: SuggestionsBuilder,
221    ) -> Suggestions {
222        match &self.value {
223            ArgumentBuilderType::Literal(literal) => {
224                if literal
225                    .value
226                    .to_lowercase()
227                    .starts_with(builder.remaining_lowercase())
228                {
229                    builder.suggest(&literal.value).build()
230                } else {
231                    Suggestions::default()
232                }
233            }
234            ArgumentBuilderType::Argument(argument) => argument.list_suggestions(builder),
235        }
236    }
237}
238
239impl<S> Debug for CommandNode<S> {
240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241        f.debug_struct("CommandNode")
242            .field("value", &self.value)
243            .field("children", &self.children)
244            .field("command", &self.command.is_some())
245            // .field("requirement", &self.requirement)
246            .field("redirect", &self.redirect)
247            .field("forks", &self.forks)
248            // .field("modifier", &self.modifier)
249            .finish()
250    }
251}
252
253impl<S> Default for CommandNode<S> {
254    fn default() -> Self {
255        Self {
256            value: ArgumentBuilderType::Literal(Literal::default()),
257
258            children: BTreeMap::new(),
259            literals: HashMap::new(),
260            arguments: HashMap::new(),
261
262            command: None,
263            requirement: Arc::new(|_| true),
264            redirect: None,
265            forks: false,
266            modifier: None,
267        }
268    }
269}
270
271impl<S> Hash for CommandNode<S> {
272    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
273        // hash the children
274        for (k, v) in &self.children {
275            k.hash(state);
276            v.read().hash(state);
277        }
278        // i hope this works because if doesn't then that'll be a problem
279        ptr::hash(&self.command, state);
280    }
281}
282
283impl<S> PartialEq for CommandNode<S> {
284    fn eq(&self, other: &Self) -> bool {
285        if self.children.len() != other.children.len() {
286            return false;
287        }
288        for (k, v) in &self.children {
289            let other_child = other.children.get(k).unwrap();
290            if !Arc::ptr_eq(v, other_child) {
291                return false;
292            }
293        }
294
295        match &self.command {
296            Some(selfexecutes) => {
297                // idk how to do this better since we can't compare `dyn Fn`s
298                match &other.command {
299                    Some(otherexecutes) =>
300                    {
301                        #[allow(ambiguous_wide_pointer_comparisons)]
302                        if !Arc::ptr_eq(selfexecutes, otherexecutes) {
303                            return false;
304                        }
305                    }
306                    _ => {
307                        return false;
308                    }
309                }
310            }
311            _ => {
312                if other.command.is_some() {
313                    return false;
314                }
315            }
316        }
317        true
318    }
319}
320impl<S> Eq for CommandNode<S> {}