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