Skip to main content

azalea_brigadier/tree/
mod.rs

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