Skip to main content

steel_core/command/brigadier/
context_chain.rs

1//! Immutable Brigadier redirect and execution stages.
2
3use std::sync::Arc;
4
5use super::{BrigadierRuntime, CommandContext, CommandRuntime, CommandSyntaxError};
6
7pub(crate) type CommandResultConsumer<S> = dyn Fn(&CommandContext<S, BrigadierRuntime>, bool, i32);
8
9/// Whether the current context transforms sources or runs a command.
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub(crate) enum ContextChainStage {
12    Modify,
13    Execute,
14}
15
16/// A flattened sequence of redirect contexts followed by one executable context.
17pub(crate) struct ContextChain<S, R = BrigadierRuntime>
18where
19    R: CommandRuntime<S>,
20{
21    contexts: Arc<[Arc<CommandContext<S, R>>]>,
22    position: usize,
23}
24
25impl<S, R> ContextChain<S, R>
26where
27    R: CommandRuntime<S>,
28{
29    pub(super) fn try_flatten(root: Arc<CommandContext<S, R>>) -> Option<Self> {
30        let mut contexts = Vec::new();
31        let mut current = root;
32        loop {
33            let child = current.child_arc().map(Arc::clone);
34            contexts.push(current);
35            let Some(child) = child else {
36                break;
37            };
38            current = child;
39        }
40
41        if contexts
42            .last()
43            .is_none_or(|context| context.executor().is_none())
44        {
45            return None;
46        }
47        Some(Self {
48            contexts: contexts.into(),
49            position: 0,
50        })
51    }
52
53    /// Returns the kind of work represented by the current stage.
54    pub(crate) fn stage(&self) -> ContextChainStage {
55        if self.position + 1 == self.contexts.len() {
56            ContextChainStage::Execute
57        } else {
58            ContextChainStage::Modify
59        }
60    }
61
62    /// Returns the parsed context at the current stage.
63    pub(crate) fn top_context(&self) -> &CommandContext<S, R> {
64        &self.contexts[self.position]
65    }
66
67    /// Advances to the next redirect or executable stage.
68    pub(crate) fn next_stage(&self) -> Option<Self> {
69        let position = self.position + 1;
70        (position < self.contexts.len()).then(|| Self {
71            contexts: Arc::clone(&self.contexts),
72            position,
73        })
74    }
75}
76
77impl<S> ContextChain<S, BrigadierRuntime> {
78    /// Applies the current stage's source modifier.
79    pub(crate) fn run_modifier(
80        &self,
81        source: Arc<S>,
82        consumer: &CommandResultConsumer<S>,
83        forked: bool,
84    ) -> Result<Vec<Arc<S>>, CommandSyntaxError> {
85        let template = self.top_context();
86        let Some(modifier) = template.modifier() else {
87            return Ok(vec![source]);
88        };
89        let context = template.copy_for(source);
90        match modifier(&context) {
91            Ok(sources) => Ok(sources.into_iter().map(Arc::new).collect()),
92            Err(error) => {
93                consumer(&context, false, 0);
94                if forked { Ok(Vec::new()) } else { Err(error) }
95            }
96        }
97    }
98
99    /// Runs the current stage's terminal command.
100    pub(crate) fn run_executable(
101        &self,
102        source: Arc<S>,
103        consumer: &CommandResultConsumer<S>,
104        forked: bool,
105    ) -> Result<i32, CommandSyntaxError> {
106        let context = self.top_context().copy_for(source);
107        let Some(executor) = context.executor() else {
108            unreachable!("a context chain's final stage is always executable")
109        };
110        match executor(&context) {
111            Ok(result) => {
112                consumer(&context, true, result);
113                Ok(if forked { 1 } else { result })
114            }
115            Err(error) => {
116                consumer(&context, false, 0);
117                if forked { Ok(0) } else { Err(error) }
118            }
119        }
120    }
121
122    /// Executes the complete chain with Brigadier's synchronous semantics.
123    pub(crate) fn execute_all(
124        &self,
125        source: S,
126        consumer: &CommandResultConsumer<S>,
127    ) -> Result<i32, CommandSyntaxError> {
128        let mut stage = self.clone();
129        let mut forked = false;
130        let mut sources = vec![Arc::new(source)];
131
132        while stage.stage() == ContextChainStage::Modify {
133            forked |= stage.top_context().is_forked();
134            let mut next_sources = Vec::new();
135            for source in sources {
136                next_sources.extend(stage.run_modifier(source, consumer, forked)?);
137            }
138            if next_sources.is_empty() {
139                return Ok(0);
140            }
141            sources = next_sources;
142            let Some(next_stage) = stage.next_stage() else {
143                unreachable!("a modifying context chain stage always has a following stage")
144            };
145            stage = next_stage;
146        }
147
148        let mut result = 0_i32;
149        for source in sources {
150            result = result.wrapping_add(stage.run_executable(source, consumer, forked)?);
151        }
152        Ok(result)
153    }
154}
155
156impl<S, R> Clone for ContextChain<S, R>
157where
158    R: CommandRuntime<S>,
159{
160    fn clone(&self) -> Self {
161        Self {
162            contexts: Arc::clone(&self.contexts),
163            position: self.position,
164        }
165    }
166}