1use std::{error::Error, fmt, sync::Arc};
4
5use glam::DVec3;
6use steel_protocol::packets::game::{
7 ArgumentType as ProtocolArgumentType, SuggestionType as ProtocolSuggestionType,
8};
9use steel_utils::{DowncastType, Identifier};
10use text_components::TextComponent;
11
12use super::{
13 brigadier::{
14 ArgumentType, CommandArgumentParser as InternalCommandArgumentParser, CommandNodeBuilder,
15 CommandRequirement, CommandSyntaxError, CommandSyntaxErrorKind, ReaderCursor, StringReader,
16 SuggestionProvider as InternalSuggestionProvider, SuggestionsBuilder,
17 },
18 execution::{
19 CommandArgumentSource, CommandPermissionSource, CommandResultSuspension,
20 CommandResultSuspensionPoll, CommandSource as InternalCommandSource,
21 CommandSuspensionOrder, SteelArgumentParser, SteelArgumentSuggestionContext,
22 SteelArgumentType, SteelCommandContext, SteelCommandRuntime,
23 },
24 incorrectly_typed_argument,
25 registration::{
26 CommandDispatcherBuilder, CommandRegistration as InternalCommandRegistration,
27 CommandRegistrationError as InternalCommandRegistrationError,
28 },
29};
30use crate::command::brigadier::ArgumentSuggestionContext;
31use crate::{
32 entity::SharedEntity,
33 permission::{PermissionExpr, PermissionState},
34 player::Player,
35 server::Server,
36 world::World,
37};
38
39pub struct CommandRegistration {
41 inner: InternalCommandRegistration<InternalCommandSource>,
42}
43
44impl CommandRegistration {
45 pub fn new(id: Identifier, factory: impl FnOnce() -> CommandNode + Send + 'static) -> Self {
47 Self {
48 inner: InternalCommandRegistration::new(id, move |_| factory().inner),
49 }
50 }
51
52 #[must_use]
54 pub fn alias(mut self, alias: impl Into<Box<str>>) -> Self {
55 self.inner = self.inner.alias(alias);
56 self
57 }
58
59 #[must_use]
61 pub fn default_access(mut self) -> Self {
62 self.inner = self.inner.default_access();
63 self
64 }
65
66 #[must_use]
68 pub fn permission(mut self, permission: PermissionExpr) -> Self {
69 self.inner = self.inner.permission(permission);
70 self
71 }
72
73 #[must_use]
75 pub fn subcommand_permission<I, T>(mut self, path: I) -> Self
76 where
77 I: IntoIterator<Item = T>,
78 T: Into<Box<str>>,
79 {
80 self.inner = self.inner.subcommand_permission(path);
81 self
82 }
83}
84
85pub struct CommandRegistry {
87 inner: CommandDispatcherBuilder<InternalCommandSource>,
88}
89
90impl CommandRegistry {
91 #[must_use]
93 pub fn new() -> Self {
94 Self {
95 inner: CommandDispatcherBuilder::new(),
96 }
97 }
98
99 pub fn declare_permission(
101 &mut self,
102 permission: impl Into<String>,
103 ) -> Result<&mut Self, CommandRegistrationError> {
104 self.inner
105 .declare_permission(permission)
106 .map_err(CommandRegistrationError::from)?;
107 Ok(self)
108 }
109
110 pub fn register(
112 &mut self,
113 registration: CommandRegistration,
114 ) -> Result<&mut Self, CommandRegistrationError> {
115 self.inner
116 .register(registration.inner)
117 .map_err(CommandRegistrationError::from)?;
118 Ok(self)
119 }
120
121 pub(crate) fn into_inner(self) -> CommandDispatcherBuilder<InternalCommandSource> {
122 self.inner
123 }
124}
125
126impl Default for CommandRegistry {
127 fn default() -> Self {
128 Self::new()
129 }
130}
131
132#[derive(Debug)]
134pub struct CommandRegistrationError {
135 inner: InternalCommandRegistrationError,
136}
137
138impl From<InternalCommandRegistrationError> for CommandRegistrationError {
139 fn from(inner: InternalCommandRegistrationError) -> Self {
140 Self { inner }
141 }
142}
143
144impl fmt::Display for CommandRegistrationError {
145 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
146 self.inner.fmt(formatter)
147 }
148}
149
150impl Error for CommandRegistrationError {
151 fn source(&self) -> Option<&(dyn Error + 'static)> {
152 Some(&self.inner)
153 }
154}
155
156pub struct CommandNode {
158 inner: CommandNodeBuilder<InternalCommandSource, SteelCommandRuntime>,
159}
160
161impl CommandNode {
162 #[must_use]
164 pub fn literal(name: impl Into<Box<str>>) -> Self {
165 Self {
166 inner: CommandNodeBuilder::literal(name),
167 }
168 }
169
170 #[must_use]
172 pub fn argument(name: impl Into<Box<str>>, argument: CommandArgument) -> Self {
173 Self {
174 inner: CommandNodeBuilder::argument(name, argument.inner),
175 }
176 }
177
178 #[must_use]
180 pub fn then(mut self, child: Self) -> Self {
181 self.inner = self.inner.then(child.inner);
182 self
183 }
184
185 #[must_use]
187 pub fn executes(
188 mut self,
189 executor: impl for<'context> Fn(&CommandContext<'context>) -> Result<i32, CommandError>
190 + Send
191 + Sync
192 + 'static,
193 ) -> Self {
194 self.inner = self.inner.executes(move |context| {
195 executor(&CommandContext { inner: context }).map_err(CommandError::into_inner)
196 });
197 self
198 }
199
200 #[must_use]
202 pub fn executes_suspended<T>(
203 mut self,
204 executor: impl for<'context> Fn(&CommandContext<'context>) -> Result<T, CommandError>
205 + Send
206 + Sync
207 + 'static,
208 ) -> Self
209 where
210 T: SuspendedCommand,
211 {
212 self.inner = self.inner.executes_suspended(move |context| {
213 executor(&CommandContext { inner: context })
214 .map(ExternalCommandSuspension)
215 .map_err(CommandError::into_inner)
216 });
217 self
218 }
219
220 #[must_use]
222 pub fn requires(
223 mut self,
224 requirement: impl for<'source> Fn(CommandSource<'source>) -> bool + Send + Sync + 'static,
225 ) -> Self {
226 self.inner = self
227 .inner
228 .requires(CommandRequirement::contextual(move |source| {
229 requirement(CommandSource { inner: source })
230 }));
231 self
232 }
233
234 #[must_use]
237 pub fn suggests(mut self, suggestion: &'static (impl SuggestionProvider + 'static)) -> Self {
238 self.inner = self.inner.suggests(SuggestionProviderWrapper(suggestion));
239 self
240 }
241}
242
243#[must_use]
245pub fn literal(name: impl Into<Box<str>>) -> CommandNode {
246 CommandNode::literal(name)
247}
248
249#[must_use]
251pub fn argument(name: impl Into<Box<str>>, argument: CommandArgument) -> CommandNode {
252 CommandNode::argument(name, argument)
253}
254
255pub trait SuggestionProvider: Send + Sync {
261 fn list_suggestions(
263 &self,
264 context: &CommandSuggestionContext,
265 builder: &mut SuggestionsBuilder<'_>,
266 );
267}
268
269impl<F> SuggestionProvider for F
272where
273 F: for<'a> Fn(&CommandSuggestionContext, &mut SuggestionsBuilder<'a>) + Send + Sync,
274{
275 fn list_suggestions(
276 &self,
277 context: &CommandSuggestionContext,
278 builder: &mut SuggestionsBuilder<'_>,
279 ) {
280 self(context, builder);
281 }
282}
283
284struct SuggestionProviderWrapper(&'static (dyn SuggestionProvider + 'static));
285
286impl InternalSuggestionProvider<InternalCommandSource, SteelArgumentType>
287 for SuggestionProviderWrapper
288{
289 fn list_suggestions(
290 &self,
291 context: &ArgumentSuggestionContext<
292 '_,
293 InternalCommandSource,
294 <SteelArgumentType as InternalCommandArgumentParser<InternalCommandSource>>::Value,
295 >,
296 builder: &mut SuggestionsBuilder<'_>,
297 ) {
298 SuggestionProvider::list_suggestions(
299 self.0,
300 &CommandSuggestionContext { inner: context },
301 builder,
302 );
303 }
304}
305
306#[derive(Clone, Copy)]
308pub struct CommandContext<'context> {
309 inner: &'context SteelCommandContext<InternalCommandSource>,
310}
311
312impl<'context> CommandContext<'context> {
313 #[must_use]
315 pub fn source(self) -> CommandSource<'context> {
316 CommandSource {
317 inner: self.inner.source(),
318 }
319 }
320
321 pub fn value<T: DowncastType>(self, name: &str) -> Result<&'context T, CommandError> {
323 self.inner
324 .argument(name)?
325 .downcast_ref::<T>()
326 .ok_or_else(|| incorrectly_typed_argument(name))
327 .map_err(CommandError::from)
328 }
329
330 pub fn boolean(self, name: &str) -> Result<bool, CommandError> {
332 self.inner.boolean(name).map_err(CommandError::from)
333 }
334
335 pub fn integer(self, name: &str) -> Result<i32, CommandError> {
337 self.inner.integer(name).map_err(CommandError::from)
338 }
339
340 pub fn long(self, name: &str) -> Result<i64, CommandError> {
342 self.inner.long(name).map_err(CommandError::from)
343 }
344
345 pub fn float(self, name: &str) -> Result<f32, CommandError> {
347 self.inner.float(name).map_err(CommandError::from)
348 }
349
350 pub fn double(self, name: &str) -> Result<f64, CommandError> {
352 self.inner.double(name).map_err(CommandError::from)
353 }
354
355 pub fn string(self, name: &str) -> Result<&'context str, CommandError> {
357 self.inner.string(name).map_err(CommandError::from)
358 }
359
360 pub fn domain(self, name: &str) -> Result<&'context str, CommandError> {
362 self.inner.domain(name).map_err(CommandError::from)
363 }
364
365 pub fn world(self, name: &str) -> Result<Arc<World>, CommandError> {
367 let world = self.inner.world_argument(name)?;
368 world
369 .resolve(self.inner.source())
370 .map_err(CommandError::from)
371 }
372
373 pub fn players(self, name: &str) -> Result<Vec<Arc<Player>>, CommandError> {
375 self.inner.players(name).map_err(CommandError::from)
376 }
377
378 pub fn player(self, name: &str) -> Result<Arc<Player>, CommandError> {
380 self.inner.player(name).map_err(CommandError::from)
381 }
382
383 pub fn entities(self, name: &str) -> Result<Vec<SharedEntity>, CommandError> {
385 self.inner.entities(name).map_err(CommandError::from)
386 }
387
388 pub fn entity(self, name: &str) -> Result<SharedEntity, CommandError> {
390 self.inner.entity(name).map_err(CommandError::from)
391 }
392}
393
394#[derive(Clone, Copy)]
396pub struct CommandSource<'source> {
397 inner: &'source InternalCommandSource,
398}
399
400impl<'source> CommandSource<'source> {
401 #[must_use]
403 pub const fn player(self) -> Option<&'source Arc<Player>> {
404 self.inner.player()
405 }
406
407 #[must_use]
409 pub const fn entity(self) -> Option<&'source SharedEntity> {
410 self.inner.entity()
411 }
412
413 #[must_use]
415 pub const fn world(self) -> &'source Arc<World> {
416 self.inner.world()
417 }
418
419 #[must_use]
421 pub const fn server(self) -> &'source Arc<Server> {
422 self.inner.server()
423 }
424
425 #[must_use]
427 pub const fn position(self) -> DVec3 {
428 self.inner.position()
429 }
430
431 #[must_use]
433 pub const fn rotation(self) -> (f32, f32) {
434 self.inner.rotation()
435 }
436
437 #[must_use]
439 pub fn permission_state(self, permission: &PermissionExpr) -> Option<PermissionState> {
440 CommandPermissionSource::permission_state(self.inner, permission)
441 }
442
443 pub fn send_success(self, message: &TextComponent, broadcast_to_admins: bool) {
445 self.inner.send_success(message, broadcast_to_admins);
446 }
447
448 pub fn send_failure(self, message: TextComponent) {
450 self.inner.send_failure(message);
451 }
452}
453
454#[derive(Debug)]
456pub struct CommandError {
457 inner: CommandSyntaxError,
458}
459
460impl CommandError {
461 #[must_use]
463 pub fn new(message: impl Into<TextComponent>) -> Self {
464 Self {
465 inner: CommandSyntaxError::dynamic(message),
466 }
467 }
468
469 fn into_inner(self) -> CommandSyntaxError {
470 self.inner
471 }
472}
473
474impl From<CommandSyntaxError> for CommandError {
475 fn from(inner: CommandSyntaxError) -> Self {
476 Self { inner }
477 }
478}
479
480impl From<String> for CommandError {
481 fn from(message: String) -> Self {
482 Self::new(TextComponent::plain(message))
483 }
484}
485
486impl From<&str> for CommandError {
487 fn from(message: &str) -> Self {
488 Self::new(TextComponent::plain(message.to_owned()))
489 }
490}
491
492impl From<TextComponent> for CommandError {
493 fn from(message: TextComponent) -> Self {
494 Self::new(message)
495 }
496}
497
498impl fmt::Display for CommandError {
499 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
500 self.inner.fmt(formatter)
501 }
502}
503
504impl Error for CommandError {
505 fn source(&self) -> Option<&(dyn Error + 'static)> {
506 Some(&self.inner)
507 }
508}
509
510pub struct CommandArgument {
512 inner: SteelArgumentType,
513}
514
515impl CommandArgument {
516 #[must_use]
518 pub fn boolean() -> Self {
519 Self::primitive(ArgumentType::bool())
520 }
521
522 #[must_use]
524 pub fn integer(minimum: i32, maximum: i32) -> Self {
525 Self::primitive(ArgumentType::integer(minimum, maximum))
526 }
527
528 #[must_use]
530 pub fn long(minimum: i64, maximum: i64) -> Self {
531 Self::primitive(ArgumentType::long(minimum, maximum))
532 }
533
534 #[must_use]
536 pub fn float(minimum: f32, maximum: f32) -> Self {
537 Self::primitive(ArgumentType::float(minimum, maximum))
538 }
539
540 #[must_use]
542 pub fn double(minimum: f64, maximum: f64) -> Self {
543 Self::primitive(ArgumentType::double(minimum, maximum))
544 }
545
546 #[must_use]
548 pub fn word() -> Self {
549 Self::primitive(ArgumentType::word())
550 }
551
552 #[must_use]
554 pub fn string() -> Self {
555 Self::primitive(ArgumentType::string())
556 }
557
558 #[must_use]
560 pub fn greedy_string() -> Self {
561 Self::primitive(ArgumentType::greedy_string())
562 }
563
564 #[must_use]
566 pub fn entity() -> Self {
567 Self {
568 inner: SteelArgumentType::entity(),
569 }
570 }
571
572 #[must_use]
574 pub fn entities() -> Self {
575 Self {
576 inner: SteelArgumentType::entities(),
577 }
578 }
579
580 #[must_use]
582 pub fn player() -> Self {
583 Self {
584 inner: SteelArgumentType::player(),
585 }
586 }
587
588 #[must_use]
590 pub fn players() -> Self {
591 Self {
592 inner: SteelArgumentType::players(),
593 }
594 }
595
596 #[must_use]
598 pub fn domain() -> Self {
599 Self {
600 inner: SteelArgumentType::domain(),
601 }
602 }
603
604 #[must_use]
606 pub fn world() -> Self {
607 Self {
608 inner: SteelArgumentType::world(),
609 }
610 }
611
612 #[must_use]
614 pub fn custom<P>(parser: P) -> Self
615 where
616 P: CommandArgumentParser,
617 {
618 Self {
619 inner: SteelArgumentType::new(parser),
620 }
621 }
622
623 fn primitive(argument: ArgumentType) -> Self {
624 Self {
625 inner: SteelArgumentType::from(argument),
626 }
627 }
628}
629
630pub trait CommandArgumentParser:
632 DowncastType + fmt::Debug + PartialEq + Send + Sync + 'static
633{
634 type Value: DowncastType + fmt::Debug + Send + Sync + 'static;
636
637 fn parse(
639 &self,
640 reader: &mut CommandReader<'_, '_>,
641 source: CommandParserSource<'_>,
642 ) -> Result<Self::Value, CommandError>;
643
644 fn list_suggestions(
646 &self,
647 _context: CommandSuggestionContext<'_>,
648 _suggestions: &mut CommandSuggestions<'_, '_>,
649 ) {
650 }
651
652 fn protocol_argument(&self) -> (ProtocolArgumentType, Option<ProtocolSuggestionType>);
654}
655
656impl<P> SteelArgumentParser for P
657where
658 P: CommandArgumentParser,
659{
660 type Value = P::Value;
661
662 fn parse(
663 &self,
664 reader: &mut StringReader<'_>,
665 source: &dyn CommandArgumentSource,
666 ) -> Result<Self::Value, CommandSyntaxError> {
667 CommandArgumentParser::parse(
668 self,
669 &mut CommandReader { inner: reader },
670 CommandParserSource { inner: source },
671 )
672 .map_err(CommandError::into_inner)
673 }
674
675 fn list_suggestions(
676 &self,
677 context: &dyn SteelArgumentSuggestionContext,
678 builder: &mut SuggestionsBuilder<'_>,
679 ) {
680 CommandArgumentParser::list_suggestions(
681 self,
682 CommandSuggestionContext { inner: context },
683 &mut CommandSuggestions { inner: builder },
684 );
685 }
686
687 fn protocol_argument(&self) -> (ProtocolArgumentType, Option<ProtocolSuggestionType>) {
688 CommandArgumentParser::protocol_argument(self)
689 }
690}
691
692#[derive(Clone, Copy)]
694pub struct CommandReaderCursor(ReaderCursor);
695
696pub struct CommandReader<'reader, 'input> {
698 inner: &'reader mut StringReader<'input>,
699}
700
701impl<'input> CommandReader<'_, 'input> {
702 #[must_use]
704 pub const fn input(&self) -> &'input str {
705 self.inner.input()
706 }
707
708 #[must_use]
710 pub const fn cursor(&self) -> usize {
711 self.inner.byte_cursor()
712 }
713
714 #[must_use]
716 pub fn remaining(&self) -> &'input str {
717 self.inner.remaining()
718 }
719
720 #[must_use]
722 pub fn peek(&self) -> Option<char> {
723 self.inner.peek()
724 }
725
726 pub fn read(&mut self) -> Option<char> {
728 self.inner.read()
729 }
730
731 pub fn skip_whitespace(&mut self) {
733 self.inner.skip_whitespace();
734 }
735
736 pub fn read_unquoted_string(&mut self) -> &'input str {
738 self.inner.read_unquoted_string()
739 }
740
741 pub fn read_string(&mut self) -> Result<String, CommandError> {
743 self.inner.read_string().map_err(CommandError::from)
744 }
745
746 pub fn read_integer(&mut self) -> Result<i32, CommandError> {
748 self.inner.read_int().map_err(CommandError::from)
749 }
750
751 pub fn read_long(&mut self) -> Result<i64, CommandError> {
753 self.inner.read_long().map_err(CommandError::from)
754 }
755
756 pub fn read_float(&mut self) -> Result<f32, CommandError> {
758 self.inner.read_float().map_err(CommandError::from)
759 }
760
761 pub fn read_double(&mut self) -> Result<f64, CommandError> {
763 self.inner.read_double().map_err(CommandError::from)
764 }
765
766 pub fn read_boolean(&mut self) -> Result<bool, CommandError> {
768 self.inner.read_boolean().map_err(CommandError::from)
769 }
770
771 pub fn expect(&mut self, expected: char) -> Result<(), CommandError> {
773 self.inner.expect(expected).map_err(CommandError::from)
774 }
775
776 #[must_use]
778 pub const fn checkpoint(&self) -> CommandReaderCursor {
779 CommandReaderCursor(self.inner.checkpoint())
780 }
781
782 pub const fn restore(&mut self, checkpoint: CommandReaderCursor) {
784 self.inner.restore(checkpoint.0);
785 }
786
787 #[must_use]
789 pub fn error(&self, message: impl Into<TextComponent>) -> CommandError {
790 CommandError::from(
791 self.inner
792 .error(CommandSyntaxErrorKind::Dynamic(Box::new(message.into()))),
793 )
794 }
795}
796
797#[cfg(test)]
798mod tests {
799 use super::CommandReader;
800 use crate::command::brigadier::StringReader;
801
802 #[test]
803 fn command_reader_cursor_is_a_utf8_byte_offset() {
804 let mut inner = StringReader::new("é🦀");
805 let mut reader = CommandReader { inner: &mut inner };
806
807 assert_eq!(reader.read(), Some('é'));
808 assert_eq!(reader.cursor(), 2);
809 assert_eq!(reader.read(), Some('🦀'));
810 assert_eq!(reader.cursor(), 6);
811 assert_eq!(&reader.input()[..reader.cursor()], "é🦀");
812 }
813}
814
815#[derive(Clone, Copy)]
817pub struct CommandParserSource<'source> {
818 inner: &'source dyn CommandArgumentSource,
819}
820
821impl CommandParserSource<'_> {
822 #[must_use]
824 pub fn domain_names(&self) -> Vec<&str> {
825 self.inner.domain_names()
826 }
827
828 #[must_use]
830 pub fn world_names(&self) -> Vec<String> {
831 self.inner.command_world_names()
832 }
833
834 #[must_use]
836 pub fn player_names(&self) -> Vec<String> {
837 self.inner.selector_player_names()
838 }
839
840 #[must_use]
842 pub fn permission_group_names(&self) -> Vec<String> {
843 self.inner.permission_group_names()
844 }
845}
846
847#[derive(Clone, Copy)]
849pub struct CommandSuggestionContext<'context> {
850 inner: &'context dyn SteelArgumentSuggestionContext,
851}
852
853impl<'context> CommandSuggestionContext<'context> {
854 #[must_use]
856 pub fn source(&self) -> CommandParserSource<'context> {
857 CommandParserSource {
858 inner: self.inner.source(),
859 }
860 }
861
862 pub fn value<T: DowncastType>(&self, name: &str) -> Result<&'context T, CommandError> {
864 self.inner
865 .argument(name)?
866 .downcast_ref::<T>()
867 .ok_or_else(|| incorrectly_typed_argument(name))
868 .map_err(CommandError::from)
869 }
870}
871
872pub struct CommandSuggestions<'builder, 'input> {
874 inner: &'builder mut SuggestionsBuilder<'input>,
875}
876
877impl CommandSuggestions<'_, '_> {
878 #[must_use]
880 pub fn remaining(&self) -> &str {
881 self.inner.remaining()
882 }
883
884 pub fn suggest(&mut self, text: impl Into<Box<str>>) {
886 self.inner.suggest(text);
887 }
888
889 pub fn suggest_with_tooltip(&mut self, text: impl Into<Box<str>>, tooltip: TextComponent) {
891 self.inner.suggest_with_tooltip(text, tooltip);
892 }
893
894 pub fn suggest_integer(&mut self, value: i32) {
896 self.inner.suggest_integer(value);
897 }
898}
899
900pub enum SuspendedCommandPoll {
902 Pending,
904 Ready(Result<i32, CommandError>),
906}
907
908pub trait SuspendedCommand: Send + 'static {
910 fn order(&self) -> CommandSuspensionOrder {
912 CommandSuspensionOrder::Source
913 }
914
915 fn poll(&mut self) -> SuspendedCommandPoll;
917
918 fn cancel(&mut self) {}
920}
921
922struct ExternalCommandSuspension<T>(T);
923
924impl<T> CommandResultSuspension for ExternalCommandSuspension<T>
925where
926 T: SuspendedCommand,
927{
928 fn order(&self) -> CommandSuspensionOrder {
929 self.0.order()
930 }
931
932 fn poll(&mut self) -> CommandResultSuspensionPoll {
933 match self.0.poll() {
934 SuspendedCommandPoll::Pending => CommandResultSuspensionPoll::Pending,
935 SuspendedCommandPoll::Ready(result) => {
936 CommandResultSuspensionPoll::Ready(result.map_err(CommandError::into_inner))
937 }
938 }
939 }
940
941 fn cancel(&mut self) {
942 self.0.cancel();
943 }
944}