From a72a47ced76065caf739898954cd18edbc39174b Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 14:02:13 -0500 Subject: [PATCH] Rewrite brigadier --- azalea-brigadier/src/ambiguity_consumer.rs | 1 - .../src/arguments/argument_type.rs | 60 ---- .../src/arguments/bool_argument_type.rs | 59 ---- .../src/arguments/double_argument_type.rs | 1 - .../src/arguments/float_argument_type.rs | 1 - .../src/arguments/integer_argument_type.rs | 1 - .../src/arguments/long_argument_type.rs | 1 - azalea-brigadier/src/arguments/mod.rs | 7 - .../src/arguments/string_argument_type.rs | 1 - .../src/builder/argument_builder.rs | 297 ++++++++++-------- .../src/builder/literal_argument_builder.rs | 62 ++-- .../src/builder/required_argument_builder.rs | 121 +++---- azalea-brigadier/src/command.rs | 12 - azalea-brigadier/src/command_dispatcher.rs | 46 --- azalea-brigadier/src/context.rs | 145 +++++++++ .../src/context/command_context.rs | 93 ------ .../src/context/command_context_builder.rs | 176 ----------- azalea-brigadier/src/context/mod.rs | 6 - .../src/context/parsed_argument.rs | 24 -- .../src/context/parsed_command_node.rs | 30 -- .../src/context/suggestion_context.rs | 6 - azalea-brigadier/src/dispatcher.rs | 242 ++++++++++++++ .../exceptions/builtin_exception_provider.rs | 1 - .../src/exceptions/builtin_exceptions.rs | 16 +- .../exceptions/command_syntax_exception.rs | 6 +- azalea-brigadier/src/exceptions/mod.rs | 1 - .../src/immutable_string_reader.rs | 12 - azalea-brigadier/src/lib.rs | 73 +++-- azalea-brigadier/src/literal_message.rs | 1 - azalea-brigadier/src/main.rs | 38 +++ azalea-brigadier/src/message.rs | 4 +- azalea-brigadier/src/modifier.rs | 9 + azalea-brigadier/src/parse_results.rs | 20 ++ azalea-brigadier/src/parsers.rs | 21 ++ azalea-brigadier/src/redirect_modifier.rs | 11 - azalea-brigadier/src/result_consumer.rs | 1 - .../src/single_redirect_modifier.rs | 8 - .../src/{context => }/string_range.rs | 4 +- azalea-brigadier/src/string_reader.rs | 189 ++++++----- .../src/suggestion/integer_suggestion.rs | 1 - azalea-brigadier/src/suggestion/mod.rs | 5 - azalea-brigadier/src/suggestion/suggestion.rs | 90 ------ .../src/suggestion/suggestion_provider.rs | 14 - .../src/suggestion/suggestions.rs | 141 --------- .../src/suggestion/suggestions_builder.rs | 116 ------- azalea-brigadier/src/tree.rs | 269 ++++++++++++++++ .../src/tree/argument_command_node.rs | 176 ----------- azalea-brigadier/src/tree/command_node.rs | 143 --------- .../src/tree/literal_command_node.rs | 131 -------- azalea-brigadier/src/tree/mod.rs | 4 - .../src/tree/root_command_node.rs | 79 ----- 51 files changed, 1127 insertions(+), 1849 deletions(-) delete mode 100644 azalea-brigadier/src/ambiguity_consumer.rs delete mode 100644 azalea-brigadier/src/arguments/argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/bool_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/double_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/float_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/integer_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/long_argument_type.rs delete mode 100644 azalea-brigadier/src/arguments/mod.rs delete mode 100644 azalea-brigadier/src/arguments/string_argument_type.rs delete mode 100644 azalea-brigadier/src/command.rs delete mode 100644 azalea-brigadier/src/command_dispatcher.rs create mode 100644 azalea-brigadier/src/context.rs delete mode 100644 azalea-brigadier/src/context/command_context.rs delete mode 100644 azalea-brigadier/src/context/command_context_builder.rs delete mode 100644 azalea-brigadier/src/context/mod.rs delete mode 100644 azalea-brigadier/src/context/parsed_argument.rs delete mode 100644 azalea-brigadier/src/context/parsed_command_node.rs delete mode 100644 azalea-brigadier/src/context/suggestion_context.rs create mode 100644 azalea-brigadier/src/dispatcher.rs delete mode 100644 azalea-brigadier/src/exceptions/builtin_exception_provider.rs delete mode 100644 azalea-brigadier/src/immutable_string_reader.rs delete mode 100644 azalea-brigadier/src/literal_message.rs create mode 100644 azalea-brigadier/src/main.rs create mode 100644 azalea-brigadier/src/modifier.rs create mode 100644 azalea-brigadier/src/parsers.rs delete mode 100644 azalea-brigadier/src/redirect_modifier.rs delete mode 100644 azalea-brigadier/src/result_consumer.rs delete mode 100644 azalea-brigadier/src/single_redirect_modifier.rs rename azalea-brigadier/src/{context => }/string_range.rs (87%) delete mode 100644 azalea-brigadier/src/suggestion/integer_suggestion.rs delete mode 100644 azalea-brigadier/src/suggestion/mod.rs delete mode 100644 azalea-brigadier/src/suggestion/suggestion.rs delete mode 100644 azalea-brigadier/src/suggestion/suggestion_provider.rs delete mode 100644 azalea-brigadier/src/suggestion/suggestions.rs delete mode 100644 azalea-brigadier/src/suggestion/suggestions_builder.rs create mode 100644 azalea-brigadier/src/tree.rs delete mode 100644 azalea-brigadier/src/tree/argument_command_node.rs delete mode 100644 azalea-brigadier/src/tree/command_node.rs delete mode 100644 azalea-brigadier/src/tree/literal_command_node.rs delete mode 100644 azalea-brigadier/src/tree/mod.rs delete mode 100644 azalea-brigadier/src/tree/root_command_node.rs 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 { - match self { - Bool(t) => t, - Entity(t) => t - } - } -} -*/ - -pub trait ArgumentType { - type Into; - // T parse(StringReader reader) throws CommandSyntaxException; - - // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { - // return Suggestions.empty(); - // } - - // default Collection getExamples() { - // return Collections.emptyList(); - // } - - fn parse(&self, reader: &mut StringReader) -> Result; - - fn list_suggestions( - &self, - context: &CommandContext, - builder: &SuggestionsBuilder, - ) -> Result - where - Self: Sized, - S: Sized; - - fn get_examples(&self) -> Vec; -} 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 { - Ok(reader.read_boolean()?) - } - - fn list_suggestions( - &self, - context: &CommandContext, - builder: &mut SuggestionsBuilder, - ) -> Result - 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 { - vec![] - } -} - -impl BoolArgumentType { - const EXAMPLES: &'static [&'static str] = &["true", "false"]; - - fn bool() -> Self { - Self {} - } - - fn get_bool(context: CommandContext, name: String) { - context.get_argument::(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; +use crate::{context::CommandContext, modifier::RedirectModifier, tree::CommandNode}; -pub struct BaseArgumentBuilder<'a, S> { - arguments: RootCommandNode<'a, S>, - command: Option>>, - requirement: Box bool>, - target: Option>>, - modifier: Option>>, +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), +} + +/// A node that hasn't yet been built. +#[derive(Clone)] +pub struct ArgumentBuilder { + value: ArgumentBuilderType, + + children: BTreeMap>>>, + literals: BTreeMap>>>, + arguments: BTreeMap>>>, + + executes: Option) -> i32>>, + requirement: Rc) -> bool>, forks: bool, + modifier: Option>>, } -pub trait ArgumentBuilder { - fn build(self) -> Box>; -} +// todo: maybe remake this to be based on a CommandNode like vanilla does? -impl<'a, S> BaseArgumentBuilder<'a, S> { - pub fn then(&mut self, argument: Box>) -> 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) - } - - pub fn arguments(&self) -> &Vec<&dyn CommandNodeTrait> { - &self.arguments.get_children() - } - - pub fn executes(&mut self, command: Box>) -> &mut Self { - self.command = Some(command); - self - } - - pub fn command(&self) -> Option>> { - self.command - } - - pub fn requires(&mut self, requirement: Box bool>) -> &mut Self { - self.requirement = requirement; - self - } - - pub fn requirement(&self) -> Box bool> { - self.requirement - } - - pub fn redirect(&mut self, target: Box>) -> &mut Self { - self.forward(target, None, false) - } - - pub fn redirect_modifier( - &mut self, - target: &dyn CommandNodeTrait, - modifier: &dyn SingleRedirectModifier, - ) -> &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) - } - - pub fn fork( - &mut self, - target: &dyn CommandNodeTrait, - modifier: &dyn RedirectModifier, - ) -> &mut Self { - self.forward(target, Some(modifier), true) - } - - pub fn forward( - &mut self, - target: Option>>, - modifier: Option<&dyn RedirectModifier>, - 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) - } - - pub fn get_redirect(&self) -> Option<&dyn CommandNodeTrait> { - self.target.as_ref() - } - - pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - self.modifier.as_ref() - } - - pub fn is_fork(&self) -> bool { - self.forks - } - - 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, - - arguments: Default::default(), - children: Default::default(), - literals: Default::default(), - }; - - for argument in self.arguments() { - result.add_child(argument); - } - - result - } -} - -impl Default for BaseArgumentBuilder<'_, S> { - fn default() -> Self { +/// A node that isn't yet built. +impl ArgumentBuilder { + pub fn new(value: ArgumentBuilderType) -> Self { Self { - arguments: Default::default(), - command: Default::default(), - requirement: Default::default(), - target: Default::default(), - modifier: Default::default(), - forks: Default::default(), + value, + children: BTreeMap::new(), + literals: BTreeMap::new(), + arguments: BTreeMap::new(), + executes: None, + requirement: Rc::new(|_| true), + forks: false, + modifier: None, + } + } + + pub fn then(&mut self, node: ArgumentBuilder) -> &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 executes(&mut self, f: F) -> Self + where + F: Fn(&CommandContext) -> i32 + 'static, + { + self.executes = Some(Rc::new(f)); + self.clone() + } + + pub fn build(self) -> CommandNode { + println!("building {:?}", self); + CommandNode { + value: self.value, + + children: self.children, + literals: self.literals, + arguments: self.arguments, + + command: self.executes.clone(), + requirement: self.requirement.clone(), + redirect: None, + forks: self.forks, + modifier: self.modifier, } } } + +impl Debug for ArgumentBuilder { + 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() + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use crate::{ + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + parsers::integer, + }; + + use super::ArgumentBuilder; + + // public class ArgumentBuilderTest { + // private TestableArgumentBuilder builder; + + // @Before + // public void setUp() throws Exception { + // builder = new TestableArgumentBuilder<>(); + // } + + // @Test + // public void testArguments() throws Exception { + // final RequiredArgumentBuilder argument = argument("bar", integer()); + + // builder.then(argument); + + // assertThat(builder.getArguments(), hasSize(1)); + // assertThat(builder.getArguments(), hasItem((CommandNode) argument.build())); + // } + + #[test] + fn test_arguments() { + let mut builder: ArgumentBuilder<()> = literal("foo"); + + 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 target = mock(CommandNode.class); + // builder.redirect(target); + // assertThat(builder.getRedirect(), is(target)); + // } + + // @Test(expected = IllegalStateException.class) + // public void testRedirect_withChild() throws Exception { + // final CommandNode target = mock(CommandNode.class); + // builder.then(literal("foo")); + // builder.redirect(target); + // } + + // @Test(expected = IllegalStateException.class) + // public void testThen_withRedirect() throws Exception { + // final CommandNode target = mock(CommandNode.class); + // builder.redirect(target); + // builder.then(literal("foo")); + // } + + // private static class TestableArgumentBuilder extends ArgumentBuilder> { + // @Override + // protected TestableArgumentBuilder getThis() { + // return this; + // } + + // @Override + // public CommandNode 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 { - arguments: RootCommandNode, - command: Option>>, - requirement: Box bool>, - target: Option>>, - modifier: Option>>, - forks: bool, - literal: String, +use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; + +#[derive(Debug, Clone, Default)] +pub struct Literal { + pub value: String, } - -impl LiteralArgumentBuilder { - pub fn new(literal: String) -> Self { +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 ArgumentBuilder for LiteralArgumentBuilder { - fn build(self) -> Box> { - let result = LiteralCommandNode::new(self.literal, self.base.build()); - - for argument in self.base.arguments() { - result.add_child(argument); - } - - Box::new(result) +impl From for ArgumentBuilderType { + fn from(literal: Literal) -> Self { + Self::Literal(literal) } } + +/// Shortcut for creating a new literal builder node. +pub fn literal(value: &str) -> ArgumentBuilder { + 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; +use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; +use crate::{parsers::Parser, string_reader::StringReader}; +use std::{any::Any, fmt::Debug, rc::Rc}; -pub struct RequiredArgumentBuilder<'a, S> { - arguments: RootCommandNode<'a, S>, - command: Option>>, - requirement: Box bool>, - target: Option>>, - modifier: Option>>, - forks: bool, - - name: String, - type_: Box>, - suggestions_provider: Option>>, +/// 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, } - -impl<'a, S> RequiredArgumentBuilder<'a, S> { - pub fn new(name: String, type_: Box>) -> Self { +impl Argument { + pub fn new(name: &str, parser: Rc) -> 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>) -> Self { - Self::new(name, type_) - } - - pub fn suggests(mut self, provider: Box>) -> Self { - self.suggestions_provider = Some(provider); - self - } - - pub fn suggestions_provider(&self) -> Option>> { - self.suggestions_provider - } - - pub fn get_type(&self) -> Box> { - self.type_ - } - - pub fn name(&self) -> &str { - &self.name - } - - // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); - - // for (final CommandNode 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 + pub fn parse(&self, reader: &mut StringReader) -> Option> { + self.parser.parse(reader) } } + +impl From for ArgumentBuilderType { + fn from(argument: Argument) -> Self { + Self::Argument(argument) + } +} + +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() + } +} + +/// Shortcut for creating a new argument builder node. +pub fn argument<'a, S: Any + Clone>( + name: &'a str, + parser: impl Parser + 'static, +) -> ArgumentBuilder { + 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: Clone { - fn run(&self, context: &mut CommandContext) -> Result; -} 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 { - root: RootCommandNode, -} - -impl CommandDispatcher { - /// 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) -> 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 { + pub arguments: HashMap, + pub root: Rc>>, + pub nodes: Vec>>, + pub dispatcher: Rc>, + pub source: Rc, + pub command: Option) -> i32>>, + pub child: Option>>, + pub range: StringRange, + pub modifier: Option>>, + pub forks: bool, +} + +impl CommandContextBuilder { + // CommandDispatcher dispatcher, final S source, final CommandNode rootNode, final int start + pub fn new( + dispatcher: Rc>, + source: Rc, + root_node: Rc>>, + 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) -> i32>>, + ) -> &Self { + self.command = command.clone(); + self + } + pub fn with_child(&mut self, child: Rc>) -> &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>, 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 { + 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 Debug for CommandContextBuilder { + 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, +} + +#[derive(Clone)] +/// A built `CommandContextBuilder`. +pub struct CommandContext { + pub source: Rc, + pub input: String, + pub arguments: HashMap, + pub command: Option) -> i32>>, + pub root_node: Rc>>, + pub nodes: Vec>>, + pub range: StringRange, + pub child: Option>>, + pub modifier: Option>>, + pub forks: bool, +} + +impl CommandContext { + pub fn copy_for(&self, source: Rc) -> 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, - arguments: HashMap>>, - root_node: &'a dyn CommandNodeTrait, - nodes: Vec>, - range: StringRange, - child: Option<&'a CommandContext<'a, S>>, - modifier: Option<&'a dyn RedirectModifier>, - forks: bool, -} - -impl 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> { - &self.child - } - - fn last_child(&self) -> &CommandContext { - let mut result = self; - while result.child.is_some() { - result = result.child.as_ref().unwrap(); - } - result - } - - fn command(&self) -> &dyn Command { - &self.command - } - - fn source(&self) -> &S { - &self.source - } - - // public V getArgument(final String name, final Class clazz) { - // final ParsedArgument 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(&self, name: &str) -> Result { - 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 { -// private final Map> arguments = new LinkedHashMap<>(); -// private final CommandNode rootNode; -// private final List> nodes = new ArrayList<>(); -// private final CommandDispatcher dispatcher; -// private S source; -// private Command command; -// private CommandContextBuilder child; -// private StringRange range; -// private RedirectModifier modifier = null; -// private boolean forks; - -#[derive(Clone)] -pub struct CommandContextBuilder<'a, S> { - arguments: HashMap>>, - root_node: &'a dyn CommandNodeTrait, - nodes: Vec>, - dispatcher: CommandDispatcher<'a, S>, - source: S, - command: Box>, - child: Box>>, - range: StringRange, - modifier: Option>>, - forks: bool, -} - -// public CommandContextBuilder(final CommandDispatcher dispatcher, final S source, final CommandNode rootNode, final int start) { -// this.rootNode = rootNode; -// this.dispatcher = dispatcher; -// this.source = source; -// this.range = StringRange.at(start); -// } - -impl CommandContextBuilder<'_, S> { - pub fn new( - dispatcher: CommandDispatcher, - source: S, - root_node: &dyn CommandNodeTrait, - 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 { - &self.root_node - } - - pub fn with_argument(mut self, name: String, argument: ParsedArgument>) -> Self { - self.arguments.insert(name, argument); - self - } - - pub fn arguments(&self) -> &HashMap>> { - &self.arguments - } - - pub fn with_command(mut self, command: &dyn Command) -> Self { - self.command = command; - self - } - - pub fn with_node(mut self, node: dyn CommandNodeTrait, 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) -> Self { - self.child = Some(child); - self - } - - pub fn child(&self) -> Option<&CommandContextBuilder> { - self.child.as_ref() - } - - pub fn last_child(&self) -> Option<&CommandContextBuilder> { - let mut result = self; - while let Some(child) = result.child() { - result = child; - } - Some(result) - } - - pub fn command(&self) -> &dyn Command { - &*self.command - } - - pub fn nodes(&self) -> &Vec> { - &self.nodes - } - - pub fn build(self, input: &str) -> CommandContext { - 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 { - &self.dispatcher - } - - pub fn range(&self) -> &StringRange { - &self.range - } - - pub fn find_suggestion_context(&self, cursor: i32) -> Result, 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 { - range: StringRange, - result: T, -} - -impl ParsedArgument { - 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 { - node: Box>, - range: StringRange, -} - -impl ParsedCommandNode { - fn new(node: dyn CommandNodeTrait, range: StringRange) -> Self { - Self { node, range } - } - - fn node(&self) -> &dyn CommandNodeTrait { - &self.node - } - - fn range(&self) -> &StringRange { - &self.range - } -} - -impl Clone for ParsedCommandNode { - 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, - 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 { + root: Rc>>, + _marker: PhantomData, +} + +impl CommandDispatcher { + pub fn new() -> Self { + Self { + root: Rc::new(RefCell::new(CommandNode::default())), + _marker: PhantomData, + } + } + + pub fn register(&mut self, node: ArgumentBuilder) { + 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 { + 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>>, + original_reader: &StringReader, + context_so_far: CommandContextBuilder, + ) -> Result, CommandSyntaxException> { + let source = context_so_far.source.clone(); + let mut errors = HashMap::>, CommandSyntaxException>::new(); + let mut potentials: Vec> = 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) -> Result { + 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> = 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 Clone for CommandDispatcher { + 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::>::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::>::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::>::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::>::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); +pub struct Message(String); impl Message { pub fn string(&self) -> String { @@ -11,6 +11,6 @@ impl Message { impl From 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 { + fn apply(&self, context: &CommandContext) -> Result>, 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 { + pub context: CommandContextBuilder, + pub reader: StringReader, + pub exceptions: HashMap>, CommandSyntaxException>, +} + +impl Debug for ParseResults { + 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>; +} + +struct Integer {} +impl Parser for Integer { + fn parse(&self, reader: &mut StringReader) -> Option> { + 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: Clone { - fn apply(&self, context: CommandContext) -> Result, 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 { - fn apply(&self, context: CommandContext) -> Result; -} diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/string_range.rs similarity index 87% rename from azalea-brigadier/src/context/string_range.rs rename to 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 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 { 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 { 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 { 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, -} - -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 { - 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 { - fn suggestions( - &self, - context: &CommandContext, - builder: &SuggestionsBuilder, - ) -> Result; -} 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, -} - -impl Suggestions { - fn range(&self) -> &StringRange { - &self.range - } - - fn list(&self) -> &Vec { - &self.suggestions - } - - fn is_empty(&self) -> bool { - self.suggestions.is_empty() - } - - fn merge(command: &str, input: &Vec) { - 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 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 texts = new HashSet<>(); - // for (final Suggestion suggestion : suggestions) { - // texts.add(suggestion.expand(command, range)); - // } - // final List sorted = new ArrayList<>(texts); - // sorted.sort((a, b) -> a.compareToIgnoreCase(b)); - // return new Suggestions(range, sorted); - pub fn new(command: String, suggestions: Vec) -> 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, -} - -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 { + pub value: ArgumentBuilderType, + + // we use BTreeMap instead of HashMap because it can be hashed + pub children: BTreeMap>>>, + pub literals: BTreeMap>>>, + pub arguments: BTreeMap>>>, + + pub command: Option) -> i32>>, + pub requirement: Rc) -> bool>, + pub redirect: Option>>>, + pub forks: bool, + pub modifier: Option>>, +} + +impl CommandNode { + // pub fn new() + // TODO: precalculate `literals` and `arguments` and include them in CommandNode + fn literals(&self) -> &BTreeMap>>> { + &self.literals + } + fn arguments(&self) -> &BTreeMap>>> { + &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>>> { + 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) -> bool { + (self.requirement)(source) + } + + pub fn add_child(&mut self, node: &Rc>>) { + 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, + ) -> 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 { + 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 Debug for CommandNode { + 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 Default for CommandNode { + 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 Hash for CommandNode { + fn hash(&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 PartialEq for CommandNode { + 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 Eq for CommandNode {} 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 { - name: String, - type_: Box>, - custom_suggestions: Option>>, - - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - pub requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - pub command: Option>>, -} - -impl ArgumentCommandNode { - fn get_type(&self) -> &dyn ArgumentType { - &*self.type_ - } - - fn custom_suggestions(&self) -> &Option>> { - &self.custom_suggestions - } -} - -impl CommandNodeTrait for ArgumentCommandNode { - fn name(&self) -> &str { - &self.name - } - - fn parse( - &self, - reader: &mut StringReader, - context_builder: CommandContextBuilder, - ) -> Result<(), CommandSyntaxException> { - // final int start = reader.getCursor(); - // final T result = type.parse(reader); - // final ParsedArgument 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, - builder: &SuggestionsBuilder, - ) -> Result { - 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 { - 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 { - self.type_.get_examples() - } - - fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - 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>) -> Result<(), String> { - let dynamic_node = node as &dyn Any; - if dynamic_node.is::>() { - 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::>() { - self.literals.insert(node.name().to_string(), *dynamic_node); - } else if let Some(dynamic_node) = dynamic_node.downcast_ref::>() - { - self.arguments - .insert(node.name().to_string(), *dynamic_node); - } - Ok(()) - } - } -} - -impl Display for ArgumentCommandNode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "", 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 { - Literal(LiteralCommandNode), - Argument(ArgumentCommandNode), - Root(RootCommandNode), -} - -impl CommandNodeEnum { - fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - (*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>) -> Result<(), String> { - let dynamic_node = node as &dyn Any; - if dynamic_node.is::>() { - 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::>() { - self.literals.insert(node.name().to_string(), *dynamic_node); - } else if let Some(dynamic_node) = dynamic_node.downcast_ref::>() - { - self.arguments - .insert(node.name().to_string(), *dynamic_node); - } - Ok(()) - } - } -} -pub struct BaseCommandNode { - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - pub requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - pub command: Option>>, -} - -// impl 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 Debug for BaseCommandNode { - 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 Default for BaseCommandNode { - 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 { - fn name(&self) -> &str; - fn usage_text(&self) -> &str; - fn parse( - &self, - reader: &mut StringReader, - context_builder: CommandContextBuilder, - ) -> Result<(), CommandSyntaxException>; - fn list_suggestions( - &self, - context: CommandContext, - builder: &SuggestionsBuilder, - ) -> Result; - fn is_valid_input(&self, input: &str) -> bool; - fn create_builder(&self) -> Box>; - fn get_examples(&self) -> Vec; - - fn redirect_modifier(&self) -> Option<&dyn RedirectModifier>; - fn can_use(&self, source: S) -> bool; - fn add_child(&self, node: &Box>) -> 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 { - literal: String, - literal_lowercase: String, - - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - pub requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - pub command: Option>>, -} - -impl LiteralCommandNode { - 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 CommandNodeTrait for LiteralCommandNode { - fn name(&self) -> &str { - &self.literal - } - - fn parse( - &self, - reader: &mut StringReader<'_>, - context_builder: CommandContextBuilder, - ) -> 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, - builder: &SuggestionsBuilder, - ) -> Result { - 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> { - 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 { - 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 { - // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - pub requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - pub command: Option>>, -} - -impl CommandNodeTrait for RootCommandNode { - fn name(&self) -> &str { - "" - } - - fn parse( - &self, - reader: &mut StringReader<'_>, - context_builder: CommandContextBuilder, - ) -> Result<(), CommandSyntaxException> { - Ok(()) - } - - fn list_suggestions( - &self, - context: CommandContext, - builder: &SuggestionsBuilder, - ) -> Result { - Ok(Suggestions::default()) - } - - fn is_valid_input(&self, input: &str) -> bool { - false - } - - fn usage_text(&self) -> &str { - "" - } - - fn create_builder(&self) -> Box> { - panic!("Cannot convert root into a builder"); - } - - fn get_examples(&self) -> Vec { - vec![] - } -} - -impl Display for RootCommandNode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "") - } -}