steel_core/command/brigadier/
context_chain.rs1use 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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub(crate) enum ContextChainStage {
12 Modify,
13 Execute,
14}
15
16pub(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 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 pub(crate) fn top_context(&self) -> &CommandContext<S, R> {
64 &self.contexts[self.position]
65 }
66
67 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 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 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 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}