Skip to main content

steel_core/command/brigadier/
builder.rs

1//! Command node builders.
2
3use std::sync::Arc;
4
5use super::{
6    ArgumentType, BrigadierRuntime, CommandContext, CommandRequirement, CommandRuntime,
7    CommandSyntaxError, NodeId, RegistrationError, RegistrationErrorKind,
8    node::{
9        CommandNodeData, CommandRedirect, CommandRedirectTarget, UnregisteredCommandNode,
10        merge_or_push,
11    },
12    runtime::{BrigadierExecutor, BrigadierModifier},
13};
14
15/// Builds one literal or argument command node and its descendants.
16pub(crate) struct CommandNodeBuilder<S, R = BrigadierRuntime>
17where
18    R: CommandRuntime<S>,
19{
20    data: CommandNodeData<R::Argument>,
21    children: Vec<Self>,
22    executor: Option<Arc<R::Executor>>,
23    requirement: CommandRequirement<S>,
24    execution_requirement: CommandRequirement<S>,
25    redirect: Option<CommandRedirect<S, R>>,
26}
27
28/// Requirements for traversing a scoped route and executing its current node.
29pub(crate) struct CommandRequirementRoute<S> {
30    traversal: CommandRequirement<S>,
31    execution: CommandRequirement<S>,
32}
33
34impl<S> CommandRequirementRoute<S> {
35    /// Creates the requirements for one resolved route through a command tree.
36    pub(crate) const fn new(
37        traversal: CommandRequirement<S>,
38        execution: CommandRequirement<S>,
39    ) -> Self {
40        Self {
41            traversal,
42            execution,
43        }
44    }
45}
46
47impl<S> Clone for CommandRequirementRoute<S> {
48    fn clone(&self) -> Self {
49        Self {
50            traversal: self.traversal.clone(),
51            execution: self.execution.clone(),
52        }
53    }
54}
55
56#[derive(Clone, Debug, PartialEq, Eq)]
57struct RequirementRouteKey {
58    governing_scope: Option<usize>,
59    descendant_scopes: Vec<usize>,
60}
61
62#[derive(Clone, Copy)]
63struct ActiveRequirementScope<'path> {
64    index: usize,
65    remaining: &'path [Box<str>],
66}
67
68impl<S, R> Clone for CommandNodeBuilder<S, R>
69where
70    R: CommandRuntime<S>,
71    R::Argument: Clone,
72{
73    fn clone(&self) -> Self {
74        Self {
75            data: self.data.clone(),
76            children: self.children.clone(),
77            executor: self.executor.as_ref().map(Arc::clone),
78            requirement: self.requirement.clone(),
79            execution_requirement: self.execution_requirement.clone(),
80            redirect: self.redirect.clone(),
81        }
82    }
83}
84
85/// Creates a literal using the standard synchronous Brigadier runtime.
86pub(crate) fn literal<S>(name: impl Into<Box<str>>) -> CommandNodeBuilder<S> {
87    CommandNodeBuilder::literal(name)
88}
89
90/// Creates an argument using the standard synchronous Brigadier runtime.
91pub(crate) fn argument<S>(
92    name: impl Into<Box<str>>,
93    argument_type: ArgumentType,
94) -> CommandNodeBuilder<S> {
95    CommandNodeBuilder::argument(name, argument_type)
96}
97
98impl<S, R> CommandNodeBuilder<S, R>
99where
100    R: CommandRuntime<S>,
101{
102    /// Creates a literal for this runtime model.
103    pub(crate) fn literal(name: impl Into<Box<str>>) -> Self {
104        Self {
105            data: CommandNodeData::Literal(name.into()),
106            children: Vec::new(),
107            executor: None,
108            requirement: CommandRequirement::allow_all(),
109            execution_requirement: CommandRequirement::allow_all(),
110            redirect: None,
111        }
112    }
113
114    /// Creates an argument for this runtime model.
115    pub(crate) fn argument(name: impl Into<Box<str>>, argument_type: R::Argument) -> Self {
116        Self {
117            data: CommandNodeData::Argument {
118                name: name.into(),
119                argument_type,
120            },
121            children: Vec::new(),
122            executor: None,
123            requirement: CommandRequirement::allow_all(),
124            execution_requirement: CommandRequirement::allow_all(),
125            redirect: None,
126        }
127    }
128
129    /// Returns this node's literal name, or `None` for an argument node.
130    pub(crate) fn literal_name(&self) -> Option<&str> {
131        match &self.data {
132            CommandNodeData::Literal(name) => Some(name),
133            CommandNodeData::Root | CommandNodeData::Argument { .. } => None,
134        }
135    }
136
137    /// Replaces this node's literal name, returning `None` for an argument node.
138    pub(crate) fn with_literal_name(mut self, name: impl Into<Box<str>>) -> Option<Self> {
139        let CommandNodeData::Literal(literal) = &mut self.data else {
140            return None;
141        };
142        *literal = name.into();
143        Some(self)
144    }
145
146    /// Adds a child while preserving registration order.
147    #[must_use]
148    pub(crate) fn then(mut self, child: Self) -> Self {
149        self.children.push(child);
150        self
151    }
152
153    /// Attaches an executor payload without interpreting it.
154    #[must_use]
155    pub(crate) fn executes_with_executor(mut self, executor: Arc<R::Executor>) -> Self {
156        self.executor = Some(executor);
157        self
158    }
159
160    /// Replaces the allow-all requirement with `requirement`.
161    #[must_use]
162    pub(crate) fn requires(mut self, requirement: CommandRequirement<S>) -> Self {
163        self.requirement = requirement;
164        self
165    }
166
167    /// Adds a requirement while preserving any predicate already on this node.
168    #[must_use]
169    pub(crate) fn also_requires(mut self, requirement: CommandRequirement<S>) -> Self
170    where
171        S: 'static,
172    {
173        self.requirement = self.requirement.and(requirement);
174        self
175    }
176
177    /// Adds a requirement that applies only when this node's executor is selected.
178    #[must_use]
179    pub(crate) fn also_requires_execution(mut self, requirement: CommandRequirement<S>) -> Self
180    where
181        S: 'static,
182    {
183        self.execution_requirement = self.execution_requirement.and(requirement);
184        self
185    }
186
187    /// Returns the number of occurrences of one literal path below this node.
188    ///
189    /// Argument nodes do not consume path segments.
190    pub(crate) fn literal_path_match_count(&self, path: &[Box<str>]) -> usize {
191        let Some((name, remaining)) = path.split_first() else {
192            return 0;
193        };
194        let mut matches = 0;
195        for child in &self.children {
196            let Some(literal) = child.literal_name() else {
197                matches += child.literal_path_match_count(path);
198                continue;
199            };
200            if literal != name.as_ref() {
201                continue;
202            }
203            if remaining.is_empty() {
204                matches += 1;
205            } else {
206                matches += child.literal_path_match_count(remaining);
207            }
208        }
209        matches
210    }
211
212    /// Applies independently scoped requirements using literal-only paths.
213    ///
214    /// A descendant scope may traverse its ancestors, but it cannot execute an
215    /// ancestor unless the route's governing requirement also permits it.
216    pub(crate) fn apply_scoped_requirements(
217        &mut self,
218        scope_paths: &[Vec<Box<str>>],
219        mut requirements_for: impl FnMut(Option<usize>, &[usize]) -> CommandRequirementRoute<S>,
220    ) where
221        S: 'static,
222    {
223        let active = scope_paths
224            .iter()
225            .enumerate()
226            .map(|(index, path)| ActiveRequirementScope {
227                index,
228                remaining: path,
229            })
230            .collect::<Vec<_>>();
231        let mut cache = Vec::new();
232        self.apply_scoped_requirements_inner(
233            None,
234            &active,
235            None,
236            &mut cache,
237            &mut requirements_for,
238        );
239    }
240
241    fn apply_scoped_requirements_inner<F>(
242        &mut self,
243        inherited_scope: Option<usize>,
244        active: &[ActiveRequirementScope<'_>],
245        parent_route: Option<&RequirementRouteKey>,
246        cache: &mut Vec<(RequirementRouteKey, CommandRequirementRoute<S>)>,
247        requirements_for: &mut F,
248    ) where
249        S: 'static,
250        F: FnMut(Option<usize>, &[usize]) -> CommandRequirementRoute<S>,
251    {
252        let governing_scope = active
253            .iter()
254            .find(|scope| scope.remaining.is_empty())
255            .map_or(inherited_scope, |scope| Some(scope.index));
256        let descendant_scopes = active
257            .iter()
258            .filter(|scope| !scope.remaining.is_empty())
259            .map(|scope| scope.index)
260            .collect::<Vec<_>>();
261        let route = RequirementRouteKey {
262            governing_scope,
263            descendant_scopes,
264        };
265        let requirements = cached_route_requirements(cache, &route, requirements_for);
266
267        if parent_route != Some(&route) {
268            self.requirement = self.requirement.clone().and(requirements.traversal);
269        }
270        if !route.descendant_scopes.is_empty() && self.executor.is_some() {
271            self.execution_requirement = self
272                .execution_requirement
273                .clone()
274                .and(requirements.execution);
275        }
276
277        for child in &mut self.children {
278            let child_active = if let Some(literal) = child.literal_name() {
279                active
280                    .iter()
281                    .filter_map(|scope| {
282                        let (name, remaining) = scope.remaining.split_first()?;
283                        (name.as_ref() == literal).then_some(ActiveRequirementScope {
284                            index: scope.index,
285                            remaining,
286                        })
287                    })
288                    .collect::<Vec<_>>()
289            } else {
290                active
291                    .iter()
292                    .filter(|scope| {
293                        !scope.remaining.is_empty()
294                            && child.literal_path_match_count(scope.remaining) > 0
295                    })
296                    .copied()
297                    .collect::<Vec<_>>()
298            };
299            child.apply_scoped_requirements_inner(
300                governing_scope,
301                &child_active,
302                Some(&route),
303                cache,
304                requirements_for,
305            );
306        }
307    }
308
309    /// Redirects parsing to an existing node without transforming the source.
310    #[must_use]
311    pub(crate) fn redirects(mut self, target: impl Into<CommandRedirectTarget>) -> Self {
312        self.redirect = Some(CommandRedirect::identity(target.into()));
313        self
314    }
315
316    /// Redirects with an opaque runtime modifier payload.
317    #[must_use]
318    pub(crate) fn redirects_with_modifier(
319        mut self,
320        target: impl Into<CommandRedirectTarget>,
321        modifier: Arc<R::Modifier>,
322        forks: bool,
323    ) -> Self {
324        self.redirect = Some(CommandRedirect::with_modifier(
325            target.into(),
326            modifier,
327            forks,
328        ));
329        self
330    }
331
332    pub(super) fn normalize(self) -> Result<UnregisteredCommandNode<S, R>, RegistrationError> {
333        let mut children = Vec::new();
334        for child in self.children {
335            merge_or_push(&mut children, child.normalize()?)?;
336        }
337        if self.redirect.is_some() && !children.is_empty() {
338            return Err(RegistrationError::new(
339                RegistrationErrorKind::RedirectWithChildren {
340                    name: self.data.name().into(),
341                },
342            ));
343        }
344
345        Ok(UnregisteredCommandNode {
346            data: self.data,
347            children,
348            executor: self.executor,
349            requirement: self.requirement,
350            execution_requirement: self.execution_requirement,
351            redirect: self.redirect,
352        })
353    }
354}
355
356fn cached_route_requirements<S, F>(
357    cache: &mut Vec<(RequirementRouteKey, CommandRequirementRoute<S>)>,
358    route: &RequirementRouteKey,
359    requirements_for: &mut F,
360) -> CommandRequirementRoute<S>
361where
362    F: FnMut(Option<usize>, &[usize]) -> CommandRequirementRoute<S>,
363{
364    if let Some((_, requirements)) = cache.iter().find(|(cached, _)| cached == route) {
365        return requirements.clone();
366    }
367    let requirements = requirements_for(route.governing_scope, &route.descendant_scopes);
368    cache.push((route.clone(), requirements.clone()));
369    requirements
370}
371
372impl<S> CommandNodeBuilder<S, BrigadierRuntime> {
373    /// Attaches a standard synchronous command callback.
374    #[must_use]
375    pub(crate) fn executes(
376        self,
377        executor: impl Fn(&CommandContext<S>) -> Result<i32, CommandSyntaxError> + Send + Sync + 'static,
378    ) -> Self {
379        let executor: Arc<BrigadierExecutor<S>> = Arc::new(executor);
380        self.executes_with_executor(executor)
381    }
382
383    /// Redirects parsing and transforms the source once before continuing.
384    #[must_use]
385    pub(crate) fn redirects_with(
386        self,
387        target: NodeId,
388        modifier: impl Fn(&CommandContext<S>) -> Result<S, CommandSyntaxError> + Send + Sync + 'static,
389    ) -> Self {
390        let modifier: Arc<BrigadierModifier<S>> =
391            Arc::new(move |context| modifier(context).map(|source| vec![source]));
392        self.redirects_with_modifier(target, modifier, false)
393    }
394
395    /// Redirects parsing and expands one source into zero or more sources.
396    #[must_use]
397    pub(crate) fn forks(
398        self,
399        target: NodeId,
400        modifier: impl Fn(&CommandContext<S>) -> Result<Vec<S>, CommandSyntaxError>
401        + Send
402        + Sync
403        + 'static,
404    ) -> Self {
405        let modifier: Arc<BrigadierModifier<S>> = Arc::new(modifier);
406        self.redirects_with_modifier(target, modifier, true)
407    }
408}