From a5e7ff771d657258cedcc7a8b3ce265c655f0860 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 30 May 2025 20:07:28 -0330 Subject: implement missing brigadier features and cleanup some more --- azalea-brigadier/src/arguments/argument_type.rs | 4 +- .../src/arguments/bool_argument_type.rs | 4 +- .../src/arguments/double_argument_type.rs | 8 +- .../src/arguments/float_argument_type.rs | 8 +- .../src/arguments/integer_argument_type.rs | 8 +- .../src/arguments/long_argument_type.rs | 8 +- .../src/arguments/string_argument_type.rs | 6 +- azalea-brigadier/src/builder/argument_builder.rs | 11 ++ .../src/builder/required_argument_builder.rs | 4 +- azalea-brigadier/src/command_dispatcher.rs | 119 ++++----------- azalea-brigadier/src/context/command_context.rs | 72 +++++++-- azalea-brigadier/src/context/context_chain.rs | 169 +++++++++++++++++++++ azalea-brigadier/src/context/mod.rs | 2 + azalea-brigadier/src/errors/builtin_errors.rs | 153 +++++++++++++++++++ .../src/errors/command_syntax_error.rs | 93 ++++++++++++ azalea-brigadier/src/errors/mod.rs | 5 + .../src/exceptions/builtin_exceptions.rs | 153 ------------------- .../src/exceptions/command_syntax_exception.rs | 94 ------------ azalea-brigadier/src/exceptions/mod.rs | 5 - azalea-brigadier/src/lib.rs | 3 +- azalea-brigadier/src/modifier.rs | 4 +- azalea-brigadier/src/parse_results.rs | 6 +- azalea-brigadier/src/result_consumer.rs | 12 ++ azalea-brigadier/src/string_reader.rs | 53 +++---- azalea-brigadier/src/tree/mod.rs | 9 +- azalea-brigadier/tests/bevy_app_usage.rs | 4 +- azalea-brigadier/tests/command_dispatcher_test.rs | 50 ++---- azalea-brigadier/tests/string_reader_test.rs | 52 +++---- 28 files changed, 633 insertions(+), 486 deletions(-) create mode 100644 azalea-brigadier/src/context/context_chain.rs create mode 100644 azalea-brigadier/src/errors/builtin_errors.rs create mode 100644 azalea-brigadier/src/errors/command_syntax_error.rs create mode 100644 azalea-brigadier/src/errors/mod.rs delete mode 100644 azalea-brigadier/src/exceptions/builtin_exceptions.rs delete mode 100644 azalea-brigadier/src/exceptions/command_syntax_exception.rs delete mode 100644 azalea-brigadier/src/exceptions/mod.rs create mode 100644 azalea-brigadier/src/result_consumer.rs diff --git a/azalea-brigadier/src/arguments/argument_type.rs b/azalea-brigadier/src/arguments/argument_type.rs index d7bfa7d6..45859538 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -1,13 +1,13 @@ use std::{any::Any, sync::Arc}; use crate::{ - exceptions::CommandSyntaxException, + errors::CommandSyntaxError, string_reader::StringReader, suggestion::{Suggestions, SuggestionsBuilder}, }; pub trait ArgumentType { - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxError>; fn list_suggestions(&self, _builder: SuggestionsBuilder) -> Suggestions { Suggestions::default() diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index efb86509..225e985e 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc}; use super::ArgumentType; use crate::{ context::CommandContext, - exceptions::CommandSyntaxException, + errors::CommandSyntaxError, string_reader::StringReader, suggestion::{Suggestions, SuggestionsBuilder}, }; @@ -12,7 +12,7 @@ use crate::{ struct Boolean; impl ArgumentType for Boolean { - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxError> { Ok(Arc::new(reader.read_boolean()?)) } diff --git a/azalea-brigadier/src/arguments/double_argument_type.rs b/azalea-brigadier/src/arguments/double_argument_type.rs index 2e50d291..5b5d8cce 100644 --- a/azalea-brigadier/src/arguments/double_argument_type.rs +++ b/azalea-brigadier/src/arguments/double_argument_type.rs @@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc}; use super::ArgumentType; use crate::{ context::CommandContext, - exceptions::{BuiltInExceptions, CommandSyntaxException}, + errors::{BuiltInError, CommandSyntaxError}, string_reader::StringReader, }; @@ -14,14 +14,14 @@ struct Double { } impl ArgumentType for Double { - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxError> { let start = reader.cursor; let result = reader.read_double()?; if let Some(minimum) = self.minimum && result < minimum { reader.cursor = start; - return Err(BuiltInExceptions::DoubleTooSmall { + return Err(BuiltInError::DoubleTooSmall { found: result, min: minimum, } @@ -31,7 +31,7 @@ impl ArgumentType for Double { && result > maximum { reader.cursor = start; - return Err(BuiltInExceptions::DoubleTooBig { + return Err(BuiltInError::DoubleTooBig { found: result, max: maximum, } diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs index 23dc88a5..8ea3a417 100644 --- a/azalea-brigadier/src/arguments/float_argument_type.rs +++ b/azalea-brigadier/src/arguments/float_argument_type.rs @@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc}; use super::ArgumentType; use crate::{ context::CommandContext, - exceptions::{BuiltInExceptions, CommandSyntaxException}, + errors::{BuiltInError, CommandSyntaxError}, string_reader::StringReader, }; @@ -14,14 +14,14 @@ struct Float { } impl ArgumentType for Float { - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxError> { let start = reader.cursor; let result = reader.read_float()?; if let Some(minimum) = self.minimum && result < minimum { reader.cursor = start; - return Err(BuiltInExceptions::FloatTooSmall { + return Err(BuiltInError::FloatTooSmall { found: result, min: minimum, } @@ -31,7 +31,7 @@ impl ArgumentType for Float { && result > maximum { reader.cursor = start; - return Err(BuiltInExceptions::FloatTooBig { + return Err(BuiltInError::FloatTooBig { found: result, max: maximum, } diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs index b993d200..9cdb9c34 100644 --- a/azalea-brigadier/src/arguments/integer_argument_type.rs +++ b/azalea-brigadier/src/arguments/integer_argument_type.rs @@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc}; use super::ArgumentType; use crate::{ context::CommandContext, - exceptions::{BuiltInExceptions, CommandSyntaxException}, + errors::{BuiltInError, CommandSyntaxError}, string_reader::StringReader, }; @@ -14,14 +14,14 @@ struct Integer { } impl ArgumentType for Integer { - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxError> { let start = reader.cursor; let result = reader.read_int()?; if let Some(minimum) = self.minimum && result < minimum { reader.cursor = start; - return Err(BuiltInExceptions::IntegerTooSmall { + return Err(BuiltInError::IntegerTooSmall { found: result, min: minimum, } @@ -31,7 +31,7 @@ impl ArgumentType for Integer { && result > maximum { reader.cursor = start; - return Err(BuiltInExceptions::IntegerTooBig { + return Err(BuiltInError::IntegerTooBig { found: result, max: maximum, } diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs index 1e27cf9d..1d4b3c9b 100644 --- a/azalea-brigadier/src/arguments/long_argument_type.rs +++ b/azalea-brigadier/src/arguments/long_argument_type.rs @@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc}; use super::ArgumentType; use crate::{ context::CommandContext, - exceptions::{BuiltInExceptions, CommandSyntaxException}, + errors::{BuiltInError, CommandSyntaxError}, string_reader::StringReader, }; @@ -14,14 +14,14 @@ struct Long { } impl ArgumentType for Long { - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxError> { let start = reader.cursor; let result = reader.read_long()?; if let Some(minimum) = self.minimum && result < minimum { reader.cursor = start; - return Err(BuiltInExceptions::LongTooSmall { + return Err(BuiltInError::LongTooSmall { found: result, min: minimum, } @@ -31,7 +31,7 @@ impl ArgumentType for Long { && result > maximum { reader.cursor = start; - return Err(BuiltInExceptions::LongTooBig { + return Err(BuiltInError::LongTooBig { found: result, max: maximum, } diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs index 96b9c998..bcb040d6 100644 --- a/azalea-brigadier/src/arguments/string_argument_type.rs +++ b/azalea-brigadier/src/arguments/string_argument_type.rs @@ -1,9 +1,7 @@ use std::{any::Any, sync::Arc}; use super::ArgumentType; -use crate::{ - context::CommandContext, exceptions::CommandSyntaxException, string_reader::StringReader, -}; +use crate::{context::CommandContext, errors::CommandSyntaxError, string_reader::StringReader}; pub enum StringArgument { /// Match up until the next space. @@ -16,7 +14,7 @@ pub enum StringArgument { } impl ArgumentType for StringArgument { - fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxError> { let result = match self { StringArgument::SingleWord => reader.read_unquoted_string().to_string(), StringArgument::QuotablePhrase => reader.read_string()?, diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index bc7f0ac6..1731d44d 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -5,6 +5,7 @@ use parking_lot::RwLock; use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; use crate::{ context::CommandContext, + errors::CommandSyntaxError, modifier::RedirectModifier, tree::{Command, CommandNode}, }; @@ -89,6 +90,16 @@ impl ArgumentBuilder { pub fn executes(mut self, f: F) -> Self where F: Fn(&CommandContext) -> i32 + Send + Sync + 'static, + { + self.command = Some(Arc::new(move |ctx: &CommandContext| Ok(f(ctx)))); + self + } + + /// Same as [`Self::executes`] but returns a `Result`. + pub fn executes_result(mut self, f: F) -> Self + where + F: Fn(&CommandContext) -> Result + Send + Sync + 'static, { self.command = Some(Arc::new(f)); self diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 7c0f1015..3fc33143 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -8,7 +8,7 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; use crate::{ arguments::ArgumentType, context::CommandContext, - exceptions::CommandSyntaxException, + errors::CommandSyntaxError, string_reader::StringReader, suggestion::{SuggestionProvider, Suggestions, SuggestionsBuilder}, }; @@ -33,7 +33,7 @@ impl Argument { } } - pub fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { + pub fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxError> { self.parser.parse(reader) } diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index 4a3b51d7..df732291 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,7 +1,7 @@ use std::{ cmp::Ordering, collections::{HashMap, HashSet}, - mem, ptr, + ptr, rc::Rc, sync::Arc, }; @@ -10,9 +10,10 @@ use parking_lot::RwLock; use crate::{ builder::argument_builder::ArgumentBuilder, - context::{CommandContext, CommandContextBuilder}, - exceptions::{BuiltInExceptions, CommandSyntaxException}, + context::{CommandContextBuilder, ContextChain}, + errors::{BuiltInError, CommandSyntaxError}, parse_results::ParseResults, + result_consumer::{DefaultResultConsumer, ResultConsumer}, string_reader::StringReader, suggestion::{Suggestions, SuggestionsBuilder}, tree::CommandNode, @@ -30,12 +31,14 @@ where Self: Sync + Send, { pub root: Arc>>, + consumer: Box + Send + Sync>, } impl CommandDispatcher { pub fn new() -> Self { Self { root: Arc::new(RwLock::new(CommandNode::default())), + consumer: Box::new(DefaultResultConsumer), } } @@ -64,10 +67,10 @@ impl CommandDispatcher { node: &Arc>>, original_reader: &StringReader, context_so_far: CommandContextBuilder<'a, S>, - ) -> Result, CommandSyntaxException> { + ) -> Result, CommandSyntaxError> { let source = context_so_far.source.clone(); #[allow(clippy::mutable_key_type)] // this is fine because we don't mutate the key - let mut errors = HashMap::>, CommandSyntaxException>::new(); + let mut errors = HashMap::>, CommandSyntaxError>::new(); let mut potentials: Vec> = vec![]; let cursor = original_reader.cursor(); @@ -83,7 +86,7 @@ impl CommandDispatcher { if let Err(ex) = parse_with_context_result { errors.insert( Rc::new((*child.read()).clone()), - BuiltInExceptions::DispatcherParseException { + BuiltInError::DispatcherParseException { message: ex.message(), } .create_with_context(&reader), @@ -94,8 +97,7 @@ impl CommandDispatcher { if reader.can_read() && reader.peek() != ' ' { errors.insert( Rc::new((*child.read()).clone()), - BuiltInExceptions::DispatcherExpectedArgumentSeparator - .create_with_context(&reader), + BuiltInError::DispatcherExpectedArgumentSeparator.create_with_context(&reader), ); reader.cursor = cursor; continue; @@ -179,11 +181,11 @@ impl CommandDispatcher { &self, input: impl Into, source: S, - ) -> Result { + ) -> Result { let input = input.into(); let parse = self.parse(input, source); - Self::execute_parsed(parse) + self.execute_parsed(parse) } pub fn add_paths( @@ -235,91 +237,26 @@ impl CommandDispatcher { } /// Executes a given pre-parsed command. - pub fn execute_parsed(parse: ParseResults) -> Result { + pub fn execute_parsed(&self, 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 { - 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(context); - match results { - Ok(results) => { - if !results.is_empty() { - next.extend( - results.iter().map(|s| child.copy_for(s.clone())), - ); - } - } - _ => { - // 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(); + return Err(if parse.exceptions.len() == 1 { + parse.exceptions.values().next().unwrap().clone() + } else if parse.context.range.is_empty() { + BuiltInError::DispatcherUnknownCommand.create_with_context(&parse.reader) + } else { + BuiltInError::DispatcherUnknownArgument.create_with_context(&parse.reader) + }); } - if !found_command { - // consumer.on_command_complete(original, false, 0); - return Err( - BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader) - ); - } + let command = parse.reader.string(); + let original = Rc::new(parse.context.build(command)); + let flat_context = ContextChain::try_flatten(original.clone()); + let Some(flat_context) = flat_context else { + self.consumer.on_command_complete(original, false, 0); + return Err(BuiltInError::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 }) + flat_context.execute_all(original.source().clone(), self.consumer.as_ref()) } pub fn get_all_usage( diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 202d6f21..224f2d63 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -10,16 +10,16 @@ use crate::{ /// A built `CommandContextBuilder`. pub struct CommandContext { - pub source: Arc, - pub input: String, - pub arguments: HashMap, - pub command: Command, - pub root_node: Arc>>, - pub nodes: Vec>, - pub range: StringRange, - pub child: Option>>, - pub modifier: Option>>, - pub forks: bool, + pub(super) source: Arc, + pub(super) input: String, + pub(super) arguments: HashMap, + pub(super) command: Command, + pub(super) root_node: Arc>>, + pub(super) nodes: Vec>, + pub(super) range: StringRange, + pub(super) child: Option>>, + pub(super) modifier: Option>>, + pub(super) forks: bool, } impl Clone for CommandContext { @@ -59,8 +59,10 @@ impl Debug for CommandContext { impl CommandContext { pub fn copy_for(&self, source: Arc) -> Self { if Arc::ptr_eq(&source, &self.source) { + // fast path return self.clone(); } + CommandContext { source, input: self.input.clone(), @@ -75,12 +77,56 @@ impl CommandContext { } } + pub fn child(&self) -> Option<&CommandContext> { + self.child.as_ref().map(|c| c.as_ref()) + } + + pub fn last_child(&self) -> &CommandContext { + let mut result = self; + while let Some(child) = result.child() { + result = child; + } + result + } + + pub fn command(&self) -> &Command { + &self.command + } + + pub fn source(&self) -> &Arc { + &self.source + } + + pub fn argument(&self, name: &str) -> Option<&dyn Any> { + let argument = self.arguments.get(name); + argument.map(|a| a.result.as_ref()) + } + + pub fn redirect_modifier(&self) -> Option<&RedirectModifier> { + self.modifier.as_ref().map(|m| m.as_ref()) + } + + pub fn range(&self) -> &StringRange { + &self.range + } + + pub fn input(&self) -> &str { + &self.input + } + + pub fn root_node(&self) -> &Arc>> { + &self.root_node + } + + pub fn nodes(&self) -> &[ParsedCommandNode] { + &self.nodes + } + 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()) + pub fn is_forked(&self) -> bool { + self.forks } } diff --git a/azalea-brigadier/src/context/context_chain.rs b/azalea-brigadier/src/context/context_chain.rs new file mode 100644 index 00000000..74fe6e01 --- /dev/null +++ b/azalea-brigadier/src/context/context_chain.rs @@ -0,0 +1,169 @@ +use std::{rc::Rc, sync::Arc}; + +use super::CommandContext; +use crate::{errors::CommandSyntaxError, result_consumer::ResultConsumer}; + +pub struct ContextChain { + modifiers: Vec>>, + executable: Rc>, + next_stage_cache: Option>>, +} + +impl ContextChain { + pub fn new(modifiers: Vec>>, executable: Rc>) -> Self { + if executable.command.is_none() { + panic!("Last command in chain must be executable"); + } + Self { + modifiers, + executable, + next_stage_cache: None, + } + } + + pub fn try_flatten(root_context: Rc>) -> Option { + let mut modifiers = Vec::new(); + let mut current = root_context; + loop { + let child = current.child.clone(); + let Some(child) = child else { + // Last entry must be executable command + if current.command.is_none() { + return None; + } + + return Some(ContextChain::new(modifiers, current)); + }; + + modifiers.push(current); + current = child; + } + } + + pub fn run_modifier( + modifier: Rc>, + source: Arc, + result_consumer: &dyn ResultConsumer, + forked_mode: bool, + ) -> Result>, CommandSyntaxError> { + let source_modifier = modifier.redirect_modifier(); + let Some(source_modifier) = source_modifier else { + return Ok(vec![source]); + }; + + let context_to_use = Rc::new(modifier.copy_for(source)); + let err = match (source_modifier)(&context_to_use) { + Ok(res) => return Ok(res), + Err(e) => e, + }; + + result_consumer.on_command_complete(context_to_use, false, 0); + if forked_mode { + return Ok(vec![]); + } + Err(err) + } + + pub fn run_executable( + &self, + executable: Rc>, + source: Arc, + result_consumer: &dyn ResultConsumer, + forked_mode: bool, + ) -> Result { + let context_to_use = Rc::new(executable.copy_for(source)); + let Some(command) = &executable.command else { + unimplemented!(); + }; + + let err = match (command)(&context_to_use) { + Ok(result) => { + result_consumer.on_command_complete(context_to_use, true, result); + return if forked_mode { Ok(1) } else { Ok(result) }; + } + Err(err) => err, + }; + + result_consumer.on_command_complete(context_to_use, false, 0); + if forked_mode { Ok(0) } else { Err(err) } + } + + pub fn execute_all( + &self, + source: Arc, + result_consumer: &(dyn ResultConsumer), + ) -> Result { + if self.modifiers.is_empty() { + return self.run_executable(self.executable.clone(), source, result_consumer, false); + } + + let mut forked_mode = false; + let mut current_sources = vec![source]; + + for modifier in &self.modifiers { + forked_mode |= modifier.is_forked(); + + let mut next_sources = Vec::new(); + for source_to_run in current_sources { + next_sources.extend(Self::run_modifier( + modifier.clone(), + source_to_run.clone(), + result_consumer, + forked_mode, + )?); + } + if next_sources.is_empty() { + return Ok(0); + } + current_sources = next_sources; + } + + let mut result = 0; + for execution_source in current_sources { + result += self.run_executable( + self.executable.clone(), + execution_source, + result_consumer, + forked_mode, + )?; + } + + Ok(result) + } + + pub fn stage(&self) -> Stage { + if self.modifiers.is_empty() { + Stage::Execute + } else { + Stage::Modify + } + } + + pub fn top_context(&self) -> Rc> { + self.modifiers + .first() + .cloned() + .unwrap_or_else(|| self.executable.clone()) + } + + pub fn next_stage(&mut self) -> Option>> { + let modifier_count = self.modifiers.len(); + if modifier_count == 0 { + return None; + } + + if self.next_stage_cache.is_none() { + self.next_stage_cache = Some(Rc::new(ContextChain::new( + self.modifiers[1..].to_vec(), + self.executable.clone(), + ))); + } + + self.next_stage_cache.clone() + } +} + +pub enum Stage { + Modify, + Execute, +} diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs index 28e1a12e..815892bf 100644 --- a/azalea-brigadier/src/context/mod.rs +++ b/azalea-brigadier/src/context/mod.rs @@ -1,5 +1,6 @@ mod command_context; mod command_context_builder; +mod context_chain; mod parsed_argument; mod parsed_command_node; mod string_range; @@ -7,6 +8,7 @@ pub mod suggestion_context; pub use command_context::CommandContext; pub use command_context_builder::CommandContextBuilder; +pub use context_chain::ContextChain; pub use parsed_argument::ParsedArgument; pub use parsed_command_node::ParsedCommandNode; pub use string_range::StringRange; diff --git a/azalea-brigadier/src/errors/builtin_errors.rs b/azalea-brigadier/src/errors/builtin_errors.rs new file mode 100644 index 00000000..36803397 --- /dev/null +++ b/azalea-brigadier/src/errors/builtin_errors.rs @@ -0,0 +1,153 @@ +use std::fmt; + +use super::command_syntax_error::CommandSyntaxError; +use crate::string_reader::StringReader; + +#[derive(Clone, PartialEq)] +pub enum BuiltInError { + DoubleTooSmall { found: f64, min: f64 }, + DoubleTooBig { found: f64, max: f64 }, + + FloatTooSmall { found: f32, min: f32 }, + FloatTooBig { found: f32, max: f32 }, + + IntegerTooSmall { found: i32, min: i32 }, + IntegerTooBig { found: i32, max: i32 }, + + LongTooSmall { found: i64, min: i64 }, + LongTooBig { found: i64, max: i64 }, + + LiteralIncorrect { expected: String }, + + ReaderExpectedStartOfQuote, + ReaderExpectedEndOfQuote, + ReaderInvalidEscape { character: char }, + ReaderInvalidBool { value: String }, + ReaderInvalidInt { value: String }, + ReaderExpectedInt, + ReaderInvalidLong { value: String }, + ReaderExpectedLong, + ReaderInvalidDouble { value: String }, + ReaderExpectedDouble, + ReaderInvalidFloat { value: String }, + ReaderExpectedFloat, + ReaderExpectedBool, + ReaderExpectedSymbol { symbol: char }, + + DispatcherUnknownCommand, + DispatcherUnknownArgument, + DispatcherExpectedArgumentSeparator, + DispatcherParseException { message: String }, +} + +impl fmt::Debug for BuiltInError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BuiltInError::DoubleTooSmall { found, min } => { + write!(f, "Double must not be less than {min}, found {found}") + } + BuiltInError::DoubleTooBig { found, max } => { + write!(f, "Double must not be more than {max}, found {found}") + } + + BuiltInError::FloatTooSmall { found, min } => { + write!(f, "Float must not be less than {min}, found {found}") + } + BuiltInError::FloatTooBig { found, max } => { + write!(f, "Float must not be more than {max}, found {found}") + } + + BuiltInError::IntegerTooSmall { found, min } => { + write!(f, "Integer must not be less than {min}, found {found}") + } + BuiltInError::IntegerTooBig { found, max } => { + write!(f, "Integer must not be more than {max}, found {found}") + } + + BuiltInError::LongTooSmall { found, min } => { + write!(f, "Long must not be less than {min}, found {found}") + } + BuiltInError::LongTooBig { found, max } => { + write!(f, "Long must not be more than {max}, found {found}") + } + + BuiltInError::LiteralIncorrect { expected } => { + write!(f, "Expected literal {expected}") + } + + BuiltInError::ReaderExpectedStartOfQuote => { + write!(f, "Expected quote to start a string") + } + BuiltInError::ReaderExpectedEndOfQuote => { + write!(f, "Unclosed quoted string") + } + BuiltInError::ReaderInvalidEscape { character } => { + write!(f, "Invalid escape sequence '{character}' in quoted string") + } + BuiltInError::ReaderInvalidBool { value } => { + write!( + f, + "Invalid bool, expected true or false but found '{value}'" + ) + } + BuiltInError::ReaderInvalidInt { value } => { + write!(f, "Invalid Integer '{value}'") + } + BuiltInError::ReaderExpectedInt => { + write!(f, "Expected Integer") + } + BuiltInError::ReaderInvalidLong { value } => { + write!(f, "Invalid long '{value}'") + } + BuiltInError::ReaderExpectedLong => { + write!(f, "Expected long") + } + BuiltInError::ReaderInvalidDouble { value } => { + write!(f, "Invalid double '{value}'") + } + BuiltInError::ReaderExpectedDouble => { + write!(f, "Expected double") + } + BuiltInError::ReaderInvalidFloat { value } => { + write!(f, "Invalid Float '{value}'") + } + BuiltInError::ReaderExpectedFloat => { + write!(f, "Expected Float") + } + BuiltInError::ReaderExpectedBool => { + write!(f, "Expected bool") + } + BuiltInError::ReaderExpectedSymbol { symbol } => { + write!(f, "Expected '{symbol}'") + } + + BuiltInError::DispatcherUnknownCommand => { + write!(f, "Unknown command") + } + BuiltInError::DispatcherUnknownArgument => { + write!(f, "Incorrect argument for command") + } + BuiltInError::DispatcherExpectedArgumentSeparator => { + write!( + f, + "Expected whitespace to end one argument, but found trailing data" + ) + } + BuiltInError::DispatcherParseException { message } => { + write!(f, "Could not parse command: {message}") + } + } + } +} + +impl BuiltInError { + pub fn create(self) -> CommandSyntaxError { + let message = format!("{self:?}"); + CommandSyntaxError::create(self, message) + } + + pub fn create_with_context(self, reader: &StringReader) -> CommandSyntaxError { + let message = format!("{self:?}"); + CommandSyntaxError::new(self, message, reader.string(), reader.cursor()) + } +} diff --git a/azalea-brigadier/src/errors/command_syntax_error.rs b/azalea-brigadier/src/errors/command_syntax_error.rs new file mode 100644 index 00000000..a476fec4 --- /dev/null +++ b/azalea-brigadier/src/errors/command_syntax_error.rs @@ -0,0 +1,93 @@ +use std::{ + cmp, + fmt::{self, Debug, Write}, +}; + +use super::builtin_errors::BuiltInError; + +#[derive(Clone, PartialEq)] +pub struct CommandSyntaxError { + kind: BuiltInError, + message: String, + input: Option, + cursor: Option, +} + +const CONTEXT_AMOUNT: usize = 10; + +impl CommandSyntaxError { + pub fn new(kind: BuiltInError, message: String, input: &str, cursor: usize) -> Self { + Self { + kind, + message, + input: Some(input.to_string()), + cursor: Some(cursor), + } + } + + pub fn create(kind: BuiltInError, message: String) -> Self { + Self { + kind, + message, + input: None, + cursor: None, + } + } + + pub fn message(&self) -> String { + let mut message = self.message.clone(); + let context = self.context(); + if let Some(context) = context { + write!( + message, + " at position {}: {context}", + self.cursor.unwrap_or(usize::MAX) + ) + .unwrap(); + } + message + } + + pub fn raw_message(&self) -> &String { + &self.message + } + + pub fn context(&self) -> Option { + if let Some(input) = &self.input + && let Some(cursor) = self.cursor + { + let mut builder = String::new(); + let cursor = cmp::min(input.len(), cursor); + + if cursor > CONTEXT_AMOUNT { + builder.push_str("..."); + } + + builder.push_str( + &input[(cmp::max(0, cursor as isize - CONTEXT_AMOUNT as isize) as usize)..cursor], + ); + builder.push_str("<--[HERE]"); + + return Some(builder); + } + None + } + + pub fn kind(&self) -> &BuiltInError { + &self.kind + } + + pub fn input(&self) -> &Option { + &self.input + } + + pub fn cursor(&self) -> Option { + self.cursor + } +} + +impl Debug for CommandSyntaxError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message()) + } +} diff --git a/azalea-brigadier/src/errors/mod.rs b/azalea-brigadier/src/errors/mod.rs new file mode 100644 index 00000000..facebe5e --- /dev/null +++ b/azalea-brigadier/src/errors/mod.rs @@ -0,0 +1,5 @@ +mod builtin_errors; +mod command_syntax_error; + +pub use builtin_errors::BuiltInError; +pub use command_syntax_error::CommandSyntaxError; diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs deleted file mode 100644 index bf2072c1..00000000 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ /dev/null @@ -1,153 +0,0 @@ -use std::fmt; - -use super::command_syntax_exception::CommandSyntaxException; -use crate::string_reader::StringReader; - -#[derive(Clone, PartialEq)] -pub enum BuiltInExceptions { - DoubleTooSmall { found: f64, min: f64 }, - DoubleTooBig { found: f64, max: f64 }, - - FloatTooSmall { found: f32, min: f32 }, - FloatTooBig { found: f32, max: f32 }, - - IntegerTooSmall { found: i32, min: i32 }, - IntegerTooBig { found: i32, max: i32 }, - - LongTooSmall { found: i64, min: i64 }, - LongTooBig { found: i64, max: i64 }, - - LiteralIncorrect { expected: String }, - - ReaderExpectedStartOfQuote, - ReaderExpectedEndOfQuote, - ReaderInvalidEscape { character: char }, - ReaderInvalidBool { value: String }, - ReaderInvalidInt { value: String }, - ReaderExpectedInt, - ReaderInvalidLong { value: String }, - ReaderExpectedLong, - ReaderInvalidDouble { value: String }, - ReaderExpectedDouble, - ReaderInvalidFloat { value: String }, - ReaderExpectedFloat, - ReaderExpectedBool, - ReaderExpectedSymbol { symbol: char }, - - DispatcherUnknownCommand, - DispatcherUnknownArgument, - DispatcherExpectedArgumentSeparator, - DispatcherParseException { message: String }, -} - -impl fmt::Debug for BuiltInExceptions { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - BuiltInExceptions::DoubleTooSmall { found, min } => { - write!(f, "Double must not be less than {min}, found {found}") - } - BuiltInExceptions::DoubleTooBig { found, max } => { - write!(f, "Double must not be more than {max}, found {found}") - } - - BuiltInExceptions::FloatTooSmall { found, min } => { - write!(f, "Float must not be less than {min}, found {found}") - } - BuiltInExceptions::FloatTooBig { found, max } => { - write!(f, "Float must not be more than {max}, found {found}") - } - - BuiltInExceptions::IntegerTooSmall { found, min } => { - write!(f, "Integer must not be less than {min}, found {found}") - } - BuiltInExceptions::IntegerTooBig { found, max } => { - write!(f, "Integer must not be more than {max}, found {found}") - } - - BuiltInExceptions::LongTooSmall { found, min } => { - write!(f, "Long must not be less than {min}, found {found}") - } - BuiltInExceptions::LongTooBig { found, max } => { - write!(f, "Long must not be more than {max}, found {found}") - } - - BuiltInExceptions::LiteralIncorrect { expected } => { - write!(f, "Expected literal {expected}") - } - - BuiltInExceptions::ReaderExpectedStartOfQuote => { - write!(f, "Expected quote to start a string") - } - BuiltInExceptions::ReaderExpectedEndOfQuote => { - write!(f, "Unclosed quoted string") - } - BuiltInExceptions::ReaderInvalidEscape { character } => { - write!(f, "Invalid escape sequence '{character}' in quoted string") - } - BuiltInExceptions::ReaderInvalidBool { value } => { - write!( - f, - "Invalid bool, expected true or false but found '{value}'" - ) - } - BuiltInExceptions::ReaderInvalidInt { value } => { - write!(f, "Invalid Integer '{value}'") - } - BuiltInExceptions::ReaderExpectedInt => { - write!(f, "Expected Integer") - } - BuiltInExceptions::ReaderInvalidLong { value } => { - write!(f, "Invalid long '{value}'") - } - BuiltInExceptions::ReaderExpectedLong => { - write!(f, "Expected long") - } - BuiltInExceptions::ReaderInvalidDouble { value } => { - write!(f, "Invalid double '{value}'") - } - BuiltInExceptions::ReaderExpectedDouble => { - write!(f, "Expected double") - } - BuiltInExceptions::ReaderInvalidFloat { value } => { - write!(f, "Invalid Float '{value}'") - } - BuiltInExceptions::ReaderExpectedFloat => { - write!(f, "Expected Float") - } - BuiltInExceptions::ReaderExpectedBool => { - write!(f, "Expected bool") - } - BuiltInExceptions::ReaderExpectedSymbol { symbol } => { - write!(f, "Expected '{symbol}'") - } - - BuiltInExceptions::DispatcherUnknownCommand => { - write!(f, "Unknown command") - } - BuiltInExceptions::DispatcherUnknownArgument => { - write!(f, "Incorrect argument for command") - } - BuiltInExceptions::DispatcherExpectedArgumentSeparator => { - write!( - f, - "Expected whitespace to end one argument, but found trailing data" - ) - } - BuiltInExceptions::DispatcherParseException { message } => { - write!(f, "Could not parse command: {message}") - } - } - } -} - -impl BuiltInExceptions { - pub fn create(self) -> CommandSyntaxException { - let message = format!("{self:?}"); - CommandSyntaxException::create(self, message) - } - - pub fn create_with_context(self, reader: &StringReader) -> CommandSyntaxException { - let message = 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 deleted file mode 100644 index c9b8134f..00000000 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::{ - cmp, - fmt::{self, Write}, -}; - -use super::builtin_exceptions::BuiltInExceptions; - -#[derive(Clone, PartialEq)] -pub struct CommandSyntaxException { - pub type_: BuiltInExceptions, - message: String, - input: Option, - cursor: Option, -} - -const CONTEXT_AMOUNT: usize = 10; - -impl CommandSyntaxException { - pub fn new(type_: BuiltInExceptions, message: String, input: &str, cursor: usize) -> Self { - Self { - type_, - message, - input: Some(input.to_string()), - cursor: Some(cursor), - } - } - - pub fn create(type_: BuiltInExceptions, message: String) -> Self { - Self { - type_, - message, - input: None, - cursor: None, - } - } - - pub fn message(&self) -> String { - let mut message = self.message.clone(); - let context = self.context(); - if let Some(context) = context { - write!( - message, - " at position {}: {}", - self.cursor.unwrap_or(usize::MAX), - context - ) - .unwrap(); - } - message - } - - pub fn raw_message(&self) -> &String { - &self.message - } - - pub fn context(&self) -> Option { - if let Some(input) = &self.input - && let Some(cursor) = self.cursor - { - let mut builder = String::new(); - let cursor = cmp::min(input.len(), cursor); - - if cursor > CONTEXT_AMOUNT { - builder.push_str("..."); - } - - builder.push_str( - &input[(cmp::max(0, cursor as isize - CONTEXT_AMOUNT as isize) as usize)..cursor], - ); - builder.push_str("<--[HERE]"); - - return Some(builder); - } - None - } - - pub fn get_type(&self) -> &BuiltInExceptions { - &self.type_ - } - - pub fn input(&self) -> &Option { - &self.input - } - - pub fn cursor(&self) -> Option { - self.cursor - } -} - -impl fmt::Debug for CommandSyntaxException { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message()) - } -} diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs deleted file mode 100644 index 6b9c8d62..00000000 --- a/azalea-brigadier/src/exceptions/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod builtin_exceptions; -mod command_syntax_exception; - -pub use builtin_exceptions::BuiltInExceptions; -pub use command_syntax_exception::CommandSyntaxException; diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index 4f704d46..28f529f2 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -4,9 +4,10 @@ pub mod arguments; pub mod builder; pub mod command_dispatcher; pub mod context; -pub mod exceptions; +pub mod errors; pub mod modifier; pub mod parse_results; +pub mod result_consumer; pub mod string_reader; pub mod suggestion; pub mod tree; diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs index bebdd0cb..aba5e95f 100644 --- a/azalea-brigadier/src/modifier.rs +++ b/azalea-brigadier/src/modifier.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::{context::CommandContext, exceptions::CommandSyntaxException}; +use crate::{context::CommandContext, errors::CommandSyntaxError}; pub type RedirectModifier = - dyn Fn(&CommandContext) -> Result>, CommandSyntaxException> + Send + Sync; + dyn Fn(&CommandContext) -> Result>, CommandSyntaxError> + Send + Sync; diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index a2cefcf7..73de8d47 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -1,14 +1,14 @@ use std::{collections::HashMap, fmt::Debug, rc::Rc}; use crate::{ - context::CommandContextBuilder, exceptions::CommandSyntaxException, - string_reader::StringReader, tree::CommandNode, + context::CommandContextBuilder, errors::CommandSyntaxError, string_reader::StringReader, + tree::CommandNode, }; pub struct ParseResults<'a, S> { pub context: CommandContextBuilder<'a, S>, pub reader: StringReader, - pub exceptions: HashMap>, CommandSyntaxException>, + pub exceptions: HashMap>, CommandSyntaxError>, } impl Debug for ParseResults<'_, S> { diff --git a/azalea-brigadier/src/result_consumer.rs b/azalea-brigadier/src/result_consumer.rs new file mode 100644 index 00000000..fb9dd135 --- /dev/null +++ b/azalea-brigadier/src/result_consumer.rs @@ -0,0 +1,12 @@ +use std::rc::Rc; + +use crate::context::CommandContext; + +pub trait ResultConsumer { + fn on_command_complete(&self, context: Rc>, success: bool, result: i32); +} + +pub struct DefaultResultConsumer; +impl ResultConsumer for DefaultResultConsumer { + fn on_command_complete(&self, _context: Rc>, _success: bool, _result: i32) {} +} diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 963a2244..8dd41ed3 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use crate::exceptions::{BuiltInExceptions, CommandSyntaxException}; +use crate::errors::{BuiltInError, CommandSyntaxError}; #[derive(Clone)] pub struct StringReader { @@ -91,19 +91,19 @@ impl StringReader { } } - pub fn read_int(&mut self) -> Result { + pub fn read_int(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; if number.is_empty() { - return Err(BuiltInExceptions::ReaderExpectedInt.create_with_context(self)); + return Err(BuiltInError::ReaderExpectedInt.create_with_context(self)); } let result = i32::from_str(number); if result.is_err() { self.cursor = start; - return Err(BuiltInExceptions::ReaderInvalidInt { + return Err(BuiltInError::ReaderInvalidInt { value: number.to_string(), } .create_with_context(self)); @@ -112,19 +112,19 @@ impl StringReader { Ok(result.unwrap()) } - pub fn read_long(&mut self) -> Result { + pub fn read_long(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; if number.is_empty() { - return Err(BuiltInExceptions::ReaderExpectedLong.create_with_context(self)); + return Err(BuiltInError::ReaderExpectedLong.create_with_context(self)); } let result = i64::from_str(number); if result.is_err() { self.cursor = start; - return Err(BuiltInExceptions::ReaderInvalidLong { + return Err(BuiltInError::ReaderInvalidLong { value: number.to_string(), } .create_with_context(self)); @@ -133,19 +133,19 @@ impl StringReader { Ok(result.unwrap()) } - pub fn read_double(&mut self) -> Result { + pub fn read_double(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; if number.is_empty() { - return Err(BuiltInExceptions::ReaderExpectedDouble.create_with_context(self)); + return Err(BuiltInError::ReaderExpectedDouble.create_with_context(self)); } let result = f64::from_str(number); if result.is_err() { self.cursor = start; - return Err(BuiltInExceptions::ReaderInvalidDouble { + return Err(BuiltInError::ReaderInvalidDouble { value: number.to_string(), } .create_with_context(self)); @@ -154,19 +154,19 @@ impl StringReader { Ok(result.unwrap()) } - pub fn read_float(&mut self) -> Result { + pub fn read_float(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::is_allowed_number(self.peek()) { self.skip(); } let number = &self.string[start..self.cursor]; if number.is_empty() { - return Err(BuiltInExceptions::ReaderExpectedFloat.create_with_context(self)); + return Err(BuiltInError::ReaderExpectedFloat.create_with_context(self)); } let result = f32::from_str(number); if result.is_err() { self.cursor = start; - return Err(BuiltInExceptions::ReaderInvalidFloat { + return Err(BuiltInError::ReaderInvalidFloat { value: number.to_string(), } .create_with_context(self)); @@ -193,22 +193,19 @@ impl StringReader { &self.string[start..self.cursor] } - pub fn read_quoted_string(&mut self) -> Result { + pub fn read_quoted_string(&mut self) -> Result { if !self.can_read() { return Ok(String::new()); } let next = self.peek(); if !StringReader::is_quoted_string_start(next) { - return Err(BuiltInExceptions::ReaderExpectedStartOfQuote.create_with_context(self)); + return Err(BuiltInError::ReaderExpectedStartOfQuote.create_with_context(self)); } self.skip(); self.read_string_until(next) } - pub fn read_string_until( - &mut self, - terminator: char, - ) -> Result { + pub fn read_string_until(&mut self, terminator: char) -> Result { let mut result = String::new(); let mut escaped = false; while self.can_read() { @@ -219,7 +216,7 @@ impl StringReader { escaped = false; } else { self.cursor -= 1; - return Err(BuiltInExceptions::ReaderInvalidEscape { character: c } + return Err(BuiltInError::ReaderInvalidEscape { character: c } .create_with_context(self)); } } else if c == SYNTAX_ESCAPE { @@ -231,10 +228,10 @@ impl StringReader { } } - Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self)) + Err(BuiltInError::ReaderExpectedEndOfQuote.create_with_context(self)) } - pub fn read_string(&mut self) -> Result { + pub fn read_string(&mut self) -> Result { if !self.can_read() { return Ok(String::new()); } @@ -246,11 +243,11 @@ impl StringReader { Ok(self.read_unquoted_string().to_string()) } - pub fn read_boolean(&mut self) -> Result { + pub fn read_boolean(&mut self) -> Result { let start = self.cursor; let value = self.read_string()?; if value.is_empty() { - return Err(BuiltInExceptions::ReaderExpectedBool.create_with_context(self)); + return Err(BuiltInError::ReaderExpectedBool.create_with_context(self)); } if value == "true" { @@ -259,15 +256,13 @@ impl StringReader { Ok(false) } else { self.cursor = start; - Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self)) + Err(BuiltInError::ReaderInvalidBool { value }.create_with_context(self)) } } - pub fn expect(&mut self, c: char) -> Result<(), CommandSyntaxException> { + pub fn expect(&mut self, c: char) -> Result<(), CommandSyntaxError> { if !self.can_read() || self.peek() != c { - return Err( - BuiltInExceptions::ReaderExpectedSymbol { symbol: c }.create_with_context(self) - ); + return Err(BuiltInError::ReaderExpectedSymbol { symbol: c }.create_with_context(self)); } self.skip(); Ok(()) diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs index 8181f817..bf53b4ff 100644 --- a/azalea-brigadier/src/tree/mod.rs +++ b/azalea-brigadier/src/tree/mod.rs @@ -14,13 +14,14 @@ use crate::{ required_argument_builder::Argument, }, context::{CommandContext, CommandContextBuilder, ParsedArgument, StringRange}, - exceptions::{BuiltInExceptions, CommandSyntaxException}, + errors::{BuiltInError, CommandSyntaxError}, modifier::RedirectModifier, string_reader::StringReader, suggestion::{Suggestions, SuggestionsBuilder}, }; -pub type Command = Option) -> i32 + Send + Sync>>; +pub type Command = + Option) -> Result + Send + Sync>>; /// An ArgumentBuilder that has been built. #[non_exhaustive] @@ -149,7 +150,7 @@ impl CommandNode { &self, reader: &mut StringReader, context_builder: &mut CommandContextBuilder, - ) -> Result<(), CommandSyntaxException> { + ) -> Result<(), CommandSyntaxError> { match self.value { ArgumentBuilderType::Argument(ref argument) => { let start = reader.cursor(); @@ -176,7 +177,7 @@ impl CommandNode { return Ok(()); } - Err(BuiltInExceptions::LiteralIncorrect { + Err(BuiltInError::LiteralIncorrect { expected: literal.value.clone(), } .create_with_context(reader)) diff --git a/azalea-brigadier/tests/bevy_app_usage.rs b/azalea-brigadier/tests/bevy_app_usage.rs index e962d7d1..d4660a5d 100644 --- a/azalea-brigadier/tests/bevy_app_usage.rs +++ b/azalea-brigadier/tests/bevy_app_usage.rs @@ -131,7 +131,7 @@ impl DispatchStorage { /// /// Spawns an entity with the [`SpawnedEntity`] component. fn command_spawn_entity(context: &CommandContext) -> i32 { - context.source.lock().spawn(SpawnedEntity); + context.source().lock().spawn(SpawnedEntity); 0 } @@ -143,7 +143,7 @@ impl DispatchStorage { let num = get_integer(context, "entities").unwrap(); for _ in 0..num { - context.source.lock().spawn(SpawnedEntity); + context.source().lock().spawn(SpawnedEntity); } 0 diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs index eecbf668..f04ddfdf 100644 --- a/azalea-brigadier/tests/command_dispatcher_test.rs +++ b/azalea-brigadier/tests/command_dispatcher_test.rs @@ -5,7 +5,7 @@ use azalea_brigadier::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, command_dispatcher::CommandDispatcher, context::CommandContext, - exceptions::{BuiltInExceptions, CommandSyntaxException}, + errors::{BuiltInError, CommandSyntaxError}, string_reader::StringReader, }; @@ -50,10 +50,7 @@ fn execute_unknown_command() { let execute_result = subject.execute("foo", &CommandSource {}); let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } + assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownCommand); assert_eq!(err.cursor().unwrap(), 0); } @@ -65,10 +62,7 @@ fn execute_impermissible_command() { let execute_result = subject.execute("foo", &CommandSource {}); let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } + assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownCommand); assert_eq!(err.cursor().unwrap(), 0); } @@ -80,10 +74,7 @@ fn execute_empty_command() { let execute_result = subject.execute("", &CommandSource {}); let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownCommand => {} - _ => panic!("Unexpected error"), - } + assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownCommand); assert_eq!(err.cursor().unwrap(), 0); } @@ -95,10 +86,7 @@ fn execute_unknown_subcommand() { let execute_result = subject.execute("foo bar", &CommandSource {}); let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } + assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownArgument); assert_eq!(err.cursor().unwrap(), 4); } @@ -110,10 +98,7 @@ fn execute_incorrect_literal() { let execute_result = subject.execute("foo baz", &CommandSource {}); let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } + assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownArgument); assert_eq!(err.cursor().unwrap(), 4); } @@ -130,10 +115,7 @@ fn execute_ambiguous_incorrect_argument() { let execute_result = subject.execute("foo unknown", &CommandSource {}); let err = execute_result.err().unwrap(); - match err.type_ { - BuiltInExceptions::DispatcherUnknownArgument => {} - _ => panic!("Unexpected error"), - } + assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownArgument); assert_eq!(err.cursor().unwrap(), 4); } @@ -245,7 +227,7 @@ fn execute_redirected_multiple_times() { ); assert_eq!(*child2.unwrap().nodes[0].node.read(), *concrete_node.read()); - assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); + assert_eq!(subject.execute_parsed(parse).unwrap(), 42); } #[test] @@ -255,7 +237,7 @@ fn execute_redirected() { let source1 = Arc::new(CommandSource {}); let source2 = Arc::new(CommandSource {}); - let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { + let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxError> { Ok(vec![source1.clone(), source2.clone()]) }; @@ -281,7 +263,7 @@ fn execute_redirected() { assert_eq!(*parent.nodes[0].node.read(), *concrete_node.read()); assert_eq!(*parent.source, CommandSource {}); - assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); + assert_eq!(subject.execute_parsed(parse).unwrap(), 2); } #[test] @@ -297,10 +279,7 @@ fn execute_orphaned_subcommand() { let result = subject.execute("foo 5", &CommandSource {}); assert!(result.is_err()); let result = result.unwrap_err(); - assert_eq!( - *result.get_type(), - BuiltInExceptions::DispatcherUnknownCommand - ); + assert_eq!(*result.kind(), BuiltInError::DispatcherUnknownCommand); assert_eq!(result.cursor(), Some(5)); } @@ -327,10 +306,7 @@ fn parse_no_space_separator() { let result = subject.execute("foo$", &CommandSource {}); assert!(result.is_err()); let result = result.unwrap_err(); - assert_eq!( - *result.get_type(), - BuiltInExceptions::DispatcherUnknownCommand - ); + assert_eq!(*result.kind(), BuiltInError::DispatcherUnknownCommand); assert_eq!(result.cursor(), Some(0)); } @@ -348,7 +324,7 @@ fn execute_invalid_subcommand() { 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.get_type(), BuiltInError::ReaderExpectedInt); assert_eq!(result.cursor(), Some(4)); } diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs index de605e99..3b70043b 100644 --- a/azalea-brigadier/tests/string_reader_test.rs +++ b/azalea-brigadier/tests/string_reader_test.rs @@ -1,4 +1,4 @@ -use azalea_brigadier::{exceptions::BuiltInExceptions, string_reader::StringReader}; +use azalea_brigadier::{errors::BuiltInError, string_reader::StringReader}; #[test] fn can_read() { @@ -222,7 +222,7 @@ fn read_quoted_string_no_open() { let result = reader.read_quoted_string(); assert!(result.is_err()); if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedStartOfQuote); + assert_eq!(e.kind(), &BuiltInError::ReaderExpectedStartOfQuote); assert_eq!(e.cursor(), Some(0)); } } @@ -233,7 +233,7 @@ fn read_quoted_string_no_close() { let result = reader.read_quoted_string(); assert!(result.is_err()); if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedEndOfQuote); + assert_eq!(e.kind(), &BuiltInError::ReaderExpectedEndOfQuote); assert_eq!(e.cursor(), Some(12)); } } @@ -245,8 +245,8 @@ fn read_quoted_string_invalid_escape() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidEscape { character: 'n' } + e.kind(), + &BuiltInError::ReaderInvalidEscape { character: 'n' } ); assert_eq!(e.cursor(), Some(7)); } @@ -259,8 +259,8 @@ fn read_quoted_string_invalid_quote_escape() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidEscape { character: '"' } + e.kind(), + &BuiltInError::ReaderInvalidEscape { character: '"' } ); assert_eq!(e.cursor(), Some(7)); } @@ -313,8 +313,8 @@ fn read_int_invalid() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidInt { + e.kind(), + &BuiltInError::ReaderInvalidInt { value: "12.34".to_string() } ); @@ -328,7 +328,7 @@ fn read_int_none() { let result = reader.read_int(); assert!(result.is_err()); if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedInt); + assert_eq!(e.kind(), &BuiltInError::ReaderExpectedInt); assert_eq!(e.cursor(), Some(0)); } } @@ -372,8 +372,8 @@ fn read_long_invalid() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidLong { + e.kind(), + &BuiltInError::ReaderInvalidLong { value: "12.34".to_string() } ); @@ -387,7 +387,7 @@ fn read_long_none() { let result = reader.read_long(); assert!(result.is_err()); if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedLong); + assert_eq!(e.kind(), &BuiltInError::ReaderExpectedLong); assert_eq!(e.cursor(), Some(0)); } } @@ -439,8 +439,8 @@ fn read_double_invalid() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidDouble { + e.kind(), + &BuiltInError::ReaderInvalidDouble { value: "12.34.56".to_string() } ); @@ -454,7 +454,7 @@ fn read_double_none() { let result = reader.read_double(); assert!(result.is_err()); if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedDouble); + assert_eq!(e.kind(), &BuiltInError::ReaderExpectedDouble); assert_eq!(e.cursor(), Some(0)); } } @@ -506,8 +506,8 @@ fn read_float_invalid() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidFloat { + e.kind(), + &BuiltInError::ReaderInvalidFloat { value: "12.34.56".to_string() } ); @@ -521,7 +521,7 @@ fn read_float_none() { let result = reader.read_float(); assert!(result.is_err()); if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedFloat); + assert_eq!(e.kind(), &BuiltInError::ReaderExpectedFloat); assert_eq!(e.cursor(), Some(0)); } } @@ -556,8 +556,8 @@ fn expect_incorrect() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + e.kind(), + &BuiltInError::ReaderExpectedSymbol { symbol: 'a' } ); assert_eq!(e.cursor(), Some(0)); } @@ -570,8 +570,8 @@ fn expect_none() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + e.kind(), + &BuiltInError::ReaderExpectedSymbol { symbol: 'a' } ); assert_eq!(e.cursor(), Some(0)); } @@ -591,8 +591,8 @@ fn read_boolean_incorrect() { assert!(result.is_err()); if let Err(e) = result { assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidBool { + e.kind(), + &BuiltInError::ReaderInvalidBool { value: "tuesday".to_string() } ); @@ -606,7 +606,7 @@ fn read_boolean_none() { let result = reader.read_boolean(); assert!(result.is_err()); if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedBool); + assert_eq!(e.kind(), &BuiltInError::ReaderExpectedBool); assert_eq!(e.cursor(), Some(0)); } } -- cgit v1.2.3