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, CommandNodeBuilder, CommandRequirement, CommandSyntaxError,
15 CommandSyntaxErrorKind, ReaderCursor, StringReader, SuggestionsBuilder,
16 },
17 execution::{
18 CommandArgumentSource, CommandPermissionSource, CommandResultSuspension,
19 CommandResultSuspensionPoll, CommandSource as InternalCommandSource,
20 CommandSuspensionOrder, SteelArgumentParser, SteelArgumentSuggestionContext,
21 SteelArgumentType, SteelCommandContext, SteelCommandRuntime,
22 },
23 registration::{
24 CommandDispatcherBuilder, CommandRegistration as InternalCommandRegistration,
25 CommandRegistrationError as InternalCommandRegistrationError,
26 },
27};
28use crate::{
29 entity::SharedEntity,
30 permission::{PermissionExpr, PermissionState},
31 player::Player,
32 server::Server,
33 world::World,
34};
35
36pub struct CommandRegistration {
38 inner: InternalCommandRegistration<InternalCommandSource>,
39}
40
41impl CommandRegistration {
42 pub fn new(id: Identifier, factory: impl FnOnce() -> CommandNode + Send + 'static) -> Self {
44 Self {
45 inner: InternalCommandRegistration::new(id, move |_| factory().inner),
46 }
47 }
48
49 #[must_use]
51 pub fn alias(mut self, alias: impl Into<Box<str>>) -> Self {
52 self.inner = self.inner.alias(alias);
53 self
54 }
55
56 #[must_use]
58 pub fn default_access(mut self) -> Self {
59 self.inner = self.inner.default_access();
60 self
61 }
62
63 #[must_use]
65 pub fn permission(mut self, permission: PermissionExpr) -> Self {
66 self.inner = self.inner.permission(permission);
67 self
68 }
69
70 #[must_use]
72 pub fn subcommand_permission<I, T>(mut self, path: I) -> Self
73 where
74 I: IntoIterator<Item = T>,
75 T: Into<Box<str>>,
76 {
77 self.inner = self.inner.subcommand_permission(path);
78 self
79 }
80}
81
82pub struct CommandRegistry {
84 inner: CommandDispatcherBuilder<InternalCommandSource>,
85}
86
87impl CommandRegistry {
88 #[must_use]
90 pub fn new() -> Self {
91 Self {
92 inner: CommandDispatcherBuilder::new(),
93 }
94 }
95
96 pub fn declare_permission(
98 &mut self,
99 permission: impl Into<String>,
100 ) -> Result<&mut Self, CommandRegistrationError> {
101 self.inner
102 .declare_permission(permission)
103 .map_err(CommandRegistrationError::from)?;
104 Ok(self)
105 }
106
107 pub fn register(
109 &mut self,
110 registration: CommandRegistration,
111 ) -> Result<&mut Self, CommandRegistrationError> {
112 self.inner
113 .register(registration.inner)
114 .map_err(CommandRegistrationError::from)?;
115 Ok(self)
116 }
117
118 pub(crate) fn into_inner(self) -> CommandDispatcherBuilder<InternalCommandSource> {
119 self.inner
120 }
121}
122
123impl Default for CommandRegistry {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129#[derive(Debug)]
131pub struct CommandRegistrationError {
132 inner: InternalCommandRegistrationError,
133}
134
135impl From<InternalCommandRegistrationError> for CommandRegistrationError {
136 fn from(inner: InternalCommandRegistrationError) -> Self {
137 Self { inner }
138 }
139}
140
141impl fmt::Display for CommandRegistrationError {
142 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
143 self.inner.fmt(formatter)
144 }
145}
146
147impl Error for CommandRegistrationError {
148 fn source(&self) -> Option<&(dyn Error + 'static)> {
149 Some(&self.inner)
150 }
151}
152
153pub struct CommandNode {
155 inner: CommandNodeBuilder<InternalCommandSource, SteelCommandRuntime>,
156}
157
158impl CommandNode {
159 #[must_use]
161 pub fn literal(name: impl Into<Box<str>>) -> Self {
162 Self {
163 inner: CommandNodeBuilder::literal(name),
164 }
165 }
166
167 #[must_use]
169 pub fn argument(name: impl Into<Box<str>>, argument: CommandArgument) -> Self {
170 Self {
171 inner: CommandNodeBuilder::argument(name, argument.inner),
172 }
173 }
174
175 #[must_use]
177 pub fn then(mut self, child: Self) -> Self {
178 self.inner = self.inner.then(child.inner);
179 self
180 }
181
182 #[must_use]
184 pub fn executes(
185 mut self,
186 executor: impl for<'context> Fn(&CommandContext<'context>) -> Result<i32, CommandError>
187 + Send
188 + Sync
189 + 'static,
190 ) -> Self {
191 self.inner = self.inner.executes(move |context| {
192 executor(&CommandContext { inner: context }).map_err(CommandError::into_inner)
193 });
194 self
195 }
196
197 #[must_use]
199 pub fn executes_suspended<T>(
200 mut self,
201 executor: impl for<'context> Fn(&CommandContext<'context>) -> Result<T, CommandError>
202 + Send
203 + Sync
204 + 'static,
205 ) -> Self
206 where
207 T: SuspendedCommand,
208 {
209 self.inner = self.inner.executes_suspended(move |context| {
210 executor(&CommandContext { inner: context })
211 .map(ExternalCommandSuspension)
212 .map_err(CommandError::into_inner)
213 });
214 self
215 }
216
217 #[must_use]
219 pub fn requires(
220 mut self,
221 requirement: impl for<'source> Fn(CommandSource<'source>) -> bool + Send + Sync + 'static,
222 ) -> Self {
223 self.inner = self
224 .inner
225 .requires(CommandRequirement::contextual(move |source| {
226 requirement(CommandSource { inner: source })
227 }));
228 self
229 }
230}
231
232#[must_use]
234pub fn literal(name: impl Into<Box<str>>) -> CommandNode {
235 CommandNode::literal(name)
236}
237
238#[must_use]
240pub fn argument(name: impl Into<Box<str>>, argument: CommandArgument) -> CommandNode {
241 CommandNode::argument(name, argument)
242}
243
244#[derive(Clone, Copy)]
246pub struct CommandContext<'context> {
247 inner: &'context SteelCommandContext<InternalCommandSource>,
248}
249
250impl<'context> CommandContext<'context> {
251 #[must_use]
253 pub fn source(self) -> CommandSource<'context> {
254 CommandSource {
255 inner: self.inner.source(),
256 }
257 }
258
259 #[must_use]
261 pub fn value<T: DowncastType>(self, name: &str) -> Option<&'context T> {
262 self.inner.argument(name)?.downcast_ref::<T>()
263 }
264
265 #[must_use]
266 pub fn boolean(self, name: &str) -> Option<bool> {
268 self.inner.boolean(name)
269 }
270
271 #[must_use]
272 pub fn integer(self, name: &str) -> Option<i32> {
274 self.inner.integer(name)
275 }
276
277 #[must_use]
278 pub fn long(self, name: &str) -> Option<i64> {
280 self.inner.long(name)
281 }
282
283 #[must_use]
284 pub fn float(self, name: &str) -> Option<f32> {
286 self.inner.float(name)
287 }
288
289 #[must_use]
290 pub fn double(self, name: &str) -> Option<f64> {
292 self.inner.double(name)
293 }
294
295 #[must_use]
296 pub fn string(self, name: &str) -> Option<&'context str> {
298 self.inner.string(name)
299 }
300
301 #[must_use]
303 pub fn domain(self, name: &str) -> Option<&'context str> {
304 self.inner.domain(name)
305 }
306
307 pub fn world(self, name: &str) -> Result<Arc<World>, CommandError> {
309 let Some(world) = self.inner.world_argument(name) else {
310 return Err(CommandError::from(format!(
311 "missing parsed world argument '{name}'"
312 )));
313 };
314 world
315 .resolve(self.inner.source())
316 .map_err(CommandError::from)
317 }
318
319 pub fn players(self, name: &str) -> Result<Vec<Arc<Player>>, CommandError> {
321 self.inner.players(name).map_err(CommandError::from)
322 }
323
324 pub fn player(self, name: &str) -> Result<Arc<Player>, CommandError> {
326 self.inner.player(name).map_err(CommandError::from)
327 }
328
329 pub fn entities(self, name: &str) -> Result<Vec<SharedEntity>, CommandError> {
331 self.inner.entities(name).map_err(CommandError::from)
332 }
333
334 pub fn entity(self, name: &str) -> Result<SharedEntity, CommandError> {
336 self.inner.entity(name).map_err(CommandError::from)
337 }
338}
339
340#[derive(Clone, Copy)]
342pub struct CommandSource<'source> {
343 inner: &'source InternalCommandSource,
344}
345
346impl<'source> CommandSource<'source> {
347 #[must_use]
349 pub const fn player(self) -> Option<&'source Arc<Player>> {
350 self.inner.player()
351 }
352
353 #[must_use]
355 pub const fn entity(self) -> Option<&'source SharedEntity> {
356 self.inner.entity()
357 }
358
359 #[must_use]
361 pub const fn world(self) -> &'source Arc<World> {
362 self.inner.world()
363 }
364
365 #[must_use]
367 pub const fn server(self) -> &'source Arc<Server> {
368 self.inner.server()
369 }
370
371 #[must_use]
373 pub const fn position(self) -> DVec3 {
374 self.inner.position()
375 }
376
377 #[must_use]
379 pub const fn rotation(self) -> (f32, f32) {
380 self.inner.rotation()
381 }
382
383 #[must_use]
385 pub fn permission_state(self, permission: &PermissionExpr) -> Option<PermissionState> {
386 CommandPermissionSource::permission_state(self.inner, permission)
387 }
388
389 pub fn send_success(self, message: &TextComponent, broadcast_to_admins: bool) {
391 self.inner.send_success(message, broadcast_to_admins);
392 }
393
394 pub fn send_failure(self, message: TextComponent) {
396 self.inner.send_failure(message);
397 }
398}
399
400#[derive(Debug)]
402pub struct CommandError {
403 inner: CommandSyntaxError,
404}
405
406impl CommandError {
407 #[must_use]
409 pub fn new(message: impl Into<TextComponent>) -> Self {
410 Self {
411 inner: CommandSyntaxError::dynamic(message),
412 }
413 }
414
415 fn into_inner(self) -> CommandSyntaxError {
416 self.inner
417 }
418}
419
420impl From<CommandSyntaxError> for CommandError {
421 fn from(inner: CommandSyntaxError) -> Self {
422 Self { inner }
423 }
424}
425
426impl From<String> for CommandError {
427 fn from(message: String) -> Self {
428 Self::new(TextComponent::plain(message))
429 }
430}
431
432impl From<&str> for CommandError {
433 fn from(message: &str) -> Self {
434 Self::new(TextComponent::plain(message.to_owned()))
435 }
436}
437
438impl From<TextComponent> for CommandError {
439 fn from(message: TextComponent) -> Self {
440 Self::new(message)
441 }
442}
443
444impl fmt::Display for CommandError {
445 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
446 self.inner.fmt(formatter)
447 }
448}
449
450impl Error for CommandError {
451 fn source(&self) -> Option<&(dyn Error + 'static)> {
452 Some(&self.inner)
453 }
454}
455
456pub struct CommandArgument {
458 inner: SteelArgumentType,
459}
460
461impl CommandArgument {
462 #[must_use]
464 pub fn boolean() -> Self {
465 Self::primitive(ArgumentType::bool())
466 }
467
468 #[must_use]
470 pub fn integer(minimum: i32, maximum: i32) -> Self {
471 Self::primitive(ArgumentType::integer(minimum, maximum))
472 }
473
474 #[must_use]
476 pub fn long(minimum: i64, maximum: i64) -> Self {
477 Self::primitive(ArgumentType::long(minimum, maximum))
478 }
479
480 #[must_use]
482 pub fn float(minimum: f32, maximum: f32) -> Self {
483 Self::primitive(ArgumentType::float(minimum, maximum))
484 }
485
486 #[must_use]
488 pub fn double(minimum: f64, maximum: f64) -> Self {
489 Self::primitive(ArgumentType::double(minimum, maximum))
490 }
491
492 #[must_use]
494 pub fn word() -> Self {
495 Self::primitive(ArgumentType::word())
496 }
497
498 #[must_use]
500 pub fn string() -> Self {
501 Self::primitive(ArgumentType::string())
502 }
503
504 #[must_use]
506 pub fn greedy_string() -> Self {
507 Self::primitive(ArgumentType::greedy_string())
508 }
509
510 #[must_use]
512 pub fn entity() -> Self {
513 Self {
514 inner: SteelArgumentType::entity(),
515 }
516 }
517
518 #[must_use]
520 pub fn entities() -> Self {
521 Self {
522 inner: SteelArgumentType::entities(),
523 }
524 }
525
526 #[must_use]
528 pub fn player() -> Self {
529 Self {
530 inner: SteelArgumentType::player(),
531 }
532 }
533
534 #[must_use]
536 pub fn players() -> Self {
537 Self {
538 inner: SteelArgumentType::players(),
539 }
540 }
541
542 #[must_use]
544 pub fn domain() -> Self {
545 Self {
546 inner: SteelArgumentType::domain(),
547 }
548 }
549
550 #[must_use]
552 pub fn world() -> Self {
553 Self {
554 inner: SteelArgumentType::world(),
555 }
556 }
557
558 #[must_use]
560 pub fn custom<P>(parser: P) -> Self
561 where
562 P: CommandArgumentParser,
563 {
564 Self {
565 inner: SteelArgumentType::new(parser),
566 }
567 }
568
569 fn primitive(argument: ArgumentType) -> Self {
570 Self {
571 inner: SteelArgumentType::from(argument),
572 }
573 }
574}
575
576pub trait CommandArgumentParser:
578 DowncastType + fmt::Debug + PartialEq + Send + Sync + 'static
579{
580 type Value: DowncastType + fmt::Debug + Send + Sync + 'static;
582
583 fn parse(
585 &self,
586 reader: &mut CommandReader<'_, '_>,
587 source: CommandParserSource<'_>,
588 ) -> Result<Self::Value, CommandError>;
589
590 fn list_suggestions(
592 &self,
593 _context: CommandSuggestionContext<'_>,
594 _suggestions: &mut CommandSuggestions<'_, '_>,
595 ) {
596 }
597
598 fn protocol_argument(&self) -> (ProtocolArgumentType, Option<ProtocolSuggestionType>);
600}
601
602impl<P> SteelArgumentParser for P
603where
604 P: CommandArgumentParser,
605{
606 type Value = P::Value;
607
608 fn parse(
609 &self,
610 reader: &mut StringReader<'_>,
611 source: &dyn CommandArgumentSource,
612 ) -> Result<Self::Value, CommandSyntaxError> {
613 CommandArgumentParser::parse(
614 self,
615 &mut CommandReader { inner: reader },
616 CommandParserSource { inner: source },
617 )
618 .map_err(CommandError::into_inner)
619 }
620
621 fn list_suggestions(
622 &self,
623 context: &dyn SteelArgumentSuggestionContext,
624 builder: &mut SuggestionsBuilder<'_>,
625 ) {
626 CommandArgumentParser::list_suggestions(
627 self,
628 CommandSuggestionContext { inner: context },
629 &mut CommandSuggestions { inner: builder },
630 );
631 }
632
633 fn protocol_argument(&self) -> (ProtocolArgumentType, Option<ProtocolSuggestionType>) {
634 CommandArgumentParser::protocol_argument(self)
635 }
636}
637
638#[derive(Clone, Copy)]
640pub struct CommandReaderCursor(ReaderCursor);
641
642pub struct CommandReader<'reader, 'input> {
644 inner: &'reader mut StringReader<'input>,
645}
646
647impl<'input> CommandReader<'_, 'input> {
648 #[must_use]
650 pub const fn input(&self) -> &'input str {
651 self.inner.input()
652 }
653
654 #[must_use]
656 pub const fn cursor(&self) -> usize {
657 self.inner.byte_cursor()
658 }
659
660 #[must_use]
662 pub fn remaining(&self) -> &'input str {
663 self.inner.remaining()
664 }
665
666 #[must_use]
668 pub fn peek(&self) -> Option<char> {
669 self.inner.peek()
670 }
671
672 pub fn read(&mut self) -> Option<char> {
674 self.inner.read()
675 }
676
677 pub fn skip_whitespace(&mut self) {
679 self.inner.skip_whitespace();
680 }
681
682 pub fn read_unquoted_string(&mut self) -> &'input str {
684 self.inner.read_unquoted_string()
685 }
686
687 pub fn read_string(&mut self) -> Result<String, CommandError> {
689 self.inner.read_string().map_err(CommandError::from)
690 }
691
692 pub fn read_integer(&mut self) -> Result<i32, CommandError> {
694 self.inner.read_int().map_err(CommandError::from)
695 }
696
697 pub fn read_long(&mut self) -> Result<i64, CommandError> {
699 self.inner.read_long().map_err(CommandError::from)
700 }
701
702 pub fn read_float(&mut self) -> Result<f32, CommandError> {
704 self.inner.read_float().map_err(CommandError::from)
705 }
706
707 pub fn read_double(&mut self) -> Result<f64, CommandError> {
709 self.inner.read_double().map_err(CommandError::from)
710 }
711
712 pub fn read_boolean(&mut self) -> Result<bool, CommandError> {
714 self.inner.read_boolean().map_err(CommandError::from)
715 }
716
717 pub fn expect(&mut self, expected: char) -> Result<(), CommandError> {
719 self.inner.expect(expected).map_err(CommandError::from)
720 }
721
722 #[must_use]
724 pub const fn checkpoint(&self) -> CommandReaderCursor {
725 CommandReaderCursor(self.inner.checkpoint())
726 }
727
728 pub const fn restore(&mut self, checkpoint: CommandReaderCursor) {
730 self.inner.restore(checkpoint.0);
731 }
732
733 #[must_use]
735 pub fn error(&self, message: impl Into<TextComponent>) -> CommandError {
736 CommandError::from(
737 self.inner
738 .error(CommandSyntaxErrorKind::Dynamic(Box::new(message.into()))),
739 )
740 }
741}
742
743#[cfg(test)]
744mod tests {
745 use super::CommandReader;
746 use crate::command::brigadier::StringReader;
747
748 #[test]
749 fn command_reader_cursor_is_a_utf8_byte_offset() {
750 let mut inner = StringReader::new("é🦀");
751 let mut reader = CommandReader { inner: &mut inner };
752
753 assert_eq!(reader.read(), Some('é'));
754 assert_eq!(reader.cursor(), 2);
755 assert_eq!(reader.read(), Some('🦀'));
756 assert_eq!(reader.cursor(), 6);
757 assert_eq!(&reader.input()[..reader.cursor()], "é🦀");
758 }
759}
760
761#[derive(Clone, Copy)]
763pub struct CommandParserSource<'source> {
764 inner: &'source dyn CommandArgumentSource,
765}
766
767impl CommandParserSource<'_> {
768 #[must_use]
770 pub fn domain_names(&self) -> Vec<&str> {
771 self.inner.domain_names()
772 }
773
774 #[must_use]
776 pub fn world_names(&self) -> Vec<String> {
777 self.inner.command_world_names()
778 }
779
780 #[must_use]
782 pub fn player_names(&self) -> Vec<String> {
783 self.inner.selector_player_names()
784 }
785
786 #[must_use]
788 pub fn permission_group_names(&self) -> Vec<String> {
789 self.inner.permission_group_names()
790 }
791}
792
793#[derive(Clone, Copy)]
795pub struct CommandSuggestionContext<'context> {
796 inner: &'context dyn SteelArgumentSuggestionContext,
797}
798
799impl<'context> CommandSuggestionContext<'context> {
800 #[must_use]
802 pub fn source(&self) -> CommandParserSource<'context> {
803 CommandParserSource {
804 inner: self.inner.source(),
805 }
806 }
807
808 #[must_use]
810 pub fn value<T: DowncastType>(&self, name: &str) -> Option<&'context T> {
811 self.inner.argument(name)?.downcast_ref::<T>()
812 }
813}
814
815pub struct CommandSuggestions<'builder, 'input> {
817 inner: &'builder mut SuggestionsBuilder<'input>,
818}
819
820impl CommandSuggestions<'_, '_> {
821 #[must_use]
823 pub fn remaining(&self) -> &str {
824 self.inner.remaining()
825 }
826
827 pub fn suggest(&mut self, text: impl Into<Box<str>>) {
829 self.inner.suggest(text);
830 }
831
832 pub fn suggest_with_tooltip(&mut self, text: impl Into<Box<str>>, tooltip: TextComponent) {
834 self.inner.suggest_with_tooltip(text, tooltip);
835 }
836
837 pub fn suggest_integer(&mut self, value: i32) {
839 self.inner.suggest_integer(value);
840 }
841}
842
843pub enum SuspendedCommandPoll {
845 Pending,
847 Ready(Result<i32, CommandError>),
849}
850
851pub trait SuspendedCommand: Send + 'static {
853 fn order(&self) -> CommandSuspensionOrder {
855 CommandSuspensionOrder::Source
856 }
857
858 fn poll(&mut self) -> SuspendedCommandPoll;
860
861 fn cancel(&mut self) {}
863}
864
865struct ExternalCommandSuspension<T>(T);
866
867impl<T> CommandResultSuspension for ExternalCommandSuspension<T>
868where
869 T: SuspendedCommand,
870{
871 fn order(&self) -> CommandSuspensionOrder {
872 self.0.order()
873 }
874
875 fn poll(&mut self) -> CommandResultSuspensionPoll {
876 match self.0.poll() {
877 SuspendedCommandPoll::Pending => CommandResultSuspensionPoll::Pending,
878 SuspendedCommandPoll::Ready(result) => {
879 CommandResultSuspensionPoll::Ready(result.map_err(CommandError::into_inner))
880 }
881 }
882 }
883
884 fn cancel(&mut self) {
885 self.0.cancel();
886 }
887}