aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2022-04-17 14:02:13 -0500
committermat <github@matdoes.dev>2022-04-17 14:02:13 -0500
commita72a47ced76065caf739898954cd18edbc39174b (patch)
tree5526c7663f253bbd7c8318b9d98413f1f2074852
parent4ff67d4917ce333232189e86aee09f2d82451fc6 (diff)
downloadazalea-drasl-a72a47ced76065caf739898954cd18edbc39174b.tar.xz
Rewrite brigadier
-rw-r--r--azalea-brigadier/src/ambiguity_consumer.rs1
-rw-r--r--azalea-brigadier/src/arguments/argument_type.rs60
-rw-r--r--azalea-brigadier/src/arguments/bool_argument_type.rs59
-rw-r--r--azalea-brigadier/src/arguments/double_argument_type.rs1
-rw-r--r--azalea-brigadier/src/arguments/float_argument_type.rs1
-rw-r--r--azalea-brigadier/src/arguments/integer_argument_type.rs1
-rw-r--r--azalea-brigadier/src/arguments/long_argument_type.rs1
-rw-r--r--azalea-brigadier/src/arguments/mod.rs7
-rw-r--r--azalea-brigadier/src/arguments/string_argument_type.rs1
-rw-r--r--azalea-brigadier/src/builder/argument_builder.rs257
-rw-r--r--azalea-brigadier/src/builder/literal_argument_builder.rs60
-rw-r--r--azalea-brigadier/src/builder/required_argument_builder.rs113
-rw-r--r--azalea-brigadier/src/command.rs12
-rw-r--r--azalea-brigadier/src/command_dispatcher.rs46
-rw-r--r--azalea-brigadier/src/context.rs145
-rw-r--r--azalea-brigadier/src/context/command_context.rs93
-rw-r--r--azalea-brigadier/src/context/command_context_builder.rs176
-rw-r--r--azalea-brigadier/src/context/mod.rs6
-rw-r--r--azalea-brigadier/src/context/parsed_argument.rs24
-rw-r--r--azalea-brigadier/src/context/parsed_command_node.rs30
-rw-r--r--azalea-brigadier/src/context/suggestion_context.rs6
-rw-r--r--azalea-brigadier/src/dispatcher.rs242
-rw-r--r--azalea-brigadier/src/exceptions/builtin_exception_provider.rs1
-rw-r--r--azalea-brigadier/src/exceptions/builtin_exceptions.rs16
-rw-r--r--azalea-brigadier/src/exceptions/command_syntax_exception.rs6
-rw-r--r--azalea-brigadier/src/exceptions/mod.rs1
-rw-r--r--azalea-brigadier/src/immutable_string_reader.rs12
-rw-r--r--azalea-brigadier/src/lib.rs73
-rw-r--r--azalea-brigadier/src/literal_message.rs1
-rw-r--r--azalea-brigadier/src/main.rs38
-rw-r--r--azalea-brigadier/src/message.rs4
-rw-r--r--azalea-brigadier/src/modifier.rs9
-rw-r--r--azalea-brigadier/src/parse_results.rs20
-rw-r--r--azalea-brigadier/src/parsers.rs21
-rw-r--r--azalea-brigadier/src/redirect_modifier.rs11
-rw-r--r--azalea-brigadier/src/result_consumer.rs1
-rw-r--r--azalea-brigadier/src/single_redirect_modifier.rs8
-rw-r--r--azalea-brigadier/src/string_range.rs (renamed from azalea-brigadier/src/context/string_range.rs)4
-rw-r--r--azalea-brigadier/src/string_reader.rs189
-rw-r--r--azalea-brigadier/src/suggestion/integer_suggestion.rs1
-rw-r--r--azalea-brigadier/src/suggestion/mod.rs5
-rw-r--r--azalea-brigadier/src/suggestion/suggestion.rs90
-rw-r--r--azalea-brigadier/src/suggestion/suggestion_provider.rs14
-rw-r--r--azalea-brigadier/src/suggestion/suggestions.rs141
-rw-r--r--azalea-brigadier/src/suggestion/suggestions_builder.rs116
-rw-r--r--azalea-brigadier/src/tree.rs269
-rw-r--r--azalea-brigadier/src/tree/argument_command_node.rs176
-rw-r--r--azalea-brigadier/src/tree/command_node.rs143
-rw-r--r--azalea-brigadier/src/tree/literal_command_node.rs131
-rw-r--r--azalea-brigadier/src/tree/mod.rs4
-rw-r--r--azalea-brigadier/src/tree/root_command_node.rs79
51 files changed, 1102 insertions, 1824 deletions
diff --git a/azalea-brigadier/src/ambiguity_consumer.rs b/azalea-brigadier/src/ambiguity_consumer.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/ambiguity_consumer.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs
deleted file mode 100644
index 37cc9354..00000000
--- a/azalea-brigadier/src/arguments/argument_type.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-use std::any::Any;
-
-use super::bool_argument_type::BoolArgumentType;
-use crate::{
- context::command_context::CommandContext,
- exceptions::command_syntax_exception::CommandSyntaxException,
- string_reader::StringReader,
- suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
-};
-use dyn_clonable::*;
-
-/*
-#[derive(Types)]
-enum BrigadierTypes {
- Entity(EntityArgumentType)
-}
-
-===
-
-enum BrigadierTypes {
- Bool(BoolArgumentType)
-
- Entity(EntityArgumentType)
-}
-
-impl Types for BrigadierTypes {
- fn inner(&self) -> dyn ArgumentType<dyn Types> {
- match self {
- Bool(t) => t,
- Entity(t) => t
- }
- }
-}
-*/
-
-pub trait ArgumentType {
- type Into;
- // T parse(StringReader reader) throws CommandSyntaxException;
-
- // default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
- // return Suggestions.empty();
- // }
-
- // default Collection<String> getExamples() {
- // return Collections.emptyList();
- // }
-
- fn parse(&self, reader: &mut StringReader) -> Result<Self::Into, CommandSyntaxException>;
-
- fn list_suggestions<S>(
- &self,
- context: &CommandContext<S>,
- builder: &SuggestionsBuilder,
- ) -> Result<Suggestions, CommandSyntaxException>
- where
- Self: Sized,
- S: Sized;
-
- fn get_examples(&self) -> Vec<String>;
-}
diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs
deleted file mode 100644
index c06f40c1..00000000
--- a/azalea-brigadier/src/arguments/bool_argument_type.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use crate::{
- context::command_context::CommandContext,
- exceptions::command_syntax_exception::CommandSyntaxException,
- string_reader::StringReader,
- suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
-};
-
-use super::argument_type::ArgumentType;
-
-#[derive(Clone)]
-pub struct BoolArgumentType {}
-
-impl ArgumentType for BoolArgumentType {
- type Into = bool;
-
- fn parse(&self, reader: &mut StringReader) -> Result<Self::Into, CommandSyntaxException> {
- Ok(reader.read_boolean()?)
- }
-
- fn list_suggestions<S>(
- &self,
- context: &CommandContext<S>,
- builder: &mut SuggestionsBuilder,
- ) -> Result<Suggestions, CommandSyntaxException>
- where
- S: Sized,
- {
- // if ("true".startsWith(builder.getRemainingLowerCase())) {
- // builder.suggest("true");
- // }
- // if ("false".startsWith(builder.getRemainingLowerCase())) {
- // builder.suggest("false");
- // }
- // return builder.buildFuture();
- if "true".starts_with(builder.remaining_lowercase()) {
- builder.suggest("true");
- }
- if "false".starts_with(builder.remaining_lowercase()) {
- builder.suggest("false");
- }
- Ok(builder.build())
- }
-
- fn get_examples(&self) -> Vec<String> {
- vec![]
- }
-}
-
-impl BoolArgumentType {
- const EXAMPLES: &'static [&'static str] = &["true", "false"];
-
- fn bool() -> Self {
- Self {}
- }
-
- fn get_bool<S>(context: CommandContext<S>, name: String) {
- context.get_argument::<bool>(name)
- }
-}
diff --git a/azalea-brigadier/src/arguments/double_argument_type.rs b/azalea-brigadier/src/arguments/double_argument_type.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/arguments/double_argument_type.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/arguments/float_argument_type.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/arguments/integer_argument_type.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/arguments/long_argument_type.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs
deleted file mode 100644
index 487c5db7..00000000
--- a/azalea-brigadier/src/arguments/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-pub mod argument_type;
-pub mod bool_argument_type;
-pub mod double_argument_type;
-pub mod float_argument_type;
-pub mod integer_argument_type;
-pub mod long_argument_type;
-pub mod string_argument_type;
diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/arguments/string_argument_type.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs
index d0770be2..1fb775c2 100644
--- a/azalea-brigadier/src/builder/argument_builder.rs
+++ b/azalea-brigadier/src/builder/argument_builder.rs
@@ -1,137 +1,178 @@
-use crate::{
- arguments::argument_type::ArgumentType,
- command::Command,
- redirect_modifier::RedirectModifier,
- single_redirect_modifier::SingleRedirectModifier,
- tree::{
- command_node::{BaseCommandNode, CommandNodeTrait},
- root_command_node::RootCommandNode,
- },
-};
-use std::fmt::Debug;
-
-pub struct BaseArgumentBuilder<'a, S> {
- arguments: RootCommandNode<'a, S>,
- command: Option<Box<dyn Command<S>>>,
- requirement: Box<dyn Fn(&S) -> bool>,
- target: Option<Box<dyn CommandNodeTrait<S>>>,
- modifier: Option<Box<dyn RedirectModifier<S>>>,
- forks: bool,
+use crate::{context::CommandContext, modifier::RedirectModifier, tree::CommandNode};
+
+use super::{literal_argument_builder::Literal, required_argument_builder::Argument};
+use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, rc::Rc};
+
+#[derive(Debug, Clone)]
+pub enum ArgumentBuilderType {
+ Literal(Literal),
+ Argument(Argument),
}
-pub trait ArgumentBuilder<S> {
- fn build(self) -> Box<dyn CommandNodeTrait<S>>;
+/// A node that hasn't yet been built.
+#[derive(Clone)]
+pub struct ArgumentBuilder<S: Any + Clone> {
+ value: ArgumentBuilderType,
+
+ 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>>,
+ requirement: Rc<dyn Fn(Rc<S>) -> bool>,
+ forks: bool,
+ modifier: Option<Rc<dyn RedirectModifier<S>>>,
}
-impl<'a, S> BaseArgumentBuilder<'a, S> {
- pub fn then(&mut self, argument: Box<dyn ArgumentBuilder<S>>) -> Result<&mut Self, String> {
- if self.target.is_some() {
- return Err("Cannot add children to a redirected node".to_string());
- }
- self.arguments.add_child(argument.build());
- Ok(self)
- }
+// todo: maybe remake this to be based on a CommandNode like vanilla does?
- pub fn arguments(&self) -> &Vec<&dyn CommandNodeTrait<S>> {
- &self.arguments.get_children()
+/// A node that isn't yet built.
+impl<S: Any + Clone> ArgumentBuilder<S> {
+ pub fn new(value: ArgumentBuilderType) -> Self {
+ Self {
+ value,
+ children: BTreeMap::new(),
+ literals: BTreeMap::new(),
+ arguments: BTreeMap::new(),
+ executes: None,
+ requirement: Rc::new(|_| true),
+ forks: false,
+ modifier: None,
+ }
}
- pub fn executes(&mut self, command: Box<dyn Command<S>>) -> &mut Self {
- self.command = Some(command);
+ pub fn then(&mut self, node: ArgumentBuilder<S>) -> &mut Self {
+ let built_node = node.build();
+ let name = built_node.name();
+ let node_reference = Rc::new(RefCell::new(built_node.clone()));
+ self.children
+ .insert(name.to_string(), node_reference.clone());
+ match &built_node.value {
+ ArgumentBuilderType::Literal(literal) => {
+ self.literals.insert(name.to_string(), node_reference);
+ }
+ ArgumentBuilderType::Argument(argument) => {
+ self.arguments.insert(name.to_string(), node_reference);
+ }
+ }
self
}
- pub fn command(&self) -> Option<Box<dyn Command<S>>> {
- self.command
+ pub fn executes<F>(&mut self, f: F) -> Self
+ where
+ F: Fn(&CommandContext<S>) -> i32 + 'static,
+ {
+ self.executes = Some(Rc::new(f));
+ self.clone()
}
- pub fn requires(&mut self, requirement: Box<dyn Fn(&S) -> bool>) -> &mut Self {
- self.requirement = requirement;
- self
- }
+ pub fn build(self) -> CommandNode<S> {
+ println!("building {:?}", self);
+ CommandNode {
+ value: self.value,
- pub fn requirement(&self) -> Box<dyn Fn(&S) -> bool> {
- self.requirement
- }
+ children: self.children,
+ literals: self.literals,
+ arguments: self.arguments,
- pub fn redirect(&mut self, target: Box<dyn CommandNodeTrait<S>>) -> &mut Self {
- self.forward(target, None, false)
+ command: self.executes.clone(),
+ requirement: self.requirement.clone(),
+ redirect: None,
+ forks: self.forks,
+ modifier: self.modifier,
+ }
}
+}
- pub fn redirect_modifier(
- &mut self,
- target: &dyn CommandNodeTrait<S>,
- modifier: &dyn SingleRedirectModifier<S>,
- ) -> &mut Self {
- // forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false);
- self.forward(target, modifier.map(|m| |o| vec![m.apply(o)]), false)
+impl<S: Any + Clone> Debug for ArgumentBuilder<S> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ArgumentBuilder")
+ .field("value", &self.value)
+ .field("children", &self.children)
+ .field("literals", &self.literals)
+ .field("arguments", &self.arguments)
+ .field("executes", &self.executes.is_some())
+ // .field("requirement", &self.requirement)
+ .field("forks", &self.forks)
+ // .field("modifier", &self.modifier)
+ .finish()
}
+}
- pub fn fork(
- &mut self,
- target: &dyn CommandNodeTrait<S>,
- modifier: &dyn RedirectModifier<S>,
- ) -> &mut Self {
- self.forward(target, Some(modifier), true)
- }
+#[cfg(test)]
+mod tests {
+ use std::rc::Rc;
- pub fn forward(
- &mut self,
- target: Option<Box<dyn CommandNodeTrait<S>>>,
- modifier: Option<&dyn RedirectModifier<S>>,
- fork: bool,
- ) -> Result<&mut Self, String> {
- if !self.arguments.get_children().is_empty() {
- return Err("Cannot forward a node with children".to_string());
- }
- self.target = target;
- self.modifier = modifier;
- self.forks = fork;
- Ok(self)
- }
+ use crate::{
+ builder::{literal_argument_builder::literal, required_argument_builder::argument},
+ parsers::integer,
+ };
- pub fn get_redirect(&self) -> Option<&dyn CommandNodeTrait<S>> {
- self.target.as_ref()
- }
+ use super::ArgumentBuilder;
- pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier<S>> {
- self.modifier.as_ref()
- }
+ // public class ArgumentBuilderTest {
+ // private TestableArgumentBuilder<Object> builder;
- pub fn is_fork(&self) -> bool {
- self.forks
- }
+ // @Before
+ // public void setUp() throws Exception {
+ // builder = new TestableArgumentBuilder<>();
+ // }
- pub fn build(self) -> BaseCommandNode<'a, S> {
- let result: BaseCommandNode<'a, S> = BaseCommandNode {
- command: self.command,
- requirement: self.requirement,
- redirect: self.target,
- modifier: self.modifier,
- forks: self.forks,
+ // @Test
+ // public void testArguments() throws Exception {
+ // final RequiredArgumentBuilder<Object, ?> argument = argument("bar", integer());
- arguments: Default::default(),
- children: Default::default(),
- literals: Default::default(),
- };
+ // builder.then(argument);
- for argument in self.arguments() {
- result.add_child(argument);
- }
+ // assertThat(builder.getArguments(), hasSize(1));
+ // assertThat(builder.getArguments(), hasItem((CommandNode<Object>) argument.build()));
+ // }
- result
- }
-}
+ #[test]
+ fn test_arguments() {
+ let mut builder: ArgumentBuilder<()> = literal("foo");
-impl<S> Default for BaseArgumentBuilder<'_, S> {
- fn default() -> Self {
- Self {
- arguments: Default::default(),
- command: Default::default(),
- requirement: Default::default(),
- target: Default::default(),
- modifier: Default::default(),
- forks: Default::default(),
- }
+ let argument: ArgumentBuilder<()> = argument("bar", integer());
+ builder.then(argument.clone());
+ assert_eq!(builder.children.len(), 1);
+ let built_argument = Rc::new(argument.build());
+ assert!(builder
+ .children
+ .values()
+ .any(|e| *e.borrow() == *built_argument));
}
+
+ // @Test
+ // public void testRedirect() throws Exception {
+ // final CommandNode<Object> target = mock(CommandNode.class);
+ // builder.redirect(target);
+ // assertThat(builder.getRedirect(), is(target));
+ // }
+
+ // @Test(expected = IllegalStateException.class)
+ // public void testRedirect_withChild() throws Exception {
+ // final CommandNode<Object> target = mock(CommandNode.class);
+ // builder.then(literal("foo"));
+ // builder.redirect(target);
+ // }
+
+ // @Test(expected = IllegalStateException.class)
+ // public void testThen_withRedirect() throws Exception {
+ // final CommandNode<Object> target = mock(CommandNode.class);
+ // builder.redirect(target);
+ // builder.then(literal("foo"));
+ // }
+
+ // private static class TestableArgumentBuilder<S> extends ArgumentBuilder<S, TestableArgumentBuilder<S>> {
+ // @Override
+ // protected TestableArgumentBuilder<S> getThis() {
+ // return this;
+ // }
+
+ // @Override
+ // public CommandNode<S> build() {
+ // return null;
+ // }
+ // }
+ // }
}
diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs
index 4a95755c..d8898540 100644
--- a/azalea-brigadier/src/builder/literal_argument_builder.rs
+++ b/azalea-brigadier/src/builder/literal_argument_builder.rs
@@ -1,51 +1,35 @@
-use super::argument_builder::{ArgumentBuilder, BaseArgumentBuilder};
+use std::any::Any;
+
use crate::{
- arguments::argument_type::ArgumentType,
- command::Command,
- redirect_modifier::RedirectModifier,
- tree::{
- command_node::CommandNodeTrait, literal_command_node::LiteralCommandNode,
- root_command_node::RootCommandNode,
+ context::CommandContextBuilder,
+ exceptions::{
+ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
},
+ string_range::StringRange,
+ string_reader::StringReader,
};
-use std::fmt::Debug;
-pub struct LiteralArgumentBuilder<S> {
- arguments: RootCommandNode<S>,
- command: Option<Box<dyn Command<S>>>,
- requirement: Box<dyn Fn(&S) -> bool>,
- target: Option<Box<dyn CommandNodeTrait<S>>>,
- modifier: Option<Box<dyn RedirectModifier<S>>>,
- forks: bool,
- literal: String,
-}
+use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType};
-impl<S> LiteralArgumentBuilder<S> {
- pub fn new(literal: String) -> Self {
+#[derive(Debug, Clone, Default)]
+pub struct Literal {
+ pub value: String,
+}
+impl Literal {
+ pub fn new(value: &str) -> Self {
Self {
- literal,
- arguments: RootCommandNode::new(),
- command: None,
- requirement: Box::new(|_| true),
- target: None,
- modifier: None,
- forks: false,
+ value: value.to_string(),
}
}
+}
- pub fn literal(name: String) -> Self {
- Self::new(name)
+impl From<Literal> for ArgumentBuilderType {
+ fn from(literal: Literal) -> Self {
+ Self::Literal(literal)
}
}
-impl<S> ArgumentBuilder<S> for LiteralArgumentBuilder<S> {
- fn build(self) -> Box<dyn CommandNodeTrait<S>> {
- let result = LiteralCommandNode::new(self.literal, self.base.build());
-
- for argument in self.base.arguments() {
- result.add_child(argument);
- }
-
- Box::new(result)
- }
+/// Shortcut for creating a new literal builder node.
+pub fn literal<S: Any + Clone>(value: &str) -> ArgumentBuilder<S> {
+ ArgumentBuilder::new(ArgumentBuilderType::Literal(Literal::new(value)))
}
diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs
index fe6f2ecc..95f4da01 100644
--- a/azalea-brigadier/src/builder/required_argument_builder.rs
+++ b/azalea-brigadier/src/builder/required_argument_builder.rs
@@ -1,91 +1,46 @@
-use super::argument_builder::BaseArgumentBuilder;
-use crate::{
- arguments::argument_type::ArgumentType,
- command::Command,
- redirect_modifier::RedirectModifier,
- suggestion::suggestion_provider::SuggestionProvider,
- tree::{
- argument_command_node::ArgumentCommandNode,
- command_node::{BaseCommandNode, CommandNodeTrait},
- root_command_node::RootCommandNode,
- },
-};
-use std::any::Any;
-use std::fmt::Debug;
-
-pub struct RequiredArgumentBuilder<'a, S> {
- arguments: RootCommandNode<'a, S>,
- command: Option<Box<dyn Command<S>>>,
- requirement: Box<dyn Fn(&S) -> bool>,
- target: Option<Box<dyn CommandNodeTrait<S>>>,
- modifier: Option<Box<dyn RedirectModifier<S>>>,
- forks: bool,
-
- name: String,
- type_: Box<dyn ArgumentType<Into = dyn Any>>,
- suggestions_provider: Option<Box<dyn SuggestionProvider<S>>>,
+use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType};
+use crate::{parsers::Parser, string_reader::StringReader};
+use std::{any::Any, fmt::Debug, rc::Rc};
+
+/// An argument node type. The `T` type parameter is the type of the argument,
+/// which can be anything.
+#[derive(Clone)]
+pub struct Argument {
+ pub name: String,
+ parser: Rc<dyn Parser>,
}
-
-impl<'a, S> RequiredArgumentBuilder<'a, S> {
- pub fn new(name: String, type_: Box<dyn ArgumentType<Into = dyn Any>>) -> Self {
+impl Argument {
+ pub fn new(name: &str, parser: Rc<dyn Parser>) -> Self {
Self {
- name,
- type_: type_,
- suggestions_provider: None,
- arguments: RootCommandNode::new(),
- command: None,
- requirement: Box::new(|_| true),
- target: None,
- modifier: None,
- forks: false,
+ name: name.to_string(),
+ parser: parser,
}
}
- pub fn argument(name: String, type_: Box<dyn ArgumentType<Into = dyn Any>>) -> Self {
- Self::new(name, type_)
- }
-
- pub fn suggests(mut self, provider: Box<dyn SuggestionProvider<S>>) -> Self {
- self.suggestions_provider = Some(provider);
- self
- }
-
- pub fn suggestions_provider(&self) -> Option<Box<dyn SuggestionProvider<S>>> {
- self.suggestions_provider
+ pub fn parse(&self, reader: &mut StringReader) -> Option<Rc<dyn Any>> {
+ self.parser.parse(reader)
}
+}
- pub fn get_type(&self) -> Box<dyn ArgumentType<Into = dyn Any>> {
- self.type_
+impl From<Argument> for ArgumentBuilderType {
+ fn from(argument: Argument) -> Self {
+ Self::Argument(argument)
}
+}
- pub fn name(&self) -> &str {
- &self.name
+impl Debug for Argument {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Argument")
+ .field("name", &self.name)
+ // .field("parser", &self.parser)
+ .finish()
}
+}
- // final ArgumentCommandNode<S> result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider());
-
- // for (final CommandNode<S> argument : getArguments()) {
- // result.addChild(argument);
- // }
-
- // return result;
- pub fn build(self) -> ArgumentCommandNode<'a, S> {
- let result = ArgumentCommandNode {
- name: self.name,
- type_: self.type_,
- command: self.base.command(),
- requirement: self.base.requirement(),
- redirect: self.base.get_redirect(),
- modifier: self.base.get_redirect_modifier(),
- forks: self.base.forks,
- custom_suggestions: self.base.custom_suggestions,
- ..ArgumentCommandNode::default()
- };
-
- for argument in self.base.arguments() {
- result.add_child(argument);
- }
-
- result
- }
+/// Shortcut for creating a new argument builder node.
+pub fn argument<'a, S: Any + Clone>(
+ name: &'a str,
+ parser: impl Parser + 'static,
+) -> ArgumentBuilder<S> {
+ ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into())
}
diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs
deleted file mode 100644
index a529f23c..00000000
--- a/azalea-brigadier/src/command.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-use crate::{
- context::command_context::CommandContext,
- exceptions::command_syntax_exception::CommandSyntaxException,
-};
-use dyn_clonable::*;
-
-pub const SINGLE_SUCCESS: i32 = 1;
-
-#[clonable]
-pub trait Command<S>: Clone {
- fn run(&self, context: &mut CommandContext<S>) -> Result<i32, CommandSyntaxException>;
-}
diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs
deleted file mode 100644
index f8ffddff..00000000
--- a/azalea-brigadier/src/command_dispatcher.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-use crate::{arguments::argument_type::ArgumentType, tree::root_command_node::RootCommandNode};
-use std::fmt::Debug;
-
-/// The core command dispatcher, for registering, parsing, and executing commands.
-/// The `S` generic is a custom "source" type, such as a user or originator of a command
-#[derive(Default, Clone)]
-pub struct CommandDispatcher<S> {
- root: RootCommandNode<S>,
-}
-
-impl<S> CommandDispatcher<S> {
- /// The string required to separate individual arguments in an input string
- ///
- /// See: [`ARGUMENT_SEPARATOR_CHAR`]
- const ARGUMENT_SEPARATOR: &'static str = " ";
-
- /// The char required to separate individual arguments in an input string
- ///
- /// See: [`ARGUMENT_SEPARATOR`]
- const ARGUMENT_SEPARATOR_CHAR: char = ' ';
-
- const USAGE_OPTIONAL_OPEN: &'static str = "[";
- const USAGE_OPTIONAL_CLOSE: &'static str = "]";
- const USAGE_REQUIRED_OPEN: &'static str = "(";
- const USAGE_REQUIRED_CLOSE: &'static str = ")";
- const USAGE_OR: &'static str = "|";
-
- /// Create a new [`CommandDispatcher`] with the specified root node.
- /// This is often useful to copy existing or pre-defined command trees.
- /// # Example
- /// ```
- /// use azalea_brigadier::{
- /// command_dispatcher::CommandDispatcher,
- /// tree::root_command_node::RootCommandNode,
- /// };
- ///
- /// let mut dispatcher = CommandDispatcher::new(RootCommandNode::new());
- /// ```
- /// # Arguments
- /// * `root` - the existing [`RootCommandNode`] to use as the basis for this tree
- /// # Returns
- /// A new [`CommandDispatcher`] with the specified root node.
- fn new(root: RootCommandNode<S>) -> Self {
- Self { root }
- }
-}
diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs
new file mode 100644
index 00000000..6d4dec88
--- /dev/null
+++ b/azalea-brigadier/src/context.rs
@@ -0,0 +1,145 @@
+use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, ptr, rc::Rc};
+
+use crate::{
+ dispatcher::CommandDispatcher, modifier::RedirectModifier, string_range::StringRange,
+ tree::CommandNode,
+};
+
+#[derive(Clone)]
+pub struct CommandContextBuilder<S: Any + Clone> {
+ pub arguments: HashMap<String, ParsedArgument>,
+ pub root: Rc<RefCell<CommandNode<S>>>,
+ pub nodes: Vec<Rc<CommandNode<S>>>,
+ pub dispatcher: Rc<CommandDispatcher<S>>,
+ pub source: Rc<S>,
+ pub command: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
+ pub child: Option<Rc<CommandContextBuilder<S>>>,
+ pub range: StringRange,
+ pub modifier: Option<Rc<dyn RedirectModifier<S>>>,
+ pub forks: bool,
+}
+
+impl<S: Any + Clone> CommandContextBuilder<S> {
+ // CommandDispatcher<S> dispatcher, final S source, final CommandNode<S> rootNode, final int start
+ pub fn new(
+ dispatcher: Rc<CommandDispatcher<S>>,
+ source: Rc<S>,
+ root_node: Rc<RefCell<CommandNode<S>>>,
+ start: usize,
+ ) -> Self {
+ Self {
+ arguments: HashMap::new(),
+ root: root_node,
+ source,
+ range: StringRange::at(start),
+ command: None,
+ dispatcher,
+ nodes: vec![],
+ // rootNode,
+ // start,
+ child: None,
+ modifier: None,
+ forks: false,
+ }
+ }
+
+ pub fn with_command(
+ &mut self,
+ command: &Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
+ ) -> &Self {
+ self.command = command.clone();
+ self
+ }
+ pub fn with_child(&mut self, child: Rc<CommandContextBuilder<S>>) -> &Self {
+ self.child = Some(child);
+ self
+ }
+ pub fn with_argument(&mut self, name: &str, argument: ParsedArgument) -> &Self {
+ self.arguments.insert(name.to_string(), argument);
+ self
+ }
+ pub fn with_node(&mut self, node: Rc<CommandNode<S>>, range: StringRange) -> &Self {
+ self.nodes.push(node.clone());
+ self.range = StringRange::encompassing(&self.range, &range);
+ self.modifier = node.modifier.clone();
+ self.forks = node.forks;
+ self
+ }
+
+ pub fn build(&self, input: &str) -> CommandContext<S> {
+ CommandContext {
+ arguments: self.arguments.clone(),
+ root_node: self.root.clone(),
+ nodes: self.nodes.clone(),
+ source: self.source.clone(),
+ command: self.command.clone(),
+ child: self.child.clone().map(|c| Rc::new(c.build(&input))),
+ range: self.range.clone(),
+ forks: self.forks,
+ modifier: self.modifier.clone(),
+ input: input.to_string(),
+ }
+ }
+}
+
+impl<S: Any + Clone> Debug for CommandContextBuilder<S> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CommandContextBuilder")
+ // .field("arguments", &self.arguments)
+ .field("root", &self.root)
+ .field("nodes", &self.nodes)
+ // .field("dispatcher", &self.dispatcher)
+ // .field("source", &self.source)
+ // .field("command", &self.command)
+ .field("child", &self.child)
+ .field("range", &self.range)
+ // .field("modifier", &self.modifier)
+ .field("forks", &self.forks)
+ .finish()
+ }
+}
+
+#[derive(Clone)]
+pub struct ParsedArgument {
+ pub range: StringRange,
+ pub result: Rc<dyn Any>,
+}
+
+#[derive(Clone)]
+/// A built `CommandContextBuilder`.
+pub struct CommandContext<S: Any + Clone> {
+ pub source: Rc<S>,
+ pub input: String,
+ pub arguments: HashMap<String, ParsedArgument>,
+ pub command: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
+ pub root_node: Rc<RefCell<CommandNode<S>>>,
+ pub nodes: Vec<Rc<CommandNode<S>>>,
+ pub range: StringRange,
+ pub child: Option<Rc<CommandContext<S>>>,
+ pub modifier: Option<Rc<dyn RedirectModifier<S>>>,
+ pub forks: bool,
+}
+
+impl<S: Any + Clone> CommandContext<S> {
+ pub fn copy_for(&self, source: Rc<S>) -> Self {
+ if Rc::ptr_eq(&source, &self.source) {
+ return self.clone();
+ }
+ return CommandContext {
+ source,
+ input: self.input.clone(),
+ arguments: self.arguments.clone(),
+ command: self.command.clone(),
+ root_node: self.root_node.clone(),
+ nodes: self.nodes.clone(),
+ range: self.range.clone(),
+ child: self.child.clone(),
+ modifier: self.modifier.clone(),
+ forks: self.forks,
+ };
+ }
+
+ pub fn has_nodes(&self) -> bool {
+ return !self.nodes.is_empty();
+ }
+}
diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs
deleted file mode 100644
index 8db1487f..00000000
--- a/azalea-brigadier/src/context/command_context.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-use super::{
- parsed_argument::ParsedArgument, parsed_command_node::ParsedCommandNode,
- string_range::StringRange,
-};
-use crate::{
- arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier,
- tree::command_node::CommandNodeTrait,
-};
-use std::{any::Any, collections::HashMap};
-
-pub struct CommandContext<'a, S> {
- source: S,
- input: String,
- command: &'a dyn Command<S>,
- arguments: HashMap<String, ParsedArgument<Box<dyn Any>>>,
- root_node: &'a dyn CommandNodeTrait<S>,
- nodes: Vec<ParsedCommandNode<S>>,
- range: StringRange,
- child: Option<&'a CommandContext<'a, S>>,
- modifier: Option<&'a dyn RedirectModifier<S>>,
- forks: bool,
-}
-
-impl<S> CommandContext<'_, S>
-where
- S: PartialEq,
-{
- pub fn clone_for(&self, source: S) -> Self {
- if self.source == source {
- return *self;
- }
- Self {
- source,
- input: self.input.clone(),
- command: self.command.clone(),
- arguments: self.arguments.clone(),
- root_node: self.root_node.clone(),
- nodes: self.nodes.clone(),
- range: self.range.clone(),
- child: self.child.clone(),
- modifier: self.modifier.clone(),
- forks: self.forks,
- }
- }
-
- fn child(&self) -> &Option<CommandContext<S>> {
- &self.child
- }
-
- fn last_child(&self) -> &CommandContext<S> {
- let mut result = self;
- while result.child.is_some() {
- result = result.child.as_ref().unwrap();
- }
- result
- }
-
- fn command(&self) -> &dyn Command<S> {
- &self.command
- }
-
- fn source(&self) -> &S {
- &self.source
- }
-
- // public <V> V getArgument(final String name, final Class<V> clazz) {
- // final ParsedArgument<S, ?> argument = arguments.get(name);
-
- // if (argument == null) {
- // throw new IllegalArgumentException("No such argument '" + name + "' exists on this command");
- // }
-
- // final Object result = argument.getResult();
- // if (PRIMITIVE_TO_WRAPPER.getOrDefault(clazz, clazz).isAssignableFrom(result.getClass())) {
- // return (V) result;
- // } else {
- // throw new IllegalArgumentException("Argument '" + name + "' is defined as " + result.getClass().getSimpleName() + ", not " + clazz);
- // }
- // }
- fn get_argument<V>(&self, name: &str) -> Result<V, String> {
- let argument = self.arguments.get(name);
-
- if argument.is_none() {
- return Err(format!(
- "No such argument '{}' exists on this command",
- name
- ));
- }
-
- let result = argument.unwrap().result();
- Ok(result)
- }
-}
diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs
deleted file mode 100644
index ba25849c..00000000
--- a/azalea-brigadier/src/context/command_context_builder.rs
+++ /dev/null
@@ -1,176 +0,0 @@
-use crate::{
- arguments::argument_type::ArgumentType, command::Command,
- command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier,
- tree::command_node::CommandNodeTrait,
-};
-use std::fmt::Debug;
-use std::{any::Any, collections::HashMap};
-
-use super::{
- command_context::CommandContext, parsed_argument::ParsedArgument,
- parsed_command_node::ParsedCommandNode, string_range::StringRange,
- suggestion_context::SuggestionContext,
-};
-
-// public class CommandContextBuilder<S> {
-// private final Map<String, ParsedArgument<S, ?>> arguments = new LinkedHashMap<>();
-// private final CommandNode<S> rootNode;
-// private final List<ParsedCommandNode<S>> nodes = new ArrayList<>();
-// private final CommandDispatcher<S> dispatcher;
-// private S source;
-// private Command<S> command;
-// private CommandContextBuilder<S> child;
-// private StringRange range;
-// private RedirectModifier<S> modifier = null;
-// private boolean forks;
-
-#[derive(Clone)]
-pub struct CommandContextBuilder<'a, S> {
- arguments: HashMap<String, ParsedArgument<Box<dyn Any>>>,
- root_node: &'a dyn CommandNodeTrait<S>,
- nodes: Vec<ParsedCommandNode<S>>,
- dispatcher: CommandDispatcher<'a, S>,
- source: S,
- command: Box<dyn Command<S>>,
- child: Box<Option<CommandContextBuilder<'a, S>>>,
- range: StringRange,
- modifier: Option<Box<dyn RedirectModifier<S>>>,
- forks: bool,
-}
-
-// public CommandContextBuilder(final CommandDispatcher<S> dispatcher, final S source, final CommandNode<S> rootNode, final int start) {
-// this.rootNode = rootNode;
-// this.dispatcher = dispatcher;
-// this.source = source;
-// this.range = StringRange.at(start);
-// }
-
-impl<S> CommandContextBuilder<'_, S> {
- pub fn new(
- dispatcher: CommandDispatcher<S>,
- source: S,
- root_node: &dyn CommandNodeTrait<S>,
- start: usize,
- ) -> Self {
- Self {
- root_node: &root_node,
- dispatcher,
- source,
- range: StringRange::at(start),
- ..Default::default()
- }
- }
-
- pub fn with_source(mut self, source: S) -> Self {
- self.source = source;
- self
- }
-
- pub fn source(&self) -> &S {
- &self.source
- }
-
- pub fn root_node(&self) -> &dyn CommandNodeTrait<S> {
- &self.root_node
- }
-
- pub fn with_argument(mut self, name: String, argument: ParsedArgument<Box<dyn Any>>) -> Self {
- self.arguments.insert(name, argument);
- self
- }
-
- pub fn arguments(&self) -> &HashMap<String, ParsedArgument<Box<dyn Any>>> {
- &self.arguments
- }
-
- pub fn with_command(mut self, command: &dyn Command<S>) -> Self {
- self.command = command;
- self
- }
-
- pub fn with_node(mut self, node: dyn CommandNodeTrait<S>, range: StringRange) -> Self {
- self.nodes.push(ParsedCommandNode::new(node, range));
- self.range = StringRange::encompassing(&self.range, &range);
- self.modifier = node.redirect_modifier();
- self.forks = node.is_fork();
- self
- }
-
- pub fn with_child(mut self, child: CommandContextBuilder<S>) -> Self {
- self.child = Some(child);
- self
- }
-
- pub fn child(&self) -> Option<&CommandContextBuilder<S>> {
- self.child.as_ref()
- }
-
- pub fn last_child(&self) -> Option<&CommandContextBuilder<S>> {
- let mut result = self;
- while let Some(child) = result.child() {
- result = child;
- }
- Some(result)
- }
-
- pub fn command(&self) -> &dyn Command<S> {
- &*self.command
- }
-
- pub fn nodes(&self) -> &Vec<ParsedCommandNode<S>> {
- &self.nodes
- }
-
- pub fn build(self, input: &str) -> CommandContext<S> {
- CommandContext {
- source: self.source,
- input,
- arguments: self.arguments,
- command: self.command,
- root_node: self.root_node,
- nodes: self.nodes,
- range: self.range,
- child: self.child.map(|child| child.build(input)),
- modifier: self.modifier,
- forks: self.forks,
- }
- }
-
- pub fn dispatcher(&self) -> &CommandDispatcher<S> {
- &self.dispatcher
- }
-
- pub fn range(&self) -> &StringRange {
- &self.range
- }
-
- pub fn find_suggestion_context(&self, cursor: i32) -> Result<SuggestionContext<S>, String> {
- if self.range.start() <= cursor {
- if self.range.end() < cursor {
- if let Some(child) = self.child() {
- child.find_suggestion_context(cursor);
- } else if !self.nodes.is_empty() {
- let last = self.nodes.last().unwrap();
- let end = last.range().end() + 1;
- return SuggestionContext::new(last.node(), end);
- } else {
- return SuggestionContext::new(self.root_node, self.range.start());
- }
- } else {
- let prev = self.root_node;
- for node in &self.nodes {
- let node_range = node.range();
- if node_range.start() <= cursor && cursor <= node_range.end() {
- return SuggestionContext::new(prev, node_range.start());
- }
- prev = node.node();
- }
- if prev.is_none() {
- return Err(String::from("Can't find node before cursor"));
- }
- return SuggestionContext::new(prev.unwrap(), self.range.start());
- }
- }
- Err(String::from("Can't find node before cursor"))
- }
-}
diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs
deleted file mode 100644
index 196d7c5b..00000000
--- a/azalea-brigadier/src/context/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub mod command_context;
-pub mod command_context_builder;
-pub mod parsed_argument;
-pub mod parsed_command_node;
-pub mod string_range;
-pub mod suggestion_context;
diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs
deleted file mode 100644
index e0bdf97b..00000000
--- a/azalea-brigadier/src/context/parsed_argument.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use super::string_range::StringRange;
-
-#[derive(PartialEq, Eq, Hash, Clone)]
-pub struct ParsedArgument<T> {
- range: StringRange,
- result: T,
-}
-
-impl<T> ParsedArgument<T> {
- fn new(start: usize, end: usize, result: &T) -> Self {
- Self {
- range: StringRange::between(start, end),
- result,
- }
- }
-
- fn range(&self) -> &StringRange {
- &self.range
- }
-
- fn result(&self) -> &T {
- &self.result
- }
-}
diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs
deleted file mode 100644
index 21d1b2e9..00000000
--- a/azalea-brigadier/src/context/parsed_command_node.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use super::string_range::StringRange;
-use crate::tree::command_node::CommandNodeTrait;
-
-pub struct ParsedCommandNode<S> {
- node: Box<dyn CommandNodeTrait<S>>,
- range: StringRange,
-}
-
-impl<S> ParsedCommandNode<S> {
- fn new(node: dyn CommandNodeTrait<S>, range: StringRange) -> Self {
- Self { node, range }
- }
-
- fn node(&self) -> &dyn CommandNodeTrait<S> {
- &self.node
- }
-
- fn range(&self) -> &StringRange {
- &self.range
- }
-}
-
-impl<S> Clone for ParsedCommandNode<S> {
- fn clone_from(&mut self, source: &Self) {
- Self {
- node: self.node.clone(),
- range: self.range.clone(),
- }
- }
-}
diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs
deleted file mode 100644
index 51a053c1..00000000
--- a/azalea-brigadier/src/context/suggestion_context.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use crate::tree::command_node::CommandNodeTrait;
-
-pub struct SuggestionContext<'a, S> {
- parent: &'a dyn CommandNodeTrait<S>,
- start_pos: usize,
-}
diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs
new file mode 100644
index 00000000..65fe5b0a
--- /dev/null
+++ b/azalea-brigadier/src/dispatcher.rs
@@ -0,0 +1,242 @@
+use crate::{
+ builder::argument_builder::ArgumentBuilder,
+ context::{CommandContext, CommandContextBuilder},
+ exceptions::{
+ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
+ },
+ parse_results::ParseResults,
+ string_range::StringRange,
+ string_reader::StringReader,
+ tree::CommandNode,
+};
+use std::{
+ any::Any, cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomData, mem, rc::Rc,
+};
+
+#[derive(Default)]
+pub struct CommandDispatcher<S: Any + Clone> {
+ root: Rc<RefCell<CommandNode<S>>>,
+ _marker: PhantomData<S>,
+}
+
+impl<S: Any + Clone> CommandDispatcher<S> {
+ pub fn new() -> Self {
+ Self {
+ root: Rc::new(RefCell::new(CommandNode::default())),
+ _marker: PhantomData,
+ }
+ }
+
+ pub fn register(&mut self, node: ArgumentBuilder<S>) {
+ println!("register {:#?}", node);
+ let build = Rc::new(RefCell::new(node.build()));
+ self.root.borrow_mut().add_child(&build);
+ // println!("build: {:#?}", build);
+ }
+
+ pub fn parse(&self, command: StringReader, source: S) -> ParseResults<S> {
+ let context = CommandContextBuilder::new(
+ Rc::new(self.clone()),
+ Rc::new(source),
+ self.root.clone(),
+ command.cursor(),
+ );
+ self.parse_nodes(&self.root, &command, context).unwrap()
+ }
+
+ fn parse_nodes(
+ &self,
+ node: &Rc<RefCell<CommandNode<S>>>,
+ original_reader: &StringReader,
+ context_so_far: CommandContextBuilder<S>,
+ ) -> Result<ParseResults<S>, CommandSyntaxException> {
+ let source = context_so_far.source.clone();
+ let mut errors = HashMap::<Rc<CommandNode<S>>, CommandSyntaxException>::new();
+ let mut potentials: Vec<ParseResults<S>> = vec![];
+ let cursor = original_reader.cursor();
+
+ for child in node
+ .borrow()
+ .get_relevant_nodes(&mut original_reader.clone())
+ {
+ if !child.borrow().can_use(source.clone()) {
+ continue;
+ }
+ let mut context = context_so_far.clone();
+ let mut reader = original_reader.clone();
+
+ let parse_with_context_result =
+ child.borrow().parse_with_context(&mut reader, &mut context);
+ if let Err(ex) = parse_with_context_result {
+ errors.insert(
+ Rc::new((*child.borrow()).clone()),
+ BuiltInExceptions::DispatcherParseException {
+ message: ex.message(),
+ }
+ .create_with_context(&reader),
+ );
+ reader.cursor = cursor;
+ continue;
+ }
+ if reader.can_read() {
+ if reader.peek() != ' ' {
+ errors.insert(
+ Rc::new((*child.borrow()).clone()),
+ BuiltInExceptions::DispatcherExpectedArgumentSeparator
+ .create_with_context(&reader),
+ );
+ reader.cursor = cursor;
+ continue;
+ }
+ }
+
+ context.with_command(&child.borrow().command);
+ if reader.can_read_length(if child.borrow().redirect.is_none() {
+ 2
+ } else {
+ 1
+ }) {
+ reader.skip();
+ if let Some(redirect) = &child.borrow().redirect {
+ let child_context = CommandContextBuilder::new(
+ Rc::new(self.clone()),
+ source.clone(),
+ redirect.clone(),
+ reader.cursor,
+ );
+ let parse = self
+ .parse_nodes(redirect, &reader, child_context)
+ .expect("Parsing nodes failed");
+ context.with_child(Rc::new(parse.context));
+ } else {
+ let parse = self
+ .parse_nodes(&child, &reader, context)
+ .expect("Parsing nodes failed");
+ potentials.push(parse);
+ }
+ } else {
+ potentials.push(ParseResults {
+ context,
+ reader,
+ exceptions: HashMap::new(),
+ });
+ }
+ }
+
+ if potentials.len() > 0 {
+ if potentials.len() > 1 {
+ potentials.sort_by(|a, b| {
+ if !a.reader.can_read() && b.reader.can_read() {
+ return Ordering::Less;
+ };
+ if a.reader.can_read() && !b.reader.can_read() {
+ return Ordering::Greater;
+ };
+ if a.exceptions.is_empty() && !b.exceptions.is_empty() {
+ return Ordering::Less;
+ };
+ if !a.exceptions.is_empty() && b.exceptions.is_empty() {
+ return Ordering::Greater;
+ };
+ Ordering::Equal
+ })
+ }
+ let best_potential = potentials.into_iter().next().unwrap();
+ println!("chosen {:#?}", best_potential);
+ return Ok(best_potential);
+ }
+
+ Ok(ParseResults {
+ context: context_so_far,
+ reader: original_reader.clone(),
+ exceptions: errors,
+ })
+ }
+
+ /// Executes a given pre-parsed command.
+ pub fn execute(parse: ParseResults<S>) -> Result<i32, CommandSyntaxException> {
+ if parse.reader.can_read() {
+ println!("can read from reader {}", parse.reader.cursor);
+ if parse.exceptions.len() == 1 {
+ return Err(parse.exceptions.values().next().unwrap().clone());
+ }
+ if parse.context.range.is_empty() {
+ return Err(
+ BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader)
+ );
+ }
+ return Err(
+ BuiltInExceptions::DispatcherUnknownArgument.create_with_context(&parse.reader)
+ );
+ }
+ println!("a");
+ let mut result = 0i32;
+ let mut successful_forks = 0;
+ let mut forked = false;
+ let mut found_command = false;
+ let command = parse.reader.string();
+ let original = parse.context.build(&command);
+ let mut contexts = vec![original];
+ let mut next: Vec<CommandContext<S>> = vec![];
+
+ while contexts.len() > 0 {
+ for context in contexts.iter() {
+ let child = &context.child;
+ if let Some(child) = child {
+ forked |= child.forks;
+ if child.has_nodes() {
+ found_command = true;
+ let modifier = &context.modifier;
+ if let Some(modifier) = modifier {
+ let results = modifier.apply(context);
+ if let Ok(results) = results {
+ if !results.is_empty() {
+ next.extend(results.iter().map(|s| child.copy_for(s.clone())));
+ }
+ } else {
+ // TODO
+ // self.consumer.on_command_complete(context, false, 0);
+ if !forked {
+ return Err(results.err().unwrap());
+ }
+ }
+ } else {
+ next.push(child.copy_for(context.source.clone()));
+ }
+ }
+ } else if let Some(context_command) = &context.command {
+ found_command = true;
+
+ let value = context_command(context);
+ result += value;
+ // consumer.on_command_complete(context, true, value);
+ successful_forks += 1;
+
+ // TODO: allow context_command to error and handle those errors
+ }
+ }
+
+ // move next into contexts and clear next
+ mem::swap(&mut contexts, &mut next);
+ next.clear();
+ }
+
+ if !found_command {
+ // consumer.on_command_complete(original, false, 0);
+ return Err(
+ BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader)
+ );
+ }
+
+ Ok(if forked { successful_forks } else { result })
+ }
+}
+
+impl<S: Any + Clone> Clone for CommandDispatcher<S> {
+ fn clone(&self) -> Self {
+ Self {
+ root: self.root.clone(),
+ _marker: PhantomData,
+ }
+ }
+}
diff --git a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs b/azalea-brigadier/src/exceptions/builtin_exception_provider.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs
index 1533364b..5f2e1605 100644
--- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs
+++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs
@@ -1,6 +1,6 @@
use std::fmt;
-use crate::{immutable_string_reader::ImmutableStringReader, message::Message};
+use crate::{message::Message, string_reader::StringReader};
use super::command_syntax_exception::CommandSyntaxException;
@@ -35,9 +35,9 @@ pub enum BuiltInExceptions {
ReaderExpectedBool,
ReaderExpectedSymbol { symbol: char },
- ReaderUnknownCommand,
- ReaderUnknownArgument,
- DusoatcgerExpectedArgumentSeparator,
+ DispatcherUnknownCommand,
+ DispatcherUnknownArgument,
+ DispatcherExpectedArgumentSeparator,
DispatcherParseException { message: String },
}
@@ -127,13 +127,13 @@ impl fmt::Debug for BuiltInExceptions {
write!(f, "Expected '{}'", symbol)
}
- BuiltInExceptions::ReaderUnknownCommand => {
+ BuiltInExceptions::DispatcherUnknownCommand => {
write!(f, "Unknown command")
}
- BuiltInExceptions::ReaderUnknownArgument => {
+ BuiltInExceptions::DispatcherUnknownArgument => {
write!(f, "Incorrect argument for command")
}
- BuiltInExceptions::DusoatcgerExpectedArgumentSeparator => {
+ BuiltInExceptions::DispatcherExpectedArgumentSeparator => {
write!(
f,
"Expected whitespace to end one argument, but found trailing data"
@@ -152,7 +152,7 @@ impl BuiltInExceptions {
CommandSyntaxException::create(self, message)
}
- pub fn create_with_context(self, reader: &dyn ImmutableStringReader) -> CommandSyntaxException {
+ pub fn create_with_context(self, reader: &StringReader) -> CommandSyntaxException {
let message = Message::from(format!("{:?}", self));
CommandSyntaxException::new(self, message, reader.string(), reader.cursor())
}
diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs
index 38aa1c3a..6cd4e53d 100644
--- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs
+++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs
@@ -3,6 +3,7 @@ use std::{cmp, fmt, rc::Rc};
use super::builtin_exceptions::BuiltInExceptions;
use crate::message::Message;
+#[derive(Clone)]
pub struct CommandSyntaxException {
type_: BuiltInExceptions,
message: Message,
@@ -59,7 +60,10 @@ impl CommandSyntaxException {
builder.push_str("...");
}
- builder.push_str(&input[cmp::max(0, cursor - CONTEXT_AMOUNT)..cursor]);
+ builder.push_str(
+ &input
+ [(cmp::max(0, cursor as isize - CONTEXT_AMOUNT as isize) as usize)..cursor],
+ );
builder.push_str("<--[HERE]");
return Some(builder);
diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs
index 4a82b01e..0bca556e 100644
--- a/azalea-brigadier/src/exceptions/mod.rs
+++ b/azalea-brigadier/src/exceptions/mod.rs
@@ -1,3 +1,2 @@
-pub mod builtin_exception_provider;
pub mod builtin_exceptions;
pub mod command_syntax_exception;
diff --git a/azalea-brigadier/src/immutable_string_reader.rs b/azalea-brigadier/src/immutable_string_reader.rs
deleted file mode 100644
index 53531c64..00000000
--- a/azalea-brigadier/src/immutable_string_reader.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-pub trait ImmutableStringReader {
- fn string(&self) -> &str;
- fn remaining_length(&self) -> usize;
- fn total_length(&self) -> usize;
- fn cursor(&self) -> usize;
- fn get_read(&self) -> &str;
- fn remaining(&self) -> &str;
- fn can_read_length(&self, length: usize) -> bool;
- fn can_read(&self) -> bool;
- fn peek(&self) -> char;
- fn peek_offset(&self, offset: usize) -> char;
-}
diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs
index a1ac267c..476764d0 100644
--- a/azalea-brigadier/src/lib.rs
+++ b/azalea-brigadier/src/lib.rs
@@ -1,32 +1,55 @@
-#[macro_use]
-extern crate lazy_static;
-
-#[macro_use]
-extern crate enum_dispatch;
-
-mod ambiguity_consumer;
-mod arguments;
-mod builder;
-mod command;
-mod command_dispatcher;
-mod context;
-mod exceptions;
-mod immutable_string_reader;
-mod literal_message;
-mod message;
-mod parse_results;
-mod redirect_modifier;
-mod result_consumer;
-mod single_redirect_modifier;
-mod string_reader;
-mod suggestion;
-mod tree;
+pub mod builder;
+pub mod context;
+pub mod dispatcher;
+pub mod exceptions;
+pub mod message;
+pub mod modifier;
+pub mod parse_results;
+pub mod parsers;
+pub mod string_range;
+pub mod string_reader;
+pub mod tree;
#[cfg(test)]
mod tests {
+
+ use std::rc::Rc;
+
+ use crate::{
+ builder::{literal_argument_builder::literal, required_argument_builder::argument},
+ dispatcher::CommandDispatcher,
+ parsers::integer,
+ };
+
+ struct CommandSourceStack {
+ player: String,
+ }
+
#[test]
fn it_works() {
- let result = 2 + 2;
- assert_eq!(result, 4);
+ let mut dispatcher = CommandDispatcher::<Rc<CommandSourceStack>>::new();
+
+ let source = Rc::new(CommandSourceStack {
+ player: "player".to_string(),
+ });
+
+ dispatcher.register(
+ literal("foo")
+ .then(argument("bar", integer()).executes(|c| {
+ // println!("Bar is {}", get_integer(c, "bar"));
+ 2
+ }))
+ .executes(|c| {
+ println!("Called foo with no arguments");
+ 1
+ }),
+ );
+
+ let parse = dispatcher.parse("foo 123".to_string().into(), source);
+ println!(
+ "{}",
+ CommandDispatcher::<Rc<CommandSourceStack>>::execute(parse).unwrap()
+ );
+ // assert_eq!(dispatcher.execute("foo bar", source), 2);
}
}
diff --git a/azalea-brigadier/src/literal_message.rs b/azalea-brigadier/src/literal_message.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/literal_message.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/main.rs b/azalea-brigadier/src/main.rs
new file mode 100644
index 00000000..53f2efa8
--- /dev/null
+++ b/azalea-brigadier/src/main.rs
@@ -0,0 +1,38 @@
+use std::rc::Rc;
+
+use rust_command_parser::{
+ builder::{literal_argument_builder::literal, required_argument_builder::argument},
+ dispatcher::CommandDispatcher,
+ parsers::integer,
+};
+
+struct CommandSourceStack {
+ player: String,
+}
+
+pub fn main() {
+ let mut dispatcher = CommandDispatcher::<Rc<CommandSourceStack>>::new();
+
+ let source = Rc::new(CommandSourceStack {
+ player: "player".to_string(),
+ });
+
+ dispatcher.register(
+ literal("foo")
+ .then(argument("bar", integer()).executes(|c| {
+ // println!("Bar is {}", get_integer(c, "bar"));
+ 2
+ }))
+ .executes(|c| {
+ println!("Called foo with no arguments");
+ 1
+ }),
+ );
+
+ let parse = dispatcher.parse("foo 123".to_string().into(), source);
+ println!("{:?}", parse);
+ println!(
+ "{}",
+ CommandDispatcher::<Rc<CommandSourceStack>>::execute(parse).unwrap()
+ );
+}
diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs
index 42894d0e..9d133c7e 100644
--- a/azalea-brigadier/src/message.rs
+++ b/azalea-brigadier/src/message.rs
@@ -1,7 +1,7 @@
use std::rc::Rc;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
-pub struct Message(Rc<String>);
+pub struct Message(String);
impl Message {
pub fn string(&self) -> String {
@@ -11,6 +11,6 @@ impl Message {
impl From<String> for Message {
fn from(s: String) -> Self {
- Self(Rc::new(s))
+ Self(s)
}
}
diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs
new file mode 100644
index 00000000..84528696
--- /dev/null
+++ b/azalea-brigadier/src/modifier.rs
@@ -0,0 +1,9 @@
+use std::{any::Any, rc::Rc};
+
+use crate::{
+ context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException,
+};
+
+pub trait RedirectModifier<S: Any + Clone> {
+ fn apply(&self, context: &CommandContext<S>) -> Result<Vec<Rc<S>>, CommandSyntaxException>;
+}
diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs
index 8b137891..fb862dec 100644
--- a/azalea-brigadier/src/parse_results.rs
+++ b/azalea-brigadier/src/parse_results.rs
@@ -1 +1,21 @@
+use crate::{
+ context::CommandContextBuilder, exceptions::command_syntax_exception::CommandSyntaxException,
+ string_reader::StringReader, tree::CommandNode,
+};
+use std::{any::Any, collections::HashMap, fmt::Debug, rc::Rc};
+pub struct ParseResults<S: Any + Clone> {
+ pub context: CommandContextBuilder<S>,
+ pub reader: StringReader,
+ pub exceptions: HashMap<Rc<CommandNode<S>>, CommandSyntaxException>,
+}
+
+impl<S: Any + Clone> Debug for ParseResults<S> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ParseResults")
+ .field("context", &self.context)
+ // .field("reader", &self.reader)
+ .field("exceptions", &self.exceptions)
+ .finish()
+ }
+}
diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs
new file mode 100644
index 00000000..a497e0d1
--- /dev/null
+++ b/azalea-brigadier/src/parsers.rs
@@ -0,0 +1,21 @@
+use std::{any::Any, marker::PhantomData, rc::Rc};
+
+use crate::string_reader::StringReader;
+
+pub trait Parser {
+ fn parse(&self, reader: &mut StringReader) -> Option<Rc<dyn Any>>;
+}
+
+struct Integer {}
+impl Parser for Integer {
+ fn parse(&self, reader: &mut StringReader) -> Option<Rc<dyn Any>> {
+ let start = reader.cursor;
+ let result = reader.read_int();
+ // TODO: check min and max
+ Some(Rc::new(result))
+ }
+}
+
+pub fn integer() -> impl Parser {
+ Integer {}
+}
diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs
deleted file mode 100644
index 7a6d4db5..00000000
--- a/azalea-brigadier/src/redirect_modifier.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use dyn_clonable::*;
-
-use crate::{
- context::command_context::CommandContext,
- exceptions::command_syntax_exception::CommandSyntaxException,
-};
-
-#[clonable]
-pub trait RedirectModifier<S>: Clone {
- fn apply(&self, context: CommandContext<S>) -> Result<Vec<S>, CommandSyntaxException>;
-}
diff --git a/azalea-brigadier/src/result_consumer.rs b/azalea-brigadier/src/result_consumer.rs
deleted file mode 100644
index 8b137891..00000000
--- a/azalea-brigadier/src/result_consumer.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs
deleted file mode 100644
index dd63244d..00000000
--- a/azalea-brigadier/src/single_redirect_modifier.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use crate::{
- context::command_context::CommandContext,
- exceptions::command_syntax_exception::CommandSyntaxException,
-};
-
-pub trait SingleRedirectModifier<S> {
- fn apply(&self, context: CommandContext<S>) -> Result<S, CommandSyntaxException>;
-}
diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/string_range.rs
index 87098a1a..8ca88624 100644
--- a/azalea-brigadier/src/context/string_range.rs
+++ b/azalea-brigadier/src/string_range.rs
@@ -1,6 +1,6 @@
use std::cmp;
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct StringRange {
start: usize,
end: usize,
@@ -31,7 +31,7 @@ impl StringRange {
self.end
}
- pub fn get(&self, reader: &str) -> &str {
+ pub fn get<'a>(&self, reader: &'a str) -> &'a str {
&reader[self.start..self.end]
}
diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs
index 694edccb..4b390155 100644
--- a/azalea-brigadier/src/string_reader.rs
+++ b/azalea-brigadier/src/string_reader.rs
@@ -1,14 +1,11 @@
-use crate::{
- exceptions::{
- builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
- },
- immutable_string_reader::ImmutableStringReader,
+use crate::exceptions::{
+ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
};
-use std::str::FromStr;
+use std::{rc::Rc, str::FromStr};
#[derive(Clone)]
-pub struct StringReader<'a> {
- string: &'a str,
+pub struct StringReader {
+ string: String,
pub cursor: usize,
}
@@ -16,63 +13,53 @@ const SYNTAX_ESCAPE: char = '\\';
const SYNTAX_DOUBLE_QUOTE: char = '"';
const SYNTAX_SINGLE_QUOTE: char = '\'';
-// impl<'a> From<&'a str> for &StringReader<'a> {}
-
-// impl StringReader<'_> {
-// fn from(string: &str) -> StringReader {
-// StringReader { string, cursor: 0 }
-// }
-// }
-
-impl<'a> From<&'a str> for StringReader<'a> {
- fn from(string: &'a str) -> Self {
+impl From<String> for StringReader {
+ fn from(string: String) -> Self {
Self { string, cursor: 0 }
}
}
-impl ImmutableStringReader for StringReader<'_> {
- fn string(&self) -> &str {
- self.string
+impl StringReader {
+ pub fn string(&self) -> &str {
+ &self.string
}
- fn remaining_length(&self) -> usize {
+ pub fn remaining_length(&self) -> usize {
self.string.len() - self.cursor
}
- fn total_length(&self) -> usize {
+ pub fn total_length(&self) -> usize {
self.string.len()
}
- fn get_read(&self) -> &str {
+ pub fn get_read(&self) -> &str {
&self.string[..self.cursor]
}
- fn remaining(&self) -> &str {
+ pub fn remaining(&self) -> &str {
&self.string[self.cursor..]
}
- fn can_read_length(&self, length: usize) -> bool {
+ pub fn can_read_length(&self, length: usize) -> bool {
self.cursor + length <= self.string.len()
}
- fn can_read(&self) -> bool {
+ pub fn can_read(&self) -> bool {
self.can_read_length(1)
}
- fn peek(&self) -> char {
+ pub fn peek(&self) -> char {
self.string.chars().nth(self.cursor).unwrap()
}
- fn peek_offset(&self, offset: usize) -> char {
+ pub fn peek_offset(&self, offset: usize) -> char {
self.string.chars().nth(self.cursor + offset).unwrap()
}
- fn cursor(&self) -> usize {
+ pub fn cursor(&self) -> usize {
self.cursor
}
-}
-impl StringReader<'_> {
pub fn read(&mut self) -> char {
let c = self.peek();
self.cursor += 1;
@@ -99,7 +86,7 @@ impl StringReader<'_> {
pub fn read_int(&mut self) -> Result<i32, CommandSyntaxException> {
let start = self.cursor;
- while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) {
+ while self.can_read() && StringReader::is_allowed_number(self.peek()) {
self.skip();
}
let number = &self.string[start..self.cursor];
@@ -120,7 +107,7 @@ impl StringReader<'_> {
pub fn read_long(&mut self) -> Result<i64, CommandSyntaxException> {
let start = self.cursor;
- while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) {
+ while self.can_read() && StringReader::is_allowed_number(self.peek()) {
self.skip();
}
let number = &self.string[start..self.cursor];
@@ -162,7 +149,7 @@ impl StringReader<'_> {
pub fn read_float(&mut self) -> Result<f32, CommandSyntaxException> {
let start = self.cursor;
- while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) {
+ while self.can_read() && StringReader::is_allowed_number(self.peek()) {
self.skip();
}
let number = &self.string[start..self.cursor];
@@ -204,7 +191,7 @@ impl StringReader<'_> {
return Ok(String::new());
}
let next = self.peek();
- if !StringReader::<'_>::is_quoted_string_start(next) {
+ if !StringReader::is_quoted_string_start(next) {
return Err(BuiltInExceptions::ReaderExpectedStartOfQuote.create_with_context(self));
}
self.skip();
@@ -245,7 +232,7 @@ impl StringReader<'_> {
return Ok(String::new());
}
let next = self.peek();
- if StringReader::<'_>::is_quoted_string_start(next) {
+ if StringReader::is_quoted_string_start(next) {
self.skip();
return self.read_string_until(next);
}
@@ -286,7 +273,7 @@ mod test {
#[test]
fn can_read() {
- let mut reader = StringReader::from("abc");
+ let mut reader = StringReader::from("abc".to_string());
assert_eq!(reader.can_read(), true);
reader.skip(); // 'a'
assert_eq!(reader.can_read(), true);
@@ -298,7 +285,7 @@ mod test {
#[test]
fn get_remaining_length() {
- let mut reader = StringReader::from("abc");
+ let mut reader = StringReader::from("abc".to_string());
assert_eq!(reader.remaining_length(), 3);
reader.cursor = 1;
assert_eq!(reader.remaining_length(), 2);
@@ -310,7 +297,7 @@ mod test {
#[test]
fn can_read_length() {
- let reader = StringReader::from("abc");
+ let reader = StringReader::from("abc".to_string());
assert_eq!(reader.can_read_length(1), true);
assert_eq!(reader.can_read_length(2), true);
assert_eq!(reader.can_read_length(3), true);
@@ -320,7 +307,7 @@ mod test {
#[test]
fn peek() {
- let mut reader = StringReader::from("abc");
+ let mut reader = StringReader::from("abc".to_string());
assert_eq!(reader.peek(), 'a');
assert_eq!(reader.cursor(), 0);
reader.cursor = 2;
@@ -330,7 +317,7 @@ mod test {
#[test]
fn peek_length() {
- let mut reader = StringReader::from("abc");
+ let mut reader = StringReader::from("abc".to_string());
assert_eq!(reader.peek_offset(0), 'a');
assert_eq!(reader.peek_offset(2), 'c');
assert_eq!(reader.cursor(), 0);
@@ -341,7 +328,7 @@ mod test {
#[test]
fn read() {
- let mut reader = StringReader::from("abc");
+ let mut reader = StringReader::from("abc".to_string());
assert_eq!(reader.read(), 'a');
assert_eq!(reader.read(), 'b');
assert_eq!(reader.read(), 'c');
@@ -350,14 +337,14 @@ mod test {
#[test]
fn skip() {
- let mut reader = StringReader::from("abc");
+ let mut reader = StringReader::from("abc".to_string());
reader.skip();
assert_eq!(reader.cursor(), 1);
}
#[test]
fn get_remaining() {
- let mut reader = StringReader::from("Hello!");
+ let mut reader = StringReader::from("Hello!".to_string());
assert_eq!(reader.remaining(), "Hello!");
reader.cursor = 3;
assert_eq!(reader.remaining(), "lo!");
@@ -367,7 +354,7 @@ mod test {
#[test]
fn get_read() {
- let mut reader = StringReader::from("Hello!");
+ let mut reader = StringReader::from("Hello!".to_string());
assert_eq!(reader.get_read(), "");
reader.cursor = 3;
assert_eq!(reader.get_read(), "Hel");
@@ -377,28 +364,28 @@ mod test {
#[test]
fn skip_whitespace_none() {
- let mut reader = StringReader::from("Hello!");
+ let mut reader = StringReader::from("Hello!".to_string());
reader.skip_whitespace();
assert_eq!(reader.cursor(), 0);
}
#[test]
fn skip_whitespace_mixed() {
- let mut reader = StringReader::from(" \t \t\nHello!");
+ let mut reader = StringReader::from(" \t \t\nHello!".to_string());
reader.skip_whitespace();
assert_eq!(reader.cursor(), 5);
}
#[test]
fn skip_whitespace_empty() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
reader.skip_whitespace();
assert_eq!(reader.cursor(), 0);
}
#[test]
fn read_unquoted_string() {
- let mut reader = StringReader::from("hello world");
+ let mut reader = StringReader::from("hello world".to_string());
assert_eq!(reader.read_unquoted_string(), "hello");
assert_eq!(reader.get_read(), "hello");
assert_eq!(reader.remaining(), " world");
@@ -406,7 +393,7 @@ mod test {
#[test]
fn read_unquoted_string_empty() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
assert_eq!(reader.read_unquoted_string(), "");
assert_eq!(reader.get_read(), "");
assert_eq!(reader.remaining(), "");
@@ -414,7 +401,7 @@ mod test {
#[test]
fn read_unquoted_string_empty_with_remaining() {
- let mut reader = StringReader::from(" hello world");
+ let mut reader = StringReader::from(" hello world".to_string());
assert_eq!(reader.read_unquoted_string(), "");
assert_eq!(reader.get_read(), "");
assert_eq!(reader.remaining(), " hello world");
@@ -422,7 +409,7 @@ mod test {
#[test]
fn read_quoted_string() {
- let mut reader = StringReader::from("\"hello world\"");
+ let mut reader = StringReader::from("\"hello world\"".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "hello world");
assert_eq!(reader.get_read(), "\"hello world\"");
assert_eq!(reader.remaining(), "");
@@ -430,7 +417,7 @@ mod test {
#[test]
fn read_single_quoted_string() {
- let mut reader = StringReader::from("'hello world'");
+ let mut reader = StringReader::from("'hello world'".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "hello world");
assert_eq!(reader.get_read(), "'hello world'");
assert_eq!(reader.remaining(), "");
@@ -438,7 +425,7 @@ mod test {
#[test]
fn read_mixed_quoted_string_double_inside_single() {
- let mut reader = StringReader::from("'hello \"world\"'");
+ let mut reader = StringReader::from("'hello \"world\"'".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\"");
assert_eq!(reader.get_read(), "'hello \"world\"'");
assert_eq!(reader.remaining(), "");
@@ -446,7 +433,7 @@ mod test {
#[test]
fn read_mixed_quoted_string_single_inside_double() {
- let mut reader = StringReader::from("\"hello 'world'\"");
+ let mut reader = StringReader::from("\"hello 'world'\"".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "hello 'world'");
assert_eq!(reader.get_read(), "\"hello 'world'\"");
assert_eq!(reader.remaining(), "");
@@ -454,7 +441,7 @@ mod test {
#[test]
fn read_quoted_string_empty_quoted() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "");
assert_eq!(reader.get_read(), "");
assert_eq!(reader.remaining(), "");
@@ -462,7 +449,7 @@ mod test {
#[test]
fn read_quoted_string_empty_quoted_with_remaining() {
- let mut reader = StringReader::from("\"\" hello world");
+ let mut reader = StringReader::from("\"\" hello world".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "");
assert_eq!(reader.get_read(), "\"\"");
assert_eq!(reader.remaining(), " hello world");
@@ -470,7 +457,7 @@ mod test {
#[test]
fn read_quoted_string_with_escaped_quote() {
- let mut reader = StringReader::from("\"hello \\\"world\\\"\"");
+ let mut reader = StringReader::from("\"hello \\\"world\\\"\"".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\"");
assert_eq!(reader.get_read(), "\"hello \\\"world\\\"\"");
assert_eq!(reader.remaining(), "");
@@ -478,7 +465,7 @@ mod test {
#[test]
fn read_quoted_string_with_escaped_escapes() {
- let mut reader = StringReader::from("\"\\\\o/\"");
+ let mut reader = StringReader::from("\"\\\\o/\"".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "\\o/");
assert_eq!(reader.get_read(), "\"\\\\o/\"");
assert_eq!(reader.remaining(), "");
@@ -486,7 +473,7 @@ mod test {
#[test]
fn read_quoted_string_with_remaining() {
- let mut reader = StringReader::from("\"hello world\" foo bar");
+ let mut reader = StringReader::from("\"hello world\" foo bar".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "hello world");
assert_eq!(reader.get_read(), "\"hello world\"");
assert_eq!(reader.remaining(), " foo bar");
@@ -494,7 +481,7 @@ mod test {
#[test]
fn read_quoted_string_with_immediate_remaining() {
- let mut reader = StringReader::from("\"hello world\"foo bar");
+ let mut reader = StringReader::from("\"hello world\"foo bar".to_string());
assert_eq!(reader.read_quoted_string().unwrap(), "hello world");
assert_eq!(reader.get_read(), "\"hello world\"");
assert_eq!(reader.remaining(), "foo bar");
@@ -502,7 +489,7 @@ mod test {
#[test]
fn read_quoted_string_no_open() {
- let mut reader = StringReader::from("hello world\"");
+ let mut reader = StringReader::from("hello world\"".to_string());
let result = reader.read_quoted_string();
assert!(result.is_err());
if let Err(e) = result {
@@ -513,7 +500,7 @@ mod test {
#[test]
fn read_quoted_string_no_close() {
- let mut reader = StringReader::from("\"hello world");
+ let mut reader = StringReader::from("\"hello world".to_string());
let result = reader.read_quoted_string();
assert!(result.is_err());
if let Err(e) = result {
@@ -524,7 +511,7 @@ mod test {
#[test]
fn read_quoted_string_invalid_escape() {
- let mut reader = StringReader::from("\"hello\\nworld\"");
+ let mut reader = StringReader::from("\"hello\\nworld\"".to_string());
let result = reader.read_quoted_string();
assert!(result.is_err());
if let Err(e) = result {
@@ -538,7 +525,7 @@ mod test {
#[test]
fn read_quoted_string_invalid_quote_escape() {
- let mut reader = StringReader::from("'hello\\\"\'world");
+ let mut reader = StringReader::from("'hello\\\"\'world".to_string());
let result = reader.read_quoted_string();
assert!(result.is_err());
if let Err(e) = result {
@@ -552,7 +539,7 @@ mod test {
#[test]
fn read_string_no_quotes() {
- let mut reader = StringReader::from("hello world");
+ let mut reader = StringReader::from("hello world".to_string());
assert_eq!(reader.read_string().unwrap(), "hello");
assert_eq!(reader.get_read(), "hello");
assert_eq!(reader.remaining(), " world");
@@ -560,7 +547,7 @@ mod test {
#[test]
fn read_string_single_quotes() {
- let mut reader = StringReader::from("'hello world'");
+ let mut reader = StringReader::from("'hello world'".to_string());
assert_eq!(reader.read_string().unwrap(), "hello world");
assert_eq!(reader.get_read(), "'hello world'");
assert_eq!(reader.remaining(), "");
@@ -568,7 +555,7 @@ mod test {
#[test]
fn read_string_double_quotes() {
- let mut reader = StringReader::from("\"hello world\"");
+ let mut reader = StringReader::from("\"hello world\"".to_string());
assert_eq!(reader.read_string().unwrap(), "hello world");
assert_eq!(reader.get_read(), "\"hello world\"");
assert_eq!(reader.remaining(), "");
@@ -576,7 +563,7 @@ mod test {
#[test]
fn read_int() {
- let mut reader = StringReader::from("1234567890");
+ let mut reader = StringReader::from("1234567890".to_string());
assert_eq!(reader.read_int().unwrap(), 1234567890);
assert_eq!(reader.get_read(), "1234567890");
assert_eq!(reader.remaining(), "");
@@ -584,7 +571,7 @@ mod test {
#[test]
fn read_int_negative() {
- let mut reader = StringReader::from("-1234567890");
+ let mut reader = StringReader::from("-1234567890".to_string());
assert_eq!(reader.read_int().unwrap(), -1234567890);
assert_eq!(reader.get_read(), "-1234567890");
assert_eq!(reader.remaining(), "");
@@ -592,7 +579,7 @@ mod test {
#[test]
fn read_int_invalid() {
- let mut reader = StringReader::from("12.34");
+ let mut reader = StringReader::from("12.34".to_string());
let result = reader.read_int();
assert!(result.is_err());
if let Err(e) = result {
@@ -608,7 +595,7 @@ mod test {
#[test]
fn read_int_none() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
let result = reader.read_int();
assert!(result.is_err());
if let Err(e) = result {
@@ -619,7 +606,7 @@ mod test {
#[test]
fn read_int_with_remaining() {
- let mut reader = StringReader::from("1234567890 foo bar");
+ let mut reader = StringReader::from("1234567890 foo bar".to_string());
assert_eq!(reader.read_int().unwrap(), 1234567890);
assert_eq!(reader.get_read(), "1234567890");
assert_eq!(reader.remaining(), " foo bar");
@@ -627,7 +614,7 @@ mod test {
#[test]
fn read_int_with_remaining_immediate() {
- let mut reader = StringReader::from("1234567890foo bar");
+ let mut reader = StringReader::from("1234567890foo bar".to_string());
assert_eq!(reader.read_int().unwrap(), 1234567890);
assert_eq!(reader.get_read(), "1234567890");
assert_eq!(reader.remaining(), "foo bar");
@@ -635,7 +622,7 @@ mod test {
#[test]
fn read_long() {
- let mut reader = StringReader::from("1234567890");
+ let mut reader = StringReader::from("1234567890".to_string());
assert_eq!(reader.read_long().unwrap(), 1234567890);
assert_eq!(reader.get_read(), "1234567890");
assert_eq!(reader.remaining(), "");
@@ -643,7 +630,7 @@ mod test {
#[test]
fn read_long_negative() {
- let mut reader = StringReader::from("-1234567890");
+ let mut reader = StringReader::from("-1234567890".to_string());
assert_eq!(reader.read_long().unwrap(), -1234567890);
assert_eq!(reader.get_read(), "-1234567890");
assert_eq!(reader.remaining(), "");
@@ -651,7 +638,7 @@ mod test {
#[test]
fn read_long_invalid() {
- let mut reader = StringReader::from("12.34");
+ let mut reader = StringReader::from("12.34".to_string());
let result = reader.read_long();
assert!(result.is_err());
if let Err(e) = result {
@@ -667,7 +654,7 @@ mod test {
#[test]
fn read_long_none() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
let result = reader.read_long();
assert!(result.is_err());
if let Err(e) = result {
@@ -678,7 +665,7 @@ mod test {
#[test]
fn read_long_with_remaining() {
- let mut reader = StringReader::from("1234567890 foo bar");
+ let mut reader = StringReader::from("1234567890 foo bar".to_string());
assert_eq!(reader.read_long().unwrap(), 1234567890);
assert_eq!(reader.get_read(), "1234567890");
assert_eq!(reader.remaining(), " foo bar");
@@ -686,7 +673,7 @@ mod test {
#[test]
fn read_long_with_remaining_immediate() {
- let mut reader = StringReader::from("1234567890foo bar");
+ let mut reader = StringReader::from("1234567890foo bar".to_string());
assert_eq!(reader.read_long().unwrap(), 1234567890);
assert_eq!(reader.get_read(), "1234567890");
assert_eq!(reader.remaining(), "foo bar");
@@ -694,7 +681,7 @@ mod test {
#[test]
fn read_double() {
- let mut reader = StringReader::from("123");
+ let mut reader = StringReader::from("123".to_string());
assert_eq!(reader.read_double().unwrap(), 123.0);
assert_eq!(reader.get_read(), "123");
assert_eq!(reader.remaining(), "");
@@ -702,7 +689,7 @@ mod test {
#[test]
fn read_double_with_decimal() {
- let mut reader = StringReader::from("12.34");
+ let mut reader = StringReader::from("12.34".to_string());
assert_eq!(reader.read_double().unwrap(), 12.34);
assert_eq!(reader.get_read(), "12.34");
assert_eq!(reader.remaining(), "");
@@ -710,7 +697,7 @@ mod test {
#[test]
fn read_double_negative() {
- let mut reader = StringReader::from("-123");
+ let mut reader = StringReader::from("-123".to_string());
assert_eq!(reader.read_double().unwrap(), -123.0);
assert_eq!(reader.get_read(), "-123");
assert_eq!(reader.remaining(), "");
@@ -718,7 +705,7 @@ mod test {
#[test]
fn read_double_invalid() {
- let mut reader = StringReader::from("12.34.56");
+ let mut reader = StringReader::from("12.34.56".to_string());
let result = reader.read_double();
assert!(result.is_err());
if let Err(e) = result {
@@ -734,7 +721,7 @@ mod test {
#[test]
fn read_double_none() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
let result = reader.read_double();
assert!(result.is_err());
if let Err(e) = result {
@@ -745,7 +732,7 @@ mod test {
#[test]
fn read_double_with_remaining() {
- let mut reader = StringReader::from("12.34 foo bar");
+ let mut reader = StringReader::from("12.34 foo bar".to_string());
assert_eq!(reader.read_double().unwrap(), 12.34);
assert_eq!(reader.get_read(), "12.34");
assert_eq!(reader.remaining(), " foo bar");
@@ -753,7 +740,7 @@ mod test {
#[test]
fn read_double_with_remaining_immediate() {
- let mut reader = StringReader::from("12.34foo bar");
+ let mut reader = StringReader::from("12.34foo bar".to_string());
assert_eq!(reader.read_double().unwrap(), 12.34);
assert_eq!(reader.get_read(), "12.34");
assert_eq!(reader.remaining(), "foo bar");
@@ -761,7 +748,7 @@ mod test {
#[test]
fn read_float() {
- let mut reader = StringReader::from("123");
+ let mut reader = StringReader::from("123".to_string());
assert_eq!(reader.read_float().unwrap(), 123.0f32);
assert_eq!(reader.get_read(), "123");
assert_eq!(reader.remaining(), "");
@@ -769,7 +756,7 @@ mod test {
#[test]
fn read_float_with_decimal() {
- let mut reader = StringReader::from("12.34");
+ let mut reader = StringReader::from("12.34".to_string());
assert_eq!(reader.read_float().unwrap(), 12.34f32);
assert_eq!(reader.get_read(), "12.34");
assert_eq!(reader.remaining(), "");
@@ -777,7 +764,7 @@ mod test {
#[test]
fn read_float_negative() {
- let mut reader = StringReader::from("-123");
+ let mut reader = StringReader::from("-123".to_string());
assert_eq!(reader.read_float().unwrap(), -123.0f32);
assert_eq!(reader.get_read(), "-123");
assert_eq!(reader.remaining(), "");
@@ -785,7 +772,7 @@ mod test {
#[test]
fn read_float_invalid() {
- let mut reader = StringReader::from("12.34.56");
+ let mut reader = StringReader::from("12.34.56".to_string());
let result = reader.read_float();
assert!(result.is_err());
if let Err(e) = result {
@@ -801,7 +788,7 @@ mod test {
#[test]
fn read_float_none() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
let result = reader.read_float();
assert!(result.is_err());
if let Err(e) = result {
@@ -812,7 +799,7 @@ mod test {
#[test]
fn read_float_with_remaining() {
- let mut reader = StringReader::from("12.34 foo bar");
+ let mut reader = StringReader::from("12.34 foo bar".to_string());
assert_eq!(reader.read_float().unwrap(), 12.34f32);
assert_eq!(reader.get_read(), "12.34");
assert_eq!(reader.remaining(), " foo bar");
@@ -820,7 +807,7 @@ mod test {
#[test]
fn read_float_with_remaining_immediate() {
- let mut reader = StringReader::from("12.34foo bar");
+ let mut reader = StringReader::from("12.34foo bar".to_string());
assert_eq!(reader.read_float().unwrap(), 12.34f32);
assert_eq!(reader.get_read(), "12.34");
assert_eq!(reader.remaining(), "foo bar");
@@ -828,14 +815,14 @@ mod test {
#[test]
fn expect_correct() {
- let mut reader = StringReader::from("abc");
+ let mut reader = StringReader::from("abc".to_string());
reader.expect('a');
assert_eq!(reader.cursor(), 1);
}
#[test]
fn expect_incorrect() {
- let mut reader = StringReader::from("bcd");
+ let mut reader = StringReader::from("bcd".to_string());
let result = reader.expect('a');
assert!(result.is_err());
if let Err(e) = result {
@@ -849,7 +836,7 @@ mod test {
#[test]
fn expect_none() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
let result = reader.expect('a');
assert!(result.is_err());
if let Err(e) = result {
@@ -863,14 +850,14 @@ mod test {
#[test]
fn read_boolean_correct() {
- let mut reader = StringReader::from("true");
+ let mut reader = StringReader::from("true".to_string());
assert_eq!(reader.read_boolean().unwrap(), true);
assert_eq!(reader.get_read(), "true");
}
#[test]
fn read_boolean_incorrect() {
- let mut reader = StringReader::from("tuesday");
+ let mut reader = StringReader::from("tuesday".to_string());
let result = reader.read_boolean();
assert!(result.is_err());
if let Err(e) = result {
@@ -886,7 +873,7 @@ mod test {
#[test]
fn read_boolean_none() {
- let mut reader = StringReader::from("");
+ let mut reader = StringReader::from("".to_string());
let result = reader.read_boolean();
assert!(result.is_err());
if let Err(e) = result {
diff --git a/azalea-brigadier/src/suggestion/integer_suggestion.rs b/azalea-brigadier/src/suggestion/integer_suggestion.rs
deleted file mode 100644
index acee2329..00000000
--- a/azalea-brigadier/src/suggestion/integer_suggestion.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub struct IntegerSuggestion {}
diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs
deleted file mode 100644
index 050bae6c..00000000
--- a/azalea-brigadier/src/suggestion/mod.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-pub mod integer_suggestion;
-pub mod suggestion;
-pub mod suggestion_provider;
-pub mod suggestions;
-pub mod suggestions_builder;
diff --git a/azalea-brigadier/src/suggestion/suggestion.rs b/azalea-brigadier/src/suggestion/suggestion.rs
deleted file mode 100644
index 4cbed7be..00000000
--- a/azalea-brigadier/src/suggestion/suggestion.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use std::cmp;
-
-use crate::{context::string_range::StringRange, message::Message};
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Suggestion {
- range: StringRange,
- text: String,
- tooltip: Option<Message>,
-}
-
-impl Suggestion {
- pub fn new(range: StringRange, text: String) -> Suggestion {
- Suggestion {
- range,
- text,
- tooltip: None,
- }
- }
-
- pub fn new_with_tooltip(range: StringRange, text: String, tooltip: Message) -> Suggestion {
- Suggestion {
- range,
- text,
- tooltip: Some(tooltip),
- }
- }
-
- pub fn range(&self) -> &StringRange {
- &self.range
- }
-
- pub fn text(&self) -> &String {
- &self.text
- }
-
- pub fn tooltip(&self) -> Option<&Message> {
- self.tooltip.as_ref()
- }
-
- pub fn apply(&self, input: &str) -> String {
- if self.range.start() == 0 && self.range.end() == input.len() {
- return self.text.clone();
- }
- let mut result = String::new();
- if self.range.start() > 0 {
- result.push_str(&input[0..self.range.start()]);
- }
- result.push_str(&self.text);
- if self.range.end() < input.len() {
- result.push_str(&input[self.range.end()..]);
- }
- result
- }
-
- pub fn expand(&self, command: &str, range: StringRange) -> Suggestion {
- if range == self.range {
- return self.clone();
- }
- let mut result = String::new();
- if range.start() < self.range.start() {
- result.push_str(&command[range.start()..self.range.start()]);
- }
- result.push_str(&self.text);
- if range.end() > self.range.end() {
- result.push_str(&command[self.range.end()..range.end()]);
- }
- Suggestion {
- range,
- text: result,
- tooltip: self.tooltip.clone(),
- }
- }
-
- pub fn compare_ignore_case(&self, b: &Suggestion) -> cmp::Ordering {
- self.text.to_lowercase().cmp(&b.text.to_lowercase())
- }
-}
-
-impl PartialOrd for Suggestion {
- fn partial_cmp(&self, other: &Suggestion) -> Option<cmp::Ordering> {
- Some(self.text.cmp(&other.text))
- }
-}
-
-impl Ord for Suggestion {
- fn cmp(&self, other: &Suggestion) -> cmp::Ordering {
- self.text.cmp(&other.text)
- }
-}
diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs
deleted file mode 100644
index 3027d460..00000000
--- a/azalea-brigadier/src/suggestion/suggestion_provider.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use crate::{
- context::command_context::CommandContext,
- exceptions::command_syntax_exception::CommandSyntaxException,
-};
-
-use super::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder};
-
-pub trait SuggestionProvider<S> {
- fn suggestions(
- &self,
- context: &CommandContext<S>,
- builder: &SuggestionsBuilder,
- ) -> Result<Suggestions, CommandSyntaxException>;
-}
diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs
deleted file mode 100644
index 9f0ee06d..00000000
--- a/azalea-brigadier/src/suggestion/suggestions.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-use std::{cmp, collections::HashSet};
-
-use crate::{context::string_range::StringRange, message::Message};
-
-use super::suggestion::Suggestion;
-
-#[derive(PartialEq, Eq, Hash)]
-pub struct Suggestions {
- range: StringRange,
- suggestions: Vec<Suggestions>,
-}
-
-impl Suggestions {
- fn range(&self) -> &StringRange {
- &self.range
- }
-
- fn list(&self) -> &Vec<Suggestions> {
- &self.suggestions
- }
-
- fn is_empty(&self) -> bool {
- self.suggestions.is_empty()
- }
-
- fn merge(command: &str, input: &Vec<Suggestions>) {
- if input.is_empty() {
- return Self::default();
- } else if input.len() == 1 {
- return input.iter().next();
- }
- let texts = HashSet::new();
- for suggestions in input {
- texts.extend(suggestions.list())
- }
- Self::new(command, texts)
- }
-
- // public static Suggestions create(final String command, final Collection<Suggestion> suggestions) {
- // if (suggestions.isEmpty()) {
- // return EMPTY;
- // }
- // int start = Integer.MAX_VALUE;
- // int end = Integer.MIN_VALUE;
- // for (final Suggestion suggestion : suggestions) {
- // start = Math.min(suggestion.getRange().getStart(), start);
- // end = Math.max(suggestion.getRange().getEnd(), end);
- // }
- // final StringRange range = new StringRange(start, end);
- // final Set<Suggestion> texts = new HashSet<>();
- // for (final Suggestion suggestion : suggestions) {
- // texts.add(suggestion.expand(command, range));
- // }
- // final List<Suggestion> sorted = new ArrayList<>(texts);
- // sorted.sort((a, b) -> a.compareToIgnoreCase(b));
- // return new Suggestions(range, sorted);
- pub fn new(command: String, suggestions: Vec<Suggestion>) -> Self {
- if suggestions.is_empty() {
- return Self::default();
- }
- let mut start = usize::MAX;
- let mut end = usize::MIN;
- for suggestion in suggestions {
- let start = cmp::min(suggestion.range().start(), start);
- let end = cmp::max(suggestion.range().end(), end);
- }
- let range = StringRange::new(start, end);
- let texts = HashSet::new();
- for suggestion in suggestions {
- texts.insert(suggestion.expand(command, range));
- }
- let sorted = texts.sort_by(|a, b| a.compare_ignore_case(b));
- Suggestions {
- range,
- suggestions: sorted,
- }
- }
-}
-
-impl Default for Suggestions {
- fn default() -> Self {
- Self {
- range: StringRange::at(0),
- suggestions: vec![],
- }
- }
-}
-
-// #[cfg(test)]
-// mod tests {
-// use crate::suggestion::suggestion::Suggestion;
-
-// use super::*;
-
-// #[test]
-// fn merge_empty() {
-// let merged = Suggestions::merge("foo b", vec![]);
-// assert_eq!(merged.is_empty(), true);
-// }
-
-// #[test]
-// fn merge_single() {
-// let suggestions = Suggestions::new(StringRange::at(5), "ar".to_string());
-// let merged = Suggestions::merge("foo b", vec![suggestions]);
-// assert_eq!(merged, suggestions);
-// }
-
-// #[test]
-// fn merge_multiple() {
-// let a = Suggestions::new(
-// StringRange::at(5),
-// vec![
-// Suggestion::new(StringRange::at(5), "ar".to_string()),
-// Suggestion::new(StringRange::at(5), "az".to_string()),
-// Suggestion::new(StringRange::at(5), "Az".to_string()),
-// ],
-// );
-// let b = Suggestions::new(
-// StringRange::between(4, 5),
-// vec![
-// Suggestion::new(StringRange::between(4, 5), "foo".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "qux".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "apple".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "Bar".to_string()),
-// ],
-// );
-// let merged = Suggestions::merge("foo b", vec![a, b]);
-// assert_eq!(
-// merged.get_list(),
-// vec![
-// Suggestion::new(StringRange::between(4, 5), "apple".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "bar".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "Bar".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "baz".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "bAz".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "foo".to_string()),
-// Suggestion::new(StringRange::between(4, 5), "qux".to_string()),
-// ]
-// );
-// }
-// }
diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs
deleted file mode 100644
index bc8f6f5d..00000000
--- a/azalea-brigadier/src/suggestion/suggestions_builder.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-use crate::context::string_range::StringRange;
-
-use super::{
- integer_suggestion::IntegerSuggestion, suggestion::Suggestion, suggestions::Suggestions,
-};
-
-pub struct SuggestionsBuilder {
- input: String,
- input_lowercase: String,
- start: usize,
- remaining: String,
- remaining_lowercase: String,
- result: Vec<Suggestion>,
-}
-
-impl SuggestionsBuilder {
- pub fn new_with_lowercase(
- input: String,
- input_lowercase: String,
- start: usize,
- ) -> SuggestionsBuilder {
- SuggestionsBuilder {
- input,
- input_lowercase,
- start,
- remaining: input.get(start..).unwrap().to_string(),
- remaining_lowercase: input_lowercase.get(start..).unwrap().to_string(),
- result: Vec::new(),
- }
- }
-
- pub fn new(input: String, start: usize) -> SuggestionsBuilder {
- SuggestionsBuilder::new_with_lowercase(input, input.to_lowercase(), start)
- }
-
- pub fn input(&self) -> &str {
- &self.input
- }
-
- pub fn start(&self) -> usize {
- self.start
- }
-
- pub fn remaining(&self) -> &str {
- &self.remaining
- }
-
- pub fn remaining_lowercase(&self) -> &str {
- &self.remaining_lowercase
- }
-
- pub fn build(&self) -> Suggestions {
- Suggestions::create(self.input(), self.result)
- }
-
- pub fn suggest(&mut self, text: &str) -> &mut SuggestionsBuilder {
- if text == self.remaining {
- return self;
- }
- self.result.push(Suggestion::new(
- StringRange::between(self.start, self.input.len()),
- text,
- ));
- self
- }
-
- pub fn suggest_with_tooltip(&mut self, text: &str, tooltip: &str) -> &mut SuggestionsBuilder {
- if text == self.remaining {
- return self;
- }
- self.result.push(Suggestion::new_with_tooltip(
- StringRange::between(self.start, self.input.len()),
- text,
- tooltip,
- ));
- self
- }
-
- pub fn suggest_with_value(&mut self, value: i32) -> &mut SuggestionsBuilder {
- self.result.push(IntegerSuggestion::new(
- StringRange::between(self.start, self.input.len()),
- value,
- ));
- self
- }
-
- pub fn suggest_with_value_and_tooltip(
- &mut self,
- value: i32,
- tooltip: &str,
- ) -> &mut SuggestionsBuilder {
- self.result.push(IntegerSuggestion::new_with_tooltip(
- StringRange::between(self.start, self.input.len()),
- value,
- tooltip,
- ));
- self
- }
-
- pub fn add(&mut self, other: &SuggestionsBuilder) -> &mut SuggestionsBuilder {
- self.result.extend(other.result.iter().cloned());
- self
- }
-
- pub fn create_offset(&self, start: usize) -> SuggestionsBuilder {
- SuggestionsBuilder::new_with_lowercase(
- self.input.clone(),
- self.input_lowercase.clone(),
- start,
- )
- }
-
- pub fn restart(&self) -> SuggestionsBuilder {
- self.create_offset(self.start)
- }
-}
diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs
new file mode 100644
index 00000000..2f023697
--- /dev/null
+++ b/azalea-brigadier/src/tree.rs
@@ -0,0 +1,269 @@
+use crate::{
+ builder::{
+ argument_builder::ArgumentBuilderType, literal_argument_builder::Literal,
+ required_argument_builder::Argument,
+ },
+ context::{CommandContext, CommandContextBuilder, ParsedArgument},
+ exceptions::{
+ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
+ },
+ modifier::RedirectModifier,
+ string_range::StringRange,
+ string_reader::StringReader,
+};
+use std::{
+ any::Any,
+ cell::RefCell,
+ collections::{BTreeMap, HashMap},
+ fmt::Debug,
+ hash::Hash,
+ ptr,
+ rc::Rc,
+};
+
+/// An ArgumentBuilder that has been built.
+#[derive(Clone)]
+#[non_exhaustive]
+pub struct CommandNode<S: Any + Clone> {
+ pub value: ArgumentBuilderType,
+
+ // we use BTreeMap instead of HashMap because it can be hashed
+ pub children: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
+ pub literals: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
+ pub arguments: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
+
+ pub command: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
+ pub requirement: Rc<dyn Fn(Rc<S>) -> bool>,
+ pub redirect: Option<Rc<RefCell<CommandNode<S>>>>,
+ pub forks: bool,
+ pub modifier: Option<Rc<dyn RedirectModifier<S>>>,
+}
+
+impl<S: Any + Clone> CommandNode<S> {
+ // pub fn new()
+ // TODO: precalculate `literals` and `arguments` and include them in CommandNode
+ fn literals(&self) -> &BTreeMap<String, Rc<RefCell<CommandNode<S>>>> {
+ &self.literals
+ }
+ fn arguments(&self) -> &BTreeMap<String, Rc<RefCell<CommandNode<S>>>> {
+ &self.arguments
+ }
+
+ /// Gets the literal, or panics. You should use match if you're not certain about the type.
+ pub fn literal(&self) -> &Literal {
+ match self.value {
+ ArgumentBuilderType::Literal(ref literal) => literal,
+ _ => panic!("CommandNode::literal() called on non-literal node"),
+ }
+ }
+ /// Gets the argument, or panics. You should use match if you're not certain about the type.
+ pub fn argument(&self) -> &Argument {
+ match self.value {
+ ArgumentBuilderType::Argument(ref argument) => argument,
+ _ => panic!("CommandNode::argument() called on non-argument node"),
+ }
+ }
+
+ pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec<Rc<RefCell<CommandNode<S>>>> {
+ let literals = self.literals();
+
+ println!("get relevant nodes {:?} literals={:?}", self, literals);
+
+ if literals.len() > 0 {
+ let cursor = input.cursor();
+ while input.can_read() && input.peek() != ' ' {
+ input.skip();
+ }
+ let text: String = input
+ .string()
+ .chars()
+ .skip(cursor)
+ .take(input.cursor() - cursor)
+ .collect();
+ input.cursor = cursor;
+ let literal = literals.get(&text);
+ if let Some(literal) = literal {
+ return vec![literal.clone()];
+ } else {
+ return self
+ .arguments()
+ .values()
+ .map(|argument| argument.clone())
+ .collect();
+ }
+ } else {
+ return self
+ .arguments()
+ .values()
+ .map(|argument| argument.clone())
+ .collect();
+ }
+ }
+
+ pub fn can_use(&self, source: Rc<S>) -> bool {
+ (self.requirement)(source)
+ }
+
+ pub fn add_child(&mut self, node: &Rc<RefCell<CommandNode<S>>>) {
+ let child = self.children.get(node.borrow().name());
+ if let Some(child) = child {
+ // We've found something to merge onto
+ if let Some(command) = &node.borrow().command {
+ child.borrow_mut().command = Some(command.clone());
+ }
+ for grandchild in node.borrow().children.values() {
+ child.borrow_mut().add_child(grandchild);
+ }
+ } else {
+ self.children
+ .insert(node.borrow().name().to_string(), node.clone());
+ match &node.borrow().value {
+ ArgumentBuilderType::Literal(literal) => {
+ self.literals.insert(literal.value.clone(), node.clone());
+ }
+ ArgumentBuilderType::Argument(argument) => {
+ self.arguments.insert(argument.name.clone(), node.clone());
+ }
+ }
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ match &self.value {
+ ArgumentBuilderType::Argument(argument) => &argument.name,
+ ArgumentBuilderType::Literal(literal) => &literal.value,
+ }
+ }
+
+ pub fn parse_with_context(
+ &self,
+ reader: &mut StringReader,
+ context_builder: &mut CommandContextBuilder<S>,
+ ) -> Result<(), CommandSyntaxException> {
+ match self.value {
+ ArgumentBuilderType::Argument(ref argument) => {
+ let start = reader.cursor();
+ // TODO: handle this better
+ let result = argument
+ .parse(reader)
+ .expect("Couldn't get result for some reason");
+ let parsed = ParsedArgument {
+ range: StringRange::between(start, reader.cursor()),
+ result: result,
+ };
+
+ context_builder.with_argument(&argument.name, parsed.clone());
+ context_builder.with_node(Rc::new(self.clone()), parsed.range);
+
+ Ok(())
+ }
+ ArgumentBuilderType::Literal(ref literal) => {
+ let start = reader.cursor();
+ let end = self.parse(reader);
+
+ if let Some(end) = end {
+ context_builder
+ .with_node(Rc::new(self.clone()), StringRange::between(start, end));
+ return Ok(());
+ }
+
+ Err(BuiltInExceptions::LiteralIncorrect {
+ expected: literal.value.clone(),
+ }
+ .create_with_context(reader))
+ }
+ }
+ }
+
+ fn parse(&self, reader: &mut StringReader) -> Option<usize> {
+ match self.value {
+ ArgumentBuilderType::Argument(ref argument) => {
+ panic!("Can't parse argument.")
+ }
+ ArgumentBuilderType::Literal(ref literal) => {
+ let start = reader.cursor();
+ if reader.can_read_length(literal.value.len()) {
+ let end = start + literal.value.len();
+ if reader
+ .string()
+ .get(start..end)
+ .expect("Couldn't slice reader correctly?")
+ == literal.value
+ {
+ reader.cursor = end;
+ if !reader.can_read() || reader.peek() == ' ' {
+ return Some(end);
+ } else {
+ reader.cursor = start;
+ }
+ }
+ }
+ }
+ }
+ None
+ }
+}
+
+impl<S: Any + Clone> Debug for CommandNode<S> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CommandNode")
+ .field("value", &self.value)
+ .field("children", &self.children)
+ .field("command", &self.command.is_some())
+ // .field("requirement", &self.requirement)
+ .field("redirect", &self.redirect)
+ .field("forks", &self.forks)
+ // .field("modifier", &self.modifier)
+ .finish()
+ }
+}
+
+impl<S: Any + Clone> Default for CommandNode<S> {
+ fn default() -> Self {
+ println!("making default node");
+ Self {
+ value: ArgumentBuilderType::Literal(Literal::default()),
+
+ children: BTreeMap::new(),
+ literals: BTreeMap::new(),
+ arguments: BTreeMap::new(),
+
+ command: None,
+ requirement: Rc::new(|_| true),
+ redirect: None,
+ forks: false,
+ modifier: None,
+ }
+ }
+}
+
+impl<S: Any + Clone> Hash for CommandNode<S> {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ // hash the children
+ for (k, v) in &self.children {
+ k.hash(state);
+ v.borrow().hash(state);
+ }
+ // i hope this works because if doesn't then that'll be a problem
+ ptr::hash(&self.command, state);
+ }
+}
+
+impl<S: Any + Clone> PartialEq for CommandNode<S> {
+ fn eq(&self, other: &Self) -> bool {
+ if self.children != other.children {
+ return false;
+ }
+ if let Some(selfexecutes) = &self.command {
+ if let Some(otherexecutes) = &other.command {
+ if !Rc::ptr_eq(selfexecutes, otherexecutes) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ true
+ }
+}
+impl<S: Any + Clone> Eq for CommandNode<S> {}
diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs
deleted file mode 100644
index 9d2af14e..00000000
--- a/azalea-brigadier/src/tree/argument_command_node.rs
+++ /dev/null
@@ -1,176 +0,0 @@
-use std::{
- any::Any,
- collections::HashMap,
- fmt::{Debug, Display, Formatter},
-};
-
-use crate::{
- arguments::argument_type::ArgumentType,
- builder::required_argument_builder::RequiredArgumentBuilder,
- command::Command,
- context::{
- command_context::CommandContext, command_context_builder::CommandContextBuilder,
- parsed_argument::ParsedArgument,
- },
- exceptions::command_syntax_exception::CommandSyntaxException,
- immutable_string_reader::ImmutableStringReader,
- redirect_modifier::RedirectModifier,
- string_reader::StringReader,
- suggestion::{
- suggestion_provider::SuggestionProvider, suggestions::Suggestions,
- suggestions_builder::SuggestionsBuilder,
- },
-};
-
-use super::{
- command_node::{BaseCommandNode, CommandNodeTrait},
- literal_command_node::LiteralCommandNode,
- root_command_node::RootCommandNode,
-};
-
-const USAGE_ARGUMENT_OPEN: &str = "<";
-const USAGE_ARGUMENT_CLOSE: &str = ">";
-
-pub struct ArgumentCommandNode<S> {
- name: String,
- type_: Box<dyn ArgumentType<Into = dyn Any>>,
- custom_suggestions: Option<Box<dyn SuggestionProvider<S>>>,
-
- children: HashMap<String, Box<dyn CommandNodeTrait<S>>>,
- literals: HashMap<String, LiteralCommandNode<S>>,
- arguments: HashMap<String, ArgumentCommandNode<S>>,
- pub requirement: Box<dyn Fn(&S) -> bool>,
- redirect: Option<Box<dyn CommandNodeTrait<S>>>,
- modifier: Option<Box<dyn RedirectModifier<S>>>,
- forks: bool,
- pub command: Option<Box<dyn Command<S>>>,
-}
-
-impl<S> ArgumentCommandNode<S> {
- fn get_type(&self) -> &dyn ArgumentType<Into = dyn Any> {
- &*self.type_
- }
-
- fn custom_suggestions(&self) -> &Option<Box<dyn SuggestionProvider<S>>> {
- &self.custom_suggestions
- }
-}
-
-impl<S> CommandNodeTrait<S> for ArgumentCommandNode<S> {
- fn name(&self) -> &str {
- &self.name
- }
-
- fn parse(
- &self,
- reader: &mut StringReader,
- context_builder: CommandContextBuilder<S>,
- ) -> Result<(), CommandSyntaxException> {
- // final int start = reader.getCursor();
- // final T result = type.parse(reader);
- // final ParsedArgument<S> parsed = new ParsedArgument<>(start, reader.getCursor(), result);
-
- // contextBuilder.withArgument(name, parsed);
- // contextBuilder.withNode(this, parsed.getRange());
-
- let start = reader.cursor();
- let result = self.get_type().parse(reader)?;
- let parsed = ParsedArgument::new(start, reader.get_cursor(), result);
-
- context_builder.with_argument(&self.name, parsed);
- context_builder.with_node(self, parsed.get_range());
-
- Ok(())
- }
-
- fn list_suggestions(
- &self,
- context: CommandContext<S>,
- builder: &SuggestionsBuilder,
- ) -> Result<Suggestions, CommandSyntaxException> {
- if self.custom_suggestions.is_none() {
- self.get_type().list_suggestions(context, builder)
- } else {
- self.custom_suggestions.get_suggestions(context, builder)
- }
- }
-
- fn is_valid_input(&self, input: &str) -> bool {
- let reader = StringReader::new(input);
- let result = self.get_type().parse(reader);
- if result.is_ok() {
- return !reader.can_read() || reader.peek() == ' ';
- } else {
- return false;
- }
- }
-
- fn usage_text(&self) -> &str {
- USAGE_ARGUMENT_OPEN + self.name + USAGE_ARGUMENT_CLOSE
- }
-
- fn create_builder(&self) -> RequiredArgumentBuilder<S> {
- let builder = RequiredArgumentBuilder::argument(&self.name, &self.type_);
- builder.requires(self.base.get_requirement());
- builder.forward(
- self.base.get_redirect(),
- self.base.get_redirect_modifier(),
- self.base.is_fork(),
- );
- builder.suggests(self.custom_suggestions());
- if self.base.get_command() != None {
- builder.executes(self.base.get_command().unwrap());
- }
- builder
- }
-
- fn get_examples(&self) -> Vec<String> {
- self.type_.get_examples()
- }
-
- fn redirect_modifier(&self) -> Option<&dyn RedirectModifier<S>> {
- self.modifier.as_ref().map(|modifier| modifier.as_ref())
- }
-
- fn can_use(&self, source: S) -> bool {
- (self.requirement)(&source)
- }
-
- fn add_child(&self, node: &Box<dyn CommandNodeTrait<S>>) -> Result<(), String> {
- let dynamic_node = node as &dyn Any;
- if dynamic_node.is::<RootCommandNode<S>>() {
- return Err(String::from(
- "Cannot add a RootCommandNode as a child to any other CommandNode",
- ));
- }
-
- let mut child = self.children.get(node.name());
- if let Some(child) = child {
- // We've found something to merge onto
- if let Some(command) = node.base().command() {
- child.base_mut().command = Some(*command);
- }
- for grandchild in node.base().children().values() {
- child.base_mut().add_child(&*grandchild)?;
- }
- Ok(())
- } else {
- self.children.insert(node.name().to_string(), *node);
-
- if let Some(dynamic_node) = dynamic_node.downcast_ref::<LiteralCommandNode<S>>() {
- self.literals.insert(node.name().to_string(), *dynamic_node);
- } else if let Some(dynamic_node) = dynamic_node.downcast_ref::<ArgumentCommandNode<S>>()
- {
- self.arguments
- .insert(node.name().to_string(), *dynamic_node);
- }
- Ok(())
- }
- }
-}
-
-impl<S> Display for ArgumentCommandNode<S> {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- write!(f, "<argument {}: {}>", self.name, self.type_)
- }
-}
diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs
deleted file mode 100644
index 207e114e..00000000
--- a/azalea-brigadier/src/tree/command_node.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-use super::{
- argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode,
- root_command_node::RootCommandNode,
-};
-use crate::{
- arguments::argument_type::ArgumentType,
- builder::argument_builder::ArgumentBuilder,
- command::Command,
- context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
- exceptions::command_syntax_exception::CommandSyntaxException,
- redirect_modifier::RedirectModifier,
- string_reader::StringReader,
- suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
-};
-use std::ops::Deref;
-use std::{any::Any, collections::HashMap, fmt::Debug};
-
-#[enum_dispatch(CommandNodeTrait)]
-enum CommandNodeEnum<S> {
- Literal(LiteralCommandNode<S>),
- Argument(ArgumentCommandNode<S>),
- Root(RootCommandNode<S>),
-}
-
-impl<S> CommandNodeEnum<S> {
- fn redirect_modifier(&self) -> Option<&dyn RedirectModifier<S>> {
- (*self).modifier.as_ref().map(|modifier| modifier.as_ref())
- }
-
- fn can_use(&self, source: S) -> bool {
- (self.requirement)(&source)
- }
-
- fn add_child(&self, node: &Box<dyn CommandNodeTrait<S>>) -> Result<(), String> {
- let dynamic_node = node as &dyn Any;
- if dynamic_node.is::<RootCommandNode<S>>() {
- return Err(String::from(
- "Cannot add a RootCommandNode as a child to any other CommandNode",
- ));
- }
-
- let mut child = self.children.get(node.name());
- if let Some(child) = child {
- // We've found something to merge onto
- if let Some(command) = node.base().command() {
- child.base_mut().command = Some(*command);
- }
- for grandchild in node.base().children().values() {
- child.base_mut().add_child(&*grandchild)?;
- }
- Ok(())
- } else {
- self.children.insert(node.name().to_string(), *node);
-
- if let Some(dynamic_node) = dynamic_node.downcast_ref::<LiteralCommandNode<S>>() {
- self.literals.insert(node.name().to_string(), *dynamic_node);
- } else if let Some(dynamic_node) = dynamic_node.downcast_ref::<ArgumentCommandNode<S>>()
- {
- self.arguments
- .insert(node.name().to_string(), *dynamic_node);
- }
- Ok(())
- }
- }
-}
-pub struct BaseCommandNode<S> {
- children: HashMap<String, Box<dyn CommandNodeTrait<S>>>,
- literals: HashMap<String, LiteralCommandNode<S>>,
- arguments: HashMap<String, ArgumentCommandNode<S>>,
- pub requirement: Box<dyn Fn(&S) -> bool>,
- redirect: Option<Box<dyn CommandNodeTrait<S>>>,
- modifier: Option<Box<dyn RedirectModifier<S>>>,
- forks: bool,
- pub command: Option<Box<dyn Command<S>>>,
-}
-
-// impl<S> Clone for BaseCommandNode<'_, S> {
-// fn clone(&self) -> Self {
-// Self {
-// children: self.children.clone(),
-// literals: self.literals.clone(),
-// arguments: self.arguments.clone(),
-// requirement: self.requirement.clone(),
-// redirect: self.redirect.clone(),
-// modifier: self.modifier.clone(),
-// forks: self.forks.clone(),
-// command: self.command.clone(),
-// }
-// }
-// }
-
-impl<S> Debug for BaseCommandNode<S> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("BaseCommandNode")
- .field("children", &self.children)
- .field("literals", &self.literals)
- .field("arguments", &self.arguments)
- .field("requirement", &self.requirement)
- .field("redirect", &self.redirect)
- .field("modifier", &self.modifier)
- .field("forks", &self.forks)
- .field("command", &self.command)
- .finish()
- }
-}
-
-impl<S> Default for BaseCommandNode<S> {
- fn default() -> Self {
- Self {
- children: HashMap::new(),
- literals: HashMap::new(),
- arguments: HashMap::new(),
- requirement: Box::new(|_| true),
- redirect: None,
- modifier: None,
- forks: false,
- command: None,
- }
- }
-}
-
-#[enum_dispatch]
-pub trait CommandNodeTrait<S> {
- fn name(&self) -> &str;
- fn usage_text(&self) -> &str;
- fn parse(
- &self,
- reader: &mut StringReader,
- context_builder: CommandContextBuilder<S>,
- ) -> Result<(), CommandSyntaxException>;
- fn list_suggestions(
- &self,
- context: CommandContext<S>,
- builder: &SuggestionsBuilder,
- ) -> Result<Suggestions, CommandSyntaxException>;
- fn is_valid_input(&self, input: &str) -> bool;
- fn create_builder(&self) -> Box<dyn ArgumentBuilder<S>>;
- fn get_examples(&self) -> Vec<String>;
-
- fn redirect_modifier(&self) -> Option<&dyn RedirectModifier<S>>;
- fn can_use(&self, source: S) -> bool;
- fn add_child(&self, node: &Box<dyn CommandNodeTrait<S>>) -> Result<(), String>;
-}
diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs
deleted file mode 100644
index 2db31d97..00000000
--- a/azalea-brigadier/src/tree/literal_command_node.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-use crate::{
- arguments::argument_type::ArgumentType,
- builder::{
- argument_builder::ArgumentBuilder, literal_argument_builder::LiteralArgumentBuilder,
- },
- command::Command,
- context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
- exceptions::{
- builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
- },
- immutable_string_reader::ImmutableStringReader,
- redirect_modifier::RedirectModifier,
- string_reader::StringReader,
- suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
-};
-use std::{collections::HashMap, fmt::Debug};
-
-use super::{
- argument_command_node::ArgumentCommandNode,
- command_node::{BaseCommandNode, CommandNodeTrait},
-};
-
-#[derive(Debug, Clone)]
-pub struct LiteralCommandNode<S> {
- literal: String,
- literal_lowercase: String,
-
- children: HashMap<String, Box<dyn CommandNodeTrait<S>>>,
- literals: HashMap<String, LiteralCommandNode<S>>,
- arguments: HashMap<String, ArgumentCommandNode<S>>,
- pub requirement: Box<dyn Fn(&S) -> bool>,
- redirect: Option<Box<dyn CommandNodeTrait<S>>>,
- modifier: Option<Box<dyn RedirectModifier<S>>>,
- forks: bool,
- pub command: Option<Box<dyn Command<S>>>,
-}
-
-impl<S> LiteralCommandNode<S> {
- pub fn new(literal: String) -> Self {
- let literal_lowercase = literal.to_lowercase();
- Self {
- literal,
- literal_lowercase,
- ..Default::default()
- }
- }
-
- pub fn literal(&self) -> &String {
- &self.literal
- }
-
- pub fn parse(&self, reader: &mut StringReader) -> i32 {
- let start = reader.cursor();
- if reader.can_read_length(self.literal.len()) {
- let end = start + self.literal.len();
- if reader.string()[start..end].eq(&self.literal) {
- reader.cursor = end;
- if !reader.can_read() || reader.peek() == ' ' {
- return end as i32;
- } else {
- reader.cursor = start;
- }
- }
- }
- -1
- }
-}
-
-impl<S> CommandNodeTrait<S> for LiteralCommandNode<S> {
- fn name(&self) -> &str {
- &self.literal
- }
-
- fn parse(
- &self,
- reader: &mut StringReader<'_>,
- context_builder: CommandContextBuilder<S>,
- ) -> Result<(), CommandSyntaxException> {
- let start = reader.cursor();
- let end = self.parse(reader);
- if end > -1 {
- return Ok(());
- }
-
- Err(BuiltInExceptions::LiteralIncorrect {
- expected: self.literal().to_string(),
- }
- .create_with_context(reader))
- }
-
- fn list_suggestions(
- &self,
- context: CommandContext<S>,
- builder: &SuggestionsBuilder,
- ) -> Result<Suggestions, CommandSyntaxException> {
- if self
- .literal_lowercase
- .starts_with(&builder.remaining_lowercase())
- {
- Ok(builder.suggest(self.literal()).build())
- } else {
- Ok(Suggestions::default())
- }
- }
-
- fn is_valid_input(&self, input: &str) -> bool {
- self.parse(&mut StringReader::from(input)) > -1
- }
-
- fn usage_text(&self) -> &str {
- &self.literal
- }
-
- fn create_builder(&self) -> Box<dyn ArgumentBuilder<S>> {
- let mut builder = LiteralArgumentBuilder::literal(self.literal().to_string());
- builder.base.requires(&self.base().requirement);
- builder.base.forward(
- self.base.redirect(),
- self.base.redirect_modifier(),
- self.base.is_fork(),
- );
- if self.command().is_some() {
- builder.executes(self.command().unwrap());
- }
- builder
- }
-
- fn get_examples(&self) -> Vec<String> {
- todo!()
- }
-}
diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs
deleted file mode 100644
index 3dc22583..00000000
--- a/azalea-brigadier/src/tree/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-pub mod argument_command_node;
-pub mod command_node;
-pub mod literal_command_node;
-pub mod root_command_node;
diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs
deleted file mode 100644
index 6b8bc157..00000000
--- a/azalea-brigadier/src/tree/root_command_node.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-use super::{
- argument_command_node::ArgumentCommandNode,
- command_node::{BaseCommandNode, CommandNodeTrait},
- literal_command_node::LiteralCommandNode,
-};
-use crate::{
- arguments::argument_type::ArgumentType,
- builder::argument_builder::ArgumentBuilder,
- command::Command,
- context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
- exceptions::{
- builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
- },
- redirect_modifier::RedirectModifier,
- string_reader::StringReader,
- suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
-};
-use std::{
- any::Any,
- collections::HashMap,
- fmt::{Debug, Display, Formatter},
-};
-
-#[derive(Default)]
-pub struct RootCommandNode<S> {
- // Since Rust doesn't have extending, we put the struct this is extending as the "base" field
- children: HashMap<String, Box<dyn CommandNodeTrait<S>>>,
- literals: HashMap<String, LiteralCommandNode<S>>,
- arguments: HashMap<String, ArgumentCommandNode<S>>,
- pub requirement: Box<dyn Fn(&S) -> bool>,
- redirect: Option<Box<dyn CommandNodeTrait<S>>>,
- modifier: Option<Box<dyn RedirectModifier<S>>>,
- forks: bool,
- pub command: Option<Box<dyn Command<S>>>,
-}
-
-impl<S> CommandNodeTrait<S> for RootCommandNode<S> {
- fn name(&self) -> &str {
- ""
- }
-
- fn parse(
- &self,
- reader: &mut StringReader<'_>,
- context_builder: CommandContextBuilder<S>,
- ) -> Result<(), CommandSyntaxException> {
- Ok(())
- }
-
- fn list_suggestions(
- &self,
- context: CommandContext<S>,
- builder: &SuggestionsBuilder,
- ) -> Result<Suggestions, CommandSyntaxException> {
- Ok(Suggestions::default())
- }
-
- fn is_valid_input(&self, input: &str) -> bool {
- false
- }
-
- fn usage_text(&self) -> &str {
- ""
- }
-
- fn create_builder(&self) -> Box<dyn ArgumentBuilder<S>> {
- panic!("Cannot convert root into a builder");
- }
-
- fn get_examples(&self) -> Vec<String> {
- vec![]
- }
-}
-
-impl<S> Display for RootCommandNode<S> {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- write!(f, "<root>")
- }
-}