1use std::{fmt, sync::Arc};
4
5use thiserror::Error;
6
7use super::{BrigadierRuntime, CommandRuntime};
8
9type RequirementPredicate<S> = Arc<dyn Fn(&S) -> bool + Send + Sync>;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub(crate) struct NodeId {
14 pub(super) dispatcher: u64,
15 pub(super) index: usize,
16}
17
18impl NodeId {
19 pub(super) const fn new(dispatcher: u64, index: usize) -> Self {
20 Self { dispatcher, index }
21 }
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26pub(crate) enum CommandRedirectTarget {
27 Node(NodeId),
29 CommandRoot,
31}
32
33impl From<NodeId> for CommandRedirectTarget {
34 fn from(value: NodeId) -> Self {
35 Self::Node(value)
36 }
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub(crate) enum NodeKind {
42 Root,
44 Literal,
46 Argument,
48}
49
50pub(crate) struct CommandRequirement<S> {
52 predicate: Option<RequirementPredicate<S>>,
53 kind: Option<CommandRequirementKind>,
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub(crate) enum CommandRequirementKind {
59 Authorization,
61 Context,
63}
64
65impl<S> CommandRequirement<S> {
66 pub(crate) const fn allow_all() -> Self {
68 Self {
69 predicate: None,
70 kind: None,
71 }
72 }
73
74 pub(crate) fn authorization(predicate: impl Fn(&S) -> bool + Send + Sync + 'static) -> Self {
76 Self {
77 predicate: Some(Arc::new(predicate)),
78 kind: Some(CommandRequirementKind::Authorization),
79 }
80 }
81
82 pub(crate) fn contextual(predicate: impl Fn(&S) -> bool + Send + Sync + 'static) -> Self {
84 Self {
85 predicate: Some(Arc::new(predicate)),
86 kind: Some(CommandRequirementKind::Context),
87 }
88 }
89
90 pub(crate) fn allows(&self, source: &S) -> bool {
92 self.predicate
93 .as_ref()
94 .is_none_or(|predicate| predicate(source))
95 }
96
97 pub(crate) const fn is_authorization(&self) -> bool {
99 matches!(self.kind, Some(CommandRequirementKind::Authorization))
100 }
101
102 pub(super) fn and(self, other: Self) -> Self
103 where
104 S: 'static,
105 {
106 let kind = match (self.kind, other.kind) {
107 (Some(CommandRequirementKind::Authorization), _)
108 | (_, Some(CommandRequirementKind::Authorization)) => {
109 Some(CommandRequirementKind::Authorization)
110 }
111 (Some(CommandRequirementKind::Context), _)
112 | (_, Some(CommandRequirementKind::Context)) => Some(CommandRequirementKind::Context),
113 (None, None) => None,
114 };
115 let predicate = match (self.predicate, other.predicate) {
116 (None, None) => None,
117 (Some(predicate), None) | (None, Some(predicate)) => Some(predicate),
118 (Some(first), Some(second)) => {
119 let combined: RequirementPredicate<S> =
120 Arc::new(move |source| first(source) && second(source));
121 Some(combined)
122 }
123 };
124 Self { predicate, kind }
125 }
126
127 pub(super) fn is_compatible_with(&self, other: &Self) -> bool {
128 self.kind == other.kind
129 && match (&self.predicate, &other.predicate) {
130 (None, None) => true,
131 (Some(first), Some(second)) => Arc::ptr_eq(first, second),
132 (None, Some(_)) | (Some(_), None) => false,
133 }
134 }
135}
136
137impl<S> Clone for CommandRequirement<S> {
138 fn clone(&self) -> Self {
139 Self {
140 predicate: self.predicate.as_ref().map(Arc::clone),
141 kind: self.kind,
142 }
143 }
144}
145
146impl<S> fmt::Debug for CommandRequirement<S> {
147 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
148 formatter
149 .debug_struct("CommandRequirement")
150 .field(
151 "predicate",
152 &self.predicate.as_ref().map(|_| "<source predicate>"),
153 )
154 .field("kind", &self.kind)
155 .finish()
156 }
157}
158
159pub(super) struct CommandRedirect<S, R = BrigadierRuntime>
160where
161 R: CommandRuntime<S>,
162{
163 pub(super) target: CommandRedirectTarget,
164 pub(super) modifier: Option<Arc<R::Modifier>>,
165 pub(super) forks: bool,
166}
167
168impl<S, R> CommandRedirect<S, R>
169where
170 R: CommandRuntime<S>,
171{
172 pub(super) const fn identity(target: CommandRedirectTarget) -> Self {
173 Self {
174 target,
175 modifier: None,
176 forks: false,
177 }
178 }
179
180 pub(super) const fn with_modifier(
181 target: CommandRedirectTarget,
182 modifier: Arc<R::Modifier>,
183 forks: bool,
184 ) -> Self {
185 Self {
186 target,
187 modifier: Some(modifier),
188 forks,
189 }
190 }
191
192 fn resolve_command_root(&mut self, command_root: NodeId) {
193 if self.target == CommandRedirectTarget::CommandRoot {
194 self.target = CommandRedirectTarget::Node(command_root);
195 }
196 }
197
198 fn is_compatible_with(&self, other: &Self) -> bool {
199 self.target == other.target
200 && self.forks == other.forks
201 && match (&self.modifier, &other.modifier) {
202 (None, None) => true,
203 (Some(first), Some(second)) => Arc::ptr_eq(first, second),
204 (None, Some(_)) | (Some(_), None) => false,
205 }
206 }
207}
208
209impl<S, R> Clone for CommandRedirect<S, R>
210where
211 R: CommandRuntime<S>,
212{
213 fn clone(&self) -> Self {
214 Self {
215 target: self.target,
216 modifier: self.modifier.as_ref().map(Arc::clone),
217 forks: self.forks,
218 }
219 }
220}
221
222#[derive(Clone)]
223pub(super) enum CommandNodeData<A> {
224 Root,
225 Literal(Box<str>),
226 Argument { name: Box<str>, argument_type: A },
227}
228
229impl<A> CommandNodeData<A> {
230 pub(super) fn name(&self) -> &str {
231 match self {
232 Self::Root => "",
233 Self::Literal(name) | Self::Argument { name, .. } => name,
234 }
235 }
236
237 pub(super) const fn kind(&self) -> NodeKind {
238 match self {
239 Self::Root => NodeKind::Root,
240 Self::Literal(_) => NodeKind::Literal,
241 Self::Argument { .. } => NodeKind::Argument,
242 }
243 }
244}
245
246impl<A> CommandNodeData<A>
247where
248 A: PartialEq,
249{
250 fn collision_with(&self, other: &Self) -> Option<RegistrationErrorKind> {
251 let name = other.name().into();
252 match (self, other) {
253 (Self::Literal(first), Self::Literal(second)) if first == second => None,
254 (
255 Self::Argument {
256 name: first_name,
257 argument_type: first_type,
258 },
259 Self::Argument {
260 name: second_name,
261 argument_type: second_type,
262 },
263 ) if first_name == second_name && first_type == second_type => None,
264 (Self::Argument { name: first, .. }, Self::Argument { name: second, .. })
265 if first == second =>
266 {
267 Some(RegistrationErrorKind::ArgumentTypeCollision { name })
268 }
269 _ => Some(RegistrationErrorKind::NodeKindCollision {
270 name,
271 existing: self.kind(),
272 incoming: other.kind(),
273 }),
274 }
275 }
276}
277
278pub(crate) struct CommandNode<S, R = BrigadierRuntime>
280where
281 R: CommandRuntime<S>,
282{
283 pub(super) data: CommandNodeData<R::Argument>,
284 pub(super) children: Vec<NodeId>,
285 pub(super) executor: Option<Arc<R::Executor>>,
286 pub(super) requirement: CommandRequirement<S>,
287 pub(super) execution_requirement: CommandRequirement<S>,
288 pub(super) redirect: Option<CommandRedirect<S, R>>,
289}
290
291impl<S, R> CommandNode<S, R>
292where
293 R: CommandRuntime<S>,
294{
295 pub(super) const fn root() -> Self {
296 Self {
297 data: CommandNodeData::Root,
298 children: Vec::new(),
299 executor: None,
300 requirement: CommandRequirement::allow_all(),
301 execution_requirement: CommandRequirement::allow_all(),
302 redirect: None,
303 }
304 }
305
306 pub(crate) fn name(&self) -> &str {
308 self.data.name()
309 }
310
311 pub(crate) const fn is_executable(&self) -> bool {
313 self.executor.is_some()
314 }
315
316 pub(crate) fn can_execute(&self, source: &S) -> bool {
318 self.executor.is_some()
319 && self.requirement.allows(source)
320 && self.execution_requirement.allows(source)
321 }
322
323 pub(crate) fn redirect(&self) -> Option<NodeId> {
325 self.redirect
326 .as_ref()
327 .map(|redirect| match redirect.target {
328 CommandRedirectTarget::Node(target) => target,
329 CommandRedirectTarget::CommandRoot => {
330 unreachable!("registered command redirects have concrete targets")
331 }
332 })
333 }
334
335 pub(crate) fn is_forked_redirect(&self) -> bool {
337 self.redirect
338 .as_ref()
339 .is_some_and(|redirect| redirect.forks)
340 }
341
342 pub(crate) fn has_redirect_modifier(&self) -> bool {
344 self.redirect
345 .as_ref()
346 .is_some_and(|redirect| redirect.modifier.is_some())
347 }
348
349 pub(crate) const fn kind(&self) -> NodeKind {
351 self.data.kind()
352 }
353
354 pub(crate) const fn argument_type(&self) -> Option<&R::Argument> {
356 match &self.data {
357 CommandNodeData::Argument { argument_type, .. } => Some(argument_type),
358 CommandNodeData::Root | CommandNodeData::Literal(_) => None,
359 }
360 }
361
362 pub(crate) fn allows(&self, source: &S) -> bool {
364 self.requirement.allows(source)
365 }
366
367 pub(crate) const fn is_restricted(&self) -> bool {
369 self.requirement.is_authorization() || self.execution_requirement.is_authorization()
370 }
371
372 pub(super) fn validate_compatible(
373 &self,
374 incoming: &UnregisteredCommandNode<S, R>,
375 ) -> Result<(), RegistrationError> {
376 if let Some(kind) = self.data.collision_with(&incoming.data) {
377 return Err(RegistrationError::new(kind));
378 }
379 if !self.requirement.is_compatible_with(&incoming.requirement) {
380 return Err(RegistrationError::new(
381 RegistrationErrorKind::RequirementCollision {
382 name: incoming.name().into(),
383 },
384 ));
385 }
386 if !self
387 .execution_requirement
388 .is_compatible_with(&incoming.execution_requirement)
389 {
390 return Err(RegistrationError::new(
391 RegistrationErrorKind::RequirementCollision {
392 name: incoming.name().into(),
393 },
394 ));
395 }
396 if !redirects_are_compatible(self.redirect.as_ref(), incoming.redirect.as_ref()) {
397 return Err(RegistrationError::new(
398 RegistrationErrorKind::RedirectCollision {
399 name: incoming.name().into(),
400 },
401 ));
402 }
403 Ok(())
404 }
405}
406
407pub(super) struct UnregisteredCommandNode<S, R = BrigadierRuntime>
408where
409 R: CommandRuntime<S>,
410{
411 pub(super) data: CommandNodeData<R::Argument>,
412 pub(super) children: Vec<Self>,
413 pub(super) executor: Option<Arc<R::Executor>>,
414 pub(super) requirement: CommandRequirement<S>,
415 pub(super) execution_requirement: CommandRequirement<S>,
416 pub(super) redirect: Option<CommandRedirect<S, R>>,
417}
418
419impl<S, R> UnregisteredCommandNode<S, R>
420where
421 R: CommandRuntime<S>,
422{
423 pub(super) fn name(&self) -> &str {
424 self.data.name()
425 }
426
427 pub(super) const fn kind(&self) -> NodeKind {
428 self.data.kind()
429 }
430
431 pub(super) fn resolve_command_root(&mut self, command_root: NodeId) {
432 if let Some(redirect) = &mut self.redirect {
433 redirect.resolve_command_root(command_root);
434 }
435 for child in &mut self.children {
436 child.resolve_command_root(command_root);
437 }
438 }
439
440 pub(super) fn merge(&mut self, mut incoming: Self) -> Result<(), RegistrationError> {
441 self.validate_compatible(&incoming)?;
442 if incoming.executor.is_some() {
443 self.executor = incoming.executor.take();
444 }
445 for child in incoming.children {
446 merge_or_push(&mut self.children, child)?;
447 }
448 Ok(())
449 }
450
451 fn validate_compatible(&self, incoming: &Self) -> Result<(), RegistrationError> {
452 if let Some(kind) = self.data.collision_with(&incoming.data) {
453 return Err(RegistrationError::new(kind));
454 }
455 if !self.requirement.is_compatible_with(&incoming.requirement) {
456 return Err(RegistrationError::new(
457 RegistrationErrorKind::RequirementCollision {
458 name: incoming.name().into(),
459 },
460 ));
461 }
462 if !self
463 .execution_requirement
464 .is_compatible_with(&incoming.execution_requirement)
465 {
466 return Err(RegistrationError::new(
467 RegistrationErrorKind::RequirementCollision {
468 name: incoming.name().into(),
469 },
470 ));
471 }
472 if !redirects_are_compatible(self.redirect.as_ref(), incoming.redirect.as_ref()) {
473 return Err(RegistrationError::new(
474 RegistrationErrorKind::RedirectCollision {
475 name: incoming.name().into(),
476 },
477 ));
478 }
479 Ok(())
480 }
481}
482
483fn redirects_are_compatible<S, R>(
484 first: Option<&CommandRedirect<S, R>>,
485 second: Option<&CommandRedirect<S, R>>,
486) -> bool
487where
488 R: CommandRuntime<S>,
489{
490 match (first, second) {
491 (None, None) => true,
492 (Some(first), Some(second)) => first.is_compatible_with(second),
493 (None, Some(_)) | (Some(_), None) => false,
494 }
495}
496
497pub(super) fn merge_or_push<S, R>(
498 nodes: &mut Vec<UnregisteredCommandNode<S, R>>,
499 incoming: UnregisteredCommandNode<S, R>,
500) -> Result<(), RegistrationError>
501where
502 R: CommandRuntime<S>,
503{
504 let Some(existing) = nodes
505 .iter_mut()
506 .find(|existing| existing.name() == incoming.name())
507 else {
508 nodes.push(incoming);
509 return Ok(());
510 };
511 existing.merge(incoming)
512}
513
514#[derive(Debug, Error)]
516#[error("{kind}")]
517pub(crate) struct RegistrationError {
518 kind: RegistrationErrorKind,
519}
520
521impl RegistrationError {
522 pub(super) const fn new(kind: RegistrationErrorKind) -> Self {
523 Self { kind }
524 }
525
526 pub(crate) const fn kind(&self) -> &RegistrationErrorKind {
528 &self.kind
529 }
530}
531
532#[derive(Clone, Debug, Error, PartialEq, Eq)]
534pub(crate) enum RegistrationErrorKind {
535 #[error("only literal command nodes can be registered at the root")]
537 ArgumentRoot,
538 #[error("command node '{name}' is already registered as {existing:?}, not {incoming:?}")]
540 NodeKindCollision {
541 name: Box<str>,
542 existing: NodeKind,
543 incoming: NodeKind,
544 },
545 #[error("argument node '{name}' is already registered with a different parser")]
547 ArgumentTypeCollision { name: Box<str> },
548 #[error("command node '{name}' is already registered with a different requirement")]
550 RequirementCollision { name: Box<str> },
551 #[error("command node '{name}' is already registered with a different redirect")]
553 RedirectCollision { name: Box<str> },
554 #[error("redirected command node '{name}' cannot have children")]
556 RedirectWithChildren { name: Box<str> },
557 #[error("redirect target {target:?} does not belong to this dispatcher")]
559 InvalidRedirectTarget { target: NodeId },
560}