aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--azalea-brigadier/src/builder/argument_builder.rs39
-rw-r--r--azalea-brigadier/src/dispatcher.rs293
2 files changed, 316 insertions, 16 deletions
diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs
index 11c0062c..ee6682dd 100644
--- a/azalea-brigadier/src/builder/argument_builder.rs
+++ b/azalea-brigadier/src/builder/argument_builder.rs
@@ -12,14 +12,15 @@ pub enum ArgumentBuilderType {
/// A node that hasn't yet been built.
#[derive(Clone)]
pub struct ArgumentBuilder<S: Any + Clone> {
- value: ArgumentBuilderType,
-
+ arguments: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
children: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
literals: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
- arguments: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
- executes: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
+ command: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
requirement: Rc<dyn Fn(Rc<S>) -> bool>,
+ target: Option<Rc<RefCell<CommandNode<S>>>>,
+
+ value: ArgumentBuilderType,
forks: bool,
modifier: Option<Rc<dyn RedirectModifier<S>>>,
}
@@ -34,10 +35,11 @@ impl<S: Any + Clone> ArgumentBuilder<S> {
children: BTreeMap::new(),
literals: BTreeMap::new(),
arguments: BTreeMap::new(),
- executes: None,
+ command: None,
requirement: Rc::new(|_| true),
forks: false,
modifier: None,
+ target: None,
}
}
@@ -62,7 +64,7 @@ impl<S: Any + Clone> ArgumentBuilder<S> {
where
F: Fn(&CommandContext<S>) -> i32 + 'static,
{
- self.executes = Some(Rc::new(f));
+ self.command = Some(Rc::new(f));
self.clone()
}
@@ -74,6 +76,25 @@ impl<S: Any + Clone> ArgumentBuilder<S> {
self.clone()
}
+ pub fn redirect(&mut self, target: Rc<RefCell<CommandNode<S>>>) -> Self {
+ self.forward(target, None, false)
+ }
+
+ pub fn forward(
+ &mut self,
+ target: Rc<RefCell<CommandNode<S>>>,
+ modifier: Option<Rc<dyn RedirectModifier<S>>>,
+ fork: bool,
+ ) -> Self {
+ if !self.arguments.is_empty() {
+ panic!("Cannot forward a node with children");
+ }
+ self.target = Some(target);
+ self.modifier = modifier;
+ self.forks = fork;
+ self.clone()
+ }
+
pub fn build(self) -> CommandNode<S> {
CommandNode {
value: self.value,
@@ -82,9 +103,9 @@ impl<S: Any + Clone> ArgumentBuilder<S> {
literals: self.literals,
arguments: self.arguments,
- command: self.executes.clone(),
+ command: self.command.clone(),
requirement: self.requirement.clone(),
- redirect: None,
+ redirect: self.target,
forks: self.forks,
modifier: self.modifier,
}
@@ -98,7 +119,7 @@ impl<S: Any + Clone> Debug for ArgumentBuilder<S> {
.field("children", &self.children)
.field("literals", &self.literals)
.field("arguments", &self.arguments)
- .field("executes", &self.executes.is_some())
+ .field("executes", &self.command.is_some())
// .field("requirement", &self.requirement)
.field("forks", &self.forks)
// .field("modifier", &self.modifier)
diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs
index 683348e3..a8ca2ec9 100644
--- a/azalea-brigadier/src/dispatcher.rs
+++ b/azalea-brigadier/src/dispatcher.rs
@@ -26,9 +26,10 @@ impl<S: Any + Clone> CommandDispatcher<S> {
}
}
- pub fn register(&mut self, node: ArgumentBuilder<S>) {
+ pub fn register(&mut self, node: ArgumentBuilder<S>) -> Rc<RefCell<CommandNode<S>>> {
let build = Rc::new(RefCell::new(node.build()));
self.root.borrow_mut().add_child(&build);
+ build
}
pub fn parse(&self, command: StringReader, source: S) -> ParseResults<S> {
@@ -241,7 +242,10 @@ impl<S: Any + Clone> Clone for CommandDispatcher<S> {
#[cfg(test)]
mod tests {
use super::*;
- use crate::builder::literal_argument_builder::literal;
+ use crate::{
+ builder::{literal_argument_builder::literal, required_argument_builder::argument},
+ parsers::integer,
+ };
struct CommandSource {}
@@ -260,7 +264,7 @@ mod tests {
// }
#[test]
fn create_and_execute_command() {
- let mut subject = CommandDispatcher::<Rc<CommandSource>>::new();
+ let mut subject = CommandDispatcher::new();
subject.register(literal("foo").executes(|_| 42));
assert_eq!(
@@ -279,7 +283,7 @@ mod tests {
// }
#[test]
fn create_and_execute_offset_command() {
- let mut subject = CommandDispatcher::<Rc<CommandSource>>::new();
+ let mut subject = CommandDispatcher::new();
subject.register(literal("foo").executes(|_| 42));
assert_eq!(
@@ -300,7 +304,7 @@ mod tests {
// }
#[test]
fn create_and_merge_commands() {
- let mut subject = CommandDispatcher::<Rc<CommandSource>>::new();
+ let mut subject = CommandDispatcher::new();
subject.register(literal("base").then(literal("foo").executes(|_| 42)));
subject.register(literal("base").then(literal("bar").executes(|_| 42)));
@@ -332,7 +336,7 @@ mod tests {
// }
#[test]
fn execute_unknown_command() {
- let mut subject = CommandDispatcher::<Rc<CommandSource>>::new();
+ let mut subject = CommandDispatcher::new();
subject.register(literal("bar"));
subject.register(literal("baz"));
@@ -359,7 +363,7 @@ mod tests {
// }
#[test]
fn execute_impermissible_command() {
- let mut subject = CommandDispatcher::<Rc<CommandSource>>::new();
+ let mut subject = CommandDispatcher::new();
subject.register(literal("foo").requires(|_| false));
let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {}));
@@ -371,4 +375,279 @@ mod tests {
}
assert_eq!(err.cursor().unwrap(), 0);
}
+ // @Test
+ // public void testExecuteEmptyCommand() throws Exception {
+ // subject.register(literal(""));
+
+ // try {
+ // subject.execute("", source);
+ // fail();
+ // } catch (final CommandSyntaxException ex) {
+ // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
+ // assertThat(ex.getCursor(), is(0));
+ // }
+ // }
+ #[test]
+ fn execute_empty_command() {
+ let mut subject = CommandDispatcher::new();
+ subject.register(literal(""));
+
+ let execute_result = subject.execute("".into(), Rc::new(CommandSource {}));
+
+ let err = execute_result.err().unwrap();
+ match err.type_ {
+ BuiltInExceptions::DispatcherUnknownCommand => {}
+ _ => panic!("Unexpected error"),
+ }
+ assert_eq!(err.cursor().unwrap(), 0);
+ }
+ // @Test
+ // public void testExecuteUnknownSubcommand() throws Exception {
+ // subject.register(literal("foo").executes(command));
+
+ // try {
+ // subject.execute("foo bar", source);
+ // fail();
+ // } catch (final CommandSyntaxException ex) {
+ // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
+ // assertThat(ex.getCursor(), is(4));
+ // }
+ // }
+ #[test]
+ fn execute_unknown_subcommand() {
+ let mut subject = CommandDispatcher::new();
+ subject.register(literal("foo").executes(|_| 42));
+
+ let execute_result = subject.execute("foo bar".into(), Rc::new(CommandSource {}));
+
+ let err = execute_result.err().unwrap();
+ match err.type_ {
+ BuiltInExceptions::DispatcherUnknownArgument => {}
+ _ => panic!("Unexpected error"),
+ }
+ assert_eq!(err.cursor().unwrap(), 4);
+ }
+ // @Test
+ // public void testExecuteIncorrectLiteral() throws Exception {
+ // subject.register(literal("foo").executes(command).then(literal("bar")));
+
+ // try {
+ // subject.execute("foo baz", source);
+ // fail();
+ // } catch (final CommandSyntaxException ex) {
+ // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
+ // assertThat(ex.getCursor(), is(4));
+ // }
+ // }
+ #[test]
+ fn execute_incorrect_literal() {
+ let mut subject = CommandDispatcher::new();
+ subject.register(literal("foo").executes(|_| 42).then(literal("bar")));
+
+ let execute_result = subject.execute("foo baz".into(), Rc::new(CommandSource {}));
+
+ let err = execute_result.err().unwrap();
+ match err.type_ {
+ BuiltInExceptions::DispatcherUnknownArgument => {}
+ _ => panic!("Unexpected error"),
+ }
+ assert_eq!(err.cursor().unwrap(), 4);
+ }
+ // @Test
+ // public void testExecuteAmbiguousIncorrectArgument() throws Exception {
+ // subject.register(
+ // literal("foo").executes(command)
+ // .then(literal("bar"))
+ // .then(literal("baz"))
+ // );
+
+ // try {
+ // subject.execute("foo unknown", source);
+ // fail();
+ // } catch (final CommandSyntaxException ex) {
+ // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()));
+ // assertThat(ex.getCursor(), is(4));
+ // }
+ // }
+ #[test]
+ fn execute_ambiguous_incorrect_argument() {
+ let mut subject = CommandDispatcher::new();
+ subject.register(
+ literal("foo")
+ .executes(|_| 42)
+ .then(literal("bar"))
+ .then(literal("baz")),
+ );
+
+ let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {}));
+
+ let err = execute_result.err().unwrap();
+ match err.type_ {
+ BuiltInExceptions::DispatcherUnknownArgument => {}
+ _ => panic!("Unexpected error"),
+ }
+ assert_eq!(err.cursor().unwrap(), 4);
+ }
+
+ // @Test
+ // public void testExecuteSubcommand() throws Exception {
+ // final Command<Object> subCommand = mock(Command.class);
+ // when(subCommand.run(any())).thenReturn(100);
+
+ // subject.register(literal("foo").then(
+ // literal("a")
+ // ).then(
+ // literal("=").executes(subCommand)
+ // ).then(
+ // literal("c")
+ // ).executes(command));
+
+ // assertThat(subject.execute("foo =", source), is(100));
+ // verify(subCommand).run(any(CommandContext.class));
+ // }
+ #[test]
+ fn test_execute_subcommand() {
+ let mut subject = CommandDispatcher::new();
+
+ subject.register(
+ literal("foo")
+ .then(literal("a"))
+ .then(literal("=").executes(|_| 100))
+ .then(literal("c"))
+ .executes(|_| 42),
+ );
+
+ assert_eq!(
+ subject
+ .execute("foo =".into(), Rc::new(CommandSource {}))
+ .unwrap(),
+ 100
+ );
+ }
+ // @Test
+ // public void testParseIncompleteLiteral() throws Exception {
+ // subject.register(literal("foo").then(literal("bar").executes(command)));
+
+ // final ParseResults<Object> parse = subject.parse("foo ", source);
+ // assertThat(parse.getReader().getRemaining(), equalTo(" "));
+ // assertThat(parse.getContext().getNodes().size(), is(1));
+ // }
+ #[test]
+ fn test_parse_incomplete_literal() {
+ let mut subject = CommandDispatcher::new();
+ subject.register(literal("foo").then(literal("bar").executes(|_| 42)));
+
+ let parse = subject.parse("foo ".into(), Rc::new(CommandSource {}));
+ assert_eq!(parse.reader.remaining(), " ");
+ assert_eq!(parse.context.nodes.len(), 1);
+ }
+ // @Test
+ // public void testParseIncompleteArgument() throws Exception {
+ // subject.register(literal("foo").then(argument("bar", integer()).executes(command)));
+
+ // final ParseResults<Object> parse = subject.parse("foo ", source);
+ // assertThat(parse.getReader().getRemaining(), equalTo(" "));
+ // assertThat(parse.getContext().getNodes().size(), is(1));
+ // }
+ #[test]
+ fn test_parse_incomplete_argument() {
+ let mut subject = CommandDispatcher::new();
+ subject.register(literal("foo").then(argument("bar", integer()).executes(|_| 42)));
+
+ let parse = subject.parse("foo ".into(), Rc::new(CommandSource {}));
+ assert_eq!(parse.reader.remaining(), " ");
+ assert_eq!(parse.context.nodes.len(), 1);
+ }
+
+ // @Test
+ // public void testExecuteAmbiguiousParentSubcommand() throws Exception {
+ // final Command<Object> subCommand = mock(Command.class);
+ // when(subCommand.run(any())).thenReturn(100);
+
+ // subject.register(
+ // literal("test")
+ // .then(
+ // argument("incorrect", integer())
+ // .executes(command)
+ // )
+ // .then(
+ // argument("right", integer())
+ // .then(
+ // argument("sub", integer())
+ // .executes(subCommand)
+ // )
+ // )
+ // );
+
+ // assertThat(subject.execute("test 1 2", source), is(100));
+ // verify(subCommand).run(any(CommandContext.class));
+ // verify(command, never()).run(any());
+ // }
+ #[test]
+ fn test_execute_ambiguious_parent_subcommand() {
+ let mut subject = CommandDispatcher::new();
+
+ subject.register(
+ literal("test")
+ .then(argument("incorrect", integer()).executes(|_| 42))
+ .then(
+ argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)),
+ ),
+ );
+
+ assert_eq!(
+ subject
+ .execute("test 1 2".into(), Rc::new(CommandSource {}))
+ .unwrap(),
+ 100
+ );
+ }
+
+ // @Test
+ // public void testExecuteAmbiguiousParentSubcommandViaRedirect() throws Exception {
+ // final Command<Object> subCommand = mock(Command.class);
+ // when(subCommand.run(any())).thenReturn(100);
+
+ // final LiteralCommandNode<Object> real = subject.register(
+ // literal("test")
+ // .then(
+ // argument("incorrect", integer())
+ // .executes(command)
+ // )
+ // .then(
+ // argument("right", integer())
+ // .then(
+ // argument("sub", integer())
+ // .executes(subCommand)
+ // )
+ // )
+ // );
+
+ // subject.register(literal("redirect").redirect(real));
+
+ // assertThat(subject.execute("redirect 1 2", source), is(100));
+ // verify(subCommand).run(any(CommandContext.class));
+ // verify(command, never()).run(any());
+ // }
+ // #[test]
+ // fn test_execute_ambiguious_parent_subcommand_via_redirect() {
+ // let mut subject = CommandDispatcher::new();
+
+ // let real = subject.register(
+ // literal("test")
+ // .then(argument("incorrect", integer()).executes(|_| 42))
+ // .then(
+ // argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)),
+ // ),
+ // );
+
+ // subject.register(literal("redirect").redirect(real));
+
+ // assert_eq!(
+ // subject
+ // .execute("redirect 1 2".into(), Rc::new(CommandSource {}))
+ // .unwrap(),
+ // 100
+ // );
+ // }
}