From bd87cbb4434ba8bdf16ad93c5353ccefc0497d13 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 3 Jan 2022 13:36:02 -0600 Subject: create all empty brigadier modules --- azalea-brigadier/src/context/command_context_builder.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 azalea-brigadier/src/context/command_context_builder.rs (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 315f2258190b33c63df7797a97178019f5aea02b Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 22:33:45 -0600 Subject: add some more stuff from brigadier --- .gitignore | 1 + Cargo.lock | 3 + azalea-brigadier/Cargo.toml | 3 +- azalea-brigadier/src/arguments/argument_type.rs | 4 +- .../src/arguments/bool_argument_type.rs | 25 ++- azalea-brigadier/src/arguments/mod.rs | 6 + azalea-brigadier/src/builder/argument_builder.rs | 106 ++++++++++++ azalea-brigadier/src/builder/mod.rs | 3 + azalea-brigadier/src/command.rs | 10 ++ azalea-brigadier/src/command_dispatcher.rs | 23 +++ azalea-brigadier/src/context/command_context.rs | 87 ++++++++++ .../src/context/command_context_builder.rs | 180 +++++++++++++++++++++ azalea-brigadier/src/context/parsed_argument.rs | 24 +++ .../src/context/parsed_command_node.rs | 22 +++ azalea-brigadier/src/context/string_range.rs | 45 ++++++ azalea-brigadier/src/context/suggestion_context.rs | 6 + azalea-brigadier/src/lib.rs | 3 + azalea-brigadier/src/redirect_modifier.rs | 8 + azalea-brigadier/src/single_redirect_modifier.rs | 8 + .../src/suggestion/suggestion_provider.rs | 14 ++ azalea-brigadier/src/tree/argument_command_node.rs | 118 ++++++++++++++ azalea-brigadier/src/tree/command_node.rs | 52 ++++++ azalea-brigadier/src/tree/literal_command_node.rs | 96 +++++++++++ azalea-brigadier/src/tree/mod.rs | 4 + azalea-brigadier/src/tree/root_command_node.rs | 61 +++++++ 25 files changed, 902 insertions(+), 10 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/.gitignore b/.gitignore index ea8c4bf7..f97818c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/doc \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 67259fef..0eeb6520 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,9 @@ dependencies = [ [[package]] name = "azalea-brigadier" version = "0.1.0" +dependencies = [ + "lazy_static", +] [[package]] name = "azalea-chat" diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml index c617ffb1..3694a4b7 100644 --- a/azalea-brigadier/Cargo.toml +++ b/azalea-brigadier/Cargo.toml @@ -1,8 +1,9 @@ [package] +edition = "2021" name = "azalea-brigadier" version = "0.1.0" -edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lazy_static = "^1.4" diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 4c48d6bb..34d57285 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -5,7 +5,7 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -pub trait ArgumentType { +pub trait ArgumentType { // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { @@ -16,7 +16,7 @@ pub trait ArgumentType { // return Collections.emptyList(); // } - fn parse(reader: &mut StringReader) -> Result; + fn parse(reader: &mut StringReader) -> Result; fn list_suggestions( context: &CommandContext, diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index d4a33517..f4c03373 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -1,8 +1,19 @@ -struct BoolArgumentType { - // private static final Collection EXAMPLES = Arrays.asList("true", "false"); - const EXAMPLES: &'static [&'static str] = &["true", "false"]; -} +use crate::context::command_context::CommandContext; + +use super::argument_type::ArgumentType; + +struct BoolArgumentType {} + +impl ArgumentType for BoolArgumentType {} -impl ArgumentType for BoolArgumentType { - -} \ No newline at end of file +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/mod.rs b/azalea-brigadier/src/arguments/mod.rs index 50b0f09b..487c5db7 100644 --- a/azalea-brigadier/src/arguments/mod.rs +++ b/azalea-brigadier/src/arguments/mod.rs @@ -1 +1,7 @@ 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/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index e69de29b..8a64a9e4 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -0,0 +1,106 @@ +use crate::{ + command::Command, + redirect_modifier::RedirectModifier, + single_redirect_modifier::SingleRedirectModifier, + tree::{command_node::CommandNode, root_command_node::RootCommandNode}, +}; + +pub struct BaseArgumentBuilder +where + T: ArgumentBuilder, +{ + arguments: RootCommandNode, + command: dyn Command, + requirement: dyn Fn(&S) -> bool, + target: Option>, + modifier: Option>, + forks: bool, +} + +pub trait ArgumentBuilder { + fn this() -> T; + fn build(self) -> dyn CommandNode; +} + +impl BaseArgumentBuilder +where + T: ArgumentBuilder, +{ + pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { + if self.target.is_some() { + return Err("Cannot add children to a redirected node".to_string()); + } + self.command = command; + Ok(self) + } + + pub fn arguments(&self) -> &Vec> { + &self.arguments.get_children() + } + + pub fn executes(&mut self, command: dyn Command) -> &mut T { + self.command = command; + self + } + + pub fn command(&self) -> dyn Command { + self.command + } + + pub fn requires(&mut self, requirement: dyn Fn(&S) -> bool) -> &mut T { + self.requirement = requirement; + self + } + + pub fn requirement(&self) -> dyn Fn(&S) -> bool { + self.requirement + } + + pub fn redirect(&mut self, target: dyn CommandNode) -> &mut T { + self.forward(target, None, false) + } + + pub fn redirect_modifier( + &mut self, + target: dyn CommandNode, + modifier: dyn SingleRedirectModifier, + ) -> &mut T { + // 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 CommandNode, + modifier: dyn RedirectModifier, + ) -> &mut T { + self.forward(target, Some(modifier), true) + } + + pub fn forward( + &mut self, + target: dyn CommandNode, + modifier: Option>, + fork: bool, + ) -> Result<&mut T, String> { + if !self.arguments.get_children().is_empty() { + return Err("Cannot forward a node with children".to_string()); + } + self.target = Some(target); + self.modifier = modifier; + self.forks = fork; + Ok(self) + } + + pub fn redirect(&self) -> Option<&dyn CommandNode> { + self.target.as_ref() + } + + pub fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + self.modifier.as_ref() + } + + pub fn is_fork(&self) -> bool { + self.forks + } +} diff --git a/azalea-brigadier/src/builder/mod.rs b/azalea-brigadier/src/builder/mod.rs index e69de29b..26f2f644 100644 --- a/azalea-brigadier/src/builder/mod.rs +++ b/azalea-brigadier/src/builder/mod.rs @@ -0,0 +1,3 @@ +pub mod argument_builder; +pub mod literal_argument_builder; +pub mod required_argument_builder; diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs index e69de29b..a76454b7 100644 --- a/azalea-brigadier/src/command.rs +++ b/azalea-brigadier/src/command.rs @@ -0,0 +1,10 @@ +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, +}; + +pub const SINGLE_SUCCESS: i32 = 1; + +pub trait Command { + fn run(&self, context: &mut CommandContext) -> Result; +} diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index e69de29b..c476a39b 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -0,0 +1,23 @@ +/// 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 +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 = "|"; +} diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index ddbb447e..c6210a88 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -1,3 +1,90 @@ +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::CommandNode, +}; +use std::collections::HashMap; + pub struct CommandContext { source: S, + input: String, + command: dyn Command, + arguments: HashMap>, + root_node: dyn CommandNode, + nodes: Vec>, + range: StringRange, + child: Option>, + modifier: Option>, + forks: bool, +} + +impl CommandContext { + pub fn clone_for(&self, source: S) -> Self { + if self.source == source { + return self.clone(); + } + 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 index e69de29b..e74b5b1c 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -0,0 +1,180 @@ +use std::collections::HashMap; + +use crate::{ + arguments::argument_type::ArgumentType, command::Command, + command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, + tree::command_node::CommandNode, +}; + +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 { + arguments: HashMap>, + root_node: dyn CommandNode, + nodes: Vec>, + dispatcher: CommandDispatcher, + source: S, + command: Box>, + child: Option>, + 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 { + pub fn new( + dispatcher: CommandDispatcher, + source: S, + root_node: dyn CommandNode, + start: usize, + ) -> Self { + Self { + 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 CommandNode { + &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: Box>) -> Self { + self.command = command; + self + } + + pub fn with_node(mut self, node: dyn CommandNode, 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/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index e69de29b..5f9c2cdb 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -0,0 +1,24 @@ +use super::string_range::StringRange; + +#[derive(PartialEq, Eq, Hash)] +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 index e69de29b..98e99959 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -0,0 +1,22 @@ +use super::string_range::StringRange; +use crate::tree::command_node::CommandNode; + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct ParsedCommandNode { + node: dyn CommandNode, + range: StringRange, +} + +impl ParsedCommandNode { + fn new(node: dyn CommandNode, range: StringRange) -> Self { + Self { node, range } + } + + fn node(&self) -> &dyn CommandNode { + &self.node + } + + fn range(&self) -> &StringRange { + &self.range + } +} diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs index e69de29b..d775ab68 100644 --- a/azalea-brigadier/src/context/string_range.rs +++ b/azalea-brigadier/src/context/string_range.rs @@ -0,0 +1,45 @@ +use std::cmp; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StringRange { + start: usize, + end: usize, +} + +impl StringRange { + pub fn new(start: usize, end: usize) -> Self { + Self { start, end } + } + + pub fn at(pos: usize) -> Self { + Self::new(pos, pos) + } + + pub fn between(start: usize, end: usize) -> Self { + Self::new(start, end) + } + + pub fn encompassing(a: &Self, b: &Self) -> Self { + Self::new(cmp::min(a.start, b.start), cmp::max(a.end, b.end)) + } + + pub fn start(&self) -> usize { + self.start + } + + pub fn end(&self) -> usize { + self.end + } + + pub fn get(&self, reader: &str) -> &str { + &reader[self.start..self.end] + } + + pub fn is_empty(&self) -> bool { + self.start == self.end + } + + pub fn length(&self) -> usize { + self.end - self.start + } +} diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs index e69de29b..540a5f23 100644 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ b/azalea-brigadier/src/context/suggestion_context.rs @@ -0,0 +1,6 @@ +use crate::tree::command_node::CommandNode; + +pub struct SuggestionContext { + parent: dyn CommandNode, + start_pos: usize, +} diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index d0966de3..b2345abb 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate lazy_static; + mod ambiguity_consumer; mod arguments; mod builder; diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs index e69de29b..cfefd120 100644 --- a/azalea-brigadier/src/redirect_modifier.rs +++ b/azalea-brigadier/src/redirect_modifier.rs @@ -0,0 +1,8 @@ +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, +}; + +pub trait RedirectModifier { + fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; +} diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs index e69de29b..dd63244d 100644 --- a/azalea-brigadier/src/single_redirect_modifier.rs +++ b/azalea-brigadier/src/single_redirect_modifier.rs @@ -0,0 +1,8 @@ +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/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs index e69de29b..3027d460 100644 --- a/azalea-brigadier/src/suggestion/suggestion_provider.rs +++ b/azalea-brigadier/src/suggestion/suggestion_provider.rs @@ -0,0 +1,14 @@ +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/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index e69de29b..df7d3f5c 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -0,0 +1,118 @@ +use std::fmt::{Display, Formatter}; + +use crate::{ + arguments::argument_type::ArgumentType, + context::{ + command_context::CommandContext, command_context_builder::CommandContextBuilder, + parsed_argument::ParsedArgument, + }, + exceptions::command_syntax_exception::CommandSyntaxException, + string_reader::StringReader, + suggestion::{ + suggestion_provider::SuggestionProvider, suggestions::Suggestions, + suggestions_builder::SuggestionsBuilder, + }, +}; + +use super::command_node::{BaseCommandNode, CommandNode}; + +const USAGE_ARGUMENT_OPEN: &str = "<"; +const USAGE_ARGUMENT_CLOSE: &str = ">"; + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct ArgumentCommandNode { + name: String, + type_: dyn ArgumentType, + custom_suggestions: dyn SuggestionProvider, + // Since Rust doesn't have extending, we put the struct this is extending as the "base" field + pub base: BaseCommandNode, +} + +impl ArgumentCommandNode { + fn get_type(&self) -> &dyn ArgumentType { + &self.type_ + } + + fn custom_suggestions(&self) -> &dyn SuggestionProvider { + &self.custom_suggestions + } +} + +impl CommandNode for ArgumentCommandNode { + fn name(&self) -> &str { + &self.name + } + + fn parse( + &self, + reader: 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.get_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() + } +} + +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 index e69de29b..286820b9 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -0,0 +1,52 @@ +use std::collections::HashMap; + +use crate::{ + 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 super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; + +pub struct BaseCommandNode { + // private final Map> children = new LinkedHashMap<>(); + // private final Map> literals = new LinkedHashMap<>(); + // private final Map> arguments = new LinkedHashMap<>(); + // private final Predicate requirement; + // private final CommandNode redirect; + // private final RedirectModifier modifier; + // private final boolean forks; + // private Command command; + children: HashMap>, + literals: HashMap>, + arguments: HashMap>, + requirement: Option bool>, + redirect: Option>, + modifier: Option>, + forks: bool, + command: Option>, +} + +impl BaseCommandNode {} + +pub trait CommandNode { + fn name(&self) -> &str; + fn usage_text(&self) -> &str; + fn parse( + &self, + reader: 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) -> dyn ArgumentBuilder; + fn get_examples(&self) -> Vec; +} diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index e69de29b..bb0e613c 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -0,0 +1,96 @@ +use crate::{ + context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + string_reader::StringReader, + suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, +}; + +use super::command_node::{BaseCommandNode, CommandNode}; + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct LiteralCommandNode { + literal: String, + literal_lowercase: String, + // Since Rust doesn't have extending, we put the struct this is extending as the "base" field + pub base: BaseCommandNode, +} + +impl LiteralCommandNode { + pub fn literal(&self) -> &String { + &self.literal + } + + pub fn parse(&self, reader: StringReader) -> i32 { + let start = reader.get_cursor(); + if reader.can_read(self.literal.len()) { + let end = start + self.literal.len(); + if reader.get_string()[start..end].eq(&self.literal) { + reader.set_cursor(end); + if !reader.can_read() || reader.peek() == ' ' { + return end as i32; + } else { + reader.set_cursor(start); + } + } + } + -1 + } +} + +impl CommandNode for LiteralCommandNode { + fn name(&self) -> &str { + &self.literal + } + + fn parse( + &self, + reader: StringReader, + context_builder: CommandContextBuilder, + ) -> Result<(), CommandSyntaxException> { + let start = reader.get_cursor(); + let end = self.parse(reader); + if end > -1 { + return Ok(()); + } + + Err(BuiltInExceptions::LiteralIncorrect { + expected: self.literal(), + } + .create_with_context(reader)) + } + + fn list_suggestions( + &self, + context: CommandContext, + builder: SuggestionsBuilder, + ) -> Result { + if self + .literal_lowercase + .starts_with(&builder.remaining_lowercase()) + { + builder.suggest(self.literal()) + } else { + Suggestions::empty() + } + } + + fn is_valid_input(&self, input: &str) -> bool { + self.parse(StringReader::from(input)) > -1 + } + + fn usage_text(&self) -> &str { + self.literal + } + + fn create_builder(&self) -> LiteralArgumentBuilder { + let builder = LiteralArgumentBuilder::literal(self.literal()); + builder.requires(self.requirement()); + builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork()); + if self.command().is_some() { + builder.executes(self.command().unwrap()); + } + builder + } +} diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs index e69de29b..3dc22583 100644 --- a/azalea-brigadier/src/tree/mod.rs +++ b/azalea-brigadier/src/tree/mod.rs @@ -0,0 +1,4 @@ +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 index e69de29b..004ab6a8 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -0,0 +1,61 @@ +use std::fmt::{Display, Formatter}; + +use crate::{ + context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + string_reader::StringReader, + suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, +}; + +use super::command_node::{BaseCommandNode, CommandNode}; + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct RootCommandNode { + // Since Rust doesn't have extending, we put the struct this is extending as the "base" field + pub base: BaseCommandNode, +} + +impl CommandNode for RootCommandNode { + fn name(&self) -> &str { + "" + } + + fn parse( + &self, + reader: StringReader, + context_builder: CommandContextBuilder, + ) -> Result<(), CommandSyntaxException> { + } + + fn list_suggestions( + &self, + context: CommandContext, + builder: SuggestionsBuilder, + ) -> Result { + Suggestions::empty() + } + + fn is_valid_input(&self, input: &str) -> bool { + false + } + + fn usage_text(&self) -> &str { + "" + } + + fn create_builder(&self) -> () { + 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, "") + } +} -- cgit v1.2.3 From fec7a2bfedc562306523b9d3c51b97e376dc32d9 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 23:46:23 -0600 Subject: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA --- azalea-brigadier/src/arguments/argument_type.rs | 12 ++- .../src/arguments/bool_argument_type.rs | 2 +- azalea-brigadier/src/builder/argument_builder.rs | 7 +- .../src/builder/required_argument_builder.rs | 120 +++++++++++++++++++++ azalea-brigadier/src/command_dispatcher.rs | 2 + azalea-brigadier/src/context/command_context.rs | 6 +- .../src/context/command_context_builder.rs | 14 ++- azalea-brigadier/src/context/parsed_argument.rs | 6 +- azalea-brigadier/src/tree/argument_command_node.rs | 11 +- azalea-brigadier/src/tree/command_node.rs | 11 +- 10 files changed, 160 insertions(+), 31 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 34d57285..ea453a1a 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -5,7 +5,12 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -pub trait ArgumentType { +pub trait ArgumentResult {} + +pub trait ArgumentType +where + T: ArgumentResult, +{ // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { @@ -16,12 +21,13 @@ pub trait ArgumentType { // return Collections.emptyList(); // } - fn parse(reader: &mut StringReader) -> Result; + fn parse(&self, reader: &mut StringReader) -> Result; fn list_suggestions( + &self, context: &CommandContext, builder: &mut SuggestionsBuilder, ) -> Result; - fn get_examples() -> Vec; + 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 index f4c03373..74df3331 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -4,7 +4,7 @@ use super::argument_type::ArgumentType; struct BoolArgumentType {} -impl ArgumentType for BoolArgumentType {} +impl ArgumentType for BoolArgumentType {} impl BoolArgumentType { const EXAMPLES: &'static [&'static str] = &["true", "false"]; diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 8a64a9e4..19706a22 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -10,7 +10,7 @@ where T: ArgumentBuilder, { arguments: RootCommandNode, - command: dyn Command, + command: Option>, requirement: dyn Fn(&S) -> bool, target: Option>, modifier: Option>, @@ -18,7 +18,6 @@ where } pub trait ArgumentBuilder { - fn this() -> T; fn build(self) -> dyn CommandNode; } @@ -92,11 +91,11 @@ where Ok(self) } - pub fn redirect(&self) -> Option<&dyn CommandNode> { + pub fn get_redirect(&self) -> Option<&dyn CommandNode> { self.target.as_ref() } - pub fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { self.modifier.as_ref() } diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index e69de29b..3ec613c4 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -0,0 +1,120 @@ +use crate::{ + arguments::argument_type::ArgumentType, + suggestion::suggestion_provider::SuggestionProvider, + tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, +}; + +use super::argument_builder::BaseArgumentBuilder; + +// private RequiredArgumentBuilder(final String name, final ArgumentType type) { +// this.name = name; +// this.type = type; +// } + +// public static RequiredArgumentBuilder argument(final String name, final ArgumentType type) { +// return new RequiredArgumentBuilder<>(name, type); +// } + +// public RequiredArgumentBuilder suggests(final SuggestionProvider provider) { +// this.suggestionsProvider = provider; +// return getThis(); +// } + +// public SuggestionProvider getSuggestionsProvider() { +// return suggestionsProvider; +// } + +// @Override +// protected RequiredArgumentBuilder getThis() { +// return this; +// } + +// public ArgumentType getType() { +// return type; +// } + +// public String getName() { +// return name; +// } + +// public ArgumentCommandNode build() { +// final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); + +// for (final CommandNode argument : getArguments()) { +// result.addChild(argument); +// } + +// return result; +// } + +pub struct RequiredArgumentBuilder { + // private final String name; + // private final ArgumentType type; + // private SuggestionProvider suggestionsProvider = null; + name: String, + type_: dyn ArgumentType, + suggestions_provider: Option>, + + pub base: BaseArgumentBuilder, +} + +impl RequiredArgumentBuilder { + pub fn new(name: String, type_: dyn ArgumentType) -> Self { + Self { + name, + type_, + suggestions_provider: None, + base: BaseArgumentBuilder::new(name, type_), + } + } + + pub fn argument(name: String, type_: dyn ArgumentType) -> Self { + Self::new(name, type_) + } + + pub fn suggests(mut self, provider: dyn SuggestionProvider) -> Self { + self.suggestions_provider = Some(provider); + self + } + + pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { + self.suggestions_provider.as_ref() + } + + pub fn get_type(&self) -> &dyn ArgumentType { + &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 { + let result = ArgumentCommandNode { + name: self.name, + type_: &self.type_, + base: BaseCommandNode { + command: self.base.command, + requirement: self.base.requirement, + redirect: self.base.redirect, + modifier: self.base.modifier, + forks: self.base.forks, + ..BaseCommandNode::default() + }, + custom_suggestions: self.base.custom_suggestions, + }; + + for argument in self.base.arguments { + result.add_child(argument); + } + + result + } +} diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index c476a39b..0e9b9036 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,3 +1,5 @@ +use crate::tree::root_command_node::RootCommandNode; + /// 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 pub struct CommandDispatcher { diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index c6210a88..7a2189d9 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -3,7 +3,9 @@ use super::{ string_range::StringRange, }; use crate::{ - arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, + arguments::argument_type::{ArgumentResult, ArgumentType}, + command::Command, + redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; use std::collections::HashMap; @@ -12,7 +14,7 @@ pub struct CommandContext { source: S, input: String, command: dyn Command, - arguments: HashMap>, + arguments: HashMap>>, root_node: dyn CommandNode, nodes: Vec>, range: StringRange, diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index e74b5b1c..ac4a36bb 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,8 +1,10 @@ use std::collections::HashMap; use crate::{ - arguments::argument_type::ArgumentType, command::Command, - command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, + arguments::argument_type::{ArgumentResult, ArgumentType}, + command::Command, + command_dispatcher::CommandDispatcher, + redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; @@ -26,7 +28,7 @@ use super::{ #[derive(Clone)] pub struct CommandContextBuilder { - arguments: HashMap>, + arguments: HashMap>>, root_node: dyn CommandNode, nodes: Vec>, dispatcher: CommandDispatcher, @@ -77,13 +79,15 @@ impl CommandContextBuilder { pub fn with_argument( mut self, name: String, - argument: ParsedArgument, + argument: ParsedArgument>, ) -> Self { self.arguments.insert(name, argument); self } - pub fn arguments(&self) -> &HashMap> { + pub fn arguments( + &self, + ) -> &HashMap>> { &self.arguments } diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index 5f9c2cdb..77a47078 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -1,12 +1,14 @@ +use std::marker::PhantomData; + use super::string_range::StringRange; #[derive(PartialEq, Eq, Hash)] -pub struct ParsedArgument { +pub struct ParsedArgument { range: StringRange, result: T, } -impl ParsedArgument { +impl ParsedArgument { fn new(start: usize, end: usize, result: T) -> Self { Self { range: StringRange::between(start, end), diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index df7d3f5c..51add3d5 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,7 +1,8 @@ use std::fmt::{Display, Formatter}; use crate::{ - arguments::argument_type::ArgumentType, + arguments::argument_type::{ArgumentResult, ArgumentType}, + builder::required_argument_builder::RequiredArgumentBuilder, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, parsed_argument::ParsedArgument, @@ -22,14 +23,14 @@ const USAGE_ARGUMENT_CLOSE: &str = ">"; #[derive(Hash, PartialEq, Eq, Debug, Clone)] pub struct ArgumentCommandNode { name: String, - type_: dyn ArgumentType, + type_: Box>, custom_suggestions: dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode, } impl ArgumentCommandNode { - fn get_type(&self) -> &dyn ArgumentType { + fn get_type(&self) -> &dyn ArgumentType { &self.type_ } @@ -45,7 +46,7 @@ impl CommandNode for ArgumentCommandNode { fn parse( &self, - reader: StringReader, + reader: &mut StringReader, context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { // final int start = reader.getCursor(); @@ -68,7 +69,7 @@ impl CommandNode for ArgumentCommandNode { fn list_suggestions( &self, context: CommandContext, - builder: SuggestionsBuilder, + builder: &mut SuggestionsBuilder, ) -> Result { if self.custom_suggestions.is_none() { self.get_type().list_suggestions(context, builder) diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index 286820b9..717ea5f1 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::{ + arguments::argument_type::{ArgumentResult, ArgumentType}, builder::argument_builder::ArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -13,17 +14,9 @@ use crate::{ use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; pub struct BaseCommandNode { - // private final Map> children = new LinkedHashMap<>(); - // private final Map> literals = new LinkedHashMap<>(); - // private final Map> arguments = new LinkedHashMap<>(); - // private final Predicate requirement; - // private final CommandNode redirect; - // private final RedirectModifier modifier; - // private final boolean forks; - // private Command command; children: HashMap>, literals: HashMap>, - arguments: HashMap>, + arguments: HashMap>>, requirement: Option bool>, redirect: Option>, modifier: Option>, -- cgit v1.2.3 From cb4d871f6f56a484dc87151ea3ad55f7e3bbed97 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 11 Jan 2022 00:42:30 +0000 Subject: work a bit on brigadier --- azalea-brigadier/src/arguments/argument_type.rs | 26 +++++++++++++++++----- .../src/arguments/bool_argument_type.rs | 2 +- .../src/context/command_context_builder.rs | 6 ++--- azalea-brigadier/src/tree/command_node.rs | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index ea453a1a..f5ca457b 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -1,3 +1,4 @@ +use super::bool_argument_type::BoolArgumentType; use crate::{ context::command_context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, @@ -5,12 +6,27 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -pub trait ArgumentResult {} +pub enum DefaultArguments { + Bool(BoolArgumentType), +} + +/* +define_arguments! { + Entity(EntityArgumentType) +} + +=== + +enum CustomArguments { + Entity(EntityArgumentType) +} +enum BrigadierArguments { + BuiltIn(DefaultArguments) + Custom(CustomArguments) +} +*/ -pub trait ArgumentType -where - T: ArgumentResult, -{ +pub trait ArgumentType { // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index 74df3331..9bdf42e5 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -2,7 +2,7 @@ use crate::context::command_context::CommandContext; use super::argument_type::ArgumentType; -struct BoolArgumentType {} +pub struct BoolArgumentType {} impl ArgumentType for BoolArgumentType {} diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index ac4a36bb..5766ea9a 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,10 +1,8 @@ use std::collections::HashMap; use crate::{ - arguments::argument_type::{ArgumentResult, ArgumentType}, - command::Command, - command_dispatcher::CommandDispatcher, - redirect_modifier::RedirectModifier, + arguments::argument_type::ArgumentType, command::Command, + command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index 717ea5f1..0d9212aa 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -16,7 +16,7 @@ use super::{argument_command_node::ArgumentCommandNode, literal_command_node::Li pub struct BaseCommandNode { children: HashMap>, literals: HashMap>, - arguments: HashMap>>, + arguments: HashMap>>, requirement: Option bool>, redirect: Option>, modifier: Option>, -- cgit v1.2.3 From 60b129b3a62b66dd389d1775892405fe735b9540 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 10 Jan 2022 20:29:46 -0600 Subject: progress --- azalea-brigadier/src/arguments/argument_type.rs | 28 +++++---- .../src/arguments/bool_argument_type.rs | 49 +++++++++++++-- azalea-brigadier/src/builder/argument_builder.rs | 50 ++++++++-------- .../src/builder/literal_argument_builder.rs | 32 ++++++++++ .../src/builder/required_argument_builder.rs | 70 +++++----------------- azalea-brigadier/src/command.rs | 4 +- azalea-brigadier/src/command_dispatcher.rs | 26 +++++++- azalea-brigadier/src/context/command_context.rs | 26 ++++---- .../src/context/command_context_builder.rs | 56 ++++++++--------- azalea-brigadier/src/context/parsed_argument.rs | 3 +- .../src/context/parsed_command_node.rs | 10 ++-- azalea-brigadier/src/context/suggestion_context.rs | 4 +- azalea-brigadier/src/redirect_modifier.rs | 4 +- azalea-brigadier/src/single_redirect_modifier.rs | 4 +- .../src/suggestion/suggestion_provider.rs | 4 +- azalea-brigadier/src/tree/argument_command_node.rs | 24 ++++---- azalea-brigadier/src/tree/command_node.rs | 28 ++++----- azalea-brigadier/src/tree/literal_command_node.rs | 26 +++++--- azalea-brigadier/src/tree/root_command_node.rs | 12 ++-- 19 files changed, 259 insertions(+), 201 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index f5ca457b..107b1cbf 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -6,27 +6,30 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -pub enum DefaultArguments { - Bool(BoolArgumentType), +pub trait Types { + fn bool(value: bool) -> Self; } /* -define_arguments! { +#[derive(Types)] +enum BrigadierTypes { Entity(EntityArgumentType) } === -enum CustomArguments { +enum BrigadierTypes { + Bool(BoolArgumentType) + Entity(EntityArgumentType) } -enum BrigadierArguments { - BuiltIn(DefaultArguments) - Custom(CustomArguments) -} */ -pub trait ArgumentType { +pub trait ArgumentType +where + Self: Sized, + T: Types, +{ // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { @@ -41,9 +44,12 @@ pub trait ArgumentType { fn list_suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &mut SuggestionsBuilder, - ) -> Result; + ) -> Result + where + S: Sized, + T: 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 index 9bdf42e5..dc2c6896 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -1,10 +1,51 @@ -use crate::context::command_context::CommandContext; +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; +use super::argument_type::{ArgumentType, Types}; pub struct BoolArgumentType {} -impl ArgumentType for BoolArgumentType {} +impl ArgumentType for BoolArgumentType +where + T: Types, +{ + fn parse(&self, reader: &mut StringReader) -> Result { + Ok(T::bool(reader.read_boolean()?)) + } + + fn list_suggestions( + &self, + context: &CommandContext, + builder: &mut SuggestionsBuilder, + ) -> Result + where + S: Sized, + T: 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.get_remaining_lower_case()) { + builder.suggest("true"); + } + if "false".starts_with(builder.get_remaining_lower_case()) { + builder.suggest("false"); + } + Ok(builder.build_future()) + } + + fn get_examples(&self) -> Vec { + vec![] + } +} impl BoolArgumentType { const EXAMPLES: &'static [&'static str] = &["true", "false"]; @@ -13,7 +54,7 @@ impl BoolArgumentType { Self {} } - fn get_bool(context: CommandContext, name: String) { + fn get_bool(context: CommandContext, name: String) { context.get_argument::(name) } } diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 19706a22..bd2a2c15 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -5,27 +5,25 @@ use crate::{ tree::{command_node::CommandNode, root_command_node::RootCommandNode}, }; -pub struct BaseArgumentBuilder +pub struct BaseArgumentBuilder<'a, S, T> where - T: ArgumentBuilder, + S: Sized, + T: Sized, { - arguments: RootCommandNode, - command: Option>, - requirement: dyn Fn(&S) -> bool, - target: Option>, - modifier: Option>, + arguments: RootCommandNode<'a, S, T>, + command: Option<&'a dyn Command>, + requirement: &'a dyn Fn(&S) -> bool, + target: Option<&'a dyn CommandNode>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, } pub trait ArgumentBuilder { - fn build(self) -> dyn CommandNode; + fn build(self) -> dyn CommandNode; } -impl BaseArgumentBuilder -where - T: ArgumentBuilder, -{ - pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { +impl BaseArgumentBuilder<'_, S, T> { + pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); } @@ -33,20 +31,20 @@ where Ok(self) } - pub fn arguments(&self) -> &Vec> { + pub fn arguments(&self) -> &Vec<&dyn CommandNode> { &self.arguments.get_children() } - pub fn executes(&mut self, command: dyn Command) -> &mut T { + pub fn executes(&mut self, command: dyn Command) -> &mut T { self.command = command; self } - pub fn command(&self) -> dyn Command { + pub fn command(&self) -> dyn Command { self.command } - pub fn requires(&mut self, requirement: dyn Fn(&S) -> bool) -> &mut T { + pub fn requires(&mut self, requirement: &dyn Fn(&S) -> bool) -> &mut T { self.requirement = requirement; self } @@ -55,14 +53,14 @@ where self.requirement } - pub fn redirect(&mut self, target: dyn CommandNode) -> &mut T { + pub fn redirect(&mut self, target: &dyn CommandNode) -> &mut T { self.forward(target, None, false) } pub fn redirect_modifier( &mut self, - target: dyn CommandNode, - modifier: dyn SingleRedirectModifier, + target: &dyn CommandNode, + modifier: &dyn SingleRedirectModifier, ) -> &mut T { // forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false); self.forward(target, modifier.map(|m| |o| vec![m.apply(o)]), false) @@ -70,16 +68,16 @@ where pub fn fork( &mut self, - target: dyn CommandNode, - modifier: dyn RedirectModifier, + target: &dyn CommandNode, + modifier: &dyn RedirectModifier, ) -> &mut T { self.forward(target, Some(modifier), true) } pub fn forward( &mut self, - target: dyn CommandNode, - modifier: Option>, + target: &dyn CommandNode, + modifier: Option<&dyn RedirectModifier>, fork: bool, ) -> Result<&mut T, String> { if !self.arguments.get_children().is_empty() { @@ -91,11 +89,11 @@ where Ok(self) } - pub fn get_redirect(&self) -> Option<&dyn CommandNode> { + pub fn get_redirect(&self) -> Option<&dyn CommandNode> { self.target.as_ref() } - pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { self.modifier.as_ref() } diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index e69de29b..cf9f1ee9 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -0,0 +1,32 @@ +use crate::tree::literal_command_node::LiteralCommandNode; + +use super::argument_builder::BaseArgumentBuilder; + +pub struct LiteralArgumentBuilder<'a, S, T> { + literal: String, + + pub base: BaseArgumentBuilder<'a, S, T>, +} + +impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> { + pub fn new(literal: String) -> Self { + Self { + literal, + base: BaseArgumentBuilder::default(), + } + } + + pub fn literal(name: String) -> Self { + Self::new(name) + } + + pub fn build(self) -> LiteralCommandNode<'a, S, T> { + let result = LiteralCommandNode::new(self.literal, self.base); + + for argument in self.base.arguments { + result.add_child(argument); + } + + result + } +} diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 3ec613c4..6f6fa8eb 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,92 +1,50 @@ use crate::{ - arguments::argument_type::ArgumentType, suggestion::suggestion_provider::SuggestionProvider, tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, }; use super::argument_builder::BaseArgumentBuilder; -// private RequiredArgumentBuilder(final String name, final ArgumentType type) { -// this.name = name; -// this.type = type; -// } - -// public static RequiredArgumentBuilder argument(final String name, final ArgumentType type) { -// return new RequiredArgumentBuilder<>(name, type); -// } - -// public RequiredArgumentBuilder suggests(final SuggestionProvider provider) { -// this.suggestionsProvider = provider; -// return getThis(); -// } - -// public SuggestionProvider getSuggestionsProvider() { -// return suggestionsProvider; -// } - -// @Override -// protected RequiredArgumentBuilder getThis() { -// return this; -// } - -// public ArgumentType getType() { -// return type; -// } - -// public String getName() { -// return name; -// } - -// public ArgumentCommandNode build() { -// final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); - -// for (final CommandNode argument : getArguments()) { -// result.addChild(argument); -// } - -// return result; -// } - -pub struct RequiredArgumentBuilder { +pub struct RequiredArgumentBuilder<'a, S, T> { // private final String name; // private final ArgumentType type; // private SuggestionProvider suggestionsProvider = null; name: String, - type_: dyn ArgumentType, - suggestions_provider: Option>, + type_: &'a T, + suggestions_provider: Option<&'a dyn SuggestionProvider>, - pub base: BaseArgumentBuilder, + pub base: BaseArgumentBuilder<'a, S, T>, } -impl RequiredArgumentBuilder { - pub fn new(name: String, type_: dyn ArgumentType) -> Self { +impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> { + pub fn new(name: String, type_: T) -> Self { Self { name, - type_, + type_: &type_, suggestions_provider: None, base: BaseArgumentBuilder::new(name, type_), } } - pub fn argument(name: String, type_: dyn ArgumentType) -> Self { + pub fn argument(name: String, type_: T) -> Self { Self::new(name, type_) } - pub fn suggests(mut self, provider: dyn SuggestionProvider) -> Self { + pub fn suggests(mut self, provider: &dyn SuggestionProvider) -> Self { self.suggestions_provider = Some(provider); self } - pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { + pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { self.suggestions_provider.as_ref() } - pub fn get_type(&self) -> &dyn ArgumentType { - &self.type_ + pub fn get_type(&self) -> &T { + self.type_ } pub fn name(&self) -> &str { - &self.name + self.name } // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); @@ -96,7 +54,7 @@ impl RequiredArgumentBuilder { // } // return result; - pub fn build(self) -> ArgumentCommandNode { + pub fn build(self) -> ArgumentCommandNode<'a, S, T> { let result = ArgumentCommandNode { name: self.name, type_: &self.type_, diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs index a76454b7..520c8a52 100644 --- a/azalea-brigadier/src/command.rs +++ b/azalea-brigadier/src/command.rs @@ -5,6 +5,6 @@ use crate::{ pub const SINGLE_SUCCESS: i32 = 1; -pub trait Command { - fn run(&self, context: &mut CommandContext) -> Result; +pub trait Command { + fn run(&self, context: &mut CommandContext) -> Result; } diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index 0e9b9036..d0351547 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -2,11 +2,12 @@ use crate::tree::root_command_node::RootCommandNode; /// 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 -pub struct CommandDispatcher { - root: RootCommandNode, +#[derive(Default)] +pub struct CommandDispatcher<'a, S, T> { + root: RootCommandNode<'a, S, T>, } -impl CommandDispatcher { +impl CommandDispatcher<'_, S, T> { /// The string required to separate individual arguments in an input string /// /// See: [`ARGUMENT_SEPARATOR_CHAR`] @@ -22,4 +23,23 @@ impl CommandDispatcher { 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/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 7a2189d9..36741906 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -3,27 +3,25 @@ use super::{ string_range::StringRange, }; use crate::{ - arguments::argument_type::{ArgumentResult, ArgumentType}, - command::Command, - redirect_modifier::RedirectModifier, + arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; use std::collections::HashMap; -pub struct CommandContext { +pub struct CommandContext<'a, S, T> { source: S, input: String, - command: dyn Command, - arguments: HashMap>>, - root_node: dyn CommandNode, - nodes: Vec>, + command: &'a dyn Command, + arguments: HashMap>, + root_node: &'a dyn CommandNode, + nodes: Vec>, range: StringRange, - child: Option>, - modifier: Option>, + child: Option>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, } -impl CommandContext { +impl CommandContext<'_, S, T> { pub fn clone_for(&self, source: S) -> Self { if self.source == source { return self.clone(); @@ -42,11 +40,11 @@ impl CommandContext { } } - fn child(&self) -> &Option> { + fn child(&self) -> &Option> { &self.child } - fn last_child(&self) -> &CommandContext { + fn last_child(&self) -> &CommandContext { let mut result = self; while result.child.is_some() { result = result.child.as_ref().unwrap(); @@ -54,7 +52,7 @@ impl CommandContext { result } - fn command(&self) -> &dyn Command { + fn command(&self) -> &dyn Command { &self.command } diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 5766ea9a..878d7692 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -25,16 +25,16 @@ use super::{ // private boolean forks; #[derive(Clone)] -pub struct CommandContextBuilder { - arguments: HashMap>>, - root_node: dyn CommandNode, - nodes: Vec>, - dispatcher: CommandDispatcher, +pub struct CommandContextBuilder<'a, S, T> { + arguments: HashMap>, + root_node: &'a dyn CommandNode, + nodes: Vec>, + dispatcher: CommandDispatcher<'a, S, T>, source: S, - command: Box>, - child: Option>, + command: Box>, + child: Option>, range: StringRange, - modifier: Option>>, + modifier: Option>>, forks: bool, } @@ -45,15 +45,15 @@ pub struct CommandContextBuilder { // this.range = StringRange.at(start); // } -impl CommandContextBuilder { +impl CommandContextBuilder<'_, S, T> { pub fn new( - dispatcher: CommandDispatcher, + dispatcher: CommandDispatcher, source: S, - root_node: dyn CommandNode, + root_node: dyn CommandNode, start: usize, ) -> Self { Self { - root_node, + root_node: &root_node, dispatcher, source, range: StringRange::at(start), @@ -70,31 +70,25 @@ impl CommandContextBuilder { &self.source } - pub fn root_node(&self) -> &dyn CommandNode { + pub fn root_node(&self) -> &dyn CommandNode { &self.root_node } - pub fn with_argument( - mut self, - name: String, - argument: ParsedArgument>, - ) -> Self { + pub fn with_argument(mut self, name: String, argument: ParsedArgument) -> Self { self.arguments.insert(name, argument); self } - pub fn arguments( - &self, - ) -> &HashMap>> { + pub fn arguments(&self) -> &HashMap> { &self.arguments } - pub fn with_command(mut self, command: Box>) -> Self { + pub fn with_command(mut self, command: &dyn Command) -> Self { self.command = command; self } - pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { + pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { self.nodes.push(ParsedCommandNode::new(node, range)); self.range = StringRange::encompassing(&self.range, &range); self.modifier = node.redirect_modifier(); @@ -102,16 +96,16 @@ impl CommandContextBuilder { self } - pub fn with_child(mut self, child: CommandContextBuilder) -> Self { + pub fn with_child(mut self, child: CommandContextBuilder) -> Self { self.child = Some(child); self } - pub fn child(&self) -> Option<&CommandContextBuilder> { + pub fn child(&self) -> Option<&CommandContextBuilder> { self.child.as_ref() } - pub fn last_child(&self) -> Option<&CommandContextBuilder> { + pub fn last_child(&self) -> Option<&CommandContextBuilder> { let mut result = self; while let Some(child) = result.child() { result = child; @@ -119,15 +113,15 @@ impl CommandContextBuilder { Some(result) } - pub fn command(&self) -> &dyn Command { + pub fn command(&self) -> &dyn Command { &*self.command } - pub fn nodes(&self) -> &Vec> { + pub fn nodes(&self) -> &Vec> { &self.nodes } - pub fn build(self, input: &str) -> CommandContext { + pub fn build(self, input: &str) -> CommandContext { CommandContext { source: self.source, input, @@ -142,7 +136,7 @@ impl CommandContextBuilder { } } - pub fn dispatcher(&self) -> &CommandDispatcher { + pub fn dispatcher(&self) -> &CommandDispatcher { &self.dispatcher } @@ -150,7 +144,7 @@ impl CommandContextBuilder { &self.range } - pub fn find_suggestion_context(&self, cursor: i32) -> Result, String> { + 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() { diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index 77a47078..75c07784 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -1,10 +1,9 @@ -use std::marker::PhantomData; - use super::string_range::StringRange; #[derive(PartialEq, Eq, Hash)] pub struct ParsedArgument { range: StringRange, + // T is an item in an enum result: T, } diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index 98e99959..a006aa4b 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -2,17 +2,17 @@ use super::string_range::StringRange; use crate::tree::command_node::CommandNode; #[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct ParsedCommandNode { - node: dyn CommandNode, +pub struct ParsedCommandNode<'a, S, T> { + node: &'a dyn CommandNode, range: StringRange, } -impl ParsedCommandNode { - fn new(node: dyn CommandNode, range: StringRange) -> Self { +impl ParsedCommandNode<'_, S, T> { + fn new(node: &dyn CommandNode, range: StringRange) -> Self { Self { node, range } } - fn node(&self) -> &dyn CommandNode { + fn node(&self) -> &dyn CommandNode { &self.node } diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs index 540a5f23..42bc550e 100644 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ b/azalea-brigadier/src/context/suggestion_context.rs @@ -1,6 +1,6 @@ use crate::tree::command_node::CommandNode; -pub struct SuggestionContext { - parent: dyn CommandNode, +pub struct SuggestionContext<'a, S, T> { + parent: &'a dyn CommandNode, start_pos: usize, } diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs index cfefd120..fd2e1bf7 100644 --- a/azalea-brigadier/src/redirect_modifier.rs +++ b/azalea-brigadier/src/redirect_modifier.rs @@ -3,6 +3,6 @@ use crate::{ exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait RedirectModifier { - fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; +pub trait RedirectModifier { + fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; } diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs index dd63244d..95055fd8 100644 --- a/azalea-brigadier/src/single_redirect_modifier.rs +++ b/azalea-brigadier/src/single_redirect_modifier.rs @@ -3,6 +3,6 @@ use crate::{ exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait SingleRedirectModifier { - fn apply(&self, context: CommandContext) -> Result; +pub trait SingleRedirectModifier { + fn apply(&self, context: CommandContext) -> Result; } diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs index 3027d460..9720d3b9 100644 --- a/azalea-brigadier/src/suggestion/suggestion_provider.rs +++ b/azalea-brigadier/src/suggestion/suggestion_provider.rs @@ -5,10 +5,10 @@ use crate::{ use super::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}; -pub trait SuggestionProvider { +pub trait SuggestionProvider { fn suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &SuggestionsBuilder, ) -> Result; } diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 51add3d5..4d38b41f 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Formatter}; use crate::{ - arguments::argument_type::{ArgumentResult, ArgumentType}, + arguments::argument_type::ArgumentType, builder::required_argument_builder::RequiredArgumentBuilder, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, @@ -21,25 +21,25 @@ const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; #[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct ArgumentCommandNode { +pub struct ArgumentCommandNode<'a, S, T> { name: String, - type_: Box>, - custom_suggestions: dyn SuggestionProvider, + type_: &'a T, + custom_suggestions: &'a dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode, + pub base: BaseCommandNode<'a, S, T>, } -impl ArgumentCommandNode { - fn get_type(&self) -> &dyn ArgumentType { +impl ArgumentCommandNode<'_, S, T> { + fn get_type(&self) -> &T { &self.type_ } - fn custom_suggestions(&self) -> &dyn SuggestionProvider { + fn custom_suggestions(&self) -> &dyn SuggestionProvider { &self.custom_suggestions } } -impl CommandNode for ArgumentCommandNode { +impl CommandNode for ArgumentCommandNode<'_, S, T> { fn name(&self) -> &str { &self.name } @@ -47,7 +47,7 @@ impl CommandNode for ArgumentCommandNode { fn parse( &self, reader: &mut StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { // final int start = reader.getCursor(); // final T result = type.parse(reader); @@ -68,7 +68,7 @@ impl CommandNode for ArgumentCommandNode { fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: &mut SuggestionsBuilder, ) -> Result { if self.custom_suggestions.is_none() { @@ -112,7 +112,7 @@ impl CommandNode for ArgumentCommandNode { } } -impl Display for ArgumentCommandNode { +impl Display for ArgumentCommandNode<'_, String, String> { 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 index 0d9212aa..bcba9c03 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::{ - arguments::argument_type::{ArgumentResult, ArgumentType}, + arguments::argument_type::ArgumentType, builder::argument_builder::ArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -13,33 +13,33 @@ use crate::{ use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; -pub struct BaseCommandNode { - children: HashMap>, - literals: HashMap>, - arguments: HashMap>>, - requirement: Option bool>, - redirect: Option>, - modifier: Option>, +pub struct BaseCommandNode<'a, S, T> { + children: HashMap>, + literals: HashMap>, + arguments: HashMap>, + requirement: Option<&'a dyn Fn(&S) -> bool>, + redirect: Option<&'a dyn CommandNode>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, - command: Option>, + command: Option<&'a dyn Command>, } -impl BaseCommandNode {} +impl BaseCommandNode<'_, S, T> {} -pub trait CommandNode { +pub trait CommandNode { fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException>; fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result; fn is_valid_input(&self, input: &str) -> bool; - fn create_builder(&self) -> dyn ArgumentBuilder; + fn create_builder(&self) -> dyn ArgumentBuilder; fn get_examples(&self) -> Vec; } diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index bb0e613c..fe933669 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -1,8 +1,11 @@ use crate::{ + builder::literal_argument_builder::LiteralArgumentBuilder, + 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}, }; @@ -10,14 +13,23 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; #[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct LiteralCommandNode { +pub struct LiteralCommandNode<'a, S, T> { literal: String, literal_lowercase: String, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode, + pub base: BaseCommandNode<'a, S, T>, } -impl LiteralCommandNode { +impl<'a, S, T> LiteralCommandNode<'a, S, T> { + pub fn new(literal: String, base: BaseCommandNode) -> Self { + let literal_lowercase = literal.to_lowercase(); + Self { + literal, + literal_lowercase, + base, + } + } + pub fn literal(&self) -> &String { &self.literal } @@ -39,7 +51,7 @@ impl LiteralCommandNode { } } -impl CommandNode for LiteralCommandNode { +impl CommandNode for LiteralCommandNode<'_, S, T> { fn name(&self) -> &str { &self.literal } @@ -47,7 +59,7 @@ impl CommandNode for LiteralCommandNode { fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { let start = reader.get_cursor(); let end = self.parse(reader); @@ -63,7 +75,7 @@ impl CommandNode for LiteralCommandNode { fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result { if self @@ -84,7 +96,7 @@ impl CommandNode for LiteralCommandNode { self.literal } - fn create_builder(&self) -> LiteralArgumentBuilder { + fn create_builder(&self) -> LiteralArgumentBuilder { let builder = LiteralArgumentBuilder::literal(self.literal()); builder.requires(self.requirement()); builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork()); diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index 004ab6a8..25a5a4b2 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -12,12 +12,12 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; #[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct RootCommandNode { +pub struct RootCommandNode<'a, S, T> { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode, + pub base: BaseCommandNode<'a, S, T>, } -impl CommandNode for RootCommandNode { +impl CommandNode for RootCommandNode<'_, S, T> { fn name(&self) -> &str { "" } @@ -25,13 +25,13 @@ impl CommandNode for RootCommandNode { fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { } fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result { Suggestions::empty() @@ -54,7 +54,7 @@ impl CommandNode for RootCommandNode { } } -impl Display for RootCommandNode<()> { +impl Display for RootCommandNode<'_, S, T> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "") } -- cgit v1.2.3 From cc4fe62fc82842e0bde628437a45d55c6a82f1f3 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 11 Jan 2022 00:01:47 -0600 Subject: adfsfasdfaSDQAWERTERYTUYghyubnjnrdfxcv etgvbhy0ujn- --- azalea-brigadier/src/arguments/argument_type.rs | 6 +- .../src/arguments/bool_argument_type.rs | 6 +- azalea-brigadier/src/context/command_context.rs | 11 +- .../src/context/command_context_builder.rs | 2 +- azalea-brigadier/src/context/parsed_argument.rs | 2 +- .../src/context/parsed_command_node.rs | 9 +- azalea-brigadier/src/context/string_range.rs | 2 +- azalea-brigadier/src/message.rs | 1 + azalea-brigadier/src/suggestion/suggestion.rs | 90 ++++++++++++++++ azalea-brigadier/src/suggestion/suggestions.rs | 58 +++++++++++ .../src/suggestion/suggestions_builder.rs | 115 ++++++++++++++++++++- azalea-brigadier/src/tree/argument_command_node.rs | 23 +++-- azalea-brigadier/src/tree/command_node.rs | 2 +- azalea-brigadier/src/tree/root_command_node.rs | 1 - 14 files changed, 299 insertions(+), 29 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 107b1cbf..46026735 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -7,7 +7,9 @@ use crate::{ }; pub trait Types { - fn bool(value: bool) -> Self; + fn bool(value: bool) -> Self + where + Self: Sized; } /* @@ -28,7 +30,7 @@ enum BrigadierTypes { pub trait ArgumentType where Self: Sized, - T: Types, + T: Types + ?Sized, { // T parse(StringReader reader) throws CommandSyntaxException; diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index dc2c6896..1237caa0 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -33,13 +33,13 @@ where // builder.suggest("false"); // } // return builder.buildFuture(); - if "true".starts_with(builder.get_remaining_lower_case()) { + if "true".starts_with(builder.remaining_lowercase()) { builder.suggest("true"); } - if "false".starts_with(builder.get_remaining_lower_case()) { + if "false".starts_with(builder.remaining_lowercase()) { builder.suggest("false"); } - Ok(builder.build_future()) + Ok(builder.build()) } fn get_examples(&self) -> Vec { diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 36741906..68144a40 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -14,17 +14,20 @@ pub struct CommandContext<'a, S, T> { command: &'a dyn Command, arguments: HashMap>, root_node: &'a dyn CommandNode, - nodes: Vec>, + nodes: Vec>, range: StringRange, - child: Option>, + child: Option<&'a CommandContext<'a, S, T>>, modifier: Option<&'a dyn RedirectModifier>, forks: bool, } -impl CommandContext<'_, S, T> { +impl CommandContext<'_, S, T> +where + S: PartialEq, +{ pub fn clone_for(&self, source: S) -> Self { if self.source == source { - return self.clone(); + return *self; } Self { source, diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 878d7692..88e26343 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -28,7 +28,7 @@ use super::{ pub struct CommandContextBuilder<'a, S, T> { arguments: HashMap>, root_node: &'a dyn CommandNode, - nodes: Vec>, + nodes: Vec>, dispatcher: CommandDispatcher<'a, S, T>, source: S, command: Box>, diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index 75c07784..447a1223 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -1,6 +1,6 @@ use super::string_range::StringRange; -#[derive(PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash, Clone)] pub struct ParsedArgument { range: StringRange, // T is an item in an enum diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index a006aa4b..14168a06 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -1,14 +1,13 @@ use super::string_range::StringRange; use crate::tree::command_node::CommandNode; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct ParsedCommandNode<'a, S, T> { - node: &'a dyn CommandNode, +pub struct ParsedCommandNode { + node: Box>, range: StringRange, } -impl ParsedCommandNode<'_, S, T> { - fn new(node: &dyn CommandNode, range: StringRange) -> Self { +impl ParsedCommandNode { + fn new(node: dyn CommandNode, range: StringRange) -> Self { Self { node, range } } diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs index d775ab68..87098a1a 100644 --- a/azalea-brigadier/src/context/string_range.rs +++ b/azalea-brigadier/src/context/string_range.rs @@ -1,6 +1,6 @@ use std::cmp; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StringRange { start: usize, end: usize, diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index 71d0b178..42894d0e 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -1,5 +1,6 @@ use std::rc::Rc; +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Message(Rc); impl Message { diff --git a/azalea-brigadier/src/suggestion/suggestion.rs b/azalea-brigadier/src/suggestion/suggestion.rs index e69de29b..4cbed7be 100644 --- a/azalea-brigadier/src/suggestion/suggestion.rs +++ b/azalea-brigadier/src/suggestion/suggestion.rs @@ -0,0 +1,90 @@ +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/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index 354fc418..18572d20 100644 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -1 +1,59 @@ +use std::cmp; + +use crate::{context::string_range::StringRange, message::Message}; + pub struct Suggestions {} + +// #[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 index 6960f52b..7853a3a2 100644 --- a/azalea-brigadier/src/suggestion/suggestions_builder.rs +++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs @@ -1 +1,114 @@ -pub struct SuggestionsBuilder {} +use crate::context::string_range::StringRange; + +use super::{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/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 4d38b41f..647b6c35 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,13 +1,14 @@ use std::fmt::{Display, Formatter}; use crate::{ - arguments::argument_type::ArgumentType, + arguments::argument_type::{ArgumentType, Types}, builder::required_argument_builder::RequiredArgumentBuilder, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, parsed_argument::ParsedArgument, }, exceptions::command_syntax_exception::CommandSyntaxException, + immutable_string_reader::ImmutableStringReader, string_reader::StringReader, suggestion::{ suggestion_provider::SuggestionProvider, suggestions::Suggestions, @@ -20,11 +21,15 @@ use super::command_node::{BaseCommandNode, CommandNode}; const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct ArgumentCommandNode<'a, S, T> { +pub struct ArgumentCommandNode<'a, S, T> +where + // each argument command node has its own different type + T: ArgumentType, +{ name: String, type_: &'a T, - custom_suggestions: &'a dyn SuggestionProvider, + custom_suggestions: Option<&'a dyn SuggestionProvider>, + // custom_suggestions: &'a dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S, T>, } @@ -34,12 +39,12 @@ impl ArgumentCommandNode<'_, S, T> { &self.type_ } - fn custom_suggestions(&self) -> &dyn SuggestionProvider { - &self.custom_suggestions + fn custom_suggestions(&self) -> Option<&dyn SuggestionProvider> { + self.custom_suggestions } } -impl CommandNode for ArgumentCommandNode<'_, S, T> { +impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> { fn name(&self) -> &str { &self.name } @@ -56,7 +61,7 @@ impl CommandNode for ArgumentCommandNode<'_, S, T> { // contextBuilder.withArgument(name, parsed); // contextBuilder.withNode(this, parsed.getRange()); - let start = reader.get_cursor(); + let start = reader.cursor(); let result = self.get_type().parse(reader)?; let parsed = ParsedArgument::new(start, reader.get_cursor(), result); @@ -112,7 +117,7 @@ impl CommandNode for ArgumentCommandNode<'_, S, T> { } } -impl Display for ArgumentCommandNode<'_, String, String> { +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 index bcba9c03..8e262f0b 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -31,7 +31,7 @@ pub trait CommandNode { fn usage_text(&self) -> &str; fn parse( &self, - reader: StringReader, + reader: &mut StringReader, context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException>; fn list_suggestions( diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index 25a5a4b2..36787340 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -11,7 +11,6 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] pub struct RootCommandNode<'a, S, T> { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S, T>, -- cgit v1.2.3 From 270507736af57aae6801dc9eb3c3132139d0d07b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 12 Jan 2022 00:40:43 +0000 Subject: a --- Cargo.lock | 28 +++++++ azalea-brigadier/Cargo.toml | 1 + azalea-brigadier/README.md | 1 + azalea-brigadier/src/arguments/argument_type.rs | 28 +++++-- .../src/arguments/bool_argument_type.rs | 1 + azalea-brigadier/src/builder/argument_builder.rs | 8 +- .../src/builder/literal_argument_builder.rs | 15 +++- .../src/builder/required_argument_builder.rs | 11 ++- azalea-brigadier/src/command.rs | 4 +- azalea-brigadier/src/command_dispatcher.rs | 17 ++++- .../src/context/command_context_builder.rs | 16 +++- .../src/context/parsed_command_node.rs | 9 +++ azalea-brigadier/src/redirect_modifier.rs | 5 +- .../src/suggestion/integer_suggestion.rs | 1 + azalea-brigadier/src/suggestion/suggestions.rs | 86 +++++++++++++++++++++- .../src/suggestion/suggestions_builder.rs | 4 +- azalea-brigadier/src/tree/argument_command_node.rs | 17 ++++- azalea-brigadier/src/tree/command_node.rs | 59 +++++++++++++-- azalea-brigadier/src/tree/literal_command_node.rs | 20 ++++- azalea-brigadier/src/tree/root_command_node.rs | 19 ++++- 20 files changed, 307 insertions(+), 43 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/Cargo.lock b/Cargo.lock index 0eeb6520..a0ca81fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,7 @@ dependencies = [ name = "azalea-brigadier" version = "0.1.0" dependencies = [ + "dyn-clonable", "lazy_static", ] @@ -336,6 +337,33 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + [[package]] name = "either" version = "1.6.1" diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml index 3694a4b7..4e8968d7 100644 --- a/azalea-brigadier/Cargo.toml +++ b/azalea-brigadier/Cargo.toml @@ -7,3 +7,4 @@ version = "0.1.0" [dependencies] lazy_static = "^1.4" +dyn-clonable = "^0.9" diff --git a/azalea-brigadier/README.md b/azalea-brigadier/README.md index 92c0d27e..df69b5c0 100644 --- a/azalea-brigadier/README.md +++ b/azalea-brigadier/README.md @@ -1,3 +1,4 @@ # Azalea Brigadier A Rustier port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library. + diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 46026735..890cdea0 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -5,11 +5,19 @@ use crate::{ string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; +use dyn_clonable::*; -pub trait Types { +#[clonable] +// This should be applied to an Enum +pub trait Types: Clone { fn bool(value: bool) -> Self where Self: Sized; + + /// Get the less specific ArgumentType from this enum + fn inner(&self) -> Box> + where + Self: Sized; } /* @@ -25,12 +33,21 @@ enum BrigadierTypes { Entity(EntityArgumentType) } + +impl Types for BrigadierTypes { + fn inner(&self) -> dyn ArgumentType { + match self { + Bool(t) => t, + Entity(t) => t + } + } +} */ -pub trait ArgumentType +#[clonable] +pub trait ArgumentType: Clone where - Self: Sized, - T: Types + ?Sized, + T: Types, { // T parse(StringReader reader) throws CommandSyntaxException; @@ -42,7 +59,7 @@ where // return Collections.emptyList(); // } - fn parse(&self, reader: &mut StringReader) -> Result; + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; fn list_suggestions( &self, @@ -50,6 +67,7 @@ where builder: &mut SuggestionsBuilder, ) -> Result where + Self: Sized, S: Sized, T: Sized; diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index 1237caa0..b04488c1 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -7,6 +7,7 @@ use crate::{ use super::argument_type::{ArgumentType, Types}; +#[derive(Clone)] pub struct BoolArgumentType {} impl ArgumentType for BoolArgumentType diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index bd2a2c15..0360b05a 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,4 +1,5 @@ use crate::{ + arguments::argument_type::{ArgumentType, Types}, command::Command, redirect_modifier::RedirectModifier, single_redirect_modifier::SingleRedirectModifier, @@ -8,7 +9,7 @@ use crate::{ pub struct BaseArgumentBuilder<'a, S, T> where S: Sized, - T: Sized, + T: Sized + ArgumentType, { arguments: RootCommandNode<'a, S, T>, command: Option<&'a dyn Command>, @@ -22,7 +23,10 @@ pub trait ArgumentBuilder { fn build(self) -> dyn CommandNode; } -impl BaseArgumentBuilder<'_, S, T> { +impl BaseArgumentBuilder<'_, S, T> +where + T: ArgumentType, +{ pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index cf9f1ee9..a4cb3f84 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,14 +1,23 @@ -use crate::tree::literal_command_node::LiteralCommandNode; +use crate::{ + arguments::argument_type::{ArgumentType, Types}, + tree::literal_command_node::LiteralCommandNode, +}; use super::argument_builder::BaseArgumentBuilder; -pub struct LiteralArgumentBuilder<'a, S, T> { +pub struct LiteralArgumentBuilder<'a, S, T> +where + T: ArgumentType, +{ literal: String, pub base: BaseArgumentBuilder<'a, S, T>, } -impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> { +impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> +where + T: ArgumentType, +{ pub fn new(literal: String) -> Self { Self { literal, diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 6f6fa8eb..b5f99828 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,11 +1,15 @@ use crate::{ + arguments::argument_type::{ArgumentType, Types}, suggestion::suggestion_provider::SuggestionProvider, tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, }; use super::argument_builder::BaseArgumentBuilder; -pub struct RequiredArgumentBuilder<'a, S, T> { +pub struct RequiredArgumentBuilder<'a, S, T> +where + T: ArgumentType, +{ // private final String name; // private final ArgumentType type; // private SuggestionProvider suggestionsProvider = null; @@ -16,7 +20,10 @@ pub struct RequiredArgumentBuilder<'a, S, T> { pub base: BaseArgumentBuilder<'a, S, T>, } -impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> { +impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> +where + T: ArgumentType, +{ pub fn new(name: String, type_: T) -> Self { Self { name, diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs index 520c8a52..dcbf3ffd 100644 --- a/azalea-brigadier/src/command.rs +++ b/azalea-brigadier/src/command.rs @@ -2,9 +2,11 @@ use crate::{ context::command_context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, }; +use dyn_clonable::*; pub const SINGLE_SUCCESS: i32 = 1; -pub trait Command { +#[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 index d0351547..f2fc7528 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,13 +1,22 @@ -use crate::tree::root_command_node::RootCommandNode; +use crate::{ + arguments::argument_type::{ArgumentType, Types}, + tree::root_command_node::RootCommandNode, +}; /// 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)] -pub struct CommandDispatcher<'a, S, T> { +#[derive(Default, Clone)] +pub struct CommandDispatcher<'a, S, T> +where + T: ArgumentType, +{ root: RootCommandNode<'a, S, T>, } -impl CommandDispatcher<'_, S, T> { +impl CommandDispatcher<'_, S, T> +where + T: ArgumentType, +{ /// The string required to separate individual arguments in an input string /// /// See: [`ARGUMENT_SEPARATOR_CHAR`] diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 88e26343..639a97ee 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,8 +1,10 @@ use std::collections::HashMap; use crate::{ - arguments::argument_type::ArgumentType, command::Command, - command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, + arguments::argument_type::{ArgumentType, Types}, + command::Command, + command_dispatcher::CommandDispatcher, + redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; @@ -25,7 +27,10 @@ use super::{ // private boolean forks; #[derive(Clone)] -pub struct CommandContextBuilder<'a, S, T> { +pub struct CommandContextBuilder<'a, S, T> +where + T: ArgumentType, +{ arguments: HashMap>, root_node: &'a dyn CommandNode, nodes: Vec>, @@ -45,7 +50,10 @@ pub struct CommandContextBuilder<'a, S, T> { // this.range = StringRange.at(start); // } -impl CommandContextBuilder<'_, S, T> { +impl CommandContextBuilder<'_, S, T> +where + T: ArgumentType, +{ pub fn new( dispatcher: CommandDispatcher, source: S, diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index 14168a06..c0be355c 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -19,3 +19,12 @@ impl ParsedCommandNode { &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/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs index fd2e1bf7..fdb0a080 100644 --- a/azalea-brigadier/src/redirect_modifier.rs +++ b/azalea-brigadier/src/redirect_modifier.rs @@ -1,8 +1,11 @@ +use dyn_clonable::*; + use crate::{ context::command_context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait RedirectModifier { +#[clonable] +pub trait RedirectModifier: Clone { fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; } diff --git a/azalea-brigadier/src/suggestion/integer_suggestion.rs b/azalea-brigadier/src/suggestion/integer_suggestion.rs index e69de29b..acee2329 100644 --- a/azalea-brigadier/src/suggestion/integer_suggestion.rs +++ b/azalea-brigadier/src/suggestion/integer_suggestion.rs @@ -0,0 +1 @@ +pub struct IntegerSuggestion {} diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index 18572d20..778c5de8 100644 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -1,8 +1,90 @@ -use std::cmp; +use std::{cmp, collections::HashSet}; use crate::{context::string_range::StringRange, message::Message}; -pub struct Suggestions {} +use super::suggestion::Suggestion; + +#[derive(PartialEq, Eq, Hash, Default)] +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 { diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs index 7853a3a2..bc8f6f5d 100644 --- a/azalea-brigadier/src/suggestion/suggestions_builder.rs +++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs @@ -1,6 +1,8 @@ use crate::context::string_range::StringRange; -use super::{suggestion::Suggestion, suggestions::Suggestions}; +use super::{ + integer_suggestion::IntegerSuggestion, suggestion::Suggestion, suggestions::Suggestions, +}; pub struct SuggestionsBuilder { input: String, diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 647b6c35..3fc1bb50 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -21,6 +21,7 @@ use super::command_node::{BaseCommandNode, CommandNode}; const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; +#[derive(Clone)] pub struct ArgumentCommandNode<'a, S, T> where // each argument command node has its own different type @@ -34,7 +35,10 @@ where pub base: BaseCommandNode<'a, S, T>, } -impl ArgumentCommandNode<'_, S, T> { +impl ArgumentCommandNode<'_, S, T> +where + T: ArgumentType, +{ fn get_type(&self) -> &T { &self.type_ } @@ -44,7 +48,11 @@ impl ArgumentCommandNode<'_, S, T> { } } -impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> { +impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> +where + T: ArgumentType + Clone, + S: Clone, +{ fn name(&self) -> &str { &self.name } @@ -117,7 +125,10 @@ impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> { } } -impl Display for ArgumentCommandNode<'_, (), (), ()> { +impl Display for ArgumentCommandNode<'_, S, T> +where + T: ArgumentType, +{ 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 index 8e262f0b..f3be1597 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,7 +1,6 @@ -use std::collections::HashMap; - +use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; use crate::{ - arguments::argument_type::ArgumentType, + arguments::argument_type::{ArgumentType, Types}, builder::argument_builder::ArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -10,10 +9,14 @@ use crate::{ string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; +use dyn_clonable::*; +use std::{collections::HashMap, fmt::Debug}; -use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; - -pub struct BaseCommandNode<'a, S, T> { +#[derive(Default)] +pub struct BaseCommandNode<'a, S, T> +where + T: ArgumentType, +{ children: HashMap>, literals: HashMap>, arguments: HashMap>, @@ -24,9 +27,49 @@ pub struct BaseCommandNode<'a, S, T> { command: Option<&'a dyn Command>, } -impl BaseCommandNode<'_, S, T> {} +impl BaseCommandNode<'_, S, T> where T: ArgumentType {} + +impl Clone for BaseCommandNode<'_, S, T> +where + T: ArgumentType, +{ + 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<'_, S, T> +where + T: ArgumentType, +{ + 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() + } +} -pub trait CommandNode { +#[clonable] +pub trait CommandNode: Clone +where + T: ArgumentType, +{ fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index fe933669..021a3ea6 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -1,4 +1,5 @@ use crate::{ + arguments::argument_type::{ArgumentType, Types}, builder::literal_argument_builder::LiteralArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -12,15 +13,22 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; -#[derive(Hash, PartialEq, Eq, Debug, Clone)] -pub struct LiteralCommandNode<'a, S, T> { +#[derive(Debug, Clone)] +pub struct LiteralCommandNode<'a, S, T> +where + // each argument command node has its own different type + T: ArgumentType, +{ literal: String, literal_lowercase: String, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S, T>, } -impl<'a, S, T> LiteralCommandNode<'a, S, T> { +impl<'a, S, T> LiteralCommandNode<'a, S, T> +where + T: ArgumentType, +{ pub fn new(literal: String, base: BaseCommandNode) -> Self { let literal_lowercase = literal.to_lowercase(); Self { @@ -51,7 +59,11 @@ impl<'a, S, T> LiteralCommandNode<'a, S, T> { } } -impl CommandNode for LiteralCommandNode<'_, S, T> { +impl CommandNode for LiteralCommandNode<'_, S, T> +where + T: ArgumentType + Clone, + S: Clone, +{ fn name(&self) -> &str { &self.literal } diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index 36787340..ded5fa77 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -1,6 +1,7 @@ use std::fmt::{Display, Formatter}; use crate::{ + arguments::argument_type::{ArgumentType, Types}, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, @@ -11,12 +12,21 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; -pub struct RootCommandNode<'a, S, T> { +#[derive(Clone, Default)] +pub struct RootCommandNode<'a, S, T> +where + // each argument command node has its own different type + T: ArgumentType, +{ // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S, T>, } -impl CommandNode for RootCommandNode<'_, S, T> { +impl CommandNode for RootCommandNode<'_, S, T> +where + T: ArgumentType + Clone, + S: Clone, +{ fn name(&self) -> &str { "" } @@ -53,7 +63,10 @@ impl CommandNode for RootCommandNode<'_, S, T> { } } -impl Display for RootCommandNode<'_, S, T> { +impl Display for RootCommandNode<'_, S, T> +where + T: ArgumentType, +{ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "") } -- cgit v1.2.3 From eb111be1f107696939b994f5de6e060cf972a732 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 13 Jan 2022 00:43:09 +0000 Subject: a --- azalea-brigadier/src/ambiguity_consumer.rs | 1 + azalea-brigadier/src/arguments/argument_type.rs | 26 ++------- .../src/arguments/bool_argument_type.rs | 18 +++---- .../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 + .../src/arguments/string_argument_type.rs | 1 + azalea-brigadier/src/builder/argument_builder.rs | 59 ++++++++++---------- .../src/builder/literal_argument_builder.rs | 17 ++---- .../src/builder/required_argument_builder.rs | 33 +++++------- azalea-brigadier/src/command.rs | 4 +- azalea-brigadier/src/command_dispatcher.rs | 19 ++----- azalea-brigadier/src/context/command_context.rs | 24 ++++----- .../src/context/command_context_builder.rs | 62 ++++++++++------------ azalea-brigadier/src/context/parsed_argument.rs | 3 +- .../src/context/parsed_command_node.rs | 12 ++--- azalea-brigadier/src/context/suggestion_context.rs | 4 +- .../src/exceptions/builtin_exception_provider.rs | 1 + azalea-brigadier/src/literal_message.rs | 1 + azalea-brigadier/src/parse_results.rs | 1 + azalea-brigadier/src/redirect_modifier.rs | 4 +- azalea-brigadier/src/result_consumer.rs | 1 + azalea-brigadier/src/single_redirect_modifier.rs | 4 +- .../src/suggestion/suggestion_provider.rs | 4 +- azalea-brigadier/src/suggestion/suggestions.rs | 2 +- azalea-brigadier/src/tree/argument_command_node.rs | 48 +++++++---------- azalea-brigadier/src/tree/command_node.rs | 44 ++++++--------- azalea-brigadier/src/tree/literal_command_node.rs | 26 ++++----- azalea-brigadier/src/tree/root_command_node.rs | 22 +++----- azalea-brigadier/tests/command_dispatcher_test.rs | 1 + .../tests/command_dispatcher_usages_test.rs | 1 + azalea-brigadier/tests/command_suggestions_test.rs | 1 + 33 files changed, 188 insertions(+), 260 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/ambiguity_consumer.rs b/azalea-brigadier/src/ambiguity_consumer.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/ambiguity_consumer.rs +++ b/azalea-brigadier/src/ambiguity_consumer.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index 890cdea0..3d1b1168 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -7,19 +7,6 @@ use crate::{ }; use dyn_clonable::*; -#[clonable] -// This should be applied to an Enum -pub trait Types: Clone { - fn bool(value: bool) -> Self - where - Self: Sized; - - /// Get the less specific ArgumentType from this enum - fn inner(&self) -> Box> - where - Self: Sized; -} - /* #[derive(Types)] enum BrigadierTypes { @@ -45,10 +32,8 @@ impl Types for BrigadierTypes { */ #[clonable] -pub trait ArgumentType: Clone -where - T: Types, -{ +pub trait ArgumentType: Clone { + type Into; // T parse(StringReader reader) throws CommandSyntaxException; // default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { @@ -59,17 +44,16 @@ where // return Collections.emptyList(); // } - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; + fn parse(&self, reader: &mut StringReader) -> Result; fn list_suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &mut SuggestionsBuilder, ) -> Result where Self: Sized, - S: Sized, - T: 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 index b04488c1..c06f40c1 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -5,27 +5,25 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -use super::argument_type::{ArgumentType, Types}; +use super::argument_type::ArgumentType; #[derive(Clone)] pub struct BoolArgumentType {} -impl ArgumentType for BoolArgumentType -where - T: Types, -{ - fn parse(&self, reader: &mut StringReader) -> Result { - Ok(T::bool(reader.read_boolean()?)) +impl ArgumentType for BoolArgumentType { + type Into = bool; + + fn parse(&self, reader: &mut StringReader) -> Result { + Ok(reader.read_boolean()?) } fn list_suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &mut SuggestionsBuilder, ) -> Result where S: Sized, - T: Sized, { // if ("true".startsWith(builder.getRemainingLowerCase())) { // builder.suggest("true"); @@ -55,7 +53,7 @@ impl BoolArgumentType { Self {} } - fn get_bool(context: CommandContext, name: String) { + 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 index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/double_argument_type.rs +++ b/azalea-brigadier/src/arguments/double_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/float_argument_type.rs +++ b/azalea-brigadier/src/arguments/float_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/integer_argument_type.rs +++ b/azalea-brigadier/src/arguments/integer_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/long_argument_type.rs +++ b/azalea-brigadier/src/arguments/long_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/arguments/string_argument_type.rs +++ b/azalea-brigadier/src/arguments/string_argument_type.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 0360b05a..6355456a 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,33 +1,32 @@ use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, single_redirect_modifier::SingleRedirectModifier, tree::{command_node::CommandNode, root_command_node::RootCommandNode}, }; -pub struct BaseArgumentBuilder<'a, S, T> +pub struct BaseArgumentBuilder<'a, S> where S: Sized, - T: Sized + ArgumentType, { - arguments: RootCommandNode<'a, S, T>, - command: Option<&'a dyn Command>, + arguments: RootCommandNode<'a, S>, + command: Option<&'a dyn Command>, requirement: &'a dyn Fn(&S) -> bool, - target: Option<&'a dyn CommandNode>, - modifier: Option<&'a dyn RedirectModifier>, + target: Option<&'a dyn CommandNode>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, } -pub trait ArgumentBuilder { - fn build(self) -> dyn CommandNode; -} - -impl BaseArgumentBuilder<'_, S, T> +pub trait ArgumentBuilder where - T: ArgumentType, + T: ArgumentBuilder, { - pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut T, String> { + fn build(self) -> dyn CommandNode; +} + +impl BaseArgumentBuilder<'_, S> { + pub fn then(&mut self, command: dyn CommandNode) -> Result<&mut Self, String> { if self.target.is_some() { return Err("Cannot add children to a redirected node".to_string()); } @@ -35,20 +34,20 @@ where Ok(self) } - pub fn arguments(&self) -> &Vec<&dyn CommandNode> { + pub fn arguments(&self) -> &Vec<&dyn CommandNode> { &self.arguments.get_children() } - pub fn executes(&mut self, command: dyn Command) -> &mut T { + pub fn executes(&mut self, command: dyn Command) -> &mut Self { self.command = command; self } - pub fn command(&self) -> dyn Command { + pub fn command(&self) -> dyn Command { self.command } - pub fn requires(&mut self, requirement: &dyn Fn(&S) -> bool) -> &mut T { + pub fn requires(&mut self, requirement: &dyn Fn(&S) -> bool) -> &mut Self { self.requirement = requirement; self } @@ -57,33 +56,33 @@ where self.requirement } - pub fn redirect(&mut self, target: &dyn CommandNode) -> &mut T { + pub fn redirect(&mut self, target: &dyn CommandNode) -> &mut Self { self.forward(target, None, false) } pub fn redirect_modifier( &mut self, - target: &dyn CommandNode, - modifier: &dyn SingleRedirectModifier, - ) -> &mut T { + target: &dyn CommandNode, + 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 CommandNode, - modifier: &dyn RedirectModifier, - ) -> &mut T { + target: &dyn CommandNode, + modifier: &dyn RedirectModifier, + ) -> &mut Self { self.forward(target, Some(modifier), true) } pub fn forward( &mut self, - target: &dyn CommandNode, - modifier: Option<&dyn RedirectModifier>, + target: &dyn CommandNode, + modifier: Option<&dyn RedirectModifier>, fork: bool, - ) -> Result<&mut T, String> { + ) -> Result<&mut Self, String> { if !self.arguments.get_children().is_empty() { return Err("Cannot forward a node with children".to_string()); } @@ -93,11 +92,11 @@ where Ok(self) } - pub fn get_redirect(&self) -> Option<&dyn CommandNode> { + pub fn get_redirect(&self) -> Option<&dyn CommandNode> { self.target.as_ref() } - pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { self.modifier.as_ref() } diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index a4cb3f84..8039dff0 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,23 +1,16 @@ use crate::{ - arguments::argument_type::{ArgumentType, Types}, - tree::literal_command_node::LiteralCommandNode, + arguments::argument_type::ArgumentType, tree::literal_command_node::LiteralCommandNode, }; use super::argument_builder::BaseArgumentBuilder; -pub struct LiteralArgumentBuilder<'a, S, T> -where - T: ArgumentType, -{ +pub struct LiteralArgumentBuilder<'a, S> { literal: String, - pub base: BaseArgumentBuilder<'a, S, T>, + pub base: BaseArgumentBuilder<'a, S>, } -impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> -where - T: ArgumentType, -{ +impl<'a, S> LiteralArgumentBuilder<'a, S> { pub fn new(literal: String) -> Self { Self { literal, @@ -29,7 +22,7 @@ where Self::new(name) } - pub fn build(self) -> LiteralCommandNode<'a, S, T> { + pub fn build(self) -> LiteralCommandNode<'a, S> { let result = LiteralCommandNode::new(self.literal, self.base); for argument in self.base.arguments { diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index b5f99828..29af7f6f 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,30 +1,25 @@ use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, suggestion::suggestion_provider::SuggestionProvider, tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode}, }; +use std::any::Any; use super::argument_builder::BaseArgumentBuilder; -pub struct RequiredArgumentBuilder<'a, S, T> -where - T: ArgumentType, -{ +pub struct RequiredArgumentBuilder<'a, S> { // private final String name; // private final ArgumentType type; // private SuggestionProvider suggestionsProvider = null; name: String, - type_: &'a T, - suggestions_provider: Option<&'a dyn SuggestionProvider>, + type_: Box>, + suggestions_provider: Option<&'a dyn SuggestionProvider>, - pub base: BaseArgumentBuilder<'a, S, T>, + pub base: BaseArgumentBuilder<'a, S>, } -impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> -where - T: ArgumentType, -{ - pub fn new(name: String, type_: T) -> Self { +impl<'a, S> RequiredArgumentBuilder<'a, S> { + pub fn new(name: String, type_: dyn ArgumentType) -> Self { Self { name, type_: &type_, @@ -33,20 +28,20 @@ where } } - pub fn argument(name: String, type_: T) -> Self { + pub fn argument(name: String, type_: dyn ArgumentType) -> Self { Self::new(name, type_) } - pub fn suggests(mut self, provider: &dyn SuggestionProvider) -> Self { + pub fn suggests(mut self, provider: &dyn SuggestionProvider) -> Self { self.suggestions_provider = Some(provider); self } - pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { + pub fn suggestions_provider(&self) -> Option<&dyn SuggestionProvider> { self.suggestions_provider.as_ref() } - pub fn get_type(&self) -> &T { + pub fn get_type(&self) -> &dyn ArgumentType { self.type_ } @@ -54,14 +49,14 @@ where self.name } - // final ArgumentCommandNode result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider()); + // 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, T> { + pub fn build(self) -> ArgumentCommandNode<'a, S> { let result = ArgumentCommandNode { name: self.name, type_: &self.type_, diff --git a/azalea-brigadier/src/command.rs b/azalea-brigadier/src/command.rs index dcbf3ffd..a529f23c 100644 --- a/azalea-brigadier/src/command.rs +++ b/azalea-brigadier/src/command.rs @@ -7,6 +7,6 @@ use dyn_clonable::*; pub const SINGLE_SUCCESS: i32 = 1; #[clonable] -pub trait Command: Clone { - fn run(&self, context: &mut CommandContext) -> Result; +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 index f2fc7528..72a353ad 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,22 +1,13 @@ -use crate::{ - arguments::argument_type::{ArgumentType, Types}, - tree::root_command_node::RootCommandNode, -}; +use crate::{arguments::argument_type::ArgumentType, tree::root_command_node::RootCommandNode}; /// 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<'a, S, T> -where - T: ArgumentType, -{ - root: RootCommandNode<'a, S, T>, +pub struct CommandDispatcher<'a, S> { + root: RootCommandNode<'a, S>, } -impl CommandDispatcher<'_, S, T> -where - T: ArgumentType, -{ +impl CommandDispatcher<'_, S> { /// The string required to separate individual arguments in an input string /// /// See: [`ARGUMENT_SEPARATOR_CHAR`] @@ -48,7 +39,7 @@ where /// * `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 { + fn new(root: RootCommandNode) -> Self { Self { root } } } diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 68144a40..4f0b4d49 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -6,22 +6,22 @@ use crate::{ arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; -use std::collections::HashMap; +use std::{any::Any, collections::HashMap}; -pub struct CommandContext<'a, S, T> { +pub struct CommandContext<'a, S> { source: S, input: String, - command: &'a dyn Command, - arguments: HashMap>, - root_node: &'a dyn CommandNode, - nodes: Vec>, + command: &'a dyn Command, + arguments: HashMap>>, + root_node: &'a dyn CommandNode, + nodes: Vec>, range: StringRange, - child: Option<&'a CommandContext<'a, S, T>>, - modifier: Option<&'a dyn RedirectModifier>, + child: Option<&'a CommandContext<'a, S>>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, } -impl CommandContext<'_, S, T> +impl CommandContext<'_, S> where S: PartialEq, { @@ -43,11 +43,11 @@ where } } - fn child(&self) -> &Option> { + fn child(&self) -> &Option> { &self.child } - fn last_child(&self) -> &CommandContext { + fn last_child(&self) -> &CommandContext { let mut result = self; while result.child.is_some() { result = result.child.as_ref().unwrap(); @@ -55,7 +55,7 @@ where result } - fn command(&self) -> &dyn Command { + fn command(&self) -> &dyn Command { &self.command } diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 639a97ee..95da4064 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,10 +1,8 @@ -use std::collections::HashMap; +use std::{any::Any, collections::HashMap}; use crate::{ - arguments::argument_type::{ArgumentType, Types}, - command::Command, - command_dispatcher::CommandDispatcher, - redirect_modifier::RedirectModifier, + arguments::argument_type::ArgumentType, command::Command, + command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, tree::command_node::CommandNode, }; @@ -27,19 +25,16 @@ use super::{ // private boolean forks; #[derive(Clone)] -pub struct CommandContextBuilder<'a, S, T> -where - T: ArgumentType, -{ - arguments: HashMap>, - root_node: &'a dyn CommandNode, - nodes: Vec>, - dispatcher: CommandDispatcher<'a, S, T>, +pub struct CommandContextBuilder<'a, S> { + arguments: HashMap>>, + root_node: &'a dyn CommandNode, + nodes: Vec>, + dispatcher: CommandDispatcher<'a, S>, source: S, - command: Box>, - child: Option>, + command: Box>, + child: Option>, range: StringRange, - modifier: Option>>, + modifier: Option>>, forks: bool, } @@ -50,14 +45,11 @@ where // this.range = StringRange.at(start); // } -impl CommandContextBuilder<'_, S, T> -where - T: ArgumentType, -{ +impl CommandContextBuilder<'_, S> { pub fn new( - dispatcher: CommandDispatcher, + dispatcher: CommandDispatcher, source: S, - root_node: dyn CommandNode, + root_node: dyn CommandNode, start: usize, ) -> Self { Self { @@ -78,25 +70,25 @@ where &self.source } - pub fn root_node(&self) -> &dyn CommandNode { + pub fn root_node(&self) -> &dyn CommandNode { &self.root_node } - pub fn with_argument(mut self, name: String, argument: ParsedArgument) -> Self { + pub fn with_argument(mut self, name: String, argument: ParsedArgument>) -> Self { self.arguments.insert(name, argument); self } - pub fn arguments(&self) -> &HashMap> { + pub fn arguments(&self) -> &HashMap>> { &self.arguments } - pub fn with_command(mut self, command: &dyn Command) -> Self { + pub fn with_command(mut self, command: &dyn Command) -> Self { self.command = command; self } - pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { + pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> Self { self.nodes.push(ParsedCommandNode::new(node, range)); self.range = StringRange::encompassing(&self.range, &range); self.modifier = node.redirect_modifier(); @@ -104,16 +96,16 @@ where self } - pub fn with_child(mut self, child: CommandContextBuilder) -> Self { + pub fn with_child(mut self, child: CommandContextBuilder) -> Self { self.child = Some(child); self } - pub fn child(&self) -> Option<&CommandContextBuilder> { + pub fn child(&self) -> Option<&CommandContextBuilder> { self.child.as_ref() } - pub fn last_child(&self) -> Option<&CommandContextBuilder> { + pub fn last_child(&self) -> Option<&CommandContextBuilder> { let mut result = self; while let Some(child) = result.child() { result = child; @@ -121,15 +113,15 @@ where Some(result) } - pub fn command(&self) -> &dyn Command { + pub fn command(&self) -> &dyn Command { &*self.command } - pub fn nodes(&self) -> &Vec> { + pub fn nodes(&self) -> &Vec> { &self.nodes } - pub fn build(self, input: &str) -> CommandContext { + pub fn build(self, input: &str) -> CommandContext { CommandContext { source: self.source, input, @@ -144,7 +136,7 @@ where } } - pub fn dispatcher(&self) -> &CommandDispatcher { + pub fn dispatcher(&self) -> &CommandDispatcher { &self.dispatcher } @@ -152,7 +144,7 @@ where &self.range } - pub fn find_suggestion_context(&self, cursor: i32) -> Result, String> { + 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() { diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs index 447a1223..e0bdf97b 100644 --- a/azalea-brigadier/src/context/parsed_argument.rs +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -3,12 +3,11 @@ use super::string_range::StringRange; #[derive(PartialEq, Eq, Hash, Clone)] pub struct ParsedArgument { range: StringRange, - // T is an item in an enum result: T, } impl ParsedArgument { - fn new(start: usize, end: usize, result: T) -> Self { + fn new(start: usize, end: usize, result: &T) -> Self { Self { range: StringRange::between(start, end), result, diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index c0be355c..16c6ed8b 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -1,17 +1,17 @@ use super::string_range::StringRange; use crate::tree::command_node::CommandNode; -pub struct ParsedCommandNode { - node: Box>, +pub struct ParsedCommandNode { + node: Box>, range: StringRange, } -impl ParsedCommandNode { - fn new(node: dyn CommandNode, range: StringRange) -> Self { +impl ParsedCommandNode { + fn new(node: dyn CommandNode, range: StringRange) -> Self { Self { node, range } } - fn node(&self) -> &dyn CommandNode { + fn node(&self) -> &dyn CommandNode { &self.node } @@ -20,7 +20,7 @@ impl ParsedCommandNode { } } -impl Clone for ParsedCommandNode { +impl Clone for ParsedCommandNode { fn clone_from(&mut self, source: &Self) { Self { node: self.node.clone(), diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs index 42bc550e..252cb6ed 100644 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ b/azalea-brigadier/src/context/suggestion_context.rs @@ -1,6 +1,6 @@ use crate::tree::command_node::CommandNode; -pub struct SuggestionContext<'a, S, T> { - parent: &'a dyn CommandNode, +pub struct SuggestionContext<'a, S> { + parent: &'a dyn CommandNode, start_pos: usize, } diff --git a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs b/azalea-brigadier/src/exceptions/builtin_exception_provider.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/exceptions/builtin_exception_provider.rs +++ b/azalea-brigadier/src/exceptions/builtin_exception_provider.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/literal_message.rs b/azalea-brigadier/src/literal_message.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/literal_message.rs +++ b/azalea-brigadier/src/literal_message.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/redirect_modifier.rs b/azalea-brigadier/src/redirect_modifier.rs index fdb0a080..7a6d4db5 100644 --- a/azalea-brigadier/src/redirect_modifier.rs +++ b/azalea-brigadier/src/redirect_modifier.rs @@ -6,6 +6,6 @@ use crate::{ }; #[clonable] -pub trait RedirectModifier: Clone { - fn apply(&self, context: CommandContext) -> Result, CommandSyntaxException>; +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 index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/result_consumer.rs +++ b/azalea-brigadier/src/result_consumer.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/single_redirect_modifier.rs b/azalea-brigadier/src/single_redirect_modifier.rs index 95055fd8..dd63244d 100644 --- a/azalea-brigadier/src/single_redirect_modifier.rs +++ b/azalea-brigadier/src/single_redirect_modifier.rs @@ -3,6 +3,6 @@ use crate::{ exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait SingleRedirectModifier { - fn apply(&self, context: CommandContext) -> Result; +pub trait SingleRedirectModifier { + fn apply(&self, context: CommandContext) -> Result; } diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs index 9720d3b9..3027d460 100644 --- a/azalea-brigadier/src/suggestion/suggestion_provider.rs +++ b/azalea-brigadier/src/suggestion/suggestion_provider.rs @@ -5,10 +5,10 @@ use crate::{ use super::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}; -pub trait SuggestionProvider { +pub trait SuggestionProvider { fn suggestions( &self, - context: &CommandContext, + context: &CommandContext, builder: &SuggestionsBuilder, ) -> Result; } diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index 778c5de8..9f0ee06d 100644 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -4,7 +4,7 @@ use crate::{context::string_range::StringRange, message::Message}; use super::suggestion::Suggestion; -#[derive(PartialEq, Eq, Hash, Default)] +#[derive(PartialEq, Eq, Hash)] pub struct Suggestions { range: StringRange, suggestions: Vec, diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index 3fc1bb50..fb9a75fa 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,7 +1,10 @@ -use std::fmt::{Display, Formatter}; +use std::{ + any::Any, + fmt::{Display, Formatter}, +}; use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, builder::required_argument_builder::RequiredArgumentBuilder, context::{ command_context::CommandContext, command_context_builder::CommandContextBuilder, @@ -22,35 +25,27 @@ const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; #[derive(Clone)] -pub struct ArgumentCommandNode<'a, S, T> -where - // each argument command node has its own different type - T: ArgumentType, -{ +pub struct ArgumentCommandNode<'a, S> { name: String, - type_: &'a T, - custom_suggestions: Option<&'a dyn SuggestionProvider>, - // custom_suggestions: &'a dyn SuggestionProvider, + type_: Box>, + custom_suggestions: Option<&'a dyn SuggestionProvider>, + // custom_suggestions: &'a dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S, T>, + pub base: BaseCommandNode<'a, S>, } -impl ArgumentCommandNode<'_, S, T> -where - T: ArgumentType, -{ - fn get_type(&self) -> &T { - &self.type_ +impl ArgumentCommandNode<'_, S> { + fn get_type(&self) -> &dyn ArgumentType { + self.type_ } - fn custom_suggestions(&self) -> Option<&dyn SuggestionProvider> { + fn custom_suggestions(&self) -> Option<&dyn SuggestionProvider> { self.custom_suggestions } } -impl<'a, S, T> CommandNode for ArgumentCommandNode<'a, S, T> +impl<'a, S> CommandNode for ArgumentCommandNode<'a, S> where - T: ArgumentType + Clone, S: Clone, { fn name(&self) -> &str { @@ -60,11 +55,11 @@ where fn parse( &self, reader: &mut StringReader, - context_builder: CommandContextBuilder, + 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); + // final ParsedArgument parsed = new ParsedArgument<>(start, reader.getCursor(), result); // contextBuilder.withArgument(name, parsed); // contextBuilder.withNode(this, parsed.getRange()); @@ -81,7 +76,7 @@ where fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: &mut SuggestionsBuilder, ) -> Result { if self.custom_suggestions.is_none() { @@ -105,7 +100,7 @@ where USAGE_ARGUMENT_OPEN + self.name + USAGE_ARGUMENT_CLOSE } - fn create_builder(&self) -> RequiredArgumentBuilder { + fn create_builder(&self) -> RequiredArgumentBuilder { let builder = RequiredArgumentBuilder::argument(&self.name, &self.type_); builder.requires(self.base.get_requirement()); builder.forward( @@ -125,10 +120,7 @@ where } } -impl Display for ArgumentCommandNode<'_, S, T> -where - T: ArgumentType, -{ +impl Display for ArgumentCommandNode<'_, S> { 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 index f3be1597..b8f416eb 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -1,6 +1,6 @@ use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode}; use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, builder::argument_builder::ArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -10,29 +10,23 @@ use crate::{ suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; use dyn_clonable::*; -use std::{collections::HashMap, fmt::Debug}; +use std::{any::Any, collections::HashMap, fmt::Debug}; #[derive(Default)] -pub struct BaseCommandNode<'a, S, T> -where - T: ArgumentType, -{ - children: HashMap>, - literals: HashMap>, - arguments: HashMap>, +pub struct BaseCommandNode<'a, S> { + children: HashMap>, + literals: HashMap>, + arguments: HashMap>, requirement: Option<&'a dyn Fn(&S) -> bool>, - redirect: Option<&'a dyn CommandNode>, - modifier: Option<&'a dyn RedirectModifier>, + redirect: Option<&'a dyn CommandNode>, + modifier: Option<&'a dyn RedirectModifier>, forks: bool, - command: Option<&'a dyn Command>, + command: Option<&'a dyn Command>, } -impl BaseCommandNode<'_, S, T> where T: ArgumentType {} +impl BaseCommandNode<'_, S> {} -impl Clone for BaseCommandNode<'_, S, T> -where - T: ArgumentType, -{ +impl Clone for BaseCommandNode<'_, S> { fn clone(&self) -> Self { Self { children: self.children.clone(), @@ -47,10 +41,7 @@ where } } -impl Debug for BaseCommandNode<'_, S, T> -where - T: ArgumentType, -{ +impl Debug for BaseCommandNode<'_, S> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("BaseCommandNode") .field("children", &self.children) @@ -66,23 +57,20 @@ where } #[clonable] -pub trait CommandNode: Clone -where - T: ArgumentType, -{ +pub trait CommandNode: Clone { fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( &self, reader: &mut StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException>; fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result; fn is_valid_input(&self, input: &str) -> bool; - fn create_builder(&self) -> dyn ArgumentBuilder; + fn create_builder(&self) -> dyn ArgumentBuilder; fn get_examples(&self) -> Vec; } diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index 021a3ea6..a722121f 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -1,5 +1,5 @@ use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, builder::literal_argument_builder::LiteralArgumentBuilder, command::Command, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, @@ -14,22 +14,15 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; #[derive(Debug, Clone)] -pub struct LiteralCommandNode<'a, S, T> -where - // each argument command node has its own different type - T: ArgumentType, -{ +pub struct LiteralCommandNode<'a, S> { literal: String, literal_lowercase: String, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S, T>, + pub base: BaseCommandNode<'a, S>, } -impl<'a, S, T> LiteralCommandNode<'a, S, T> -where - T: ArgumentType, -{ - pub fn new(literal: String, base: BaseCommandNode) -> Self { +impl<'a, S> LiteralCommandNode<'a, S> { + pub fn new(literal: String, base: BaseCommandNode) -> Self { let literal_lowercase = literal.to_lowercase(); Self { literal, @@ -59,9 +52,8 @@ where } } -impl CommandNode for LiteralCommandNode<'_, S, T> +impl CommandNode for LiteralCommandNode<'_, S> where - T: ArgumentType + Clone, S: Clone, { fn name(&self) -> &str { @@ -71,7 +63,7 @@ where fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { let start = reader.get_cursor(); let end = self.parse(reader); @@ -87,7 +79,7 @@ where fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result { if self @@ -108,7 +100,7 @@ where self.literal } - fn create_builder(&self) -> LiteralArgumentBuilder { + fn create_builder(&self) -> LiteralArgumentBuilder { let builder = LiteralArgumentBuilder::literal(self.literal()); builder.requires(self.requirement()); builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork()); diff --git a/azalea-brigadier/src/tree/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index ded5fa77..c3139a05 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Formatter}; use crate::{ - arguments::argument_type::{ArgumentType, Types}, + arguments::argument_type::ArgumentType, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, @@ -13,18 +13,13 @@ use crate::{ use super::command_node::{BaseCommandNode, CommandNode}; #[derive(Clone, Default)] -pub struct RootCommandNode<'a, S, T> -where - // each argument command node has its own different type - T: ArgumentType, -{ +pub struct RootCommandNode<'a, S> { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field - pub base: BaseCommandNode<'a, S, T>, + pub base: BaseCommandNode<'a, S>, } -impl CommandNode for RootCommandNode<'_, S, T> +impl CommandNode for RootCommandNode<'_, S> where - T: ArgumentType + Clone, S: Clone, { fn name(&self) -> &str { @@ -34,13 +29,13 @@ where fn parse( &self, reader: StringReader, - context_builder: CommandContextBuilder, + context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { } fn list_suggestions( &self, - context: CommandContext, + context: CommandContext, builder: SuggestionsBuilder, ) -> Result { Suggestions::empty() @@ -63,10 +58,7 @@ where } } -impl Display for RootCommandNode<'_, S, T> -where - T: ArgumentType, -{ +impl Display for RootCommandNode<'_, S> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "") } diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/tests/command_dispatcher_test.rs +++ b/azalea-brigadier/tests/command_dispatcher_test.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/tests/command_dispatcher_usages_test.rs +++ b/azalea-brigadier/tests/command_dispatcher_usages_test.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/tests/command_suggestions_test.rs b/azalea-brigadier/tests/command_suggestions_test.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/tests/command_suggestions_test.rs +++ b/azalea-brigadier/tests/command_suggestions_test.rs @@ -0,0 +1 @@ + -- cgit v1.2.3 From d9e52f8d965473517ddf6f11f9ac3be9aa14e14d Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 1 Feb 2022 00:12:46 -0600 Subject: b --- azalea-brigadier/src/builder/argument_builder.rs | 37 +++-- .../src/builder/literal_argument_builder.rs | 29 ++-- .../src/builder/required_argument_builder.rs | 35 +++-- azalea-brigadier/src/command_dispatcher.rs | 1 + azalea-brigadier/src/context/command_context.rs | 4 +- .../src/context/command_context_builder.rs | 21 +-- .../src/context/parsed_command_node.rs | 8 +- azalea-brigadier/src/context/suggestion_context.rs | 4 +- azalea-brigadier/src/string_reader.rs | 14 +- azalea-brigadier/src/tree/argument_command_node.rs | 67 +++++++-- azalea-brigadier/src/tree/command_node.rs | 152 ++++++++++----------- azalea-brigadier/src/tree/literal_command_node.rs | 61 +++++---- azalea-brigadier/src/tree/root_command_node.rs | 31 +++-- 13 files changed, 272 insertions(+), 192 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index ec2756ca..d0770be2 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -4,40 +4,35 @@ use crate::{ redirect_modifier::RedirectModifier, single_redirect_modifier::SingleRedirectModifier, tree::{ - command_node::{BaseCommandNode, CommandNode}, + command_node::{BaseCommandNode, CommandNodeTrait}, root_command_node::RootCommandNode, }, }; +use std::fmt::Debug; -pub struct BaseArgumentBuilder<'a, S> -where - S: Sized, -{ +pub struct BaseArgumentBuilder<'a, S> { arguments: RootCommandNode<'a, S>, command: Option>>, requirement: Box bool>, - target: Option>>, + target: Option>>, modifier: Option>>, forks: bool, } -pub trait ArgumentBuilder -where - T: ArgumentBuilder, -{ - fn build(self) -> Box>; +pub trait ArgumentBuilder { + fn build(self) -> Box>; } impl<'a, S> BaseArgumentBuilder<'a, S> { - pub fn then(&mut self, argument: Box>) -> Result<&mut Self, String> { + 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 CommandNode> { + pub fn arguments(&self) -> &Vec<&dyn CommandNodeTrait> { &self.arguments.get_children() } @@ -59,13 +54,13 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { self.requirement } - pub fn redirect(&mut self, target: Box>) -> &mut Self { + pub fn redirect(&mut self, target: Box>) -> &mut Self { self.forward(target, None, false) } pub fn redirect_modifier( &mut self, - target: &dyn CommandNode, + target: &dyn CommandNodeTrait, modifier: &dyn SingleRedirectModifier, ) -> &mut Self { // forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false); @@ -74,7 +69,7 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { pub fn fork( &mut self, - target: &dyn CommandNode, + target: &dyn CommandNodeTrait, modifier: &dyn RedirectModifier, ) -> &mut Self { self.forward(target, Some(modifier), true) @@ -82,20 +77,20 @@ impl<'a, S> BaseArgumentBuilder<'a, S> { pub fn forward( &mut self, - target: Box>, - modifier: Option>>, + 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 = Some(target); + self.target = target; self.modifier = modifier; self.forks = fork; Ok(self) } - pub fn get_redirect(&self) -> Option<&dyn CommandNode> { + pub fn get_redirect(&self) -> Option<&dyn CommandNodeTrait> { self.target.as_ref() } diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index 5bceb6eb..8250d45d 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -4,27 +4,38 @@ use crate::{ command::Command, redirect_modifier::RedirectModifier, tree::{ - command_node::CommandNode, literal_command_node::LiteralCommandNode, + command_node::CommandNodeTrait, literal_command_node::LiteralCommandNode, root_command_node::RootCommandNode, }, }; +use std::fmt::Debug; -pub struct LiteralArgumentBuilder<'a, S> { +pub struct LiteralArgumentBuilder<'a, S> +where + , +{ arguments: RootCommandNode<'a, S>, command: Option>>, requirement: Box bool>, - target: Option>>, + target: Option>>, modifier: Option>>, forks: bool, - literal: String, } -impl<'a, S> LiteralArgumentBuilder<'a, S> { +impl<'a, S> LiteralArgumentBuilder<'a, S> +where + , +{ pub fn new(literal: String) -> Self { Self { literal, - base: BaseArgumentBuilder::default(), + arguments: RootCommandNode::new(), + command: None, + requirement: Box::new(|_| true), + target: None, + modifier: None, + forks: false, } } @@ -33,11 +44,11 @@ impl<'a, S> LiteralArgumentBuilder<'a, S> { } } -impl<'a, S, T> ArgumentBuilder for LiteralArgumentBuilder<'a, S> +impl<'a, S> ArgumentBuilder for LiteralArgumentBuilder<'a, S> where - T: ArgumentBuilder, + , { - fn build(self) -> Box> { + fn build(self) -> Box> { let result = LiteralCommandNode::new(self.literal, self.base.build()); for argument in self.base.arguments() { diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index b577f3ed..fe6f2ecc 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,17 +1,23 @@ +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}, + tree::{ + argument_command_node::ArgumentCommandNode, + command_node::{BaseCommandNode, CommandNodeTrait}, + root_command_node::RootCommandNode, + }, }; use std::any::Any; - -use super::argument_builder::BaseArgumentBuilder; +use std::fmt::Debug; pub struct RequiredArgumentBuilder<'a, S> { arguments: RootCommandNode<'a, S>, command: Option>>, requirement: Box bool>, - target: Option>>, + target: Option>>, modifier: Option>>, forks: bool, @@ -26,7 +32,12 @@ impl<'a, S> RequiredArgumentBuilder<'a, S> { name, type_: type_, suggestions_provider: None, - base: BaseArgumentBuilder::default(), + arguments: RootCommandNode::new(), + command: None, + requirement: Box::new(|_| true), + target: None, + modifier: None, + forks: false, } } @@ -62,15 +73,13 @@ impl<'a, S> RequiredArgumentBuilder<'a, S> { let result = ArgumentCommandNode { name: self.name, type_: self.type_, - base: BaseCommandNode { - command: self.base.command(), - requirement: self.base.requirement(), - redirect: self.base.get_redirect(), - modifier: self.base.get_redirect_modifier(), - forks: self.base.forks, - ..BaseCommandNode::default() - }, + 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() { diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index 72a353ad..98288a48 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,4 +1,5 @@ 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 diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 4f0b4d49..8db1487f 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier, - tree::command_node::CommandNode, + tree::command_node::CommandNodeTrait, }; use std::{any::Any, collections::HashMap}; @@ -13,7 +13,7 @@ pub struct CommandContext<'a, S> { input: String, command: &'a dyn Command, arguments: HashMap>>, - root_node: &'a dyn CommandNode, + root_node: &'a dyn CommandNodeTrait, nodes: Vec>, range: StringRange, child: Option<&'a CommandContext<'a, S>>, diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 95da4064..969f9cfd 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,10 +1,10 @@ -use std::{any::Any, collections::HashMap}; - use crate::{ arguments::argument_type::ArgumentType, command::Command, command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier, - tree::command_node::CommandNode, + tree::command_node::CommandNodeTrait, }; +use std::fmt::Debug; +use std::{any::Any, collections::HashMap}; use super::{ command_context::CommandContext, parsed_argument::ParsedArgument, @@ -27,12 +27,12 @@ use super::{ #[derive(Clone)] pub struct CommandContextBuilder<'a, S> { arguments: HashMap>>, - root_node: &'a dyn CommandNode, + root_node: &'a dyn CommandNodeTrait, nodes: Vec>, dispatcher: CommandDispatcher<'a, S>, source: S, command: Box>, - child: Option>, + child: Box>>, range: StringRange, modifier: Option>>, forks: bool, @@ -45,11 +45,14 @@ pub struct CommandContextBuilder<'a, S> { // this.range = StringRange.at(start); // } -impl CommandContextBuilder<'_, S> { +impl CommandContextBuilder<'_, S> +where + , +{ pub fn new( dispatcher: CommandDispatcher, source: S, - root_node: dyn CommandNode, + root_node: dyn CommandNodeTrait, start: usize, ) -> Self { Self { @@ -70,7 +73,7 @@ impl CommandContextBuilder<'_, S> { &self.source } - pub fn root_node(&self) -> &dyn CommandNode { + pub fn root_node(&self) -> &dyn CommandNodeTrait { &self.root_node } @@ -88,7 +91,7 @@ impl CommandContextBuilder<'_, S> { self } - pub fn with_node(mut self, node: dyn CommandNode, range: StringRange) -> 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(); diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index 16c6ed8b..21d1b2e9 100644 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -1,17 +1,17 @@ use super::string_range::StringRange; -use crate::tree::command_node::CommandNode; +use crate::tree::command_node::CommandNodeTrait; pub struct ParsedCommandNode { - node: Box>, + node: Box>, range: StringRange, } impl ParsedCommandNode { - fn new(node: dyn CommandNode, range: StringRange) -> Self { + fn new(node: dyn CommandNodeTrait, range: StringRange) -> Self { Self { node, range } } - fn node(&self) -> &dyn CommandNode { + fn node(&self) -> &dyn CommandNodeTrait { &self.node } diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs index 252cb6ed..51a053c1 100644 --- a/azalea-brigadier/src/context/suggestion_context.rs +++ b/azalea-brigadier/src/context/suggestion_context.rs @@ -1,6 +1,6 @@ -use crate::tree::command_node::CommandNode; +use crate::tree::command_node::CommandNodeTrait; pub struct SuggestionContext<'a, S> { - parent: &'a dyn CommandNode, + parent: &'a dyn CommandNodeTrait, start_pos: usize, } diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index f32de473..694edccb 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -9,7 +9,7 @@ use std::str::FromStr; #[derive(Clone)] pub struct StringReader<'a> { string: &'a str, - cursor: usize, + pub cursor: usize, } const SYNTAX_ESCAPE: char = '\\'; @@ -18,9 +18,15 @@ 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 StringReader<'_> { +// fn from(string: &str) -> StringReader { +// StringReader { string, cursor: 0 } +// } +// } + +impl<'a> From<&'a str> for StringReader<'a> { + fn from(string: &'a str) -> Self { + Self { string, cursor: 0 } } } diff --git a/azalea-brigadier/src/tree/argument_command_node.rs b/azalea-brigadier/src/tree/argument_command_node.rs index e33c3d3e..0997ec17 100644 --- a/azalea-brigadier/src/tree/argument_command_node.rs +++ b/azalea-brigadier/src/tree/argument_command_node.rs @@ -1,17 +1,20 @@ use std::{ any::Any, - fmt::{Display, Formatter}, + 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, @@ -19,12 +22,16 @@ use crate::{ }, }; -use super::command_node::{BaseCommandNode, CommandNode}; +use super::{ + command_node::{BaseCommandNode, CommandNodeTrait}, + literal_command_node::LiteralCommandNode, + root_command_node::RootCommandNode, +}; const USAGE_ARGUMENT_OPEN: &str = "<"; const USAGE_ARGUMENT_CLOSE: &str = ">"; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ArgumentCommandNode<'a, S> { name: String, type_: Box>, @@ -32,6 +39,15 @@ pub struct ArgumentCommandNode<'a, S> { // custom_suggestions: &'a dyn SuggestionProvider, // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S>, + + children: HashMap>>, + literals: HashMap>, + arguments: HashMap>, + pub requirement: Box bool>, + redirect: Option>>, + modifier: Option>>, + forks: bool, + pub command: Option>>, } impl ArgumentCommandNode<'_, S> { @@ -44,10 +60,7 @@ impl ArgumentCommandNode<'_, S> { } } -impl<'a, S> CommandNode for ArgumentCommandNode<'a, S> -where - S: Clone, -{ +impl<'a, S> CommandNodeTrait for ArgumentCommandNode<'a, S> { fn name(&self) -> &str { &self.name } @@ -119,8 +132,44 @@ where self.type_.get_examples() } - fn base(&self) -> &BaseCommandNode { - &self.base + 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(()) + } } } diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index f59b1cef..b7801363 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -12,117 +12,112 @@ use crate::{ string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; -use dyn_clonable::*; +use std::ops::Deref; use std::{any::Any, collections::HashMap, fmt::Debug}; -pub struct BaseCommandNode<'a, S> { - children: HashMap>>, - literals: HashMap>, - arguments: HashMap>, - requirement: Box bool>, - redirect: Option>>, - modifier: Option>>, - forks: bool, - command: Option>>, +enum CommandNodeEnum<'a, S> { + Literal(LiteralCommandNode<'a, S>), + Argument(ArgumentCommandNode<'a, S>), + Root(RootCommandNode<'a, S>), } -impl BaseCommandNode<'_, S> { - pub fn command(&self) -> &Option>> { - &self.command +impl Deref for CommandNodeEnum<'_, S> { + type Target = dyn CommandNodeTrait; + + fn deref(&self) -> &Self::Target { + match self { + CommandNodeEnum::Literal(node) => node, + CommandNodeEnum::Argument(node) => node, + CommandNodeEnum::Root(node) => node, + } } +} - pub fn children(&self) -> &HashMap>> { - &self.children +impl From> for CommandNodeEnum<'_, S> { + fn from(node: LiteralCommandNode<'_, S>) -> Self { + CommandNodeEnum::Literal(node) } +} - pub fn child(&self, name: &str) -> Option<&dyn CommandNode> { - self.children.get(name).map(|child| child.as_ref()) +impl From> for CommandNodeEnum<'_, S> { + fn from(node: ArgumentCommandNode<'_, S>) -> Self { + CommandNodeEnum::Argument(node) } +} - pub fn redirect(&self) -> Option<&dyn CommandNode> { - self.redirect.as_ref().map(|redirect| redirect.as_ref()) +impl From> for CommandNodeEnum<'_, S> { + fn from(node: RootCommandNode<'_, S>) -> Self { + CommandNodeEnum::Root(node) } +} - pub fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - self.modifier.as_ref().map(|modifier| modifier.as_ref()) +impl CommandNodeEnum<'_, S> { + fn redirect_modifier(&self) -> Option<&dyn RedirectModifier> { + (*self).modifier.as_ref().map(|modifier| modifier.as_ref()) } - pub fn can_use(&self, source: S) -> bool { + fn can_use(&self, source: S) -> bool { (self.requirement)(&source) } - // public void addChild(final CommandNode node) { - // if (node instanceof RootCommandNode) { - // throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode"); - // } - - // final CommandNode child = children.get(node.getName()); - // if (child != null) { - // // We've found something to merge onto - // if (node.getCommand() != null) { - // child.command = node.getCommand(); - // } - // for (final CommandNode grandchild : node.getChildren()) { - // child.addChild(grandchild); - // } - // } else { - // children.put(node.getName(), node); - // if (node instanceof LiteralCommandNode) { - // literals.put(node.getName(), (LiteralCommandNode) node); - // } else if (node instanceof ArgumentCommandNode) { - // arguments.put(node.getName(), (ArgumentCommandNode) node); - // } - // } - // } - - pub fn add_child(&self, node: &dyn CommandNode) -> Result<(), String> { - if (&node as &dyn Any).is::>() { + 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 child = self.children.get(node.name()); + 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.command = Some(command); + if let Some(command) = node.base().command() { + child.base_mut().command = Some(*command); } - for grandchild in node.children() { - child.add_child(grandchild)?; + 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(literal) = - &node.clone_boxed() as &dyn Any as &dyn Any as &LiteralCommandNode - { - self.literals - .insert(node.name().to_string(), literal.clone_boxed()); - } else if let Some(argument) = - &node.clone_boxed() as &dyn Any as &dyn Any as &ArgumentCommandNode + 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(), argument.clone_boxed()); + .insert(node.name().to_string(), *dynamic_node); } + Ok(()) } } } - -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(), - } - } +pub struct BaseCommandNode<'a, S> { + 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<'_, S> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("BaseCommandNode") @@ -153,7 +148,7 @@ impl Default for BaseCommandNode<'_, S> { } } -pub trait CommandNode { +pub trait CommandNodeTrait { fn name(&self) -> &str; fn usage_text(&self) -> &str; fn parse( @@ -167,7 +162,6 @@ pub trait CommandNode { builder: &SuggestionsBuilder, ) -> Result; fn is_valid_input(&self, input: &str) -> bool; - fn create_builder(&self) -> dyn ArgumentBuilder; + fn create_builder(&self) -> Box>; fn get_examples(&self) -> Vec; - fn base(&self) -> &BaseCommandNode; } diff --git a/azalea-brigadier/src/tree/literal_command_node.rs b/azalea-brigadier/src/tree/literal_command_node.rs index a722121f..253b6dc5 100644 --- a/azalea-brigadier/src/tree/literal_command_node.rs +++ b/azalea-brigadier/src/tree/literal_command_node.rs @@ -1,17 +1,19 @@ use crate::{ arguments::argument_type::ArgumentType, - builder::literal_argument_builder::LiteralArgumentBuilder, - command::Command, + builder::{ + argument_builder::ArgumentBuilder, literal_argument_builder::LiteralArgumentBuilder, + }, context::{command_context::CommandContext, command_context_builder::CommandContextBuilder}, exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }, - redirect_modifier::RedirectModifier, + immutable_string_reader::ImmutableStringReader, string_reader::StringReader, suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, }; +use std::fmt::Debug; -use super::command_node::{BaseCommandNode, CommandNode}; +use super::command_node::{BaseCommandNode, CommandNodeTrait}; #[derive(Debug, Clone)] pub struct LiteralCommandNode<'a, S> { @@ -22,7 +24,7 @@ pub struct LiteralCommandNode<'a, S> { } impl<'a, S> LiteralCommandNode<'a, S> { - pub fn new(literal: String, base: BaseCommandNode) -> Self { + pub fn new(literal: String, base: BaseCommandNode<'a, S>) -> Self { let literal_lowercase = literal.to_lowercase(); Self { literal, @@ -35,16 +37,16 @@ impl<'a, S> LiteralCommandNode<'a, S> { &self.literal } - pub fn parse(&self, reader: StringReader) -> i32 { - let start = reader.get_cursor(); - if reader.can_read(self.literal.len()) { + 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.get_string()[start..end].eq(&self.literal) { - reader.set_cursor(end); + if reader.string()[start..end].eq(&self.literal) { + reader.cursor = end; if !reader.can_read() || reader.peek() == ' ' { return end as i32; } else { - reader.set_cursor(start); + reader.cursor = start; } } } @@ -52,27 +54,24 @@ impl<'a, S> LiteralCommandNode<'a, S> { } } -impl CommandNode for LiteralCommandNode<'_, S> -where - S: Clone, -{ +impl CommandNodeTrait for LiteralCommandNode<'_, S> { fn name(&self) -> &str { &self.literal } fn parse( &self, - reader: StringReader, + reader: &mut StringReader<'_>, context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { - let start = reader.get_cursor(); + let start = reader.cursor(); let end = self.parse(reader); if end > -1 { return Ok(()); } Err(BuiltInExceptions::LiteralIncorrect { - expected: self.literal(), + expected: self.literal().to_string(), } .create_with_context(reader)) } @@ -80,33 +79,41 @@ where fn list_suggestions( &self, context: CommandContext, - builder: SuggestionsBuilder, + builder: &SuggestionsBuilder, ) -> Result { if self .literal_lowercase .starts_with(&builder.remaining_lowercase()) { - builder.suggest(self.literal()) + Ok(builder.suggest(self.literal()).build()) } else { - Suggestions::empty() + Ok(Suggestions::default()) } } fn is_valid_input(&self, input: &str) -> bool { - self.parse(StringReader::from(input)) > -1 + self.parse(&mut StringReader::from(input)) > -1 } fn usage_text(&self) -> &str { - self.literal + &self.literal } - fn create_builder(&self) -> LiteralArgumentBuilder { - let builder = LiteralArgumentBuilder::literal(self.literal()); - builder.requires(self.requirement()); - builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork()); + 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/root_command_node.rs b/azalea-brigadier/src/tree/root_command_node.rs index c3139a05..0fcb8d97 100644 --- a/azalea-brigadier/src/tree/root_command_node.rs +++ b/azalea-brigadier/src/tree/root_command_node.rs @@ -1,44 +1,49 @@ -use std::fmt::{Display, Formatter}; - +use super::{ + argument_command_node::ArgumentCommandNode, + command_node::{BaseCommandNode, CommandNodeTrait}, + literal_command_node::LiteralCommandNode, +}; use crate::{ arguments::argument_type::ArgumentType, + builder::argument_builder::ArgumentBuilder, 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, + fmt::{Debug, Display, Formatter}, +}; -use super::command_node::{BaseCommandNode, CommandNode}; - -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] pub struct RootCommandNode<'a, S> { // Since Rust doesn't have extending, we put the struct this is extending as the "base" field pub base: BaseCommandNode<'a, S>, } -impl CommandNode for RootCommandNode<'_, S> -where - S: Clone, -{ +impl CommandNodeTrait for RootCommandNode<'_, S> { fn name(&self) -> &str { "" } fn parse( &self, - reader: StringReader, + reader: &mut StringReader<'_>, context_builder: CommandContextBuilder, ) -> Result<(), CommandSyntaxException> { + Ok(()) } fn list_suggestions( &self, context: CommandContext, - builder: SuggestionsBuilder, + builder: &SuggestionsBuilder, ) -> Result { - Suggestions::empty() + Ok(Suggestions::default()) } fn is_valid_input(&self, input: &str) -> bool { @@ -49,7 +54,7 @@ where "" } - fn create_builder(&self) -> () { + fn create_builder(&self) -> Box> { panic!("Cannot convert root into a builder"); } -- cgit v1.2.3 From 1b888881516c7553126e0c7fc2539d14b129e29e Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 2 Feb 2022 07:57:50 -0600 Subject: f --- azalea-brigadier/src/context/command_context_builder.rs | 7 ++----- azalea-brigadier/src/tree/command_node.rs | 8 ++++---- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 969f9cfd..ba25849c 100644 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -45,14 +45,11 @@ pub struct CommandContextBuilder<'a, S> { // this.range = StringRange.at(start); // } -impl CommandContextBuilder<'_, S> -where - , -{ +impl CommandContextBuilder<'_, S> { pub fn new( dispatcher: CommandDispatcher, source: S, - root_node: dyn CommandNodeTrait, + root_node: &dyn CommandNodeTrait, start: usize, ) -> Self { Self { diff --git a/azalea-brigadier/src/tree/command_node.rs b/azalea-brigadier/src/tree/command_node.rs index b7801363..30907163 100644 --- a/azalea-brigadier/src/tree/command_node.rs +++ b/azalea-brigadier/src/tree/command_node.rs @@ -21,14 +21,14 @@ enum CommandNodeEnum<'a, S> { Root(RootCommandNode<'a, S>), } -impl Deref for CommandNodeEnum<'_, S> { +impl<'a, S> Deref for CommandNodeEnum<'a, S> { type Target = dyn CommandNodeTrait; fn deref(&self) -> &Self::Target { match self { - CommandNodeEnum::Literal(node) => node, - CommandNodeEnum::Argument(node) => node, - CommandNodeEnum::Root(node) => node, + CommandNodeEnum::Literal(node) => *node as &Self::Target, + CommandNodeEnum::Argument(node) => *node as &Self::Target, + CommandNodeEnum::Root(node) => *node as &Self::Target, } } } -- cgit v1.2.3 From a72a47ced76065caf739898954cd18edbc39174b Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 14:02:13 -0500 Subject: Rewrite brigadier --- azalea-brigadier/src/ambiguity_consumer.rs | 1 - azalea-brigadier/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 - azalea-brigadier/src/builder/argument_builder.rs | 257 +++++++++++--------- .../src/builder/literal_argument_builder.rs | 60 ++--- .../src/builder/required_argument_builder.rs | 113 +++------ azalea-brigadier/src/command.rs | 12 - azalea-brigadier/src/command_dispatcher.rs | 46 ---- azalea-brigadier/src/context.rs | 145 +++++++++++ azalea-brigadier/src/context/command_context.rs | 93 ------- .../src/context/command_context_builder.rs | 176 -------------- azalea-brigadier/src/context/mod.rs | 6 - azalea-brigadier/src/context/parsed_argument.rs | 24 -- .../src/context/parsed_command_node.rs | 30 --- azalea-brigadier/src/context/string_range.rs | 45 ---- azalea-brigadier/src/context/suggestion_context.rs | 6 - azalea-brigadier/src/dispatcher.rs | 242 ++++++++++++++++++ .../src/exceptions/builtin_exception_provider.rs | 1 - .../src/exceptions/builtin_exceptions.rs | 16 +- .../src/exceptions/command_syntax_exception.rs | 6 +- azalea-brigadier/src/exceptions/mod.rs | 1 - azalea-brigadier/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 - azalea-brigadier/src/single_redirect_modifier.rs | 8 - azalea-brigadier/src/string_range.rs | 45 ++++ 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 -- azalea-brigadier/src/suggestion/suggestions.rs | 141 ----------- .../src/suggestion/suggestions_builder.rs | 116 --------- azalea-brigadier/src/tree.rs | 269 +++++++++++++++++++++ azalea-brigadier/src/tree/argument_command_node.rs | 176 -------------- azalea-brigadier/src/tree/command_node.rs | 143 ----------- azalea-brigadier/src/tree/literal_command_node.rs | 131 ---------- azalea-brigadier/src/tree/mod.rs | 4 - azalea-brigadier/src/tree/root_command_node.rs | 79 ------ 52 files changed, 1145 insertions(+), 1867 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/string_range.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 create mode 100644 azalea-brigadier/src/string_range.rs 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 (limited to 'azalea-brigadier/src/context/command_context_builder.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; - -pub struct BaseArgumentBuilder<'a, S> { - arguments: RootCommandNode<'a, S>, - command: Option>>, - requirement: Box bool>, - target: Option>>, - modifier: Option>>, - forks: bool, +use crate::{context::CommandContext, modifier::RedirectModifier, tree::CommandNode}; + +use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; +use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, rc::Rc}; + +#[derive(Debug, Clone)] +pub enum ArgumentBuilderType { + Literal(Literal), + Argument(Argument), } -pub trait ArgumentBuilder { - fn build(self) -> Box>; +/// 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>>, } -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) - } +// todo: maybe remake this to be based on a CommandNode like vanilla does? - pub fn arguments(&self) -> &Vec<&dyn CommandNodeTrait> { - &self.arguments.get_children() +/// A node that isn't yet built. +impl ArgumentBuilder { + pub fn new(value: ArgumentBuilderType) -> Self { + Self { + value, + children: BTreeMap::new(), + literals: BTreeMap::new(), + arguments: BTreeMap::new(), + executes: None, + requirement: Rc::new(|_| true), + forks: false, + modifier: None, + } } - pub fn executes(&mut self, command: Box>) -> &mut Self { - self.command = Some(command); + 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 command(&self) -> Option>> { - self.command + pub fn executes(&mut self, f: F) -> Self + where + F: Fn(&CommandContext) -> i32 + 'static, + { + self.executes = Some(Rc::new(f)); + self.clone() } - pub fn requires(&mut self, requirement: Box bool>) -> &mut Self { - self.requirement = requirement; - self - } + pub fn build(self) -> CommandNode { + println!("building {:?}", self); + CommandNode { + value: self.value, - pub fn requirement(&self) -> Box bool> { - self.requirement - } + children: self.children, + literals: self.literals, + arguments: self.arguments, - pub fn redirect(&mut self, target: Box>) -> &mut Self { - self.forward(target, None, false) + command: self.executes.clone(), + requirement: self.requirement.clone(), + redirect: None, + forks: self.forks, + modifier: self.modifier, + } } +} - pub fn redirect_modifier( - &mut self, - target: &dyn CommandNodeTrait, - 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) +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() } +} - pub fn fork( - &mut self, - target: &dyn CommandNodeTrait, - modifier: &dyn RedirectModifier, - ) -> &mut Self { - self.forward(target, Some(modifier), true) - } +#[cfg(test)] +mod tests { + use std::rc::Rc; - 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) - } + use crate::{ + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + parsers::integer, + }; - pub fn get_redirect(&self) -> Option<&dyn CommandNodeTrait> { - self.target.as_ref() - } + use super::ArgumentBuilder; - pub fn get_redirect_modifier(&self) -> Option<&dyn RedirectModifier> { - self.modifier.as_ref() - } + // public class ArgumentBuilderTest { + // private TestableArgumentBuilder builder; - pub fn is_fork(&self) -> bool { - self.forks - } + // @Before + // public void setUp() throws Exception { + // builder = new TestableArgumentBuilder<>(); + // } - pub fn build(self) -> BaseCommandNode<'a, S> { - let result: BaseCommandNode<'a, S> = BaseCommandNode { - command: self.command, - requirement: self.requirement, - redirect: self.target, - modifier: self.modifier, - forks: self.forks, + // @Test + // public void testArguments() throws Exception { + // final RequiredArgumentBuilder argument = argument("bar", integer()); - arguments: Default::default(), - children: Default::default(), - literals: Default::default(), - }; + // builder.then(argument); - for argument in self.arguments() { - result.add_child(argument); - } + // assertThat(builder.getArguments(), hasSize(1)); + // assertThat(builder.getArguments(), hasItem((CommandNode) argument.build())); + // } - result - } -} + #[test] + fn test_arguments() { + let mut builder: ArgumentBuilder<()> = literal("foo"); -impl Default for BaseArgumentBuilder<'_, S> { - fn default() -> Self { - Self { - arguments: Default::default(), - command: Default::default(), - requirement: Default::default(), - target: Default::default(), - modifier: Default::default(), - forks: Default::default(), - } + let argument: ArgumentBuilder<()> = argument("bar", integer()); + builder.then(argument.clone()); + assert_eq!(builder.children.len(), 1); + let built_argument = Rc::new(argument.build()); + assert!(builder + .children + .values() + .any(|e| *e.borrow() == *built_argument)); } + + // @Test + // public void testRedirect() throws Exception { + // final CommandNode 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}; -impl LiteralArgumentBuilder { - pub fn new(literal: String) -> Self { +#[derive(Debug, Clone, Default)] +pub struct Literal { + pub value: String, +} +impl Literal { + pub fn new(value: &str) -> Self { Self { - literal, - arguments: RootCommandNode::new(), - command: None, - requirement: Box::new(|_| true), - target: None, - modifier: None, - forks: false, + value: value.to_string(), } } +} - pub fn literal(name: String) -> Self { - Self::new(name) +impl From for ArgumentBuilderType { + fn from(literal: Literal) -> Self { + Self::Literal(literal) } } -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) - } +/// 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; - -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>>, +use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; +use crate::{parsers::Parser, string_reader::StringReader}; +use std::{any::Any, fmt::Debug, rc::Rc}; + +/// An argument node type. The `T` type parameter is the type of the argument, +/// which can be anything. +#[derive(Clone)] +pub struct Argument { + pub name: String, + parser: Rc, } - -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 parse(&self, reader: &mut StringReader) -> Option> { + self.parser.parse(reader) } +} - pub fn get_type(&self) -> Box> { - self.type_ +impl From for ArgumentBuilderType { + fn from(argument: Argument) -> Self { + Self::Argument(argument) } +} - pub fn name(&self) -> &str { - &self.name +impl Debug for Argument { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Argument") + .field("name", &self.name) + // .field("parser", &self.parser) + .finish() } +} - // final ArgumentCommandNode 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 - } +/// 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/string_range.rs b/azalea-brigadier/src/context/string_range.rs deleted file mode 100644 index 87098a1a..00000000 --- a/azalea-brigadier/src/context/string_range.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::cmp; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StringRange { - start: usize, - end: usize, -} - -impl StringRange { - pub fn new(start: usize, end: usize) -> Self { - Self { start, end } - } - - pub fn at(pos: usize) -> Self { - Self::new(pos, pos) - } - - pub fn between(start: usize, end: usize) -> Self { - Self::new(start, end) - } - - pub fn encompassing(a: &Self, b: &Self) -> Self { - Self::new(cmp::min(a.start, b.start), cmp::max(a.end, b.end)) - } - - pub fn start(&self) -> usize { - self.start - } - - pub fn end(&self) -> usize { - self.end - } - - pub fn get(&self, reader: &str) -> &str { - &reader[self.start..self.end] - } - - pub fn is_empty(&self) -> bool { - self.start == self.end - } - - pub fn length(&self) -> usize { - self.end - self.start - } -} 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/string_range.rs b/azalea-brigadier/src/string_range.rs new file mode 100644 index 00000000..8ca88624 --- /dev/null +++ b/azalea-brigadier/src/string_range.rs @@ -0,0 +1,45 @@ +use std::cmp; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct StringRange { + start: usize, + end: usize, +} + +impl StringRange { + pub fn new(start: usize, end: usize) -> Self { + Self { start, end } + } + + pub fn at(pos: usize) -> Self { + Self::new(pos, pos) + } + + pub fn between(start: usize, end: usize) -> Self { + Self::new(start, end) + } + + pub fn encompassing(a: &Self, b: &Self) -> Self { + Self::new(cmp::min(a.start, b.start), cmp::max(a.end, b.end)) + } + + pub fn start(&self) -> usize { + self.start + } + + pub fn end(&self) -> usize { + self.end + } + + pub fn get<'a>(&self, reader: &'a str) -> &'a str { + &reader[self.start..self.end] + } + + pub fn is_empty(&self) -> bool { + self.start == self.end + } + + pub fn length(&self) -> usize { + self.end - self.start + } +} 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, "") - } -} -- cgit v1.2.3 From 8d71fbf813391783531a9f7c70e75e105fabaf03 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 18:14:25 +0000 Subject: change a BTreeMap to a HashMap --- azalea-brigadier/src/command_dispatcher.rs | 711 +++++++++++++++++++++ azalea-brigadier/src/context/command_context.rs | 80 +++ .../src/context/command_context_builder.rs | 116 ++++ azalea-brigadier/src/context/mod.rs | 11 + azalea-brigadier/src/context/parsed_argument.rs | 8 + .../src/context/parsed_command_node.rs | 18 + azalea-brigadier/src/context/string_range.rs | 45 ++ azalea-brigadier/src/tree/mod.rs | 259 ++++++++ 8 files changed, 1248 insertions(+) create mode 100644 azalea-brigadier/src/command_dispatcher.rs create mode 100644 azalea-brigadier/src/context/command_context.rs create mode 100644 azalea-brigadier/src/context/command_context_builder.rs create mode 100644 azalea-brigadier/src/context/mod.rs create mode 100644 azalea-brigadier/src/context/parsed_argument.rs create mode 100644 azalea-brigadier/src/context/parsed_command_node.rs create mode 100644 azalea-brigadier/src/context/string_range.rs create mode 100644 azalea-brigadier/src/tree/mod.rs (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs new file mode 100644 index 00000000..ea788130 --- /dev/null +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -0,0 +1,711 @@ +use crate::{ + builder::argument_builder::ArgumentBuilder, + context::{CommandContext, CommandContextBuilder}, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + parse_results::ParseResults, + string_reader::StringReader, + tree::CommandNode, +}; +use std::{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) -> Rc>> { + let build = Rc::new(RefCell::new(node.build())); + self.root.borrow_mut().add_child(&build); + build + } + + pub fn parse(&self, command: StringReader, source: Rc) -> ParseResults { + let context = CommandContextBuilder::new( + Rc::new(self.clone()), + 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() && 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, + redirect.clone(), + reader.cursor, + ); + let parse = self + .parse_nodes(redirect, &reader, child_context) + .expect("Parsing nodes failed"); + context.with_child(Rc::new(parse.context)); + return Ok(ParseResults { + context, + reader: parse.reader, + exceptions: parse.exceptions, + }); + } 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.is_empty() { + 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(); + return Ok(best_potential); + } + + Ok(ParseResults { + context: context_so_far, + reader: original_reader.clone(), + exceptions: errors, + }) + } + + pub fn execute( + &self, + input: StringReader, + source: Rc, + ) -> Result { + let parse = self.parse(input, source); + Self::execute_parsed(parse) + } + + pub fn add_paths( + &self, + node: Rc>>, + result: &mut Vec>>>>, + parents: Vec>>>, + ) { + let mut current = parents; + current.push(node.clone()); + result.push(current.clone()); + + for child in node.borrow().children.values() { + self.add_paths(child.clone(), result, current.clone()); + } + } + + pub fn get_path(&self, target: CommandNode) -> Vec { + let rc_target = Rc::new(RefCell::new(target)); + let mut nodes: Vec>>>> = Vec::new(); + self.add_paths(self.root.clone(), &mut nodes, vec![]); + + for list in nodes { + if *list.last().expect("Nothing in list").borrow() == *rc_target.borrow() { + let mut result: Vec = Vec::with_capacity(list.len()); + for node in list { + if node != self.root { + result.push(node.borrow().name().to_string()); + } + } + return result; + } + } + vec![] + } + + pub fn find_node(&self, path: &[&str]) -> Option>>> { + let mut node = self.root.clone(); + for name in path { + if let Some(child) = node.clone().borrow().child(name) { + node = child + } else { + return None; + } + } + Some(node) + } + + /// Executes a given pre-parsed command. + pub fn execute_parsed(parse: ParseResults) -> Result { + if parse.reader.can_read() { + 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) + ); + } + 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.is_empty() { + for context in contexts.iter() { + let child = &context.child; + if let Some(child) = child { + println!("aaaaaaa {:?}", child); + forked |= child.forks; + if child.has_nodes() { + found_command = true; + let modifier = &context.modifier; + if let Some(modifier) = modifier { + let results = modifier(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) + ); + } + + // TODO: this is not how vanilla does it but it works + Ok(if successful_forks >= 2 { + successful_forks + } else { + result + }) + // Ok(if forked { successful_forks } else { result }) + } +} + +impl Clone for CommandDispatcher { + fn clone(&self) -> Self { + Self { + root: self.root.clone(), + _marker: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + parsers::integer, + }; + + #[derive(Debug, PartialEq)] + struct CommandSource {} + + fn input_with_offset(input: &str, offset: usize) -> StringReader { + let mut result: StringReader = input.into(); + result.cursor = offset; + result + } + + #[test] + fn create_and_execute_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + assert_eq!( + subject + .execute("foo".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + #[test] + fn create_and_execute_offset_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + assert_eq!( + subject + .execute(input_with_offset("/foo", 1), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + #[test] + fn create_and_merge_commands() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("base").then(literal("foo").executes(|_| 42))); + subject.register(literal("base").then(literal("bar").executes(|_| 42))); + + assert_eq!( + subject + .execute("base foo".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + assert_eq!( + subject + .execute("base bar".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + #[test] + fn execute_unknown_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("bar")); + subject.register(literal("baz")); + + let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } + + #[test] + fn execute_impermissible_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").requires(|_| false)); + + let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } + + #[test] + fn execute_empty_command() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("")); + + let execute_result = subject.execute("".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownCommand => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 0); + } + + #[test] + fn execute_unknown_subcommand() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42)); + + let execute_result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + + #[test] + fn execute_incorrect_literal() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").executes(|_| 42).then(literal("bar"))); + + let execute_result = subject.execute("foo baz".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + + #[test] + fn execute_ambiguous_incorrect_argument() { + let mut subject = CommandDispatcher::new(); + subject.register( + literal("foo") + .executes(|_| 42) + .then(literal("bar")) + .then(literal("baz")), + ); + + let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {})); + + let err = execute_result.err().unwrap(); + match err.type_ { + BuiltInExceptions::DispatcherUnknownArgument => {} + _ => panic!("Unexpected error"), + } + assert_eq!(err.cursor().unwrap(), 4); + } + + #[test] + fn execute_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(literal("a")) + .then(literal("=").executes(|_| 100)) + .then(literal("c")) + .executes(|_| 42), + ); + + assert_eq!( + subject + .execute("foo =".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } + + #[test] + fn parse_incomplete_literal() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").then(literal("bar").executes(|_| 42))); + + let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); + assert_eq!(parse.reader.remaining(), " "); + assert_eq!(parse.context.nodes.len(), 1); + } + + #[test] + fn parse_incomplete_argument() { + let mut subject = CommandDispatcher::new(); + subject.register(literal("foo").then(argument("bar", integer()).executes(|_| 42))); + + let parse = subject.parse("foo ".into(), Rc::new(CommandSource {})); + assert_eq!(parse.reader.remaining(), " "); + assert_eq!(parse.context.nodes.len(), 1); + } + + #[test] + fn execute_ambiguious_parent_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("test") + .then(argument("incorrect", integer()).executes(|_| 42)) + .then( + argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), + ), + ); + + assert_eq!( + subject + .execute("test 1 2".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } + + #[test] + fn execute_ambiguious_parent_subcommand_via_redirect() { + let mut subject = CommandDispatcher::new(); + + let real = subject.register( + literal("test") + .then(argument("incorrect", integer()).executes(|_| 42)) + .then( + argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)), + ), + ); + + subject.register(literal("redirect").redirect(real)); + + assert_eq!( + subject + .execute("redirect 1 2".into(), Rc::new(CommandSource {})) + .unwrap(), + 100 + ); + } + + #[test] + fn execute_redirected_multiple_times() { + let mut subject = CommandDispatcher::new(); + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let root = subject.root.clone(); + let redirect_node = subject.register(literal("redirected").redirect(root.clone())); + + let input = "redirected redirected actual"; + + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let child1 = parse.context.child.clone(); + assert!(child1.is_some()); + assert_eq!(child1.clone().unwrap().range.get(input), "redirected"); + assert_eq!(child1.clone().unwrap().nodes.len(), 1); + assert_eq!(child1.clone().unwrap().root, root); + assert_eq!( + child1.clone().unwrap().nodes[0].range, + child1.clone().unwrap().range + ); + assert_eq!(child1.clone().unwrap().nodes[0].node, redirect_node); + + let child2 = child1.unwrap().child.clone(); + assert!(child2.is_some()); + assert_eq!(child2.clone().unwrap().range.get(input), "actual"); + assert_eq!(child2.clone().unwrap().nodes.len(), 1); + assert_eq!(child2.clone().unwrap().root, root); + assert_eq!( + child2.clone().unwrap().nodes[0].range, + child2.clone().unwrap().range + ); + assert_eq!(child2.clone().unwrap().nodes[0].node, concrete_node); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); + } + + #[test] + fn execute_redirected() { + let mut subject = CommandDispatcher::new(); + + let source1 = Rc::new(CommandSource {}); + let source2 = Rc::new(CommandSource {}); + + let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { + Ok(vec![source1.clone(), source2.clone()]) + }; + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let redirect_node = + subject.register(literal("redirected").fork(subject.root.clone(), Rc::new(modifier))); + + let input = "redirected actual"; + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let parent = parse.context.child.clone(); + assert!(parent.is_some()); + let parent = parent.unwrap(); + assert_eq!(parent.range.get(input), "actual"); + assert_eq!(parent.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parent.nodes[0].range, parent.range); + assert_eq!(parent.nodes[0].node, concrete_node); + assert_eq!(parent.source, Rc::new(CommandSource {})); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); + } + + #[test] + fn execute_orphaned_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo 5".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + assert_eq!( + *result.get_type(), + BuiltInExceptions::DispatcherUnknownCommand + ); + assert_eq!(result.cursor(), Some(5)); + } + + #[test] + fn execute_invalid_other() { + let mut subject = CommandDispatcher::new(); + + subject.register(literal("w").executes(|_| panic!("This should not run"))); + subject.register(literal("world").executes(|_| 42)); + + assert_eq!( + subject + .execute("world".into(), Rc::new(CommandSource {})) + .unwrap(), + 42 + ); + } + + #[test] + fn parse_no_space_separator() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo$".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + assert_eq!( + *result.get_type(), + BuiltInExceptions::DispatcherUnknownCommand + ); + assert_eq!(result.cursor(), Some(0)); + } + + #[test] + fn execute_invalid_subcommand() { + let mut subject = CommandDispatcher::new(); + + subject.register( + literal("foo") + .then(argument("bar", integer())) + .executes(|_| 42), + ); + + let result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + assert!(result.is_err()); + let result = result.unwrap_err(); + // this fails for some reason, i blame mojang + // assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt); + assert_eq!(result.cursor(), Some(4)); + } + + #[test] + fn get_path() { + let mut subject = CommandDispatcher::<()>::new(); + + let bar = literal("bar").build(); + subject.register(literal("foo").then_built(bar.clone())); + + assert_eq!( + subject.get_path(bar), + vec!["foo".to_string(), "bar".to_string()] + ); + } + + #[test] + fn find_node_doesnt_exist() { + let subject = CommandDispatcher::<()>::new(); + + assert_eq!(subject.find_node(&vec!["foo", "bar"]), None) + } +} diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs new file mode 100644 index 00000000..1834a73d --- /dev/null +++ b/azalea-brigadier/src/context/command_context.rs @@ -0,0 +1,80 @@ +use super::{parsed_command_node::ParsedCommandNode, string_range::StringRange, ParsedArgument}; +use crate::{modifier::RedirectModifier, tree::CommandNode}; +use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; + +/// 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 Clone for CommandContext { + fn clone(&self) -> Self { + Self { + source: self.source.clone(), + 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, + } + } +} + +impl Debug for CommandContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CommandContext") + // .field("source", &self.source) + .field("input", &self.input) + // .field("arguments", &self.arguments) + // .field("command", &self.command) + // .field("root_node", &self.root_node) + // .field("nodes", &self.nodes) + .field("range", &self.range) + .field("child", &self.child) + // .field("modifier", &self.modifier) + .field("forks", &self.forks) + .finish() + } +} + +impl CommandContext { + pub fn copy_for(&self, source: Rc) -> Self { + if Rc::ptr_eq(&source, &self.source) { + return self.clone(); + } + 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 { + !self.nodes.is_empty() + } + + pub fn argument(&self, name: &str) -> Option> { + let argument = self.arguments.get(name); + argument.map(|a| a.result.clone()) + } +} diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs new file mode 100644 index 00000000..f192f6b7 --- /dev/null +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -0,0 +1,116 @@ +use super::{ + command_context::CommandContext, parsed_command_node::ParsedCommandNode, + string_range::StringRange, ParsedArgument, +}; +use crate::{command_dispatcher::CommandDispatcher, modifier::RedirectModifier, tree::CommandNode}; +use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; + +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 Clone for CommandContextBuilder { + fn clone(&self) -> Self { + Self { + arguments: self.arguments.clone(), + root: self.root.clone(), + nodes: self.nodes.clone(), + dispatcher: self.dispatcher.clone(), + source: self.source.clone(), + command: self.command.clone(), + child: self.child.clone(), + range: self.range.clone(), + modifier: self.modifier.clone(), + forks: self.forks, + } + } +} + +impl CommandContextBuilder { + 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![], + 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(ParsedCommandNode { + node: node.clone(), + range: range.clone(), + }); + self.range = StringRange::encompassing(&self.range, &range); + self.modifier = node.borrow().modifier.clone(); + self.forks = node.borrow().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() + } +} diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs new file mode 100644 index 00000000..d535602a --- /dev/null +++ b/azalea-brigadier/src/context/mod.rs @@ -0,0 +1,11 @@ +mod command_context; +mod command_context_builder; +mod parsed_argument; +mod parsed_command_node; +mod string_range; + +pub use command_context::CommandContext; +pub use command_context_builder::CommandContextBuilder; +pub use parsed_argument::ParsedArgument; +pub use parsed_command_node::ParsedCommandNode; +pub use string_range::StringRange; diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs new file mode 100644 index 00000000..3302b1be --- /dev/null +++ b/azalea-brigadier/src/context/parsed_argument.rs @@ -0,0 +1,8 @@ +use super::string_range::StringRange; +use std::{any::Any, rc::Rc}; + +#[derive(Clone)] +pub struct ParsedArgument { + pub range: StringRange, + pub result: Rc, +} diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs new file mode 100644 index 00000000..ed49928d --- /dev/null +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -0,0 +1,18 @@ +use super::string_range::StringRange; +use crate::tree::CommandNode; +use std::{cell::RefCell, rc::Rc}; + +#[derive(Debug)] +pub struct ParsedCommandNode { + pub node: Rc>>, + pub range: StringRange, +} + +impl Clone for ParsedCommandNode { + fn clone(&self) -> Self { + Self { + node: self.node.clone(), + range: self.range.clone(), + } + } +} diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs new file mode 100644 index 00000000..8ca88624 --- /dev/null +++ b/azalea-brigadier/src/context/string_range.rs @@ -0,0 +1,45 @@ +use std::cmp; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct StringRange { + start: usize, + end: usize, +} + +impl StringRange { + pub fn new(start: usize, end: usize) -> Self { + Self { start, end } + } + + pub fn at(pos: usize) -> Self { + Self::new(pos, pos) + } + + pub fn between(start: usize, end: usize) -> Self { + Self::new(start, end) + } + + pub fn encompassing(a: &Self, b: &Self) -> Self { + Self::new(cmp::min(a.start, b.start), cmp::max(a.end, b.end)) + } + + pub fn start(&self) -> usize { + self.start + } + + pub fn end(&self) -> usize { + self.end + } + + pub fn get<'a>(&self, reader: &'a str) -> &'a str { + &reader[self.start..self.end] + } + + pub fn is_empty(&self) -> bool { + self.start == self.end + } + + pub fn length(&self) -> usize { + self.end - self.start + } +} diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs new file mode 100644 index 00000000..b6181c73 --- /dev/null +++ b/azalea-brigadier/src/tree/mod.rs @@ -0,0 +1,259 @@ +use crate::{ + builder::{ + argument_builder::ArgumentBuilderType, literal_argument_builder::Literal, + required_argument_builder::Argument, + }, + context::{CommandContext, CommandContextBuilder, ParsedArgument, StringRange}, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + modifier::RedirectModifier, + string_reader::StringReader, +}; +use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; + +/// An ArgumentBuilder that has been built. +#[non_exhaustive] +pub struct CommandNode { + pub value: ArgumentBuilderType, + + pub children: HashMap>>>, + pub literals: HashMap>>>, + pub arguments: HashMap>>>, + + pub command: Option) -> i32>>, + pub requirement: Rc) -> bool>, + pub redirect: Option>>>, + pub forks: bool, + pub modifier: Option>>, +} + +impl Clone for CommandNode { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + children: self.children.clone(), + literals: self.literals.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + requirement: self.requirement.clone(), + redirect: self.redirect.clone(), + forks: self.forks, + modifier: self.modifier.clone(), + } + } +} + +impl CommandNode { + /// 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; + + if !literals.is_empty() { + 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().cloned().collect(); + } + } else { + self.arguments.values().cloned().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 child(&self, name: &str) -> Option>>> { + self.children.get(name).cloned() + } + + 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(); + let result = argument.parse(reader)?; + let parsed = ParsedArgument { + range: StringRange::between(start, reader.cursor()), + result, + }; + + context_builder.with_argument(&argument.name, parsed.clone()); + context_builder.with_node(Rc::new(RefCell::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(RefCell::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(_) => { + 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 { + Self { + value: ArgumentBuilderType::Literal(Literal::default()), + + children: HashMap::new(), + literals: HashMap::new(), + arguments: HashMap::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 { + // idk how to do this better since we can't compare `dyn Fn`s + if let Some(otherexecutes) = &other.command { + #[allow(clippy::vtable_address_comparisons)] + if !Rc::ptr_eq(selfexecutes, otherexecutes) { + return false; + } + } else { + return false; + } + } else if other.command.is_some() { + return false; + } + true + } +} +impl Eq for CommandNode {} -- cgit v1.2.3 From 248f752748a0033db7f8242ee0ecd73ea8ce8ec9 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 22 Apr 2022 04:33:58 +0000 Subject: simplify error handling --- .gitignore | 0 .gitpod.yml | 0 .vscode/settings.json | 0 Cargo.lock | 0 Cargo.toml | 0 README.md | 0 azalea-auth/Cargo.toml | 0 azalea-auth/src/game_profile.rs | 0 azalea-auth/src/lib.rs | 0 azalea-brigadier/Cargo.toml | 0 azalea-brigadier/README.md | 0 azalea-brigadier/src/arguments/argument_type.rs | 0 .../src/arguments/integer_argument_type.rs | 0 azalea-brigadier/src/arguments/mod.rs | 0 azalea-brigadier/src/builder/argument_builder.rs | 0 .../src/builder/literal_argument_builder.rs | 0 azalea-brigadier/src/builder/mod.rs | 0 .../src/builder/required_argument_builder.rs | 0 azalea-brigadier/src/command_dispatcher.rs | 0 azalea-brigadier/src/context/command_context.rs | 0 .../src/context/command_context_builder.rs | 0 azalea-brigadier/src/context/mod.rs | 0 azalea-brigadier/src/context/parsed_argument.rs | 0 .../src/context/parsed_command_node.rs | 0 azalea-brigadier/src/context/string_range.rs | 0 .../src/exceptions/builtin_exceptions.rs | 0 .../src/exceptions/command_syntax_exception.rs | 0 azalea-brigadier/src/exceptions/mod.rs | 0 azalea-brigadier/src/lib.rs | 0 azalea-brigadier/src/message.rs | 0 azalea-brigadier/src/modifier.rs | 0 azalea-brigadier/src/parse_results.rs | 0 azalea-brigadier/src/string_reader.rs | 0 azalea-brigadier/src/tree/mod.rs | 0 .../tests/arguments/bool_argument_type_test.rs | 0 .../tests/arguments/double_argument_type_test.rs | 0 .../tests/arguments/float_argument_type_test.rs | 0 .../tests/arguments/integer_argument_type_test.rs | 0 .../tests/arguments/long_argument_type_test.rs | 0 .../tests/arguments/string_argument_type_test.rs | 0 .../tests/builder/argument_builder_test.rs | 0 .../tests/builder/literal_argument_builder_test.rs | 0 .../builder/required_argument_builder_test.rs | 0 azalea-brigadier/tests/command_dispatcher_test.rs | 0 .../tests/command_dispatcher_usages_test.rs | 0 azalea-brigadier/tests/command_suggestions_test.rs | 0 .../tests/context/command_context_test.rs | 0 .../tests/context/parsed_argument_test.rs | 0 .../dynamic_command_syntax_exception_type_test.rs | 0 .../simple_command_syntax_exception_type_test.rs | 0 azalea-brigadier/tests/string_reader_test.rs | 0 .../tests/suggestion/suggestion_test.rs | 0 .../tests/suggestion/suggestions_builder_test.rs | 0 .../tests/suggestion/suggestions_test.rs | 0 .../tests/tree/abstract_command_node_test.rs | 0 .../tests/tree/argument_command_node_test.rs | 0 .../tests/tree/literal_command_node_test.rs | 0 .../tests/tree/root_command_node_test.rs | 0 azalea-chat/Cargo.toml | 0 azalea-chat/README.md | 0 azalea-chat/src/base_component.rs | 0 azalea-chat/src/component.rs | 0 azalea-chat/src/events.rs | 0 azalea-chat/src/lib.rs | 0 azalea-chat/src/style.rs | 0 azalea-chat/src/text_component.rs | 0 azalea-chat/src/translatable_component.rs | 0 azalea-chat/tests/integration_test.rs | 0 azalea-client/Cargo.toml | 0 azalea-client/README.md | 0 azalea-client/src/connect.rs | 0 azalea-client/src/crypt.rs | 0 azalea-client/src/lib.rs | 0 azalea-client/src/ping.rs | 0 azalea-core/Cargo.toml | 0 azalea-core/src/difficulty.rs | 0 azalea-core/src/game_type.rs | 0 azalea-core/src/lib.rs | 0 azalea-core/src/resource_location.rs | 0 azalea-core/src/serializable_uuid.rs | 0 azalea-nbt/Cargo.toml | 0 azalea-nbt/README.md | 0 azalea-nbt/benches/my_benchmark.rs | 0 azalea-nbt/src/decode.rs | 34 ++++++++++----------- azalea-nbt/src/encode.rs | 0 azalea-nbt/src/error.rs | 11 +++++++ azalea-nbt/src/lib.rs | 0 azalea-nbt/src/tag.rs | 0 azalea-nbt/tests/bigtest.nbt | Bin azalea-nbt/tests/complex_player.dat | Bin azalea-nbt/tests/hello_world.nbt | Bin azalea-nbt/tests/inttest.nbt | Bin azalea-nbt/tests/level.dat | Bin azalea-nbt/tests/simple_player.dat | Bin azalea-nbt/tests/stringtest.nbt | Bin azalea-nbt/tests/tests.rs | 0 azalea-protocol/Cargo.toml | 0 azalea-protocol/README.md | 0 azalea-protocol/packet-macros/Cargo.toml | 0 azalea-protocol/packet-macros/src/lib.rs | 0 azalea-protocol/src/connect.rs | 0 azalea-protocol/src/lib.rs | 0 azalea-protocol/src/mc_buf/mod.rs | 0 azalea-protocol/src/mc_buf/read.rs | 0 azalea-protocol/src/mc_buf/write.rs | 0 .../game/clientbound_change_difficulty_packet.rs | 0 .../game/clientbound_custom_payload_packet.rs | 0 .../game/clientbound_declare_commands_packet.rs | 0 .../src/packets/game/clientbound_login_packet.rs | 0 .../game/clientbound_player_abilities_packet.rs | 0 .../game/clientbound_set_carried_item_packet.rs | 0 .../packets/game/clientbound_update_tags_packet.rs | 0 .../clientbound_update_view_distance_packet.rs | 0 azalea-protocol/src/packets/game/mod.rs | 0 .../packets/handshake/client_intention_packet.rs | 0 azalea-protocol/src/packets/handshake/mod.rs | 0 .../login/clientbound_custom_query_packet.rs | 0 .../login/clientbound_game_profile_packet.rs | 0 .../src/packets/login/clientbound_hello_packet.rs | 0 .../login/clientbound_login_compression_packet.rs | 0 azalea-protocol/src/packets/login/mod.rs | 0 .../src/packets/login/serverbound_hello_packet.rs | 0 azalea-protocol/src/packets/mod.rs | 0 .../status/clientbound_status_response_packet.rs | 0 azalea-protocol/src/packets/status/mod.rs | 0 .../status/serverbound_status_request_packet.rs | 0 azalea-protocol/src/read.rs | 0 azalea-protocol/src/resolver.rs | 0 azalea-protocol/src/write.rs | 0 bot/Cargo.toml | 0 bot/src/main.rs | 0 131 files changed, 28 insertions(+), 17 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 .gitpod.yml mode change 100644 => 100755 .vscode/settings.json mode change 100644 => 100755 Cargo.lock mode change 100644 => 100755 Cargo.toml mode change 100644 => 100755 README.md mode change 100644 => 100755 azalea-auth/Cargo.toml mode change 100644 => 100755 azalea-auth/src/game_profile.rs mode change 100644 => 100755 azalea-auth/src/lib.rs mode change 100644 => 100755 azalea-brigadier/Cargo.toml mode change 100644 => 100755 azalea-brigadier/README.md mode change 100644 => 100755 azalea-brigadier/src/arguments/argument_type.rs mode change 100644 => 100755 azalea-brigadier/src/arguments/integer_argument_type.rs mode change 100644 => 100755 azalea-brigadier/src/arguments/mod.rs mode change 100644 => 100755 azalea-brigadier/src/builder/argument_builder.rs mode change 100644 => 100755 azalea-brigadier/src/builder/literal_argument_builder.rs mode change 100644 => 100755 azalea-brigadier/src/builder/mod.rs mode change 100644 => 100755 azalea-brigadier/src/builder/required_argument_builder.rs mode change 100644 => 100755 azalea-brigadier/src/command_dispatcher.rs mode change 100644 => 100755 azalea-brigadier/src/context/command_context.rs mode change 100644 => 100755 azalea-brigadier/src/context/command_context_builder.rs mode change 100644 => 100755 azalea-brigadier/src/context/mod.rs mode change 100644 => 100755 azalea-brigadier/src/context/parsed_argument.rs mode change 100644 => 100755 azalea-brigadier/src/context/parsed_command_node.rs mode change 100644 => 100755 azalea-brigadier/src/context/string_range.rs mode change 100644 => 100755 azalea-brigadier/src/exceptions/builtin_exceptions.rs mode change 100644 => 100755 azalea-brigadier/src/exceptions/command_syntax_exception.rs mode change 100644 => 100755 azalea-brigadier/src/exceptions/mod.rs mode change 100644 => 100755 azalea-brigadier/src/lib.rs mode change 100644 => 100755 azalea-brigadier/src/message.rs mode change 100644 => 100755 azalea-brigadier/src/modifier.rs mode change 100644 => 100755 azalea-brigadier/src/parse_results.rs mode change 100644 => 100755 azalea-brigadier/src/string_reader.rs mode change 100644 => 100755 azalea-brigadier/src/tree/mod.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/bool_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/double_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/float_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/integer_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/long_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/arguments/string_argument_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/builder/argument_builder_test.rs mode change 100644 => 100755 azalea-brigadier/tests/builder/literal_argument_builder_test.rs mode change 100644 => 100755 azalea-brigadier/tests/builder/required_argument_builder_test.rs mode change 100644 => 100755 azalea-brigadier/tests/command_dispatcher_test.rs mode change 100644 => 100755 azalea-brigadier/tests/command_dispatcher_usages_test.rs mode change 100644 => 100755 azalea-brigadier/tests/command_suggestions_test.rs mode change 100644 => 100755 azalea-brigadier/tests/context/command_context_test.rs mode change 100644 => 100755 azalea-brigadier/tests/context/parsed_argument_test.rs mode change 100644 => 100755 azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs mode change 100644 => 100755 azalea-brigadier/tests/string_reader_test.rs mode change 100644 => 100755 azalea-brigadier/tests/suggestion/suggestion_test.rs mode change 100644 => 100755 azalea-brigadier/tests/suggestion/suggestions_builder_test.rs mode change 100644 => 100755 azalea-brigadier/tests/suggestion/suggestions_test.rs mode change 100644 => 100755 azalea-brigadier/tests/tree/abstract_command_node_test.rs mode change 100644 => 100755 azalea-brigadier/tests/tree/argument_command_node_test.rs mode change 100644 => 100755 azalea-brigadier/tests/tree/literal_command_node_test.rs mode change 100644 => 100755 azalea-brigadier/tests/tree/root_command_node_test.rs mode change 100644 => 100755 azalea-chat/Cargo.toml mode change 100644 => 100755 azalea-chat/README.md mode change 100644 => 100755 azalea-chat/src/base_component.rs mode change 100644 => 100755 azalea-chat/src/component.rs mode change 100644 => 100755 azalea-chat/src/events.rs mode change 100644 => 100755 azalea-chat/src/lib.rs mode change 100644 => 100755 azalea-chat/src/style.rs mode change 100644 => 100755 azalea-chat/src/text_component.rs mode change 100644 => 100755 azalea-chat/src/translatable_component.rs mode change 100644 => 100755 azalea-chat/tests/integration_test.rs mode change 100644 => 100755 azalea-client/Cargo.toml mode change 100644 => 100755 azalea-client/README.md mode change 100644 => 100755 azalea-client/src/connect.rs mode change 100644 => 100755 azalea-client/src/crypt.rs mode change 100644 => 100755 azalea-client/src/lib.rs mode change 100644 => 100755 azalea-client/src/ping.rs mode change 100644 => 100755 azalea-core/Cargo.toml mode change 100644 => 100755 azalea-core/src/difficulty.rs mode change 100644 => 100755 azalea-core/src/game_type.rs mode change 100644 => 100755 azalea-core/src/lib.rs mode change 100644 => 100755 azalea-core/src/resource_location.rs mode change 100644 => 100755 azalea-core/src/serializable_uuid.rs mode change 100644 => 100755 azalea-nbt/Cargo.toml mode change 100644 => 100755 azalea-nbt/README.md mode change 100644 => 100755 azalea-nbt/benches/my_benchmark.rs mode change 100644 => 100755 azalea-nbt/src/decode.rs mode change 100644 => 100755 azalea-nbt/src/encode.rs mode change 100644 => 100755 azalea-nbt/src/error.rs mode change 100644 => 100755 azalea-nbt/src/lib.rs mode change 100644 => 100755 azalea-nbt/src/tag.rs mode change 100644 => 100755 azalea-nbt/tests/bigtest.nbt mode change 100644 => 100755 azalea-nbt/tests/complex_player.dat mode change 100644 => 100755 azalea-nbt/tests/hello_world.nbt mode change 100644 => 100755 azalea-nbt/tests/inttest.nbt mode change 100644 => 100755 azalea-nbt/tests/level.dat mode change 100644 => 100755 azalea-nbt/tests/simple_player.dat mode change 100644 => 100755 azalea-nbt/tests/stringtest.nbt mode change 100644 => 100755 azalea-nbt/tests/tests.rs mode change 100644 => 100755 azalea-protocol/Cargo.toml mode change 100644 => 100755 azalea-protocol/README.md mode change 100644 => 100755 azalea-protocol/packet-macros/Cargo.toml mode change 100644 => 100755 azalea-protocol/packet-macros/src/lib.rs mode change 100644 => 100755 azalea-protocol/src/connect.rs mode change 100644 => 100755 azalea-protocol/src/lib.rs mode change 100644 => 100755 azalea-protocol/src/mc_buf/mod.rs mode change 100644 => 100755 azalea-protocol/src/mc_buf/read.rs mode change 100644 => 100755 azalea-protocol/src/mc_buf/write.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_login_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/game/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/handshake/client_intention_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/handshake/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/clientbound_hello_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/login/serverbound_hello_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/status/clientbound_status_response_packet.rs mode change 100644 => 100755 azalea-protocol/src/packets/status/mod.rs mode change 100644 => 100755 azalea-protocol/src/packets/status/serverbound_status_request_packet.rs mode change 100644 => 100755 azalea-protocol/src/read.rs mode change 100644 => 100755 azalea-protocol/src/resolver.rs mode change 100644 => 100755 azalea-protocol/src/write.rs mode change 100644 => 100755 bot/Cargo.toml mode change 100644 => 100755 bot/src/main.rs (limited to 'azalea-brigadier/src/context/command_context_builder.rs') diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.gitpod.yml b/.gitpod.yml old mode 100644 new mode 100755 diff --git a/.vscode/settings.json b/.vscode/settings.json old mode 100644 new mode 100755 diff --git a/Cargo.lock b/Cargo.lock old mode 100644 new mode 100755 diff --git a/Cargo.toml b/Cargo.toml old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-auth/src/game_profile.rs b/azalea-auth/src/game_profile.rs old mode 100644 new mode 100755 diff --git a/azalea-auth/src/lib.rs b/azalea-auth/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-brigadier/README.md b/azalea-brigadier/README.md old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/builder/mod.rs b/azalea-brigadier/src/builder/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/parsed_argument.rs b/azalea-brigadier/src/context/parsed_argument.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/bool_argument_type_test.rs b/azalea-brigadier/tests/arguments/bool_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/double_argument_type_test.rs b/azalea-brigadier/tests/arguments/double_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/float_argument_type_test.rs b/azalea-brigadier/tests/arguments/float_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/integer_argument_type_test.rs b/azalea-brigadier/tests/arguments/integer_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/long_argument_type_test.rs b/azalea-brigadier/tests/arguments/long_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/arguments/string_argument_type_test.rs b/azalea-brigadier/tests/arguments/string_argument_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/builder/argument_builder_test.rs b/azalea-brigadier/tests/builder/argument_builder_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/builder/literal_argument_builder_test.rs b/azalea-brigadier/tests/builder/literal_argument_builder_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/builder/required_argument_builder_test.rs b/azalea-brigadier/tests/builder/required_argument_builder_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/command_suggestions_test.rs b/azalea-brigadier/tests/command_suggestions_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/context/command_context_test.rs b/azalea-brigadier/tests/context/command_context_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/context/parsed_argument_test.rs b/azalea-brigadier/tests/context/parsed_argument_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs b/azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/suggestion/suggestion_test.rs b/azalea-brigadier/tests/suggestion/suggestion_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs b/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/suggestion/suggestions_test.rs b/azalea-brigadier/tests/suggestion/suggestions_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/tree/abstract_command_node_test.rs b/azalea-brigadier/tests/tree/abstract_command_node_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/tree/argument_command_node_test.rs b/azalea-brigadier/tests/tree/argument_command_node_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/tree/literal_command_node_test.rs b/azalea-brigadier/tests/tree/literal_command_node_test.rs old mode 100644 new mode 100755 diff --git a/azalea-brigadier/tests/tree/root_command_node_test.rs b/azalea-brigadier/tests/tree/root_command_node_test.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/Cargo.toml b/azalea-chat/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-chat/README.md b/azalea-chat/README.md old mode 100644 new mode 100755 diff --git a/azalea-chat/src/base_component.rs b/azalea-chat/src/base_component.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/component.rs b/azalea-chat/src/component.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/events.rs b/azalea-chat/src/events.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/lib.rs b/azalea-chat/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/style.rs b/azalea-chat/src/style.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/text_component.rs b/azalea-chat/src/text_component.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/src/translatable_component.rs b/azalea-chat/src/translatable_component.rs old mode 100644 new mode 100755 diff --git a/azalea-chat/tests/integration_test.rs b/azalea-chat/tests/integration_test.rs old mode 100644 new mode 100755 diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-client/README.md b/azalea-client/README.md old mode 100644 new mode 100755 diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs old mode 100644 new mode 100755 diff --git a/azalea-client/src/crypt.rs b/azalea-client/src/crypt.rs old mode 100644 new mode 100755 diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-client/src/ping.rs b/azalea-client/src/ping.rs old mode 100644 new mode 100755 diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-core/src/difficulty.rs b/azalea-core/src/difficulty.rs old mode 100644 new mode 100755 diff --git a/azalea-core/src/game_type.rs b/azalea-core/src/game_type.rs old mode 100644 new mode 100755 diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs old mode 100644 new mode 100755 diff --git a/azalea-core/src/serializable_uuid.rs b/azalea-core/src/serializable_uuid.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-nbt/README.md b/azalea-nbt/README.md old mode 100644 new mode 100755 diff --git a/azalea-nbt/benches/my_benchmark.rs b/azalea-nbt/benches/my_benchmark.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/src/decode.rs b/azalea-nbt/src/decode.rs old mode 100644 new mode 100755 index 41689a46..e4968811 --- a/azalea-nbt/src/decode.rs +++ b/azalea-nbt/src/decode.rs @@ -11,13 +11,13 @@ async fn read_string(stream: &mut R) -> Result where R: AsyncRead + std::marker::Unpin, { - let length = stream.read_u16().await.map_err(|_| Error::InvalidTag)?; + let length = stream.read_u16().await?; let mut buf = Vec::with_capacity(length as usize); for _ in 0..length { - buf.push(stream.read_u8().await.map_err(|_| Error::InvalidTag)?); + buf.push(stream.read_u8().await?); } - String::from_utf8(buf).map_err(|_| Error::InvalidTag) + Ok(String::from_utf8(buf)?) } impl Tag { @@ -31,26 +31,26 @@ impl Tag { // a TAG_Compound, and is not named despite being in a TAG_Compound 0 => Tag::End, // A single signed byte - 1 => Tag::Byte(stream.read_i8().await.map_err(|_| Error::InvalidTag)?), + 1 => Tag::Byte(stream.read_i8().await?), // A single signed, big endian 16 bit integer - 2 => Tag::Short(stream.read_i16().await.map_err(|_| Error::InvalidTag)?), + 2 => Tag::Short(stream.read_i16().await?), // A single signed, big endian 32 bit integer - 3 => Tag::Int(stream.read_i32().await.map_err(|_| Error::InvalidTag)?), + 3 => Tag::Int(stream.read_i32().await?), // A single signed, big endian 64 bit integer - 4 => Tag::Long(stream.read_i64().await.map_err(|_| Error::InvalidTag)?), + 4 => Tag::Long(stream.read_i64().await?), // A single, big endian IEEE-754 single-precision floating point // number (NaN possible) - 5 => Tag::Float(stream.read_f32().await.map_err(|_| Error::InvalidTag)?), + 5 => Tag::Float(stream.read_f32().await?), // A single, big endian IEEE-754 double-precision floating point // number (NaN possible) - 6 => Tag::Double(stream.read_f64().await.map_err(|_| Error::InvalidTag)?), + 6 => Tag::Double(stream.read_f64().await?), // A length-prefixed array of signed bytes. The prefix is a signed // integer (thus 4 bytes) 7 => { - let length = stream.read_i32().await.map_err(|_| Error::InvalidTag)?; + let length = stream.read_i32().await?; let mut bytes = Vec::with_capacity(length as usize); for _ in 0..length { - bytes.push(stream.read_i8().await.map_err(|_| Error::InvalidTag)?); + bytes.push(stream.read_i8().await?); } Tag::ByteArray(bytes) } @@ -67,8 +67,8 @@ impl Tag { // another reference implementation by Mojang uses 1 instead; // parsers should accept any type if the length is <= 0). 9 => { - let type_id = stream.read_u8().await.map_err(|_| Error::InvalidTag)?; - let length = stream.read_i32().await.map_err(|_| Error::InvalidTag)?; + let type_id = stream.read_u8().await?; + let length = stream.read_i32().await?; let mut list = Vec::with_capacity(length as usize); for _ in 0..length { list.push(Tag::read_known(stream, type_id).await?); @@ -94,20 +94,20 @@ impl Tag { // signed integer (thus 4 bytes) and indicates the number of 4 byte // integers. 11 => { - let length = stream.read_i32().await.map_err(|_| Error::InvalidTag)?; + let length = stream.read_i32().await?; let mut ints = Vec::with_capacity(length as usize); for _ in 0..length { - ints.push(stream.read_i32().await.map_err(|_| Error::InvalidTag)?); + ints.push(stream.read_i32().await?); } Tag::IntArray(ints) } // A length-prefixed array of signed longs. The prefix is a signed // integer (thus 4 bytes) and indicates the number of 8 byte longs. 12 => { - let length = stream.read_i32().await.map_err(|_| Error::InvalidTag)?; + let length = stream.read_i32().await?; let mut longs = Vec::with_capacity(length as usize); for _ in 0..length { - longs.push(stream.read_i64().await.map_err(|_| Error::InvalidTag)?); + longs.push(stream.read_i64().await?); } Tag::LongArray(longs) } diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/src/error.rs b/azalea-nbt/src/error.rs old mode 100644 new mode 100755 index 05ff15e0..278d2770 --- a/azalea-nbt/src/error.rs +++ b/azalea-nbt/src/error.rs @@ -14,3 +14,14 @@ impl std::fmt::Display for Error { } } } + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Error::WriteError + } +} +impl From for Error { + fn from(err: std::string::FromUtf8Error) -> Self { + Error::WriteError + } +} \ No newline at end of file diff --git a/azalea-nbt/src/lib.rs b/azalea-nbt/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/bigtest.nbt b/azalea-nbt/tests/bigtest.nbt old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/complex_player.dat b/azalea-nbt/tests/complex_player.dat old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/hello_world.nbt b/azalea-nbt/tests/hello_world.nbt old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/inttest.nbt b/azalea-nbt/tests/inttest.nbt old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/level.dat b/azalea-nbt/tests/level.dat old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/simple_player.dat b/azalea-nbt/tests/simple_player.dat old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/stringtest.nbt b/azalea-nbt/tests/stringtest.nbt old mode 100644 new mode 100755 diff --git a/azalea-nbt/tests/tests.rs b/azalea-nbt/tests/tests.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-protocol/README.md b/azalea-protocol/README.md old mode 100644 new mode 100755 diff --git a/azalea-protocol/packet-macros/Cargo.toml b/azalea-protocol/packet-macros/Cargo.toml old mode 100644 new mode 100755 diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/mc_buf/mod.rs b/azalea-protocol/src/mc_buf/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs b/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_login_packet.rs b/azalea-protocol/src/packets/game/clientbound_login_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/handshake/client_intention_packet.rs b/azalea-protocol/src/packets/handshake/client_intention_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/handshake/mod.rs b/azalea-protocol/src/packets/handshake/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/clientbound_hello_packet.rs b/azalea-protocol/src/packets/login/clientbound_hello_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs b/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/status/mod.rs b/azalea-protocol/src/packets/status/mod.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/read.rs b/azalea-protocol/src/read.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/resolver.rs b/azalea-protocol/src/resolver.rs old mode 100644 new mode 100755 diff --git a/azalea-protocol/src/write.rs b/azalea-protocol/src/write.rs old mode 100644 new mode 100755 diff --git a/bot/Cargo.toml b/bot/Cargo.toml old mode 100644 new mode 100755 diff --git a/bot/src/main.rs b/bot/src/main.rs old mode 100644 new mode 100755 -- cgit v1.2.3