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/string_reader.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 azalea-brigadier/src/string_reader.rs (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From d959fb2d0cc4d8ad97eae86666876e22b2e50613 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 00:14:39 -0600 Subject: add string_reader --- Cargo.lock | 4 + Cargo.toml | 1 + .../src/arguments/bool_argument_type.rs | 8 + azalea-brigadier/src/arguments/mod.rs | 1 + azalea-brigadier/src/builder/mod.rs | 0 azalea-brigadier/src/context/mod.rs | 0 azalea-brigadier/src/exceptions/mod.rs | 0 azalea-brigadier/src/immutable_string_reader.rs | 10 + azalea-brigadier/src/lib.rs | 18 ++ azalea-brigadier/src/message.rs | 1 + azalea-brigadier/src/string_reader.rs | 277 +++++++++++++++++++++ azalea-brigadier/src/suggestion/mod.rs | 0 azalea-brigadier/src/tree/mod.rs | 0 13 files changed, 320 insertions(+) create mode 100644 azalea-brigadier/src/arguments/mod.rs create mode 100644 azalea-brigadier/src/builder/mod.rs create mode 100644 azalea-brigadier/src/context/mod.rs create mode 100644 azalea-brigadier/src/exceptions/mod.rs create mode 100644 azalea-brigadier/src/suggestion/mod.rs create mode 100644 azalea-brigadier/src/tree/mod.rs (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/Cargo.lock b/Cargo.lock index eca4d4fa..67259fef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,10 @@ dependencies = [ "uuid", ] +[[package]] +name = "azalea-brigadier" +version = "0.1.0" + [[package]] name = "azalea-chat" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d22c326b..7f958207 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ members = [ "azalea-core", "azalea-auth", "azalea-nbt", + "azalea-brigadier", ] diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index e69de29b..d4a33517 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -0,0 +1,8 @@ +struct BoolArgumentType { + // private static final Collection EXAMPLES = Arrays.asList("true", "false"); + const EXAMPLES: &'static [&'static str] = &["true", "false"]; +} + +impl ArgumentType for BoolArgumentType { + +} \ No newline at end of file diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs new file mode 100644 index 00000000..18d01d88 --- /dev/null +++ b/azalea-brigadier/src/arguments/mod.rs @@ -0,0 +1 @@ +mod argument_type; diff --git a/azalea-brigadier/src/builder/mod.rs b/azalea-brigadier/src/builder/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/immutable_string_reader.rs b/azalea-brigadier/src/immutable_string_reader.rs index e69de29b..2e067ace 100644 --- a/azalea-brigadier/src/immutable_string_reader.rs +++ b/azalea-brigadier/src/immutable_string_reader.rs @@ -0,0 +1,10 @@ +pub trait ImmutableStringReader { + fn remaining_length(&self) -> usize; + fn total_length(&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 1b4a90c9..d0966de3 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,3 +1,21 @@ +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; + #[cfg(test)] mod tests { #[test] diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index e69de29b..376cc711 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -0,0 +1,277 @@ +use crate::immutable_string_reader::ImmutableStringReader; +use std::str::FromStr; + +#[derive(Clone)] +struct StringReader<'a> { + pub string: &'a str, + pub cursor: usize, +} + +const SYNTAX_ESCAPE: char = '\\'; +const SYNTAX_DOUBLE_QUOTE: char = '"'; +const SYNTAX_SINGLE_QUOTE: char = '\''; + +impl<'a> From<&'a str> for &StringReader<'a> { + fn from(string: &'a str) -> &StringReader<'a> { + &StringReader { string, cursor: 0 } + } +} + +impl ImmutableStringReader for StringReader<'_> { + fn remaining_length(&self) -> usize { + self.string.len() - self.cursor + } + + fn total_length(&self) -> usize { + self.string.len() + } + + fn get_read(&self) -> &str { + &self.string[self.cursor..] + } + + fn remaining(&self) -> &str { + &self.string[self.cursor..] + } + + fn can_read_length(&self, length: usize) -> bool { + self.cursor + length <= self.string.len() + } + + fn can_read(&self) -> bool { + self.can_read_length(1) + } + + fn peek(&self) -> char { + self.string.chars().nth(self.cursor).unwrap() + } + + fn peek_offset(&self, offset: usize) -> char { + self.string.chars().nth(self.cursor + offset).unwrap() + } +} + +impl StringReader<'_> { + fn read(&mut self) -> char { + let c = self.peek(); + self.cursor += 1; + c + } + + fn skip(&mut self) { + self.cursor += 1; + } + + fn is_allowed_number(c: char) -> bool { + c >= '0' && c <= '9' || c == '.' || c == '-' + } + + fn is_quoted_string_start(c: char) -> bool { + c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE + } + + fn skip_whitespace(&mut self) { + while self.can_read() && self.peek().is_whitespace() { + self.skip(); + } + } + + fn read_int(&self) -> Result<(), CommandSyntaxException> { + 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(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_int() + .create_with_context(self)); + } + let result = i32::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_int() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_long(&self) -> Result<(), CommandSyntaxException> { + 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(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_long() + .create_with_context(self)); + } + let result = i64::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_long() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_double(&self) -> Result<(), CommandSyntaxException> { + 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(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_double() + .create_with_context(self)); + } + let result = f64::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_double() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_float(&self) -> Result<(), CommandSyntaxException> { + 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(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_float() + .create_with_context(self)); + } + let result = f32::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_float() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn is_allowed_in_unquoted_string(c: char) -> bool { + c >= '0' && c <= '9' + || c >= 'A' && c <= 'Z' + || c >= 'a' && c <= 'z' + || c == '_' + || c == '-' + || c == '.' + || c == '+' + } + + fn read_unquoted_string(&self) -> &str { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_in_unquoted_string(self.peek()) { + self.skip(); + } + &self.string[start..self.cursor] + } + + fn read_quoted_string(&self) -> Result<&str, CommandSyntaxException> { + if !self.can_read() { + return ""; + } + let next = self.peek(); + if !StringReader::<'_>::is_quoted_string_start(next) { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_start_of_quote() + .create_with_context(self)); + } + self.skip(); + self.read_string_until(next) + } + + fn read_string_until(&self, terminator: char) -> Result { + let result = String::new(); + let mut escaped = false; + while self.can_read() { + let c = self.read(); + if escaped { + if c == terminator || c == SYNTAX_ESCAPE { + result.push(c); + escaped = false; + } else { + self.cursor -= 1; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_escape() + .create_with_context(self, c)); + } + } else if c == SYNTAX_ESCAPE { + escaped = true; + } else if c == terminator { + return Ok(result); + } else { + result.push(c); + } + } + + Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_end_of_quote() + .create_with_context(self)) + } + + fn read_string(&self) -> Result { + // if (!canRead()) { + // return ""; + // } + // final char next = peek(); + // if (isQuotedStringStart(next)) { + // skip(); + // return readStringUntil(next); + // } + // return readUnquotedString(); + if !self.can_read() { + return Ok(String::new()); + } + let next = self.peek(); + if StringReader::<'_>::is_quoted_string_start(next) { + self.skip(); + return self.read_string_until(next); + } + Ok(self.read_unquoted_string().to_string()) + } + + fn read_boolean(&self) -> Result { + let start = self.cursor; + let value = self.read_string()?; + if value.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_bool() + .create_with_context(self)); + } + + if value == "true" { + return Ok(true); + } else if value == "false" { + return Ok(false); + } else { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_bool() + .create_with_context(self, value)); + } + } + + fn expect(&self, c: char) -> Result<(), CommandSyntaxException> { + if !self.can_read() || self.peek() != c { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_symbol() + .create_with_context(self, c)); + } + self.skip(); + } diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 8331851d972b42b68e1fb64e2ec9faef6c02be32 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 14:50:41 -0600 Subject: string reader --- azalea-brigadier/README.md | 3 + azalea-brigadier/src/arguments/argument_type.rs | 11 +- azalea-brigadier/src/arguments/mod.rs | 2 +- azalea-brigadier/src/context/command_context.rs | 3 + azalea-brigadier/src/context/mod.rs | 6 + .../src/exceptions/builtin_exceptions.rs | 158 ++++++++++ .../src/exceptions/command_exception_type.rs | 0 .../src/exceptions/command_syntax_exception.rs | 82 +++++ .../exceptions/dynamic2_command_exception_type.rs | 0 .../exceptions/dynamic3_command_exception_type.rs | 0 .../exceptions/dynamic4_command_exception_type.rs | 0 .../exceptions/dynamicN_command_exception_type.rs | 0 .../exceptions/dynamic_command_exception_type.rs | 0 azalea-brigadier/src/exceptions/mod.rs | 3 + .../exceptions/simple_command_exception_type.rs | 0 azalea-brigadier/src/immutable_string_reader.rs | 2 + azalea-brigadier/src/message.rs | 14 + azalea-brigadier/src/string_reader.rs | 341 +++++++++++++++------ azalea-brigadier/src/suggestion/mod.rs | 5 + azalea-brigadier/src/suggestion/suggestions.rs | 1 + .../src/suggestion/suggestions_builder.rs | 1 + azalea-brigadier/tests/string_reader_test.rs | 0 22 files changed, 532 insertions(+), 100 deletions(-) create mode 100644 azalea-brigadier/README.md delete mode 100644 azalea-brigadier/src/exceptions/command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs delete mode 100644 azalea-brigadier/src/exceptions/simple_command_exception_type.rs delete mode 100644 azalea-brigadier/tests/string_reader_test.rs (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/README.md b/azalea-brigadier/README.md new file mode 100644 index 00000000..92c0d27e --- /dev/null +++ b/azalea-brigadier/README.md @@ -0,0 +1,3 @@ +# 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 4dc97ee0..4c48d6bb 100644 --- a/azalea-brigadier/src/arguments/argument_type.rs +++ b/azalea-brigadier/src/arguments/argument_type.rs @@ -1,3 +1,10 @@ +use crate::{ + context::command_context::CommandContext, + exceptions::command_syntax_exception::CommandSyntaxException, + string_reader::StringReader, + suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder}, +}; + pub trait ArgumentType { // T parse(StringReader reader) throws CommandSyntaxException; @@ -9,12 +16,12 @@ pub trait ArgumentType { // return Collections.emptyList(); // } - fn parse(reader: &mut StringReader) -> Result; + fn parse(reader: &mut StringReader) -> Result; fn list_suggestions( context: &CommandContext, builder: &mut SuggestionsBuilder, - ) -> Result; + ) -> Result; fn get_examples() -> Vec; } diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs index 18d01d88..50b0f09b 100644 --- a/azalea-brigadier/src/arguments/mod.rs +++ b/azalea-brigadier/src/arguments/mod.rs @@ -1 +1 @@ -mod argument_type; +pub mod argument_type; diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index e69de29b..ddbb447e 100644 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -0,0 +1,3 @@ +pub struct CommandContext { + source: S, +} diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs index e69de29b..196d7c5b 100644 --- a/azalea-brigadier/src/context/mod.rs +++ b/azalea-brigadier/src/context/mod.rs @@ -0,0 +1,6 @@ +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/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs index e69de29b..fcca49cd 100644 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs @@ -0,0 +1,158 @@ +use std::fmt; + +use crate::{immutable_string_reader::ImmutableStringReader, message::Message}; + +use super::command_syntax_exception::CommandSyntaxException; + +pub enum BuiltInExceptions { + DoubleTooSmall { found: usize, min: usize }, + DoubleTooBig { found: usize, max: usize }, + + FloatTooSmall { found: usize, min: usize }, + FloatTooBig { found: usize, max: usize }, + + IntegerTooSmall { found: usize, min: usize }, + IntegerTooBig { found: usize, max: usize }, + + LONGTooSmall { found: usize, min: usize }, + LONGTooBig { found: usize, max: usize }, + + 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 }, + + ReaderUnknownCommand, + ReaderUnknownArgument, + DusoatcgerExpectedArgumentSeparator, + 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 {}, found {}", min, found) + } + BuiltInExceptions::DoubleTooBig { found, max } => { + write!(f, "Double must not be more than {}, found {}", max, found) + } + + BuiltInExceptions::FloatTooSmall { found, min } => { + write!(f, "Float must not be less than {}, found {}", min, found) + } + BuiltInExceptions::FloatTooBig { found, max } => { + write!(f, "Float must not be more than {}, found {}", max, found) + } + + BuiltInExceptions::IntegerTooSmall { found, min } => { + write!(f, "Integer must not be less than {}, found {}", min, found) + } + BuiltInExceptions::IntegerTooBig { found, max } => { + write!(f, "Integer must not be more than {}, found {}", max, found) + } + + BuiltInExceptions::LONGTooSmall { found, min } => { + write!(f, "Long must not be less than {}, found {}", min, found) + } + BuiltInExceptions::LONGTooBig { found, max } => { + write!(f, "Long must not be more than {}, found {}", max, 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 '{}' in quoted string", + character + ) + } + 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::ReaderUnknownCommand => { + write!(f, "Unknown command") + } + BuiltInExceptions::ReaderUnknownArgument => { + write!(f, "Incorrect argument for command") + } + BuiltInExceptions::DusoatcgerExpectedArgumentSeparator => { + 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 = Message::from(format!("{:?}", self)); + CommandSyntaxException::create(self, message) + } + + pub fn create_with_context(self, reader: &dyn ImmutableStringReader) -> CommandSyntaxException { + let message = Message::from(format!("{:?}", self)); + CommandSyntaxException::new(self, message, reader.string(), reader.cursor()) + } +} diff --git a/azalea-brigadier/src/exceptions/command_exception_type.rs b/azalea-brigadier/src/exceptions/command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index e69de29b..b9fbea45 100644 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -0,0 +1,82 @@ +use std::{cmp, rc::Rc}; + +use super::builtin_exceptions::BuiltInExceptions; +use crate::message::Message; + +pub struct CommandSyntaxException { + type_: BuiltInExceptions, + message: Message, + input: Option, + cursor: Option, +} + +const CONTEXT_AMOUNT: usize = 10; +const ENABLE_COMMAND_STACK_TRACES: bool = true; + +impl CommandSyntaxException { + pub fn new(type_: BuiltInExceptions, message: Message, input: &str, cursor: usize) -> Self { + Self { + type_, + message, + input: Some(input.to_string()), + cursor: Some(cursor), + } + } + + pub fn create(type_: BuiltInExceptions, message: Message) -> Self { + Self { + type_, + message, + input: None, + cursor: None, + } + } + + pub fn message(&self) -> String { + let mut message = self.message.string(); + let context = self.context(); + if let Some(context) = context { + message.push_str(&format!( + " at position {}: {}", + self.cursor.unwrap_or(usize::MAX), + context + )); + } + message + } + + pub fn raw_message(&self) -> &Message { + &self.message + } + + pub fn context(&self) -> Option { + if let Some(input) = &self.input { + if 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 - CONTEXT_AMOUNT)..cursor]); + builder.push_str("<--[HERE]"); + + return Some(builder); + } + } + None + } + + pub fn get_type(&self) -> &BuiltInExceptions { + &self.type_ + } + + pub fn input(&self) -> String { + self.input() + } + + pub fn cursor(&self) -> Option { + self.cursor + } +} diff --git a/azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic2_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic3_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic4_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamicN_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs b/azalea-brigadier/src/exceptions/dynamic_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs index e69de29b..4a82b01e 100644 --- a/azalea-brigadier/src/exceptions/mod.rs +++ b/azalea-brigadier/src/exceptions/mod.rs @@ -0,0 +1,3 @@ +pub mod builtin_exception_provider; +pub mod builtin_exceptions; +pub mod command_syntax_exception; diff --git a/azalea-brigadier/src/exceptions/simple_command_exception_type.rs b/azalea-brigadier/src/exceptions/simple_command_exception_type.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/src/immutable_string_reader.rs b/azalea-brigadier/src/immutable_string_reader.rs index 2e067ace..53531c64 100644 --- a/azalea-brigadier/src/immutable_string_reader.rs +++ b/azalea-brigadier/src/immutable_string_reader.rs @@ -1,6 +1,8 @@ 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; diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index 8b137891..71d0b178 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -1 +1,15 @@ +use std::rc::Rc; +pub struct Message(Rc); + +impl Message { + pub fn string(&self) -> String { + self.0.to_string() + } +} + +impl From for Message { + fn from(s: String) -> Self { + Self(Rc::new(s)) + } +} diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 376cc711..1119403a 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -1,23 +1,34 @@ -use crate::immutable_string_reader::ImmutableStringReader; +use crate::{ + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + immutable_string_reader::ImmutableStringReader, +}; use std::str::FromStr; #[derive(Clone)] -struct StringReader<'a> { - pub string: &'a str, - pub cursor: usize, +pub struct StringReader<'a> { + string: &'a str, + cursor: usize, } const SYNTAX_ESCAPE: char = '\\'; const SYNTAX_DOUBLE_QUOTE: char = '"'; const SYNTAX_SINGLE_QUOTE: char = '\''; -impl<'a> From<&'a str> for &StringReader<'a> { - fn from(string: &'a str) -> &StringReader<'a> { - &StringReader { string, cursor: 0 } +// impl<'a> From<&'a str> for &StringReader<'a> {} + +impl StringReader<'_> { + fn from(string: &str) -> StringReader { + StringReader { string, cursor: 0 } } } impl ImmutableStringReader for StringReader<'_> { + fn string(&self) -> &str { + self.string + } + fn remaining_length(&self) -> usize { self.string.len() - self.cursor } @@ -27,7 +38,7 @@ impl ImmutableStringReader for StringReader<'_> { } fn get_read(&self) -> &str { - &self.string[self.cursor..] + &self.string[..self.cursor] } fn remaining(&self) -> &str { @@ -49,122 +60,122 @@ impl ImmutableStringReader for StringReader<'_> { fn peek_offset(&self, offset: usize) -> char { self.string.chars().nth(self.cursor + offset).unwrap() } + + fn cursor(&self) -> usize { + self.cursor + } } impl StringReader<'_> { - fn read(&mut self) -> char { + pub fn read(&mut self) -> char { let c = self.peek(); self.cursor += 1; c } - fn skip(&mut self) { + pub fn skip(&mut self) { self.cursor += 1; } - fn is_allowed_number(c: char) -> bool { + pub fn is_allowed_number(c: char) -> bool { c >= '0' && c <= '9' || c == '.' || c == '-' } - fn is_quoted_string_start(c: char) -> bool { + pub fn is_quoted_string_start(c: char) -> bool { c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE } - fn skip_whitespace(&mut self) { + pub fn skip_whitespace(&mut self) { while self.can_read() && self.peek().is_whitespace() { self.skip(); } } - fn read_int(&self) -> Result<(), CommandSyntaxException> { + pub fn read_int(&mut self) -> Result<(), CommandSyntaxException> { 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(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_int() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedInt.create_with_context(self)); } let result = i32::from_str(number); if result.is_err() { self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_int() - .create_with_context(self, number)); + return Err(BuiltInExceptions::ReaderInvalidInt { + value: number.to_string(), + } + .create_with_context(self)); } Ok(()) } - fn read_long(&self) -> Result<(), CommandSyntaxException> { + pub fn read_long(&mut self) -> Result<(), CommandSyntaxException> { 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(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_long() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedLong.create_with_context(self)); } let result = i64::from_str(number); if result.is_err() { self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_long() - .create_with_context(self, number)); + return Err(BuiltInExceptions::ReaderInvalidLong { + value: number.to_string(), + } + .create_with_context(self)); } Ok(()) } - fn read_double(&self) -> Result<(), CommandSyntaxException> { + pub fn read_double(&mut self) -> Result<(), CommandSyntaxException> { 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(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_double() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedDouble.create_with_context(self)); } let result = f64::from_str(number); if result.is_err() { self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_double() - .create_with_context(self, number)); + return Err(BuiltInExceptions::ReaderInvalidDouble { + value: number.to_string(), + } + .create_with_context(self)); } Ok(()) } - fn read_float(&self) -> Result<(), CommandSyntaxException> { + pub fn read_float(&mut self) -> Result<(), CommandSyntaxException> { 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(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_float() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedFloat.create_with_context(self)); } let result = f32::from_str(number); if result.is_err() { self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_float() - .create_with_context(self, number)); + return Err(BuiltInExceptions::ReaderInvalidFloat { + value: number.to_string(), + } + .create_with_context(self)); } Ok(()) } - fn is_allowed_in_unquoted_string(c: char) -> bool { + pub fn is_allowed_in_unquoted_string(c: char) -> bool { c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' @@ -174,7 +185,7 @@ impl StringReader<'_> { || c == '+' } - fn read_unquoted_string(&self) -> &str { + pub fn read_unquoted_string(&mut self) -> &str { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_in_unquoted_string(self.peek()) { self.skip(); @@ -182,22 +193,23 @@ impl StringReader<'_> { &self.string[start..self.cursor] } - fn read_quoted_string(&self) -> Result<&str, CommandSyntaxException> { + pub fn read_quoted_string(&mut self) -> Result { if !self.can_read() { - return ""; + return Ok(String::new()); } let next = self.peek(); if !StringReader::<'_>::is_quoted_string_start(next) { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_start_of_quote() - .create_with_context(self)); + return Err(BuiltInExceptions::ReaderExpectedStartOfQuote.create_with_context(self)); } self.skip(); self.read_string_until(next) } - fn read_string_until(&self, terminator: char) -> Result { - let result = String::new(); + pub fn read_string_until( + &mut self, + terminator: char, + ) -> Result { + let mut result = String::new(); let mut escaped = false; while self.can_read() { let c = self.read(); @@ -207,9 +219,8 @@ impl StringReader<'_> { escaped = false; } else { self.cursor -= 1; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_escape() - .create_with_context(self, c)); + return Err(BuiltInExceptions::ReaderInvalidEscape { character: c } + .create_with_context(self)); } } else if c == SYNTAX_ESCAPE { escaped = true; @@ -220,21 +231,10 @@ impl StringReader<'_> { } } - Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_end_of_quote() - .create_with_context(self)) - } - - fn read_string(&self) -> Result { - // if (!canRead()) { - // return ""; - // } - // final char next = peek(); - // if (isQuotedStringStart(next)) { - // skip(); - // return readStringUntil(next); - // } - // return readUnquotedString(); + return Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self)); + } + + pub fn read_string(&mut self) -> Result { if !self.can_read() { return Ok(String::new()); } @@ -246,32 +246,179 @@ impl StringReader<'_> { Ok(self.read_unquoted_string().to_string()) } - fn read_boolean(&self) -> Result { - let start = self.cursor; - let value = self.read_string()?; - if value.is_empty() { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_bool() - .create_with_context(self)); - } - - if value == "true" { - return Ok(true); - } else if value == "false" { - return Ok(false); - } else { - self.cursor = start; - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_invalid_bool() - .create_with_context(self, value)); - } - } - - fn expect(&self, c: char) -> Result<(), CommandSyntaxException> { - if !self.can_read() || self.peek() != c { - return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS - .reader_expected_symbol() - .create_with_context(self, c)); - } - self.skip(); - } + 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)); + } + + if value == "true" { + return Ok(true); + } else if value == "false" { + return Ok(false); + } else { + self.cursor = start; + return Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self)); + } + } + + pub fn expect(&mut self, c: char) -> Result<(), CommandSyntaxException> { + if !self.can_read() || self.peek() != c { + return Err( + BuiltInExceptions::ReaderExpectedSymbol { symbol: c }.create_with_context(self) + ); + } + self.skip(); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn can_read() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.can_read(), true); + reader.skip(); // 'a' + assert_eq!(reader.can_read(), true); + reader.skip(); // 'b' + assert_eq!(reader.can_read(), true); + reader.skip(); // 'c' + assert_eq!(reader.can_read(), false); + } + + #[test] + fn get_remaining_length() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.remaining_length(), 3); + reader.cursor = 1; + assert_eq!(reader.remaining_length(), 2); + reader.cursor = 2; + assert_eq!(reader.remaining_length(), 1); + reader.cursor = 3; + assert_eq!(reader.remaining_length(), 0); + } + + #[test] + fn can_read_length() { + let reader = StringReader::from("abc"); + assert_eq!(reader.can_read_length(1), true); + assert_eq!(reader.can_read_length(2), true); + assert_eq!(reader.can_read_length(3), true); + assert_eq!(reader.can_read_length(4), false); + assert_eq!(reader.can_read_length(5), false); + } + + #[test] + fn peek() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.peek(), 'a'); + assert_eq!(reader.cursor(), 0); + reader.cursor = 2; + assert_eq!(reader.peek(), 'c'); + assert_eq!(reader.cursor(), 2); + } + + #[test] + fn peek_length() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.peek_offset(0), 'a'); + assert_eq!(reader.peek_offset(2), 'c'); + assert_eq!(reader.cursor(), 0); + reader.cursor = 1; + assert_eq!(reader.peek_offset(1), 'c'); + assert_eq!(reader.cursor(), 1); + } + + #[test] + fn read() { + let mut reader = StringReader::from("abc"); + assert_eq!(reader.read(), 'a'); + assert_eq!(reader.read(), 'b'); + assert_eq!(reader.read(), 'c'); + assert_eq!(reader.cursor(), 3); + } + + #[test] + fn skip() { + let mut reader = StringReader::from("abc"); + reader.skip(); + assert_eq!(reader.cursor(), 1); + } + + #[test] + fn get_remaining() { + let mut reader = StringReader::from("Hello!"); + assert_eq!(reader.remaining(), "Hello!"); + reader.cursor = 3; + assert_eq!(reader.remaining(), "lo!"); + reader.cursor = 6; + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn get_read() { + let mut reader = StringReader::from("Hello!"); + assert_eq!(reader.get_read(), ""); + reader.cursor = 3; + assert_eq!(reader.get_read(), "Hel"); + reader.cursor = 6; + assert_eq!(reader.get_read(), "Hello!"); + } + + #[test] + fn skip_whitespace_none() { + let mut reader = StringReader::from("Hello!"); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 0); + } + + #[test] + fn skip_whitespace_mixed() { + let mut reader = StringReader::from(" \t \t\nHello!"); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 5); + } + + #[test] + fn skip_whitespace_empty() { + let mut reader = StringReader::from(""); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 0); + } + + #[test] + fn read_unquoted_string() { + let mut reader = StringReader::from("hello world"); + assert_eq!(reader.read_unquoted_string(), "hello"); + assert_eq!(reader.get_read(), "hello"); + assert_eq!(reader.remaining(), "world"); + } + + #[test] + fn read_unquoted_string_empty() { + let mut reader = StringReader::from(""); + assert_eq!(reader.read_unquoted_string(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_unquoted_string_empty_with_remaining() { + let mut reader = StringReader::from(" hello world"); + assert_eq!(reader.read_unquoted_string(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), " hello world"); + } + + #[test] + fn read_quoted_string() { + let mut reader = StringReader::from("\"hello world\""); + assert_eq!(reader.read_unquoted_string(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), ""); + } +} diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs index e69de29b..050bae6c 100644 --- a/azalea-brigadier/src/suggestion/mod.rs +++ b/azalea-brigadier/src/suggestion/mod.rs @@ -0,0 +1,5 @@ +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/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index e69de29b..354fc418 100644 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -0,0 +1 @@ +pub struct Suggestions {} diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs index e69de29b..6960f52b 100644 --- a/azalea-brigadier/src/suggestion/suggestions_builder.rs +++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs @@ -0,0 +1 @@ +pub struct SuggestionsBuilder {} diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs deleted file mode 100644 index e69de29b..00000000 -- cgit v1.2.3 From 4626bd45cd8b3875a0ee006cf8e2c7cfb9f8cbcc Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 14:58:14 -0600 Subject: fix a couple tests --- azalea-brigadier/src/exceptions/command_syntax_exception.rs | 8 +++++++- azalea-brigadier/src/string_reader.rs | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index b9fbea45..38aa1c3a 100644 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -1,4 +1,4 @@ -use std::{cmp, rc::Rc}; +use std::{cmp, fmt, rc::Rc}; use super::builtin_exceptions::BuiltInExceptions; use crate::message::Message; @@ -80,3 +80,9 @@ impl CommandSyntaxException { 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/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 1119403a..4ef35ac0 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -187,7 +187,7 @@ impl StringReader<'_> { pub fn read_unquoted_string(&mut self) -> &str { let start = self.cursor; - while self.can_read() && StringReader::<'_>::is_allowed_in_unquoted_string(self.peek()) { + while self.can_read() && StringReader::is_allowed_in_unquoted_string(self.peek()) { self.skip(); } &self.string[start..self.cursor] @@ -395,7 +395,7 @@ mod test { let mut reader = StringReader::from("hello world"); assert_eq!(reader.read_unquoted_string(), "hello"); assert_eq!(reader.get_read(), "hello"); - assert_eq!(reader.remaining(), "world"); + assert_eq!(reader.remaining(), " world"); } #[test] @@ -417,7 +417,7 @@ mod test { #[test] fn read_quoted_string() { let mut reader = StringReader::from("\"hello world\""); - assert_eq!(reader.read_unquoted_string(), "hello world"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); assert_eq!(reader.get_read(), "\"hello world\""); assert_eq!(reader.remaining(), ""); } -- cgit v1.2.3 From c7554b40d52c33db6a5cc23a5b2e01b96dfe3a59 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 15:13:08 -0600 Subject: add some more tests --- .../src/exceptions/builtin_exceptions.rs | 1 + azalea-brigadier/src/string_reader.rs | 83 ++++++++++++++++++++++ 2 files changed, 84 insertions(+) (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs index fcca49cd..1533364b 100644 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs @@ -4,6 +4,7 @@ use crate::{immutable_string_reader::ImmutableStringReader, message::Message}; use super::command_syntax_exception::CommandSyntaxException; +#[derive(Clone, PartialEq)] pub enum BuiltInExceptions { DoubleTooSmall { found: usize, min: usize }, DoubleTooBig { found: usize, max: usize }, diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 4ef35ac0..28823bce 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -421,4 +421,87 @@ mod test { assert_eq!(reader.get_read(), "\"hello world\""); assert_eq!(reader.remaining(), ""); } + + #[test] + fn read_single_quoted_string() { + let mut reader = StringReader::from("'hello world'"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "'hello world'"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_mixed_quoted_string_double_inside_single() { + let mut reader = StringReader::from("'hello \"world\"'"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); + assert_eq!(reader.get_read(), "'hello \"world\"'"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_mixed_quoted_string_single_inside_double() { + let mut reader = StringReader::from("\"hello 'world'\""); + assert_eq!(reader.read_quoted_string().unwrap(), "hello 'world'"); + assert_eq!(reader.get_read(), "\"hello 'world'\""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_quoted_string_empty_quoted() { + let mut reader = StringReader::from(""); + assert_eq!(reader.read_quoted_string().unwrap(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_quoted_string_empty_quoted_with_remaining() { + let mut reader = StringReader::from("\"\" hello world"); + assert_eq!(reader.read_quoted_string().unwrap(), ""); + assert_eq!(reader.get_read(), "\"\""); + assert_eq!(reader.remaining(), " hello world"); + } + + #[test] + fn read_quoted_string_with_escaped_quote() { + let mut reader = StringReader::from("\"hello \\\"world\\\"\""); + assert_eq!(reader.read_quoted_string().unwrap(), "hello \"world\""); + assert_eq!(reader.get_read(), "\"hello \\\"world\\\"\""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_quoted_string_with_escaped_escapes() { + let mut reader = StringReader::from("\"\\\\o/\""); + assert_eq!(reader.read_quoted_string().unwrap(), "\\o/"); + assert_eq!(reader.get_read(), "\"\\\\o/\""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_quoted_string_with_remaining() { + let mut reader = StringReader::from("\"hello world\" foo bar"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_quoted_string_with_immediate_remaining() { + let mut reader = StringReader::from("\"hello world\"foo bar"); + assert_eq!(reader.read_quoted_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn read_quoted_string_no_open() { + let mut reader = StringReader::from("hello world\""); + 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.cursor(), Some(0)); + } + } } -- cgit v1.2.3 From d56f60c05f316ab4cc37ebe7a9ad4caf91a75de6 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 15:40:54 -0600 Subject: add all the string reader tests --- azalea-brigadier/src/string_reader.rs | 402 +++++++++++++++++++++++++++++++++- 1 file changed, 393 insertions(+), 9 deletions(-) (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 28823bce..f32de473 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -91,7 +91,7 @@ impl StringReader<'_> { } } - pub fn read_int(&mut self) -> Result<(), CommandSyntaxException> { + pub fn read_int(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); @@ -109,10 +109,10 @@ impl StringReader<'_> { .create_with_context(self)); } - Ok(()) + Ok(result.unwrap()) } - pub fn read_long(&mut self) -> Result<(), CommandSyntaxException> { + pub fn read_long(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); @@ -130,12 +130,12 @@ impl StringReader<'_> { .create_with_context(self)); } - Ok(()) + Ok(result.unwrap()) } - pub fn read_double(&mut self) -> Result<(), CommandSyntaxException> { + pub fn read_double(&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]; @@ -151,10 +151,10 @@ impl StringReader<'_> { .create_with_context(self)); } - Ok(()) + Ok(result.unwrap()) } - pub fn read_float(&mut self) -> Result<(), CommandSyntaxException> { + pub fn read_float(&mut self) -> Result { let start = self.cursor; while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { self.skip(); @@ -172,7 +172,7 @@ impl StringReader<'_> { .create_with_context(self)); } - Ok(()) + Ok(result.unwrap()) } pub fn is_allowed_in_unquoted_string(c: char) -> bool { @@ -504,4 +504,388 @@ mod test { assert_eq!(e.cursor(), Some(0)); } } + + #[test] + fn read_quoted_string_no_close() { + let mut reader = StringReader::from("\"hello world"); + 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.cursor(), Some(12)); + } + } + + #[test] + fn read_quoted_string_invalid_escape() { + let mut reader = StringReader::from("\"hello\\nworld\""); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidEscape { character: 'n' } + ); + assert_eq!(e.cursor(), Some(7)); + } + } + + #[test] + fn read_quoted_string_invalid_quote_escape() { + let mut reader = StringReader::from("'hello\\\"\'world"); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidEscape { character: '"' } + ); + assert_eq!(e.cursor(), Some(7)); + } + } + + #[test] + fn read_string_no_quotes() { + let mut reader = StringReader::from("hello world"); + assert_eq!(reader.read_string().unwrap(), "hello"); + assert_eq!(reader.get_read(), "hello"); + assert_eq!(reader.remaining(), " world"); + } + + #[test] + fn read_string_single_quotes() { + let mut reader = StringReader::from("'hello world'"); + assert_eq!(reader.read_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "'hello world'"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_string_double_quotes() { + let mut reader = StringReader::from("\"hello world\""); + assert_eq!(reader.read_string().unwrap(), "hello world"); + assert_eq!(reader.get_read(), "\"hello world\""); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_int() { + let mut reader = StringReader::from("1234567890"); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_int_negative() { + let mut reader = StringReader::from("-1234567890"); + assert_eq!(reader.read_int().unwrap(), -1234567890); + assert_eq!(reader.get_read(), "-1234567890"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_int_invalid() { + let mut reader = StringReader::from("12.34"); + let result = reader.read_int(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidInt { + value: "12.34".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_int_none() { + let mut reader = StringReader::from(""); + let result = reader.read_int(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedInt); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_int_with_remaining() { + let mut reader = StringReader::from("1234567890 foo bar"); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_int_with_remaining_immediate() { + let mut reader = StringReader::from("1234567890foo bar"); + assert_eq!(reader.read_int().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn read_long() { + let mut reader = StringReader::from("1234567890"); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_long_negative() { + let mut reader = StringReader::from("-1234567890"); + assert_eq!(reader.read_long().unwrap(), -1234567890); + assert_eq!(reader.get_read(), "-1234567890"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_long_invalid() { + let mut reader = StringReader::from("12.34"); + let result = reader.read_long(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidLong { + value: "12.34".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_long_none() { + let mut reader = StringReader::from(""); + let result = reader.read_long(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedLong); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_long_with_remaining() { + let mut reader = StringReader::from("1234567890 foo bar"); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_long_with_remaining_immediate() { + let mut reader = StringReader::from("1234567890foo bar"); + assert_eq!(reader.read_long().unwrap(), 1234567890); + assert_eq!(reader.get_read(), "1234567890"); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn read_double() { + let mut reader = StringReader::from("123"); + assert_eq!(reader.read_double().unwrap(), 123.0); + assert_eq!(reader.get_read(), "123"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_double_with_decimal() { + let mut reader = StringReader::from("12.34"); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_double_negative() { + let mut reader = StringReader::from("-123"); + assert_eq!(reader.read_double().unwrap(), -123.0); + assert_eq!(reader.get_read(), "-123"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_double_invalid() { + let mut reader = StringReader::from("12.34.56"); + let result = reader.read_double(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidDouble { + value: "12.34.56".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_double_none() { + let mut reader = StringReader::from(""); + let result = reader.read_double(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedDouble); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_double_with_remaining() { + let mut reader = StringReader::from("12.34 foo bar"); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_double_with_remaining_immediate() { + let mut reader = StringReader::from("12.34foo bar"); + assert_eq!(reader.read_double().unwrap(), 12.34); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn read_float() { + let mut reader = StringReader::from("123"); + assert_eq!(reader.read_float().unwrap(), 123.0f32); + assert_eq!(reader.get_read(), "123"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_float_with_decimal() { + let mut reader = StringReader::from("12.34"); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_float_negative() { + let mut reader = StringReader::from("-123"); + assert_eq!(reader.read_float().unwrap(), -123.0f32); + assert_eq!(reader.get_read(), "-123"); + assert_eq!(reader.remaining(), ""); + } + + #[test] + fn read_float_invalid() { + let mut reader = StringReader::from("12.34.56"); + let result = reader.read_float(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidFloat { + value: "12.34.56".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_float_none() { + let mut reader = StringReader::from(""); + let result = reader.read_float(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedFloat); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_float_with_remaining() { + let mut reader = StringReader::from("12.34 foo bar"); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), " foo bar"); + } + + #[test] + fn read_float_with_remaining_immediate() { + let mut reader = StringReader::from("12.34foo bar"); + assert_eq!(reader.read_float().unwrap(), 12.34f32); + assert_eq!(reader.get_read(), "12.34"); + assert_eq!(reader.remaining(), "foo bar"); + } + + #[test] + fn expect_correct() { + let mut reader = StringReader::from("abc"); + reader.expect('a'); + assert_eq!(reader.cursor(), 1); + } + + #[test] + fn expect_incorrect() { + let mut reader = StringReader::from("bcd"); + let result = reader.expect('a'); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn expect_none() { + let mut reader = StringReader::from(""); + let result = reader.expect('a'); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_boolean_correct() { + let mut reader = StringReader::from("true"); + 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 result = reader.read_boolean(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidBool { + value: "tuesday".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } + } + + #[test] + fn read_boolean_none() { + let mut reader = StringReader::from(""); + let result = reader.read_boolean(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedBool); + assert_eq!(e.cursor(), Some(0)); + } + } } -- 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/string_reader.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 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/string_reader.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 10cd1733cbba5c637fa0130a0cd7a7ab6e618226 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 14:40:26 -0500 Subject: add execute & get_integer --- azalea-brigadier/src/builder/argument_builder.rs | 1 - .../src/builder/required_argument_builder.rs | 7 ++- azalea-brigadier/src/context.rs | 5 +++ azalea-brigadier/src/dispatcher.rs | 13 +++--- .../src/exceptions/builtin_exceptions.rs | 20 ++++----- azalea-brigadier/src/lib.rs | 13 +++--- azalea-brigadier/src/parsers.rs | 51 ++++++++++++++++++---- azalea-brigadier/src/string_reader.rs | 8 ++++ azalea-brigadier/src/tree.rs | 3 -- 9 files changed, 82 insertions(+), 39 deletions(-) (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 1fb775c2..17e9d625 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -67,7 +67,6 @@ impl ArgumentBuilder { } pub fn build(self) -> CommandNode { - println!("building {:?}", self); CommandNode { value: self.value, diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 95f4da01..9cd089de 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,5 +1,8 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; -use crate::{parsers::Parser, string_reader::StringReader}; +use crate::{ + exceptions::command_syntax_exception::CommandSyntaxException, 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, @@ -17,7 +20,7 @@ impl Argument { } } - pub fn parse(&self, reader: &mut StringReader) -> Option> { + pub fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { self.parser.parse(reader) } } diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs index 6d4dec88..a68c1da5 100644 --- a/azalea-brigadier/src/context.rs +++ b/azalea-brigadier/src/context.rs @@ -142,4 +142,9 @@ impl CommandContext { pub fn has_nodes(&self) -> bool { return !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/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 65fe5b0a..b2004859 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -5,7 +5,6 @@ use crate::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }, parse_results::ParseResults, - string_range::StringRange, string_reader::StringReader, tree::CommandNode, }; @@ -28,10 +27,8 @@ impl CommandDispatcher { } 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 { @@ -142,7 +139,6 @@ impl CommandDispatcher { }) } let best_potential = potentials.into_iter().next().unwrap(); - println!("chosen {:#?}", best_potential); return Ok(best_potential); } @@ -153,10 +149,14 @@ impl CommandDispatcher { }) } + pub fn execute(&self, input: StringReader, source: S) -> Result { + let parse = self.parse(input, source); + Self::execute_parsed(parse) + } + /// Executes a given pre-parsed command. - pub fn execute(parse: ParseResults) -> Result { + pub fn execute_parsed(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()); } @@ -169,7 +169,6 @@ impl CommandDispatcher { BuiltInExceptions::DispatcherUnknownArgument.create_with_context(&parse.reader) ); } - println!("a"); let mut result = 0i32; let mut successful_forks = 0; let mut forked = false; diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs index 5f2e1605..09951a03 100644 --- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs +++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs @@ -6,17 +6,17 @@ use super::command_syntax_exception::CommandSyntaxException; #[derive(Clone, PartialEq)] pub enum BuiltInExceptions { - DoubleTooSmall { found: usize, min: usize }, - DoubleTooBig { found: usize, max: usize }, + DoubleTooSmall { found: f64, min: f64 }, + DoubleTooBig { found: f64, max: f64 }, - FloatTooSmall { found: usize, min: usize }, - FloatTooBig { found: usize, max: usize }, + FloatTooSmall { found: f32, min: f32 }, + FloatTooBig { found: f32, max: f32 }, - IntegerTooSmall { found: usize, min: usize }, - IntegerTooBig { found: usize, max: usize }, + IntegerTooSmall { found: i32, min: i32 }, + IntegerTooBig { found: i32, max: i32 }, - LONGTooSmall { found: usize, min: usize }, - LONGTooBig { found: usize, max: usize }, + LongTooSmall { found: i64, min: i64 }, + LongTooBig { found: i64, max: i64 }, LiteralIncorrect { expected: String }, @@ -65,10 +65,10 @@ impl fmt::Debug for BuiltInExceptions { write!(f, "Integer must not be more than {}, found {}", max, found) } - BuiltInExceptions::LONGTooSmall { found, min } => { + BuiltInExceptions::LongTooSmall { found, min } => { write!(f, "Long must not be less than {}, found {}", min, found) } - BuiltInExceptions::LONGTooBig { found, max } => { + BuiltInExceptions::LongTooBig { found, max } => { write!(f, "Long must not be more than {}, found {}", max, found) } diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index 476764d0..2f4fffe9 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -18,7 +18,7 @@ mod tests { use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, dispatcher::CommandDispatcher, - parsers::integer, + parsers::{get_integer, integer}, }; struct CommandSourceStack { @@ -36,7 +36,7 @@ mod tests { dispatcher.register( literal("foo") .then(argument("bar", integer()).executes(|c| { - // println!("Bar is {}", get_integer(c, "bar")); + println!("Bar is {:?}", get_integer(c, "bar")); 2 })) .executes(|c| { @@ -45,11 +45,8 @@ mod tests { }), ); - let parse = dispatcher.parse("foo 123".to_string().into(), source); - println!( - "{}", - CommandDispatcher::>::execute(parse).unwrap() - ); - // assert_eq!(dispatcher.execute("foo bar", source), 2); + let parse = dispatcher.parse("foo 123".into(), source.clone()); + assert_eq!(CommandDispatcher::<_>::execute_parsed(parse).unwrap(), 2); + assert_eq!(dispatcher.execute("foo".into(), source).unwrap(), 1); } } diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs index a497e0d1..bdf34438 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/parsers.rs @@ -1,21 +1,56 @@ use std::{any::Any, marker::PhantomData, rc::Rc}; -use crate::string_reader::StringReader; +use crate::{ + context::CommandContext, + exceptions::{ + builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, + }, + string_reader::StringReader, +}; pub trait Parser { - fn parse(&self, reader: &mut StringReader) -> Option>; + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException>; +} + +#[derive(Default)] +struct Integer { + pub minimum: Option, + pub maximum: Option, } -struct Integer {} impl Parser for Integer { - fn parse(&self, reader: &mut StringReader) -> Option> { + fn parse(&self, reader: &mut StringReader) -> Result, CommandSyntaxException> { let start = reader.cursor; - let result = reader.read_int(); - // TODO: check min and max - Some(Rc::new(result)) + let result = reader.read_int()?; + if let Some(minimum) = self.minimum { + if result < minimum { + return Err(BuiltInExceptions::IntegerTooSmall { + found: result, + min: minimum, + } + .create_with_context(reader)); + } + } + if let Some(maximum) = self.maximum { + if result > maximum { + return Err(BuiltInExceptions::IntegerTooBig { + found: result, + max: maximum, + } + .create_with_context(reader)); + } + } + Ok(Rc::new(result)) } } pub fn integer() -> impl Parser { - Integer {} + Integer::default() +} +pub fn get_integer(context: &CommandContext, name: &str) -> Option { + context + .argument(name) + .unwrap() + .downcast_ref::() + .map(|x| *x) } diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 4b390155..403b8e99 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -18,6 +18,14 @@ impl From for StringReader { Self { string, cursor: 0 } } } +impl From<&str> for StringReader { + fn from(string: &str) -> Self { + Self { + string: string.to_string(), + cursor: 0, + } + } +} impl StringReader { pub fn string(&self) -> &str { diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index 2f023697..c81c599f 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -67,8 +67,6 @@ impl CommandNode { 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() != ' ' { @@ -220,7 +218,6 @@ impl Debug for CommandNode { impl Default for CommandNode { fn default() -> Self { - println!("making default node"); Self { value: ArgumentBuilderType::Literal(Literal::default()), -- cgit v1.2.3 From 2e904225611b66fa72b082e4f5e188b55b333fcd Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 15:57:28 -0500 Subject: Fix clippy issues and add a couple tests to dispatcher --- azalea-brigadier/src/builder/argument_builder.rs | 8 +- .../src/builder/literal_argument_builder.rs | 9 -- .../src/builder/required_argument_builder.rs | 7 +- azalea-brigadier/src/context.rs | 10 +- azalea-brigadier/src/dispatcher.rs | 132 +++++++++++++++++++-- .../src/exceptions/command_syntax_exception.rs | 9 +- azalea-brigadier/src/message.rs | 2 - azalea-brigadier/src/parsers.rs | 6 +- azalea-brigadier/src/string_reader.rs | 18 +-- azalea-brigadier/src/tree.rs | 33 ++---- .../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 | 1 - .../tests/command_dispatcher_usages_test.rs | 1 - azalea-brigadier/tests/command_suggestions_test.rs | 1 - .../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 .../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 33 files changed, 158 insertions(+), 79 deletions(-) delete mode 100644 azalea-brigadier/tests/arguments/bool_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/double_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/float_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/integer_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/long_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/arguments/string_argument_type_test.rs delete mode 100644 azalea-brigadier/tests/builder/argument_builder_test.rs delete mode 100644 azalea-brigadier/tests/builder/literal_argument_builder_test.rs delete mode 100644 azalea-brigadier/tests/builder/required_argument_builder_test.rs delete mode 100644 azalea-brigadier/tests/command_dispatcher_test.rs delete mode 100644 azalea-brigadier/tests/command_dispatcher_usages_test.rs delete mode 100644 azalea-brigadier/tests/command_suggestions_test.rs delete mode 100644 azalea-brigadier/tests/context/command_context_test.rs delete mode 100644 azalea-brigadier/tests/context/parsed_argument_test.rs delete mode 100644 azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs delete mode 100644 azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs delete mode 100644 azalea-brigadier/tests/suggestion/suggestion_test.rs delete mode 100644 azalea-brigadier/tests/suggestion/suggestions_builder_test.rs delete mode 100644 azalea-brigadier/tests/suggestion/suggestions_test.rs delete mode 100644 azalea-brigadier/tests/tree/abstract_command_node_test.rs delete mode 100644 azalea-brigadier/tests/tree/argument_command_node_test.rs delete mode 100644 azalea-brigadier/tests/tree/literal_command_node_test.rs delete mode 100644 azalea-brigadier/tests/tree/root_command_node_test.rs (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 17e9d625..6f23457a 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -41,21 +41,21 @@ impl ArgumentBuilder { } } - pub fn then(&mut self, node: ArgumentBuilder) -> &mut Self { + pub fn then(&mut self, node: ArgumentBuilder) -> 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) => { + ArgumentBuilderType::Literal(_) => { self.literals.insert(name.to_string(), node_reference); } - ArgumentBuilderType::Argument(argument) => { + ArgumentBuilderType::Argument(_) => { self.arguments.insert(name.to_string(), node_reference); } } - self + self.clone() } pub fn executes(&mut self, f: F) -> Self diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index d8898540..e5e165d8 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -1,14 +1,5 @@ use std::any::Any; -use crate::{ - context::CommandContextBuilder, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, - string_range::StringRange, - string_reader::StringReader, -}; - use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; #[derive(Debug, Clone, Default)] diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 9cd089de..0eb5d11a 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -16,7 +16,7 @@ impl Argument { pub fn new(name: &str, parser: Rc) -> Self { Self { name: name.to_string(), - parser: parser, + parser, } } @@ -41,9 +41,6 @@ impl Debug for Argument { } /// Shortcut for creating a new argument builder node. -pub fn argument<'a, S: Any + Clone>( - name: &'a str, - parser: impl Parser + 'static, -) -> ArgumentBuilder { +pub fn argument(name: &str, parser: impl Parser + 'static) -> ArgumentBuilder { ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into()) } diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs index a68c1da5..5ffe2028 100644 --- a/azalea-brigadier/src/context.rs +++ b/azalea-brigadier/src/context.rs @@ -1,4 +1,4 @@ -use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, ptr, rc::Rc}; +use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; use crate::{ dispatcher::CommandDispatcher, modifier::RedirectModifier, string_range::StringRange, @@ -73,7 +73,7 @@ impl CommandContextBuilder { nodes: self.nodes.clone(), source: self.source.clone(), command: self.command.clone(), - child: self.child.clone().map(|c| Rc::new(c.build(&input))), + child: self.child.clone().map(|c| Rc::new(c.build(input))), range: self.range.clone(), forks: self.forks, modifier: self.modifier.clone(), @@ -125,7 +125,7 @@ impl CommandContext { if Rc::ptr_eq(&source, &self.source) { return self.clone(); } - return CommandContext { + CommandContext { source, input: self.input.clone(), arguments: self.arguments.clone(), @@ -136,11 +136,11 @@ impl CommandContext { child: self.child.clone(), modifier: self.modifier.clone(), forks: self.forks, - }; + } } pub fn has_nodes(&self) -> bool { - return !self.nodes.is_empty(); + !self.nodes.is_empty() } pub fn argument(&self, name: &str) -> Option> { diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index b2004859..029f0ed0 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -75,16 +75,14 @@ impl CommandDispatcher { 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; - } + 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); @@ -120,7 +118,7 @@ impl CommandDispatcher { } } - if potentials.len() > 0 { + if !potentials.is_empty() { if potentials.len() > 1 { potentials.sort_by(|a, b| { if !a.reader.can_read() && b.reader.can_read() { @@ -174,11 +172,11 @@ impl CommandDispatcher { let mut forked = false; let mut found_command = false; let command = parse.reader.string(); - let original = parse.context.build(&command); + let original = parse.context.build(command); let mut contexts = vec![original]; let mut next: Vec> = vec![]; - while contexts.len() > 0 { + while !contexts.is_empty() { for context in contexts.iter() { let child = &context.child; if let Some(child) = child { @@ -239,3 +237,111 @@ impl Clone for CommandDispatcher { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::builder::literal_argument_builder::literal; + + struct CommandSource {} + + fn input_with_offset(input: &str, offset: usize) -> StringReader { + let mut result: StringReader = input.into(); + result.cursor = offset; + result + } + + // @Test + // public void testCreateAndExecuteCommand() throws Exception { + // subject.register(literal("foo").executes(command)); + + // assertThat(subject.execute("foo", source), is(42)); + // verify(command).run(any(CommandContext.class)); + // } + #[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 + // public void testCreateAndExecuteOffsetCommand() throws Exception { + // subject.register(literal("foo").executes(command)); + + // assertThat(subject.execute(inputWithOffset("/foo", 1), source), is(42)); + // verify(command).run(any(CommandContext.class)); + // } + #[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 + // public void testCreateAndMergeCommands() throws Exception { + // subject.register(literal("base").then(literal("foo").executes(command))); + // subject.register(literal("base").then(literal("bar").executes(command))); + + // assertThat(subject.execute("base foo", source), is(42)); + // assertThat(subject.execute("base bar", source), is(42)); + // verify(command, times(2)).run(any(CommandContext.class)); + // } + #[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 + // public void testExecuteUnknownCommand() throws Exception { + // subject.register(literal("bar")); + // subject.register(literal("baz")); + + // try { + // subject.execute("foo", source); + // fail(); + // } catch (final CommandSyntaxException ex) { + // assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())); + // assertThat(ex.getCursor(), is(0)); + // } + // } + // #[test] + // fn execute_unknown_command() { + // let mut subject = CommandDispatcher::>::new(); + // subject.register(literal("bar")); + // subject.register(literal("baz")); + + // assert_eq!( + // subject + // .execute("foo".into(), Rc::new(CommandSource {})) + // .err() + // .unwrap(), + // BuiltInExceptions::DispatcherUnknownCommand.create() + // ); + // } +} diff --git a/azalea-brigadier/src/exceptions/command_syntax_exception.rs b/azalea-brigadier/src/exceptions/command_syntax_exception.rs index 6cd4e53d..93ac788c 100644 --- a/azalea-brigadier/src/exceptions/command_syntax_exception.rs +++ b/azalea-brigadier/src/exceptions/command_syntax_exception.rs @@ -1,9 +1,9 @@ -use std::{cmp, fmt, rc::Rc}; +use std::{cmp, fmt}; use super::builtin_exceptions::BuiltInExceptions; use crate::message::Message; -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct CommandSyntaxException { type_: BuiltInExceptions, message: Message, @@ -12,7 +12,6 @@ pub struct CommandSyntaxException { } const CONTEXT_AMOUNT: usize = 10; -const ENABLE_COMMAND_STACK_TRACES: bool = true; impl CommandSyntaxException { pub fn new(type_: BuiltInExceptions, message: Message, input: &str, cursor: usize) -> Self { @@ -76,8 +75,8 @@ impl CommandSyntaxException { &self.type_ } - pub fn input(&self) -> String { - self.input() + pub fn input(&self) -> &Option { + &self.input } pub fn cursor(&self) -> Option { diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index 9d133c7e..75e07d4e 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Message(String); diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs index bdf34438..77e57ace 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/parsers.rs @@ -1,4 +1,4 @@ -use std::{any::Any, marker::PhantomData, rc::Rc}; +use std::{any::Any, rc::Rc}; use crate::{ context::CommandContext, @@ -24,6 +24,7 @@ impl Parser for Integer { let result = reader.read_int()?; if let Some(minimum) = self.minimum { if result < minimum { + reader.cursor = start; return Err(BuiltInExceptions::IntegerTooSmall { found: result, min: minimum, @@ -33,6 +34,7 @@ impl Parser for Integer { } if let Some(maximum) = self.maximum { if result > maximum { + reader.cursor = start; return Err(BuiltInExceptions::IntegerTooBig { found: result, max: maximum, @@ -52,5 +54,5 @@ pub fn get_integer(context: &CommandContext, name: &str) -> O .argument(name) .unwrap() .downcast_ref::() - .map(|x| *x) + .copied() } diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 403b8e99..5825871f 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -1,7 +1,7 @@ use crate::exceptions::{ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, }; -use std::{rc::Rc, str::FromStr}; +use std::str::FromStr; #[derive(Clone)] pub struct StringReader { @@ -79,7 +79,7 @@ impl StringReader { } pub fn is_allowed_number(c: char) -> bool { - c >= '0' && c <= '9' || c == '.' || c == '-' + ('0'..='9').contains(&c) || c == '.' || c == '-' } pub fn is_quoted_string_start(c: char) -> bool { @@ -177,9 +177,9 @@ impl StringReader { } pub fn is_allowed_in_unquoted_string(c: char) -> bool { - c >= '0' && c <= '9' - || c >= 'A' && c <= 'Z' - || c >= 'a' && c <= 'z' + ('0'..='9').contains(&c) + || ('A'..='Z').contains(&c) + || ('a'..='z').contains(&c) || c == '_' || c == '-' || c == '.' @@ -232,7 +232,7 @@ impl StringReader { } } - return Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self)); + Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self)) } pub fn read_string(&mut self) -> Result { @@ -255,12 +255,12 @@ impl StringReader { } if value == "true" { - return Ok(true); + Ok(true) } else if value == "false" { - return Ok(false); + Ok(false) } else { self.cursor = start; - return Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self)); + Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self)) } } diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index c81c599f..5c33a879 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -11,15 +11,7 @@ use crate::{ string_range::StringRange, string_reader::StringReader, }; -use std::{ - any::Any, - cell::RefCell, - collections::{BTreeMap, HashMap}, - fmt::Debug, - hash::Hash, - ptr, - rc::Rc, -}; +use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; /// An ArgumentBuilder that has been built. #[derive(Clone)] @@ -67,7 +59,7 @@ impl CommandNode { pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { let literals = self.literals(); - if literals.len() > 0 { + if !literals.is_empty() { let cursor = input.cursor(); while input.can_read() && input.peek() != ' ' { input.skip(); @@ -83,18 +75,10 @@ impl CommandNode { if let Some(literal) = literal { return vec![literal.clone()]; } else { - return self - .arguments() - .values() - .map(|argument| argument.clone()) - .collect(); + return self.arguments().values().cloned().collect(); } } else { - return self - .arguments() - .values() - .map(|argument| argument.clone()) - .collect(); + self.arguments().values().cloned().collect() } } @@ -147,7 +131,7 @@ impl CommandNode { .expect("Couldn't get result for some reason"); let parsed = ParsedArgument { range: StringRange::between(start, reader.cursor()), - result: result, + result, }; context_builder.with_argument(&argument.name, parsed.clone()); @@ -175,7 +159,7 @@ impl CommandNode { fn parse(&self, reader: &mut StringReader) -> Option { match self.value { - ArgumentBuilderType::Argument(ref argument) => { + ArgumentBuilderType::Argument(_) => { panic!("Can't parse argument.") } ArgumentBuilderType::Literal(ref literal) => { @@ -252,7 +236,9 @@ impl PartialEq for CommandNode { 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; } @@ -260,6 +246,9 @@ impl PartialEq for CommandNode { return false; } } + else if other.command.is_some() { + return false; + } true } } diff --git a/azalea-brigadier/tests/arguments/bool_argument_type_test.rs b/azalea-brigadier/tests/arguments/bool_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/double_argument_type_test.rs b/azalea-brigadier/tests/arguments/double_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/float_argument_type_test.rs b/azalea-brigadier/tests/arguments/float_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/integer_argument_type_test.rs b/azalea-brigadier/tests/arguments/integer_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/long_argument_type_test.rs b/azalea-brigadier/tests/arguments/long_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/arguments/string_argument_type_test.rs b/azalea-brigadier/tests/arguments/string_argument_type_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/builder/argument_builder_test.rs b/azalea-brigadier/tests/builder/argument_builder_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/builder/literal_argument_builder_test.rs b/azalea-brigadier/tests/builder/literal_argument_builder_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/builder/required_argument_builder_test.rs b/azalea-brigadier/tests/builder/required_argument_builder_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/tests/command_dispatcher_test.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/tests/command_dispatcher_usages_test.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/tests/command_suggestions_test.rs b/azalea-brigadier/tests/command_suggestions_test.rs deleted file mode 100644 index 8b137891..00000000 --- a/azalea-brigadier/tests/command_suggestions_test.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/azalea-brigadier/tests/context/command_context_test.rs b/azalea-brigadier/tests/context/command_context_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/context/parsed_argument_test.rs b/azalea-brigadier/tests/context/parsed_argument_test.rs deleted file mode 100644 index e69de29b..00000000 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 deleted file mode 100644 index e69de29b..00000000 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 deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/suggestion/suggestion_test.rs b/azalea-brigadier/tests/suggestion/suggestion_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs b/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/suggestion/suggestions_test.rs b/azalea-brigadier/tests/suggestion/suggestions_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/tree/abstract_command_node_test.rs b/azalea-brigadier/tests/tree/abstract_command_node_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/tree/argument_command_node_test.rs b/azalea-brigadier/tests/tree/argument_command_node_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/tree/literal_command_node_test.rs b/azalea-brigadier/tests/tree/literal_command_node_test.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/azalea-brigadier/tests/tree/root_command_node_test.rs b/azalea-brigadier/tests/tree/root_command_node_test.rs deleted file mode 100644 index e69de29b..00000000 -- cgit v1.2.3 From 17d9f676ccdc69743e7717bb91f7ff2999c065f8 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 17:59:21 +0000 Subject: general cleanup --- azalea-brigadier/README.md | 3 +- azalea-brigadier/src/dispatcher.rs | 2 +- azalea-brigadier/src/lib.rs | 19 ++- azalea-brigadier/src/string_reader.rs | 2 +- azalea-brigadier/src/tree.rs | 279 ---------------------------------- 5 files changed, 16 insertions(+), 289 deletions(-) delete mode 100644 azalea-brigadier/src/tree.rs (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/README.md b/azalea-brigadier/README.md index df69b5c0..a7318566 100644 --- a/azalea-brigadier/README.md +++ b/azalea-brigadier/README.md @@ -1,4 +1,3 @@ # Azalea Brigadier -A Rustier port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library. - +A Rust port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library. diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index 9f8edce4..ce89b81d 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -589,7 +589,7 @@ mod tests { let source1 = Rc::new(CommandSource {}); let source2 = Rc::new(CommandSource {}); - let modifier = move |source: &CommandContext| -> Result>, CommandSyntaxException> { + let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { Ok(vec![source1.clone(), source2.clone()]) }; diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index d3db8dcf..e359e274 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -17,6 +17,7 @@ mod tests { use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, + context::CommandContext, dispatcher::CommandDispatcher, parsers::{get_integer, integer}, }; @@ -27,7 +28,7 @@ mod tests { #[test] fn it_works() { - let mut dispatcher = CommandDispatcher::::new(); + let mut dispatcher = CommandDispatcher::new(); let source = Rc::new(CommandSourceStack { player: "player".to_string(), @@ -35,11 +36,17 @@ mod tests { dispatcher.register( literal("foo") - .then(argument("bar", integer()).executes(|c| { - println!("Bar is {:?}", get_integer(c, "bar")); - 2 - })) - .executes(|c| { + .then(argument("bar", integer()).executes( + |c: &CommandContext| { + println!( + "Bar is {:?} and player is {}", + get_integer(c, "bar"), + c.source.player + ); + 2 + }, + )) + .executes(|_| { println!("Called foo with no arguments"); 1 }), diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index 5825871f..f220267a 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -824,7 +824,7 @@ mod test { #[test] fn expect_correct() { let mut reader = StringReader::from("abc".to_string()); - reader.expect('a'); + reader.expect('a').unwrap(); assert_eq!(reader.cursor(), 1); } diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs deleted file mode 100644 index 5ca199fa..00000000 --- a/azalea-brigadier/src/tree.rs +++ /dev/null @@ -1,279 +0,0 @@ -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::{cell::RefCell, collections::BTreeMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; - -/// An ArgumentBuilder that has been built. -#[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 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(), - } - } -} - -#[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(), - } - } -} - -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(); - // TODO: handle this better - 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: 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 { - // 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 b3864af9c4af83552e37fd71a46262967572f9e6 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 18:13:15 +0000 Subject: split stuff into more modules --- .../src/builder/required_argument_builder.rs | 5 +- azalea-brigadier/src/context.rs | 202 ------ azalea-brigadier/src/dispatcher.rs | 713 --------------------- azalea-brigadier/src/exceptions/mod.rs | 7 +- azalea-brigadier/src/lib.rs | 5 +- azalea-brigadier/src/modifier.rs | 4 +- azalea-brigadier/src/parse_results.rs | 2 +- azalea-brigadier/src/parsers.rs | 4 +- azalea-brigadier/src/string_range.rs | 45 -- azalea-brigadier/src/string_reader.rs | 4 +- 10 files changed, 12 insertions(+), 979 deletions(-) delete mode 100644 azalea-brigadier/src/context.rs delete mode 100644 azalea-brigadier/src/dispatcher.rs delete mode 100644 azalea-brigadier/src/string_range.rs (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index cae0cddb..a50f7ea9 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -1,8 +1,5 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; -use crate::{ - exceptions::command_syntax_exception::CommandSyntaxException, parsers::Parser, - string_reader::StringReader, -}; +use crate::{exceptions::CommandSyntaxException, 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, diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs deleted file mode 100644 index d71b3925..00000000 --- a/azalea-brigadier/src/context.rs +++ /dev/null @@ -1,202 +0,0 @@ -use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; - -use crate::{ - dispatcher::CommandDispatcher, - modifier::RedirectModifier, - string_range::StringRange, - tree::{CommandNode, ParsedCommandNode}, -}; - -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 { - // 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![], - 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() - } -} - -#[derive(Clone)] -pub struct ParsedArgument { - pub range: StringRange, - pub result: 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/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs deleted file mode 100644 index ce89b81d..00000000 --- a/azalea-brigadier/src/dispatcher.rs +++ /dev/null @@ -1,713 +0,0 @@ -use crate::{ - builder::argument_builder::ArgumentBuilder, - context::{CommandContext, CommandContextBuilder}, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::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/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs index 0bca556e..6b9c8d62 100644 --- a/azalea-brigadier/src/exceptions/mod.rs +++ b/azalea-brigadier/src/exceptions/mod.rs @@ -1,2 +1,5 @@ -pub mod builtin_exceptions; -pub mod command_syntax_exception; +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 e359e274..cffaac12 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,12 +1,11 @@ pub mod builder; +pub mod command_dispatcher; 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; @@ -17,8 +16,8 @@ mod tests { use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, + command_dispatcher::CommandDispatcher, context::CommandContext, - dispatcher::CommandDispatcher, parsers::{get_integer, integer}, }; diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs index 68a3304e..d40d59f9 100644 --- a/azalea-brigadier/src/modifier.rs +++ b/azalea-brigadier/src/modifier.rs @@ -1,8 +1,6 @@ use std::rc::Rc; -use crate::{ - context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, -}; +use crate::{context::CommandContext, exceptions::CommandSyntaxException}; pub type RedirectModifier = dyn Fn(&CommandContext) -> Result>, CommandSyntaxException>; diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index c9f26a04..3698ae82 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -1,5 +1,5 @@ use crate::{ - context::CommandContextBuilder, exceptions::command_syntax_exception::CommandSyntaxException, + context::CommandContextBuilder, exceptions::CommandSyntaxException, string_reader::StringReader, tree::CommandNode, }; use std::{collections::HashMap, fmt::Debug, rc::Rc}; diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs index 1984b52f..18ee9119 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/parsers.rs @@ -2,9 +2,7 @@ use std::{any::Any, rc::Rc}; use crate::{ context::CommandContext, - exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, - }, + exceptions::{BuiltInExceptions, CommandSyntaxException}, string_reader::StringReader, }; diff --git a/azalea-brigadier/src/string_range.rs b/azalea-brigadier/src/string_range.rs deleted file mode 100644 index 8ca88624..00000000 --- a/azalea-brigadier/src/string_range.rs +++ /dev/null @@ -1,45 +0,0 @@ -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 f220267a..dcb35fcb 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -1,6 +1,4 @@ -use crate::exceptions::{ - builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException, -}; +use crate::exceptions::{BuiltInExceptions, CommandSyntaxException}; use std::str::FromStr; #[derive(Clone)] -- cgit v1.2.3 From 78e692efc3fffc939f1b5af02e3cf32ca5302265 Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 18 Apr 2022 19:45:32 -0500 Subject: move tests into tests directory --- azalea-brigadier/src/builder/argument_builder.rs | 79 --- azalea-brigadier/src/command_dispatcher.rs | 415 +------------- azalea-brigadier/src/lib.rs | 48 -- azalea-brigadier/src/string_reader.rs | 616 --------------------- .../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 | 75 +++ .../tests/builder/literal_argument_builder_test.rs | 0 .../builder/required_argument_builder_test.rs | 0 azalea-brigadier/tests/command_dispatcher_test.rs | 410 ++++++++++++++ .../tests/command_dispatcher_usages_test.rs | 1 + azalea-brigadier/tests/command_suggestions_test.rs | 1 + .../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 | 612 ++++++++++++++++++++ .../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 28 files changed, 1100 insertions(+), 1157 deletions(-) create mode 100644 azalea-brigadier/tests/arguments/bool_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/double_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/float_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/integer_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/long_argument_type_test.rs create mode 100644 azalea-brigadier/tests/arguments/string_argument_type_test.rs create mode 100644 azalea-brigadier/tests/builder/argument_builder_test.rs create mode 100644 azalea-brigadier/tests/builder/literal_argument_builder_test.rs create mode 100644 azalea-brigadier/tests/builder/required_argument_builder_test.rs create mode 100644 azalea-brigadier/tests/command_dispatcher_test.rs create mode 100644 azalea-brigadier/tests/command_dispatcher_usages_test.rs create mode 100644 azalea-brigadier/tests/command_suggestions_test.rs create mode 100644 azalea-brigadier/tests/context/command_context_test.rs create mode 100644 azalea-brigadier/tests/context/parsed_argument_test.rs create mode 100644 azalea-brigadier/tests/exceptions/dynamic_command_syntax_exception_type_test.rs create mode 100644 azalea-brigadier/tests/exceptions/simple_command_syntax_exception_type_test.rs create mode 100644 azalea-brigadier/tests/string_reader_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestion_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestions_builder_test.rs create mode 100644 azalea-brigadier/tests/suggestion/suggestions_test.rs create mode 100644 azalea-brigadier/tests/tree/abstract_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/argument_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/literal_command_node_test.rs create mode 100644 azalea-brigadier/tests/tree/root_command_node_test.rs (limited to 'azalea-brigadier/src/string_reader.rs') diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 14d41f4e..d26b2a8a 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -135,82 +135,3 @@ impl Debug for ArgumentBuilder { .finish() } } - -#[cfg(test)] -mod tests { - use std::rc::Rc; - - use crate::{ - arguments::integer_argument_type::integer, - builder::{literal_argument_builder::literal, required_argument_builder::argument}, - }; - - use super::ArgumentBuilder; - - // public class ArgumentBuilderTest { - // private TestableArgumentBuilder builder; - - // @Before - // public void setUp() throws Exception { - // builder = new TestableArgumentBuilder<>(); - // } - - // @Test - // public void testArguments() throws Exception { - // final RequiredArgumentBuilder argument = argument("bar", integer()); - - // builder.then(argument); - - // assertThat(builder.getArguments(), hasSize(1)); - // assertThat(builder.getArguments(), hasItem((CommandNode) argument.build())); - // } - - #[test] - fn test_arguments() { - let mut builder: ArgumentBuilder<()> = literal("foo"); - - let argument: ArgumentBuilder<()> = argument("bar", integer()); - builder.then(argument.clone()); - assert_eq!(builder.arguments.children.len(), 1); - let built_argument = Rc::new(argument.build()); - assert!(builder - .arguments - .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/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index fc3f3d50..eab42dd8 100644 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -10,7 +10,7 @@ use std::{cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomDat #[derive(Default)] pub struct CommandDispatcher { - root: Rc>>, + pub root: Rc>>, _marker: PhantomData, } @@ -296,416 +296,3 @@ impl Clone for CommandDispatcher { } } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - arguments::integer_argument_type::integer, - builder::{literal_argument_builder::literal, required_argument_builder::argument}, - }; - - #[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/lib.rs b/azalea-brigadier/src/lib.rs index db46d46e..a294eb19 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -8,51 +8,3 @@ pub mod modifier; pub mod parse_results; pub mod string_reader; pub mod tree; - -#[cfg(test)] -mod tests { - - use std::rc::Rc; - - use crate::{ - arguments::integer_argument_type::{get_integer, integer}, - builder::{literal_argument_builder::literal, required_argument_builder::argument}, - command_dispatcher::CommandDispatcher, - context::CommandContext, - }; - - struct CommandSourceStack { - player: String, - } - - #[test] - fn it_works() { - let mut dispatcher = CommandDispatcher::new(); - - let source = Rc::new(CommandSourceStack { - player: "player".to_string(), - }); - - dispatcher.register( - literal("foo") - .then(argument("bar", integer()).executes( - |c: &CommandContext| { - println!( - "Bar is {:?} and player is {}", - get_integer(c, "bar"), - c.source.player - ); - 2 - }, - )) - .executes(|_| { - println!("Called foo with no arguments"); - 1 - }), - ); - - let parse = dispatcher.parse("foo 123".into(), source.clone()); - assert_eq!(CommandDispatcher::<_>::execute_parsed(parse).unwrap(), 2); - assert_eq!(dispatcher.execute("foo".into(), source).unwrap(), 1); - } -} diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index dcb35fcb..9eb09b57 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -272,619 +272,3 @@ impl StringReader { Ok(()) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn can_read() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.can_read(), true); - reader.skip(); // 'a' - assert_eq!(reader.can_read(), true); - reader.skip(); // 'b' - assert_eq!(reader.can_read(), true); - reader.skip(); // 'c' - assert_eq!(reader.can_read(), false); - } - - #[test] - fn get_remaining_length() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.remaining_length(), 3); - reader.cursor = 1; - assert_eq!(reader.remaining_length(), 2); - reader.cursor = 2; - assert_eq!(reader.remaining_length(), 1); - reader.cursor = 3; - assert_eq!(reader.remaining_length(), 0); - } - - #[test] - fn can_read_length() { - 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); - assert_eq!(reader.can_read_length(4), false); - assert_eq!(reader.can_read_length(5), false); - } - - #[test] - fn peek() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.peek(), 'a'); - assert_eq!(reader.cursor(), 0); - reader.cursor = 2; - assert_eq!(reader.peek(), 'c'); - assert_eq!(reader.cursor(), 2); - } - - #[test] - fn peek_length() { - 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); - reader.cursor = 1; - assert_eq!(reader.peek_offset(1), 'c'); - assert_eq!(reader.cursor(), 1); - } - - #[test] - fn read() { - let mut reader = StringReader::from("abc".to_string()); - assert_eq!(reader.read(), 'a'); - assert_eq!(reader.read(), 'b'); - assert_eq!(reader.read(), 'c'); - assert_eq!(reader.cursor(), 3); - } - - #[test] - fn skip() { - 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!".to_string()); - assert_eq!(reader.remaining(), "Hello!"); - reader.cursor = 3; - assert_eq!(reader.remaining(), "lo!"); - reader.cursor = 6; - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn get_read() { - let mut reader = StringReader::from("Hello!".to_string()); - assert_eq!(reader.get_read(), ""); - reader.cursor = 3; - assert_eq!(reader.get_read(), "Hel"); - reader.cursor = 6; - assert_eq!(reader.get_read(), "Hello!"); - } - - #[test] - fn skip_whitespace_none() { - 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!".to_string()); - reader.skip_whitespace(); - assert_eq!(reader.cursor(), 5); - } - - #[test] - fn skip_whitespace_empty() { - 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".to_string()); - assert_eq!(reader.read_unquoted_string(), "hello"); - assert_eq!(reader.get_read(), "hello"); - assert_eq!(reader.remaining(), " world"); - } - - #[test] - fn read_unquoted_string_empty() { - let mut reader = StringReader::from("".to_string()); - assert_eq!(reader.read_unquoted_string(), ""); - assert_eq!(reader.get_read(), ""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_unquoted_string_empty_with_remaining() { - 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"); - } - - #[test] - fn read_quoted_string() { - 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(), ""); - } - - #[test] - fn read_single_quoted_string() { - 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(), ""); - } - - #[test] - fn read_mixed_quoted_string_double_inside_single() { - 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(), ""); - } - - #[test] - fn read_mixed_quoted_string_single_inside_double() { - 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(), ""); - } - - #[test] - fn read_quoted_string_empty_quoted() { - let mut reader = StringReader::from("".to_string()); - assert_eq!(reader.read_quoted_string().unwrap(), ""); - assert_eq!(reader.get_read(), ""); - assert_eq!(reader.remaining(), ""); - } - - #[test] - fn read_quoted_string_empty_quoted_with_remaining() { - 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"); - } - - #[test] - fn read_quoted_string_with_escaped_quote() { - 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(), ""); - } - - #[test] - fn read_quoted_string_with_escaped_escapes() { - 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(), ""); - } - - #[test] - fn read_quoted_string_with_remaining() { - 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"); - } - - #[test] - fn read_quoted_string_with_immediate_remaining() { - 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"); - } - - #[test] - fn read_quoted_string_no_open() { - let mut reader = StringReader::from("hello world\"".to_string()); - 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.cursor(), Some(0)); - } - } - - #[test] - fn read_quoted_string_no_close() { - let mut reader = StringReader::from("\"hello world".to_string()); - 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.cursor(), Some(12)); - } - } - - #[test] - fn read_quoted_string_invalid_escape() { - let mut reader = StringReader::from("\"hello\\nworld\"".to_string()); - let result = reader.read_quoted_string(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidEscape { character: 'n' } - ); - assert_eq!(e.cursor(), Some(7)); - } - } - - #[test] - fn read_quoted_string_invalid_quote_escape() { - let mut reader = StringReader::from("'hello\\\"\'world".to_string()); - let result = reader.read_quoted_string(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidEscape { character: '"' } - ); - assert_eq!(e.cursor(), Some(7)); - } - } - - #[test] - fn read_string_no_quotes() { - 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"); - } - - #[test] - fn read_string_single_quotes() { - 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(), ""); - } - - #[test] - fn read_string_double_quotes() { - 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(), ""); - } - - #[test] - fn read_int() { - 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(), ""); - } - - #[test] - fn read_int_negative() { - 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(), ""); - } - - #[test] - fn read_int_invalid() { - let mut reader = StringReader::from("12.34".to_string()); - let result = reader.read_int(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidInt { - value: "12.34".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_int_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_int(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedInt); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_int_with_remaining() { - 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"); - } - - #[test] - fn read_int_with_remaining_immediate() { - 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"); - } - - #[test] - fn read_long() { - 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(), ""); - } - - #[test] - fn read_long_negative() { - 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(), ""); - } - - #[test] - fn read_long_invalid() { - let mut reader = StringReader::from("12.34".to_string()); - let result = reader.read_long(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidLong { - value: "12.34".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_long_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_long(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedLong); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_long_with_remaining() { - 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"); - } - - #[test] - fn read_long_with_remaining_immediate() { - 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"); - } - - #[test] - fn read_double() { - 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(), ""); - } - - #[test] - fn read_double_with_decimal() { - 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(), ""); - } - - #[test] - fn read_double_negative() { - 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(), ""); - } - - #[test] - fn read_double_invalid() { - let mut reader = StringReader::from("12.34.56".to_string()); - let result = reader.read_double(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidDouble { - value: "12.34.56".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_double_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_double(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedDouble); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_double_with_remaining() { - 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"); - } - - #[test] - fn read_double_with_remaining_immediate() { - 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"); - } - - #[test] - fn read_float() { - 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(), ""); - } - - #[test] - fn read_float_with_decimal() { - 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(), ""); - } - - #[test] - fn read_float_negative() { - 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(), ""); - } - - #[test] - fn read_float_invalid() { - let mut reader = StringReader::from("12.34.56".to_string()); - let result = reader.read_float(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidFloat { - value: "12.34.56".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_float_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_float(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedFloat); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_float_with_remaining() { - 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"); - } - - #[test] - fn read_float_with_remaining_immediate() { - 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"); - } - - #[test] - fn expect_correct() { - let mut reader = StringReader::from("abc".to_string()); - reader.expect('a').unwrap(); - assert_eq!(reader.cursor(), 1); - } - - #[test] - fn expect_incorrect() { - let mut reader = StringReader::from("bcd".to_string()); - let result = reader.expect('a'); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn expect_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.expect('a'); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_boolean_correct() { - 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".to_string()); - let result = reader.read_boolean(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!( - e.get_type(), - &BuiltInExceptions::ReaderInvalidBool { - value: "tuesday".to_string() - } - ); - assert_eq!(e.cursor(), Some(0)); - } - } - - #[test] - fn read_boolean_none() { - let mut reader = StringReader::from("".to_string()); - let result = reader.read_boolean(); - assert!(result.is_err()); - if let Err(e) = result { - assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedBool); - assert_eq!(e.cursor(), Some(0)); - } - } -} diff --git a/azalea-brigadier/tests/arguments/bool_argument_type_test.rs b/azalea-brigadier/tests/arguments/bool_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/double_argument_type_test.rs b/azalea-brigadier/tests/arguments/double_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/float_argument_type_test.rs b/azalea-brigadier/tests/arguments/float_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/integer_argument_type_test.rs b/azalea-brigadier/tests/arguments/integer_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/long_argument_type_test.rs b/azalea-brigadier/tests/arguments/long_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/arguments/string_argument_type_test.rs b/azalea-brigadier/tests/arguments/string_argument_type_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/builder/argument_builder_test.rs b/azalea-brigadier/tests/builder/argument_builder_test.rs new file mode 100644 index 00000000..e570c988 --- /dev/null +++ b/azalea-brigadier/tests/builder/argument_builder_test.rs @@ -0,0 +1,75 @@ +use std::rc::Rc; + +use crate::{ + arguments::integer_argument_type::integer, + builder::{literal_argument_builder::literal, required_argument_builder::argument}, +}; + +use super::ArgumentBuilder; + +// public class ArgumentBuilderTest { +// private TestableArgumentBuilder builder; + +// @Before +// public void setUp() throws Exception { +// builder = new TestableArgumentBuilder<>(); +// } + +// @Test +// public void testArguments() throws Exception { +// final RequiredArgumentBuilder argument = argument("bar", integer()); + +// builder.then(argument); + +// assertThat(builder.getArguments(), hasSize(1)); +// assertThat(builder.getArguments(), hasItem((CommandNode) argument.build())); +// } + +#[test] +fn test_arguments() { + let mut builder: ArgumentBuilder<()> = literal("foo"); + + let argument: ArgumentBuilder<()> = argument("bar", integer()); + builder.then(argument.clone()); + assert_eq!(builder.arguments.children.len(), 1); + let built_argument = Rc::new(argument.build()); + assert!(builder + .arguments + .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/tests/builder/literal_argument_builder_test.rs b/azalea-brigadier/tests/builder/literal_argument_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/builder/required_argument_builder_test.rs b/azalea-brigadier/tests/builder/required_argument_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs new file mode 100644 index 00000000..cb33ac73 --- /dev/null +++ b/azalea-brigadier/tests/command_dispatcher_test.rs @@ -0,0 +1,410 @@ +use std::rc::Rc; + +use azalea_brigadier::{ + arguments::integer_argument_type::integer, + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + command_dispatcher::CommandDispatcher, + context::CommandContext, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + string_reader::StringReader, +}; + +#[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/tests/command_dispatcher_usages_test.rs b/azalea-brigadier/tests/command_dispatcher_usages_test.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ 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 new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/azalea-brigadier/tests/command_suggestions_test.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/tests/context/command_context_test.rs b/azalea-brigadier/tests/context/command_context_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/context/parsed_argument_test.rs b/azalea-brigadier/tests/context/parsed_argument_test.rs new file mode 100644 index 00000000..e69de29b 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 new file mode 100644 index 00000000..e69de29b 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 new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/string_reader_test.rs b/azalea-brigadier/tests/string_reader_test.rs new file mode 100644 index 00000000..5008eff8 --- /dev/null +++ b/azalea-brigadier/tests/string_reader_test.rs @@ -0,0 +1,612 @@ +use azalea_brigadier::{exceptions::BuiltInExceptions, string_reader::StringReader}; + +#[test] +fn can_read() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.can_read(), true); + reader.skip(); // 'a' + assert_eq!(reader.can_read(), true); + reader.skip(); // 'b' + assert_eq!(reader.can_read(), true); + reader.skip(); // 'c' + assert_eq!(reader.can_read(), false); +} + +#[test] +fn get_remaining_length() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.remaining_length(), 3); + reader.cursor = 1; + assert_eq!(reader.remaining_length(), 2); + reader.cursor = 2; + assert_eq!(reader.remaining_length(), 1); + reader.cursor = 3; + assert_eq!(reader.remaining_length(), 0); +} + +#[test] +fn can_read_length() { + 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); + assert_eq!(reader.can_read_length(4), false); + assert_eq!(reader.can_read_length(5), false); +} + +#[test] +fn peek() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.peek(), 'a'); + assert_eq!(reader.cursor(), 0); + reader.cursor = 2; + assert_eq!(reader.peek(), 'c'); + assert_eq!(reader.cursor(), 2); +} + +#[test] +fn peek_length() { + 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); + reader.cursor = 1; + assert_eq!(reader.peek_offset(1), 'c'); + assert_eq!(reader.cursor(), 1); +} + +#[test] +fn read() { + let mut reader = StringReader::from("abc".to_string()); + assert_eq!(reader.read(), 'a'); + assert_eq!(reader.read(), 'b'); + assert_eq!(reader.read(), 'c'); + assert_eq!(reader.cursor(), 3); +} + +#[test] +fn skip() { + 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!".to_string()); + assert_eq!(reader.remaining(), "Hello!"); + reader.cursor = 3; + assert_eq!(reader.remaining(), "lo!"); + reader.cursor = 6; + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn get_read() { + let mut reader = StringReader::from("Hello!".to_string()); + assert_eq!(reader.get_read(), ""); + reader.cursor = 3; + assert_eq!(reader.get_read(), "Hel"); + reader.cursor = 6; + assert_eq!(reader.get_read(), "Hello!"); +} + +#[test] +fn skip_whitespace_none() { + 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!".to_string()); + reader.skip_whitespace(); + assert_eq!(reader.cursor(), 5); +} + +#[test] +fn skip_whitespace_empty() { + 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".to_string()); + assert_eq!(reader.read_unquoted_string(), "hello"); + assert_eq!(reader.get_read(), "hello"); + assert_eq!(reader.remaining(), " world"); +} + +#[test] +fn read_unquoted_string_empty() { + let mut reader = StringReader::from("".to_string()); + assert_eq!(reader.read_unquoted_string(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_unquoted_string_empty_with_remaining() { + 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"); +} + +#[test] +fn read_quoted_string() { + 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(), ""); +} + +#[test] +fn read_single_quoted_string() { + 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(), ""); +} + +#[test] +fn read_mixed_quoted_string_double_inside_single() { + 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(), ""); +} + +#[test] +fn read_mixed_quoted_string_single_inside_double() { + 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(), ""); +} + +#[test] +fn read_quoted_string_empty_quoted() { + let mut reader = StringReader::from("".to_string()); + assert_eq!(reader.read_quoted_string().unwrap(), ""); + assert_eq!(reader.get_read(), ""); + assert_eq!(reader.remaining(), ""); +} + +#[test] +fn read_quoted_string_empty_quoted_with_remaining() { + 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"); +} + +#[test] +fn read_quoted_string_with_escaped_quote() { + 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(), ""); +} + +#[test] +fn read_quoted_string_with_escaped_escapes() { + 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(), ""); +} + +#[test] +fn read_quoted_string_with_remaining() { + 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"); +} + +#[test] +fn read_quoted_string_with_immediate_remaining() { + 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"); +} + +#[test] +fn read_quoted_string_no_open() { + let mut reader = StringReader::from("hello world\"".to_string()); + 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.cursor(), Some(0)); + } +} + +#[test] +fn read_quoted_string_no_close() { + let mut reader = StringReader::from("\"hello world".to_string()); + 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.cursor(), Some(12)); + } +} + +#[test] +fn read_quoted_string_invalid_escape() { + let mut reader = StringReader::from("\"hello\\nworld\"".to_string()); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidEscape { character: 'n' } + ); + assert_eq!(e.cursor(), Some(7)); + } +} + +#[test] +fn read_quoted_string_invalid_quote_escape() { + let mut reader = StringReader::from("'hello\\\"\'world".to_string()); + let result = reader.read_quoted_string(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidEscape { character: '"' } + ); + assert_eq!(e.cursor(), Some(7)); + } +} + +#[test] +fn read_string_no_quotes() { + 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"); +} + +#[test] +fn read_string_single_quotes() { + 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(), ""); +} + +#[test] +fn read_string_double_quotes() { + 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(), ""); +} + +#[test] +fn read_int() { + 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(), ""); +} + +#[test] +fn read_int_negative() { + 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(), ""); +} + +#[test] +fn read_int_invalid() { + let mut reader = StringReader::from("12.34".to_string()); + let result = reader.read_int(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidInt { + value: "12.34".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_int_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_int(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedInt); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_int_with_remaining() { + 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"); +} + +#[test] +fn read_int_with_remaining_immediate() { + 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"); +} + +#[test] +fn read_long() { + 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(), ""); +} + +#[test] +fn read_long_negative() { + 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(), ""); +} + +#[test] +fn read_long_invalid() { + let mut reader = StringReader::from("12.34".to_string()); + let result = reader.read_long(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidLong { + value: "12.34".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_long_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_long(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedLong); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_long_with_remaining() { + 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"); +} + +#[test] +fn read_long_with_remaining_immediate() { + 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"); +} + +#[test] +fn read_double() { + 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(), ""); +} + +#[test] +fn read_double_with_decimal() { + 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(), ""); +} + +#[test] +fn read_double_negative() { + 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(), ""); +} + +#[test] +fn read_double_invalid() { + let mut reader = StringReader::from("12.34.56".to_string()); + let result = reader.read_double(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidDouble { + value: "12.34.56".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_double_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_double(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedDouble); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_double_with_remaining() { + 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"); +} + +#[test] +fn read_double_with_remaining_immediate() { + 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"); +} + +#[test] +fn read_float() { + 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(), ""); +} + +#[test] +fn read_float_with_decimal() { + 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(), ""); +} + +#[test] +fn read_float_negative() { + 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(), ""); +} + +#[test] +fn read_float_invalid() { + let mut reader = StringReader::from("12.34.56".to_string()); + let result = reader.read_float(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidFloat { + value: "12.34.56".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_float_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_float(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedFloat); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_float_with_remaining() { + 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"); +} + +#[test] +fn read_float_with_remaining_immediate() { + 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"); +} + +#[test] +fn expect_correct() { + let mut reader = StringReader::from("abc".to_string()); + reader.expect('a').unwrap(); + assert_eq!(reader.cursor(), 1); +} + +#[test] +fn expect_incorrect() { + let mut reader = StringReader::from("bcd".to_string()); + let result = reader.expect('a'); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn expect_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.expect('a'); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_boolean_correct() { + 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".to_string()); + let result = reader.read_boolean(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!( + e.get_type(), + &BuiltInExceptions::ReaderInvalidBool { + value: "tuesday".to_string() + } + ); + assert_eq!(e.cursor(), Some(0)); + } +} + +#[test] +fn read_boolean_none() { + let mut reader = StringReader::from("".to_string()); + let result = reader.read_boolean(); + assert!(result.is_err()); + if let Err(e) = result { + assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedBool); + assert_eq!(e.cursor(), Some(0)); + } +} diff --git a/azalea-brigadier/tests/suggestion/suggestion_test.rs b/azalea-brigadier/tests/suggestion/suggestion_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs b/azalea-brigadier/tests/suggestion/suggestions_builder_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/suggestion/suggestions_test.rs b/azalea-brigadier/tests/suggestion/suggestions_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/abstract_command_node_test.rs b/azalea-brigadier/tests/tree/abstract_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/argument_command_node_test.rs b/azalea-brigadier/tests/tree/argument_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/literal_command_node_test.rs b/azalea-brigadier/tests/tree/literal_command_node_test.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/tests/tree/root_command_node_test.rs b/azalea-brigadier/tests/tree/root_command_node_test.rs new file mode 100644 index 00000000..e69de29b -- 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/string_reader.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