mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
implement missing brigadier features and cleanup some more
This commit is contained in:
parent
da73b4316d
commit
a5e7ff771d
26 changed files with 436 additions and 289 deletions
|
@ -1,13 +1,13 @@
|
|||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
exceptions::CommandSyntaxException,
|
||||
errors::CommandSyntaxError,
|
||||
string_reader::StringReader,
|
||||
suggestion::{Suggestions, SuggestionsBuilder},
|
||||
};
|
||||
|
||||
pub trait ArgumentType {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException>;
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxError>;
|
||||
|
||||
fn list_suggestions(&self, _builder: SuggestionsBuilder) -> Suggestions {
|
||||
Suggestions::default()
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc};
|
|||
use super::ArgumentType;
|
||||
use crate::{
|
||||
context::CommandContext,
|
||||
exceptions::CommandSyntaxException,
|
||||
errors::CommandSyntaxError,
|
||||
string_reader::StringReader,
|
||||
suggestion::{Suggestions, SuggestionsBuilder},
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
|||
struct Boolean;
|
||||
|
||||
impl ArgumentType for Boolean {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxError> {
|
||||
Ok(Arc::new(reader.read_boolean()?))
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc};
|
|||
use super::ArgumentType;
|
||||
use crate::{
|
||||
context::CommandContext,
|
||||
exceptions::{BuiltInExceptions, CommandSyntaxException},
|
||||
errors::{BuiltInError, CommandSyntaxError},
|
||||
string_reader::StringReader,
|
||||
};
|
||||
|
||||
|
@ -14,14 +14,14 @@ struct Double {
|
|||
}
|
||||
|
||||
impl ArgumentType for Double {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxError> {
|
||||
let start = reader.cursor;
|
||||
let result = reader.read_double()?;
|
||||
if let Some(minimum) = self.minimum
|
||||
&& result < minimum
|
||||
{
|
||||
reader.cursor = start;
|
||||
return Err(BuiltInExceptions::DoubleTooSmall {
|
||||
return Err(BuiltInError::DoubleTooSmall {
|
||||
found: result,
|
||||
min: minimum,
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl ArgumentType for Double {
|
|||
&& result > maximum
|
||||
{
|
||||
reader.cursor = start;
|
||||
return Err(BuiltInExceptions::DoubleTooBig {
|
||||
return Err(BuiltInError::DoubleTooBig {
|
||||
found: result,
|
||||
max: maximum,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc};
|
|||
use super::ArgumentType;
|
||||
use crate::{
|
||||
context::CommandContext,
|
||||
exceptions::{BuiltInExceptions, CommandSyntaxException},
|
||||
errors::{BuiltInError, CommandSyntaxError},
|
||||
string_reader::StringReader,
|
||||
};
|
||||
|
||||
|
@ -14,14 +14,14 @@ struct Float {
|
|||
}
|
||||
|
||||
impl ArgumentType for Float {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxError> {
|
||||
let start = reader.cursor;
|
||||
let result = reader.read_float()?;
|
||||
if let Some(minimum) = self.minimum
|
||||
&& result < minimum
|
||||
{
|
||||
reader.cursor = start;
|
||||
return Err(BuiltInExceptions::FloatTooSmall {
|
||||
return Err(BuiltInError::FloatTooSmall {
|
||||
found: result,
|
||||
min: minimum,
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl ArgumentType for Float {
|
|||
&& result > maximum
|
||||
{
|
||||
reader.cursor = start;
|
||||
return Err(BuiltInExceptions::FloatTooBig {
|
||||
return Err(BuiltInError::FloatTooBig {
|
||||
found: result,
|
||||
max: maximum,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc};
|
|||
use super::ArgumentType;
|
||||
use crate::{
|
||||
context::CommandContext,
|
||||
exceptions::{BuiltInExceptions, CommandSyntaxException},
|
||||
errors::{BuiltInError, CommandSyntaxError},
|
||||
string_reader::StringReader,
|
||||
};
|
||||
|
||||
|
@ -14,14 +14,14 @@ struct Integer {
|
|||
}
|
||||
|
||||
impl ArgumentType for Integer {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxError> {
|
||||
let start = reader.cursor;
|
||||
let result = reader.read_int()?;
|
||||
if let Some(minimum) = self.minimum
|
||||
&& result < minimum
|
||||
{
|
||||
reader.cursor = start;
|
||||
return Err(BuiltInExceptions::IntegerTooSmall {
|
||||
return Err(BuiltInError::IntegerTooSmall {
|
||||
found: result,
|
||||
min: minimum,
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl ArgumentType for Integer {
|
|||
&& result > maximum
|
||||
{
|
||||
reader.cursor = start;
|
||||
return Err(BuiltInExceptions::IntegerTooBig {
|
||||
return Err(BuiltInError::IntegerTooBig {
|
||||
found: result,
|
||||
max: maximum,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{any::Any, sync::Arc};
|
|||
use super::ArgumentType;
|
||||
use crate::{
|
||||
context::CommandContext,
|
||||
exceptions::{BuiltInExceptions, CommandSyntaxException},
|
||||
errors::{BuiltInError, CommandSyntaxError},
|
||||
string_reader::StringReader,
|
||||
};
|
||||
|
||||
|
@ -14,14 +14,14 @@ struct Long {
|
|||
}
|
||||
|
||||
impl ArgumentType for Long {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxError> {
|
||||
let start = reader.cursor;
|
||||
let result = reader.read_long()?;
|
||||
if let Some(minimum) = self.minimum
|
||||
&& result < minimum
|
||||
{
|
||||
reader.cursor = start;
|
||||
return Err(BuiltInExceptions::LongTooSmall {
|
||||
return Err(BuiltInError::LongTooSmall {
|
||||
found: result,
|
||||
min: minimum,
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl ArgumentType for Long {
|
|||
&& result > maximum
|
||||
{
|
||||
reader.cursor = start;
|
||||
return Err(BuiltInExceptions::LongTooBig {
|
||||
return Err(BuiltInError::LongTooBig {
|
||||
found: result,
|
||||
max: maximum,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use super::ArgumentType;
|
||||
use crate::{
|
||||
context::CommandContext, exceptions::CommandSyntaxException, string_reader::StringReader,
|
||||
};
|
||||
use crate::{context::CommandContext, errors::CommandSyntaxError, string_reader::StringReader};
|
||||
|
||||
pub enum StringArgument {
|
||||
/// Match up until the next space.
|
||||
|
@ -16,7 +14,7 @@ pub enum StringArgument {
|
|||
}
|
||||
|
||||
impl ArgumentType for StringArgument {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
|
||||
fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxError> {
|
||||
let result = match self {
|
||||
StringArgument::SingleWord => reader.read_unquoted_string().to_string(),
|
||||
StringArgument::QuotablePhrase => reader.read_string()?,
|
||||
|
|
|
@ -5,6 +5,7 @@ use parking_lot::RwLock;
|
|||
use super::{literal_argument_builder::Literal, required_argument_builder::Argument};
|
||||
use crate::{
|
||||
context::CommandContext,
|
||||
errors::CommandSyntaxError,
|
||||
modifier::RedirectModifier,
|
||||
tree::{Command, CommandNode},
|
||||
};
|
||||
|
@ -89,6 +90,16 @@ impl<S> ArgumentBuilder<S> {
|
|||
pub fn executes<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: Fn(&CommandContext<S>) -> i32 + Send + Sync + 'static,
|
||||
{
|
||||
self.command = Some(Arc::new(move |ctx: &CommandContext<S>| Ok(f(ctx))));
|
||||
self
|
||||
}
|
||||
|
||||
/// Same as [`Self::executes`] but returns a `Result<i32,
|
||||
/// CommandSyntaxError>`.
|
||||
pub fn executes_result<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: Fn(&CommandContext<S>) -> Result<i32, CommandSyntaxError> + Send + Sync + 'static,
|
||||
{
|
||||
self.command = Some(Arc::new(f));
|
||||
self
|
||||
|
|
|
@ -8,7 +8,7 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType};
|
|||
use crate::{
|
||||
arguments::ArgumentType,
|
||||
context::CommandContext,
|
||||
exceptions::CommandSyntaxException,
|
||||
errors::CommandSyntaxError,
|
||||
string_reader::StringReader,
|
||||
suggestion::{SuggestionProvider, Suggestions, SuggestionsBuilder},
|
||||
};
|
||||
|
@ -33,7 +33,7 @@ impl<S> Argument<S> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxException> {
|
||||
pub fn parse(&self, reader: &mut StringReader) -> Result<Arc<dyn Any>, CommandSyntaxError> {
|
||||
self.parser.parse(reader)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{HashMap, HashSet},
|
||||
mem, ptr,
|
||||
ptr,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
@ -10,9 +10,10 @@ use parking_lot::RwLock;
|
|||
|
||||
use crate::{
|
||||
builder::argument_builder::ArgumentBuilder,
|
||||
context::{CommandContext, CommandContextBuilder},
|
||||
exceptions::{BuiltInExceptions, CommandSyntaxException},
|
||||
context::{CommandContextBuilder, ContextChain},
|
||||
errors::{BuiltInError, CommandSyntaxError},
|
||||
parse_results::ParseResults,
|
||||
result_consumer::{DefaultResultConsumer, ResultConsumer},
|
||||
string_reader::StringReader,
|
||||
suggestion::{Suggestions, SuggestionsBuilder},
|
||||
tree::CommandNode,
|
||||
|
@ -30,12 +31,14 @@ where
|
|||
Self: Sync + Send,
|
||||
{
|
||||
pub root: Arc<RwLock<CommandNode<S>>>,
|
||||
consumer: Box<dyn ResultConsumer<S> + Send + Sync>,
|
||||
}
|
||||
|
||||
impl<S> CommandDispatcher<S> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
root: Arc::new(RwLock::new(CommandNode::default())),
|
||||
consumer: Box::new(DefaultResultConsumer),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,10 +67,10 @@ impl<S> CommandDispatcher<S> {
|
|||
node: &Arc<RwLock<CommandNode<S>>>,
|
||||
original_reader: &StringReader,
|
||||
context_so_far: CommandContextBuilder<'a, S>,
|
||||
) -> Result<ParseResults<'a, S>, CommandSyntaxException> {
|
||||
) -> Result<ParseResults<'a, S>, CommandSyntaxError> {
|
||||
let source = context_so_far.source.clone();
|
||||
#[allow(clippy::mutable_key_type)] // this is fine because we don't mutate the key
|
||||
let mut errors = HashMap::<Rc<CommandNode<S>>, CommandSyntaxException>::new();
|
||||
let mut errors = HashMap::<Rc<CommandNode<S>>, CommandSyntaxError>::new();
|
||||
let mut potentials: Vec<ParseResults<S>> = vec![];
|
||||
let cursor = original_reader.cursor();
|
||||
|
||||
|
@ -83,7 +86,7 @@ impl<S> CommandDispatcher<S> {
|
|||
if let Err(ex) = parse_with_context_result {
|
||||
errors.insert(
|
||||
Rc::new((*child.read()).clone()),
|
||||
BuiltInExceptions::DispatcherParseException {
|
||||
BuiltInError::DispatcherParseException {
|
||||
message: ex.message(),
|
||||
}
|
||||
.create_with_context(&reader),
|
||||
|
@ -94,8 +97,7 @@ impl<S> CommandDispatcher<S> {
|
|||
if reader.can_read() && reader.peek() != ' ' {
|
||||
errors.insert(
|
||||
Rc::new((*child.read()).clone()),
|
||||
BuiltInExceptions::DispatcherExpectedArgumentSeparator
|
||||
.create_with_context(&reader),
|
||||
BuiltInError::DispatcherExpectedArgumentSeparator.create_with_context(&reader),
|
||||
);
|
||||
reader.cursor = cursor;
|
||||
continue;
|
||||
|
@ -179,11 +181,11 @@ impl<S> CommandDispatcher<S> {
|
|||
&self,
|
||||
input: impl Into<StringReader>,
|
||||
source: S,
|
||||
) -> Result<i32, CommandSyntaxException> {
|
||||
) -> Result<i32, CommandSyntaxError> {
|
||||
let input = input.into();
|
||||
|
||||
let parse = self.parse(input, source);
|
||||
Self::execute_parsed(parse)
|
||||
self.execute_parsed(parse)
|
||||
}
|
||||
|
||||
pub fn add_paths(
|
||||
|
@ -235,91 +237,26 @@ impl<S> CommandDispatcher<S> {
|
|||
}
|
||||
|
||||
/// Executes a given pre-parsed command.
|
||||
pub fn execute_parsed(parse: ParseResults<S>) -> Result<i32, CommandSyntaxException> {
|
||||
pub fn execute_parsed(&self, parse: ParseResults<S>) -> Result<i32, CommandSyntaxError> {
|
||||
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)
|
||||
);
|
||||
return Err(if parse.exceptions.len() == 1 {
|
||||
parse.exceptions.values().next().unwrap().clone()
|
||||
} else if parse.context.range.is_empty() {
|
||||
BuiltInError::DispatcherUnknownCommand.create_with_context(&parse.reader)
|
||||
} else {
|
||||
BuiltInError::DispatcherUnknownArgument.create_with_context(&parse.reader)
|
||||
});
|
||||
}
|
||||
let mut result = 0i32;
|
||||
let mut successful_forks = 0;
|
||||
let mut forked = false;
|
||||
let mut found_command = false;
|
||||
|
||||
let command = parse.reader.string();
|
||||
let original = parse.context.build(command);
|
||||
let mut contexts = vec![original];
|
||||
let mut next: Vec<CommandContext<S>> = vec![];
|
||||
let original = Rc::new(parse.context.build(command));
|
||||
let flat_context = ContextChain::try_flatten(original.clone());
|
||||
let Some(flat_context) = flat_context else {
|
||||
self.consumer.on_command_complete(original, false, 0);
|
||||
return Err(BuiltInError::DispatcherUnknownCommand.create_with_context(&parse.reader));
|
||||
};
|
||||
|
||||
while !contexts.is_empty() {
|
||||
for context in &contexts {
|
||||
let child = &context.child;
|
||||
if let Some(child) = child {
|
||||
forked |= child.forks;
|
||||
if child.has_nodes() {
|
||||
found_command = true;
|
||||
let modifier = &context.modifier;
|
||||
if let Some(modifier) = modifier {
|
||||
let results = modifier(context);
|
||||
match results {
|
||||
Ok(results) => {
|
||||
if !results.is_empty() {
|
||||
next.extend(
|
||||
results.iter().map(|s| child.copy_for(s.clone())),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// TODO
|
||||
// self.consumer.on_command_complete(context, false, 0);
|
||||
if !forked {
|
||||
return Err(results.err().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
next.push(child.copy_for(context.source.clone()));
|
||||
}
|
||||
}
|
||||
} else if let Some(context_command) = &context.command {
|
||||
found_command = true;
|
||||
|
||||
let value = context_command(context);
|
||||
result += value;
|
||||
// consumer.on_command_complete(context, true, value);
|
||||
successful_forks += 1;
|
||||
|
||||
// TODO: allow context_command to error and handle
|
||||
// those errors
|
||||
}
|
||||
}
|
||||
|
||||
// move next into contexts and clear next
|
||||
mem::swap(&mut contexts, &mut next);
|
||||
next.clear();
|
||||
}
|
||||
|
||||
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 })
|
||||
flat_context.execute_all(original.source().clone(), self.consumer.as_ref())
|
||||
}
|
||||
|
||||
pub fn get_all_usage(
|
||||
|
|
|
@ -10,16 +10,16 @@ use crate::{
|
|||
|
||||
/// A built `CommandContextBuilder`.
|
||||
pub struct CommandContext<S> {
|
||||
pub source: Arc<S>,
|
||||
pub input: String,
|
||||
pub arguments: HashMap<String, ParsedArgument>,
|
||||
pub command: Command<S>,
|
||||
pub root_node: Arc<RwLock<CommandNode<S>>>,
|
||||
pub nodes: Vec<ParsedCommandNode<S>>,
|
||||
pub range: StringRange,
|
||||
pub child: Option<Rc<CommandContext<S>>>,
|
||||
pub modifier: Option<Arc<RedirectModifier<S>>>,
|
||||
pub forks: bool,
|
||||
pub(super) source: Arc<S>,
|
||||
pub(super) input: String,
|
||||
pub(super) arguments: HashMap<String, ParsedArgument>,
|
||||
pub(super) command: Command<S>,
|
||||
pub(super) root_node: Arc<RwLock<CommandNode<S>>>,
|
||||
pub(super) nodes: Vec<ParsedCommandNode<S>>,
|
||||
pub(super) range: StringRange,
|
||||
pub(super) child: Option<Rc<CommandContext<S>>>,
|
||||
pub(super) modifier: Option<Arc<RedirectModifier<S>>>,
|
||||
pub(super) forks: bool,
|
||||
}
|
||||
|
||||
impl<S> Clone for CommandContext<S> {
|
||||
|
@ -59,8 +59,10 @@ impl<S> Debug for CommandContext<S> {
|
|||
impl<S> CommandContext<S> {
|
||||
pub fn copy_for(&self, source: Arc<S>) -> Self {
|
||||
if Arc::ptr_eq(&source, &self.source) {
|
||||
// fast path
|
||||
return self.clone();
|
||||
}
|
||||
|
||||
CommandContext {
|
||||
source,
|
||||
input: self.input.clone(),
|
||||
|
@ -75,12 +77,56 @@ impl<S> CommandContext<S> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn child(&self) -> Option<&CommandContext<S>> {
|
||||
self.child.as_ref().map(|c| c.as_ref())
|
||||
}
|
||||
|
||||
pub fn last_child(&self) -> &CommandContext<S> {
|
||||
let mut result = self;
|
||||
while let Some(child) = result.child() {
|
||||
result = child;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn command(&self) -> &Command<S> {
|
||||
&self.command
|
||||
}
|
||||
|
||||
pub fn source(&self) -> &Arc<S> {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub fn argument(&self, name: &str) -> Option<&dyn Any> {
|
||||
let argument = self.arguments.get(name);
|
||||
argument.map(|a| a.result.as_ref())
|
||||
}
|
||||
|
||||
pub fn redirect_modifier(&self) -> Option<&RedirectModifier<S>> {
|
||||
self.modifier.as_ref().map(|m| m.as_ref())
|
||||
}
|
||||
|
||||
pub fn range(&self) -> &StringRange {
|
||||
&self.range
|
||||
}
|
||||
|
||||
pub fn input(&self) -> &str {
|
||||
&self.input
|
||||
}
|
||||
|
||||
pub fn root_node(&self) -> &Arc<RwLock<CommandNode<S>>> {
|
||||
&self.root_node
|
||||
}
|
||||
|
||||
pub fn nodes(&self) -> &[ParsedCommandNode<S>] {
|
||||
&self.nodes
|
||||
}
|
||||
|
||||
pub fn has_nodes(&self) -> bool {
|
||||
!self.nodes.is_empty()
|
||||
}
|
||||
|
||||
pub fn argument(&self, name: &str) -> Option<Arc<dyn Any>> {
|
||||
let argument = self.arguments.get(name);
|
||||
argument.map(|a| a.result.clone())
|
||||
pub fn is_forked(&self) -> bool {
|
||||
self.forks
|
||||
}
|
||||
}
|
||||
|
|
169
azalea-brigadier/src/context/context_chain.rs
Normal file
169
azalea-brigadier/src/context/context_chain.rs
Normal file
|
@ -0,0 +1,169 @@
|
|||
use std::{rc::Rc, sync::Arc};
|
||||
|
||||
use super::CommandContext;
|
||||
use crate::{errors::CommandSyntaxError, result_consumer::ResultConsumer};
|
||||
|
||||
pub struct ContextChain<S> {
|
||||
modifiers: Vec<Rc<CommandContext<S>>>,
|
||||
executable: Rc<CommandContext<S>>,
|
||||
next_stage_cache: Option<Rc<ContextChain<S>>>,
|
||||
}
|
||||
|
||||
impl<S> ContextChain<S> {
|
||||
pub fn new(modifiers: Vec<Rc<CommandContext<S>>>, executable: Rc<CommandContext<S>>) -> Self {
|
||||
if executable.command.is_none() {
|
||||
panic!("Last command in chain must be executable");
|
||||
}
|
||||
Self {
|
||||
modifiers,
|
||||
executable,
|
||||
next_stage_cache: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_flatten(root_context: Rc<CommandContext<S>>) -> Option<Self> {
|
||||
let mut modifiers = Vec::new();
|
||||
let mut current = root_context;
|
||||
loop {
|
||||
let child = current.child.clone();
|
||||
let Some(child) = child else {
|
||||
// Last entry must be executable command
|
||||
if current.command.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(ContextChain::new(modifiers, current));
|
||||
};
|
||||
|
||||
modifiers.push(current);
|
||||
current = child;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_modifier(
|
||||
modifier: Rc<CommandContext<S>>,
|
||||
source: Arc<S>,
|
||||
result_consumer: &dyn ResultConsumer<S>,
|
||||
forked_mode: bool,
|
||||
) -> Result<Vec<Arc<S>>, CommandSyntaxError> {
|
||||
let source_modifier = modifier.redirect_modifier();
|
||||
let Some(source_modifier) = source_modifier else {
|
||||
return Ok(vec![source]);
|
||||
};
|
||||
|
||||
let context_to_use = Rc::new(modifier.copy_for(source));
|
||||
let err = match (source_modifier)(&context_to_use) {
|
||||
Ok(res) => return Ok(res),
|
||||
Err(e) => e,
|
||||
};
|
||||
|
||||
result_consumer.on_command_complete(context_to_use, false, 0);
|
||||
if forked_mode {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
|
||||
pub fn run_executable(
|
||||
&self,
|
||||
executable: Rc<CommandContext<S>>,
|
||||
source: Arc<S>,
|
||||
result_consumer: &dyn ResultConsumer<S>,
|
||||
forked_mode: bool,
|
||||
) -> Result<i32, CommandSyntaxError> {
|
||||
let context_to_use = Rc::new(executable.copy_for(source));
|
||||
let Some(command) = &executable.command else {
|
||||
unimplemented!();
|
||||
};
|
||||
|
||||
let err = match (command)(&context_to_use) {
|
||||
Ok(result) => {
|
||||
result_consumer.on_command_complete(context_to_use, true, result);
|
||||
return if forked_mode { Ok(1) } else { Ok(result) };
|
||||
}
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
result_consumer.on_command_complete(context_to_use, false, 0);
|
||||
if forked_mode { Ok(0) } else { Err(err) }
|
||||
}
|
||||
|
||||
pub fn execute_all(
|
||||
&self,
|
||||
source: Arc<S>,
|
||||
result_consumer: &(dyn ResultConsumer<S>),
|
||||
) -> Result<i32, CommandSyntaxError> {
|
||||
if self.modifiers.is_empty() {
|
||||
return self.run_executable(self.executable.clone(), source, result_consumer, false);
|
||||
}
|
||||
|
||||
let mut forked_mode = false;
|
||||
let mut current_sources = vec![source];
|
||||
|
||||
for modifier in &self.modifiers {
|
||||
forked_mode |= modifier.is_forked();
|
||||
|
||||
let mut next_sources = Vec::new();
|
||||
for source_to_run in current_sources {
|
||||
next_sources.extend(Self::run_modifier(
|
||||
modifier.clone(),
|
||||
source_to_run.clone(),
|
||||
result_consumer,
|
||||
forked_mode,
|
||||
)?);
|
||||
}
|
||||
if next_sources.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
current_sources = next_sources;
|
||||
}
|
||||
|
||||
let mut result = 0;
|
||||
for execution_source in current_sources {
|
||||
result += self.run_executable(
|
||||
self.executable.clone(),
|
||||
execution_source,
|
||||
result_consumer,
|
||||
forked_mode,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn stage(&self) -> Stage {
|
||||
if self.modifiers.is_empty() {
|
||||
Stage::Execute
|
||||
} else {
|
||||
Stage::Modify
|
||||
}
|
||||
}
|
||||
|
||||
pub fn top_context(&self) -> Rc<CommandContext<S>> {
|
||||
self.modifiers
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| self.executable.clone())
|
||||
}
|
||||
|
||||
pub fn next_stage(&mut self) -> Option<Rc<ContextChain<S>>> {
|
||||
let modifier_count = self.modifiers.len();
|
||||
if modifier_count == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.next_stage_cache.is_none() {
|
||||
self.next_stage_cache = Some(Rc::new(ContextChain::new(
|
||||
self.modifiers[1..].to_vec(),
|
||||
self.executable.clone(),
|
||||
)));
|
||||
}
|
||||
|
||||
self.next_stage_cache.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Stage {
|
||||
Modify,
|
||||
Execute,
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
mod command_context;
|
||||
mod command_context_builder;
|
||||
mod context_chain;
|
||||
mod parsed_argument;
|
||||
mod parsed_command_node;
|
||||
mod string_range;
|
||||
|
@ -7,6 +8,7 @@ pub mod suggestion_context;
|
|||
|
||||
pub use command_context::CommandContext;
|
||||
pub use command_context_builder::CommandContextBuilder;
|
||||
pub use context_chain::ContextChain;
|
||||
pub use parsed_argument::ParsedArgument;
|
||||
pub use parsed_command_node::ParsedCommandNode;
|
||||
pub use string_range::StringRange;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::fmt;
|
||||
|
||||
use super::command_syntax_exception::CommandSyntaxException;
|
||||
use super::command_syntax_error::CommandSyntaxError;
|
||||
use crate::string_reader::StringReader;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum BuiltInExceptions {
|
||||
pub enum BuiltInError {
|
||||
DoubleTooSmall { found: f64, min: f64 },
|
||||
DoubleTooBig { found: f64, max: f64 },
|
||||
|
||||
|
@ -40,114 +40,114 @@ pub enum BuiltInExceptions {
|
|||
DispatcherParseException { message: String },
|
||||
}
|
||||
|
||||
impl fmt::Debug for BuiltInExceptions {
|
||||
impl fmt::Debug for BuiltInError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BuiltInExceptions::DoubleTooSmall { found, min } => {
|
||||
BuiltInError::DoubleTooSmall { found, min } => {
|
||||
write!(f, "Double must not be less than {min}, found {found}")
|
||||
}
|
||||
BuiltInExceptions::DoubleTooBig { found, max } => {
|
||||
BuiltInError::DoubleTooBig { found, max } => {
|
||||
write!(f, "Double must not be more than {max}, found {found}")
|
||||
}
|
||||
|
||||
BuiltInExceptions::FloatTooSmall { found, min } => {
|
||||
BuiltInError::FloatTooSmall { found, min } => {
|
||||
write!(f, "Float must not be less than {min}, found {found}")
|
||||
}
|
||||
BuiltInExceptions::FloatTooBig { found, max } => {
|
||||
BuiltInError::FloatTooBig { found, max } => {
|
||||
write!(f, "Float must not be more than {max}, found {found}")
|
||||
}
|
||||
|
||||
BuiltInExceptions::IntegerTooSmall { found, min } => {
|
||||
BuiltInError::IntegerTooSmall { found, min } => {
|
||||
write!(f, "Integer must not be less than {min}, found {found}")
|
||||
}
|
||||
BuiltInExceptions::IntegerTooBig { found, max } => {
|
||||
BuiltInError::IntegerTooBig { found, max } => {
|
||||
write!(f, "Integer must not be more than {max}, found {found}")
|
||||
}
|
||||
|
||||
BuiltInExceptions::LongTooSmall { found, min } => {
|
||||
BuiltInError::LongTooSmall { found, min } => {
|
||||
write!(f, "Long must not be less than {min}, found {found}")
|
||||
}
|
||||
BuiltInExceptions::LongTooBig { found, max } => {
|
||||
BuiltInError::LongTooBig { found, max } => {
|
||||
write!(f, "Long must not be more than {max}, found {found}")
|
||||
}
|
||||
|
||||
BuiltInExceptions::LiteralIncorrect { expected } => {
|
||||
BuiltInError::LiteralIncorrect { expected } => {
|
||||
write!(f, "Expected literal {expected}")
|
||||
}
|
||||
|
||||
BuiltInExceptions::ReaderExpectedStartOfQuote => {
|
||||
BuiltInError::ReaderExpectedStartOfQuote => {
|
||||
write!(f, "Expected quote to start a string")
|
||||
}
|
||||
BuiltInExceptions::ReaderExpectedEndOfQuote => {
|
||||
BuiltInError::ReaderExpectedEndOfQuote => {
|
||||
write!(f, "Unclosed quoted string")
|
||||
}
|
||||
BuiltInExceptions::ReaderInvalidEscape { character } => {
|
||||
BuiltInError::ReaderInvalidEscape { character } => {
|
||||
write!(f, "Invalid escape sequence '{character}' in quoted string")
|
||||
}
|
||||
BuiltInExceptions::ReaderInvalidBool { value } => {
|
||||
BuiltInError::ReaderInvalidBool { value } => {
|
||||
write!(
|
||||
f,
|
||||
"Invalid bool, expected true or false but found '{value}'"
|
||||
)
|
||||
}
|
||||
BuiltInExceptions::ReaderInvalidInt { value } => {
|
||||
BuiltInError::ReaderInvalidInt { value } => {
|
||||
write!(f, "Invalid Integer '{value}'")
|
||||
}
|
||||
BuiltInExceptions::ReaderExpectedInt => {
|
||||
BuiltInError::ReaderExpectedInt => {
|
||||
write!(f, "Expected Integer")
|
||||
}
|
||||
BuiltInExceptions::ReaderInvalidLong { value } => {
|
||||
BuiltInError::ReaderInvalidLong { value } => {
|
||||
write!(f, "Invalid long '{value}'")
|
||||
}
|
||||
BuiltInExceptions::ReaderExpectedLong => {
|
||||
BuiltInError::ReaderExpectedLong => {
|
||||
write!(f, "Expected long")
|
||||
}
|
||||
BuiltInExceptions::ReaderInvalidDouble { value } => {
|
||||
BuiltInError::ReaderInvalidDouble { value } => {
|
||||
write!(f, "Invalid double '{value}'")
|
||||
}
|
||||
BuiltInExceptions::ReaderExpectedDouble => {
|
||||
BuiltInError::ReaderExpectedDouble => {
|
||||
write!(f, "Expected double")
|
||||
}
|
||||
BuiltInExceptions::ReaderInvalidFloat { value } => {
|
||||
BuiltInError::ReaderInvalidFloat { value } => {
|
||||
write!(f, "Invalid Float '{value}'")
|
||||
}
|
||||
BuiltInExceptions::ReaderExpectedFloat => {
|
||||
BuiltInError::ReaderExpectedFloat => {
|
||||
write!(f, "Expected Float")
|
||||
}
|
||||
BuiltInExceptions::ReaderExpectedBool => {
|
||||
BuiltInError::ReaderExpectedBool => {
|
||||
write!(f, "Expected bool")
|
||||
}
|
||||
BuiltInExceptions::ReaderExpectedSymbol { symbol } => {
|
||||
BuiltInError::ReaderExpectedSymbol { symbol } => {
|
||||
write!(f, "Expected '{symbol}'")
|
||||
}
|
||||
|
||||
BuiltInExceptions::DispatcherUnknownCommand => {
|
||||
BuiltInError::DispatcherUnknownCommand => {
|
||||
write!(f, "Unknown command")
|
||||
}
|
||||
BuiltInExceptions::DispatcherUnknownArgument => {
|
||||
BuiltInError::DispatcherUnknownArgument => {
|
||||
write!(f, "Incorrect argument for command")
|
||||
}
|
||||
BuiltInExceptions::DispatcherExpectedArgumentSeparator => {
|
||||
BuiltInError::DispatcherExpectedArgumentSeparator => {
|
||||
write!(
|
||||
f,
|
||||
"Expected whitespace to end one argument, but found trailing data"
|
||||
)
|
||||
}
|
||||
BuiltInExceptions::DispatcherParseException { message } => {
|
||||
BuiltInError::DispatcherParseException { message } => {
|
||||
write!(f, "Could not parse command: {message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BuiltInExceptions {
|
||||
pub fn create(self) -> CommandSyntaxException {
|
||||
impl BuiltInError {
|
||||
pub fn create(self) -> CommandSyntaxError {
|
||||
let message = format!("{self:?}");
|
||||
CommandSyntaxException::create(self, message)
|
||||
CommandSyntaxError::create(self, message)
|
||||
}
|
||||
|
||||
pub fn create_with_context(self, reader: &StringReader) -> CommandSyntaxException {
|
||||
pub fn create_with_context(self, reader: &StringReader) -> CommandSyntaxError {
|
||||
let message = format!("{self:?}");
|
||||
CommandSyntaxException::new(self, message, reader.string(), reader.cursor())
|
||||
CommandSyntaxError::new(self, message, reader.string(), reader.cursor())
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
use std::{
|
||||
cmp,
|
||||
fmt::{self, Write},
|
||||
fmt::{self, Debug, Write},
|
||||
};
|
||||
|
||||
use super::builtin_exceptions::BuiltInExceptions;
|
||||
use super::builtin_errors::BuiltInError;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct CommandSyntaxException {
|
||||
pub type_: BuiltInExceptions,
|
||||
pub struct CommandSyntaxError {
|
||||
kind: BuiltInError,
|
||||
message: String,
|
||||
input: Option<String>,
|
||||
cursor: Option<usize>,
|
||||
|
@ -15,19 +15,19 @@ pub struct CommandSyntaxException {
|
|||
|
||||
const CONTEXT_AMOUNT: usize = 10;
|
||||
|
||||
impl CommandSyntaxException {
|
||||
pub fn new(type_: BuiltInExceptions, message: String, input: &str, cursor: usize) -> Self {
|
||||
impl CommandSyntaxError {
|
||||
pub fn new(kind: BuiltInError, message: String, input: &str, cursor: usize) -> Self {
|
||||
Self {
|
||||
type_,
|
||||
kind,
|
||||
message,
|
||||
input: Some(input.to_string()),
|
||||
cursor: Some(cursor),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(type_: BuiltInExceptions, message: String) -> Self {
|
||||
pub fn create(kind: BuiltInError, message: String) -> Self {
|
||||
Self {
|
||||
type_,
|
||||
kind,
|
||||
message,
|
||||
input: None,
|
||||
cursor: None,
|
||||
|
@ -40,9 +40,8 @@ impl CommandSyntaxException {
|
|||
if let Some(context) = context {
|
||||
write!(
|
||||
message,
|
||||
" at position {}: {}",
|
||||
self.cursor.unwrap_or(usize::MAX),
|
||||
context
|
||||
" at position {}: {context}",
|
||||
self.cursor.unwrap_or(usize::MAX)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -74,8 +73,8 @@ impl CommandSyntaxException {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn get_type(&self) -> &BuiltInExceptions {
|
||||
&self.type_
|
||||
pub fn kind(&self) -> &BuiltInError {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn input(&self) -> &Option<String> {
|
||||
|
@ -87,7 +86,7 @@ impl CommandSyntaxException {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for CommandSyntaxException {
|
||||
impl Debug for CommandSyntaxError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.message())
|
||||
}
|
5
azalea-brigadier/src/errors/mod.rs
Normal file
5
azalea-brigadier/src/errors/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod builtin_errors;
|
||||
mod command_syntax_error;
|
||||
|
||||
pub use builtin_errors::BuiltInError;
|
||||
pub use command_syntax_error::CommandSyntaxError;
|
|
@ -1,5 +0,0 @@
|
|||
mod builtin_exceptions;
|
||||
mod command_syntax_exception;
|
||||
|
||||
pub use builtin_exceptions::BuiltInExceptions;
|
||||
pub use command_syntax_exception::CommandSyntaxException;
|
|
@ -4,9 +4,10 @@ pub mod arguments;
|
|||
pub mod builder;
|
||||
pub mod command_dispatcher;
|
||||
pub mod context;
|
||||
pub mod exceptions;
|
||||
pub mod errors;
|
||||
pub mod modifier;
|
||||
pub mod parse_results;
|
||||
pub mod result_consumer;
|
||||
pub mod string_reader;
|
||||
pub mod suggestion;
|
||||
pub mod tree;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{context::CommandContext, exceptions::CommandSyntaxException};
|
||||
use crate::{context::CommandContext, errors::CommandSyntaxError};
|
||||
|
||||
pub type RedirectModifier<S> =
|
||||
dyn Fn(&CommandContext<S>) -> Result<Vec<Arc<S>>, CommandSyntaxException> + Send + Sync;
|
||||
dyn Fn(&CommandContext<S>) -> Result<Vec<Arc<S>>, CommandSyntaxError> + Send + Sync;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use std::{collections::HashMap, fmt::Debug, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
context::CommandContextBuilder, exceptions::CommandSyntaxException,
|
||||
string_reader::StringReader, tree::CommandNode,
|
||||
context::CommandContextBuilder, errors::CommandSyntaxError, string_reader::StringReader,
|
||||
tree::CommandNode,
|
||||
};
|
||||
|
||||
pub struct ParseResults<'a, S> {
|
||||
pub context: CommandContextBuilder<'a, S>,
|
||||
pub reader: StringReader,
|
||||
pub exceptions: HashMap<Rc<CommandNode<S>>, CommandSyntaxException>,
|
||||
pub exceptions: HashMap<Rc<CommandNode<S>>, CommandSyntaxError>,
|
||||
}
|
||||
|
||||
impl<S> Debug for ParseResults<'_, S> {
|
||||
|
|
12
azalea-brigadier/src/result_consumer.rs
Normal file
12
azalea-brigadier/src/result_consumer.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::context::CommandContext;
|
||||
|
||||
pub trait ResultConsumer<S> {
|
||||
fn on_command_complete(&self, context: Rc<CommandContext<S>>, success: bool, result: i32);
|
||||
}
|
||||
|
||||
pub struct DefaultResultConsumer;
|
||||
impl<S> ResultConsumer<S> for DefaultResultConsumer {
|
||||
fn on_command_complete(&self, _context: Rc<CommandContext<S>>, _success: bool, _result: i32) {}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use crate::exceptions::{BuiltInExceptions, CommandSyntaxException};
|
||||
use crate::errors::{BuiltInError, CommandSyntaxError};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StringReader {
|
||||
|
@ -91,19 +91,19 @@ impl StringReader {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read_int(&mut self) -> Result<i32, CommandSyntaxException> {
|
||||
pub fn read_int(&mut self) -> Result<i32, CommandSyntaxError> {
|
||||
let start = self.cursor;
|
||||
while self.can_read() && StringReader::is_allowed_number(self.peek()) {
|
||||
self.skip();
|
||||
}
|
||||
let number = &self.string[start..self.cursor];
|
||||
if number.is_empty() {
|
||||
return Err(BuiltInExceptions::ReaderExpectedInt.create_with_context(self));
|
||||
return Err(BuiltInError::ReaderExpectedInt.create_with_context(self));
|
||||
}
|
||||
let result = i32::from_str(number);
|
||||
if result.is_err() {
|
||||
self.cursor = start;
|
||||
return Err(BuiltInExceptions::ReaderInvalidInt {
|
||||
return Err(BuiltInError::ReaderInvalidInt {
|
||||
value: number.to_string(),
|
||||
}
|
||||
.create_with_context(self));
|
||||
|
@ -112,19 +112,19 @@ impl StringReader {
|
|||
Ok(result.unwrap())
|
||||
}
|
||||
|
||||
pub fn read_long(&mut self) -> Result<i64, CommandSyntaxException> {
|
||||
pub fn read_long(&mut self) -> Result<i64, CommandSyntaxError> {
|
||||
let start = self.cursor;
|
||||
while self.can_read() && StringReader::is_allowed_number(self.peek()) {
|
||||
self.skip();
|
||||
}
|
||||
let number = &self.string[start..self.cursor];
|
||||
if number.is_empty() {
|
||||
return Err(BuiltInExceptions::ReaderExpectedLong.create_with_context(self));
|
||||
return Err(BuiltInError::ReaderExpectedLong.create_with_context(self));
|
||||
}
|
||||
let result = i64::from_str(number);
|
||||
if result.is_err() {
|
||||
self.cursor = start;
|
||||
return Err(BuiltInExceptions::ReaderInvalidLong {
|
||||
return Err(BuiltInError::ReaderInvalidLong {
|
||||
value: number.to_string(),
|
||||
}
|
||||
.create_with_context(self));
|
||||
|
@ -133,19 +133,19 @@ impl StringReader {
|
|||
Ok(result.unwrap())
|
||||
}
|
||||
|
||||
pub fn read_double(&mut self) -> Result<f64, CommandSyntaxException> {
|
||||
pub fn read_double(&mut self) -> Result<f64, CommandSyntaxError> {
|
||||
let start = self.cursor;
|
||||
while self.can_read() && StringReader::is_allowed_number(self.peek()) {
|
||||
self.skip();
|
||||
}
|
||||
let number = &self.string[start..self.cursor];
|
||||
if number.is_empty() {
|
||||
return Err(BuiltInExceptions::ReaderExpectedDouble.create_with_context(self));
|
||||
return Err(BuiltInError::ReaderExpectedDouble.create_with_context(self));
|
||||
}
|
||||
let result = f64::from_str(number);
|
||||
if result.is_err() {
|
||||
self.cursor = start;
|
||||
return Err(BuiltInExceptions::ReaderInvalidDouble {
|
||||
return Err(BuiltInError::ReaderInvalidDouble {
|
||||
value: number.to_string(),
|
||||
}
|
||||
.create_with_context(self));
|
||||
|
@ -154,19 +154,19 @@ impl StringReader {
|
|||
Ok(result.unwrap())
|
||||
}
|
||||
|
||||
pub fn read_float(&mut self) -> Result<f32, CommandSyntaxException> {
|
||||
pub fn read_float(&mut self) -> Result<f32, CommandSyntaxError> {
|
||||
let start = self.cursor;
|
||||
while self.can_read() && StringReader::is_allowed_number(self.peek()) {
|
||||
self.skip();
|
||||
}
|
||||
let number = &self.string[start..self.cursor];
|
||||
if number.is_empty() {
|
||||
return Err(BuiltInExceptions::ReaderExpectedFloat.create_with_context(self));
|
||||
return Err(BuiltInError::ReaderExpectedFloat.create_with_context(self));
|
||||
}
|
||||
let result = f32::from_str(number);
|
||||
if result.is_err() {
|
||||
self.cursor = start;
|
||||
return Err(BuiltInExceptions::ReaderInvalidFloat {
|
||||
return Err(BuiltInError::ReaderInvalidFloat {
|
||||
value: number.to_string(),
|
||||
}
|
||||
.create_with_context(self));
|
||||
|
@ -193,22 +193,19 @@ impl StringReader {
|
|||
&self.string[start..self.cursor]
|
||||
}
|
||||
|
||||
pub fn read_quoted_string(&mut self) -> Result<String, CommandSyntaxException> {
|
||||
pub fn read_quoted_string(&mut self) -> Result<String, CommandSyntaxError> {
|
||||
if !self.can_read() {
|
||||
return Ok(String::new());
|
||||
}
|
||||
let next = self.peek();
|
||||
if !StringReader::is_quoted_string_start(next) {
|
||||
return Err(BuiltInExceptions::ReaderExpectedStartOfQuote.create_with_context(self));
|
||||
return Err(BuiltInError::ReaderExpectedStartOfQuote.create_with_context(self));
|
||||
}
|
||||
self.skip();
|
||||
self.read_string_until(next)
|
||||
}
|
||||
|
||||
pub fn read_string_until(
|
||||
&mut self,
|
||||
terminator: char,
|
||||
) -> Result<String, CommandSyntaxException> {
|
||||
pub fn read_string_until(&mut self, terminator: char) -> Result<String, CommandSyntaxError> {
|
||||
let mut result = String::new();
|
||||
let mut escaped = false;
|
||||
while self.can_read() {
|
||||
|
@ -219,7 +216,7 @@ impl StringReader {
|
|||
escaped = false;
|
||||
} else {
|
||||
self.cursor -= 1;
|
||||
return Err(BuiltInExceptions::ReaderInvalidEscape { character: c }
|
||||
return Err(BuiltInError::ReaderInvalidEscape { character: c }
|
||||
.create_with_context(self));
|
||||
}
|
||||
} else if c == SYNTAX_ESCAPE {
|
||||
|
@ -231,10 +228,10 @@ impl StringReader {
|
|||
}
|
||||
}
|
||||
|
||||
Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self))
|
||||
Err(BuiltInError::ReaderExpectedEndOfQuote.create_with_context(self))
|
||||
}
|
||||
|
||||
pub fn read_string(&mut self) -> Result<String, CommandSyntaxException> {
|
||||
pub fn read_string(&mut self) -> Result<String, CommandSyntaxError> {
|
||||
if !self.can_read() {
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
@ -246,11 +243,11 @@ impl StringReader {
|
|||
Ok(self.read_unquoted_string().to_string())
|
||||
}
|
||||
|
||||
pub fn read_boolean(&mut self) -> Result<bool, CommandSyntaxException> {
|
||||
pub fn read_boolean(&mut self) -> Result<bool, CommandSyntaxError> {
|
||||
let start = self.cursor;
|
||||
let value = self.read_string()?;
|
||||
if value.is_empty() {
|
||||
return Err(BuiltInExceptions::ReaderExpectedBool.create_with_context(self));
|
||||
return Err(BuiltInError::ReaderExpectedBool.create_with_context(self));
|
||||
}
|
||||
|
||||
if value == "true" {
|
||||
|
@ -259,15 +256,13 @@ impl StringReader {
|
|||
Ok(false)
|
||||
} else {
|
||||
self.cursor = start;
|
||||
Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self))
|
||||
Err(BuiltInError::ReaderInvalidBool { value }.create_with_context(self))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect(&mut self, c: char) -> Result<(), CommandSyntaxException> {
|
||||
pub fn expect(&mut self, c: char) -> Result<(), CommandSyntaxError> {
|
||||
if !self.can_read() || self.peek() != c {
|
||||
return Err(
|
||||
BuiltInExceptions::ReaderExpectedSymbol { symbol: c }.create_with_context(self)
|
||||
);
|
||||
return Err(BuiltInError::ReaderExpectedSymbol { symbol: c }.create_with_context(self));
|
||||
}
|
||||
self.skip();
|
||||
Ok(())
|
||||
|
|
|
@ -14,13 +14,14 @@ use crate::{
|
|||
required_argument_builder::Argument,
|
||||
},
|
||||
context::{CommandContext, CommandContextBuilder, ParsedArgument, StringRange},
|
||||
exceptions::{BuiltInExceptions, CommandSyntaxException},
|
||||
errors::{BuiltInError, CommandSyntaxError},
|
||||
modifier::RedirectModifier,
|
||||
string_reader::StringReader,
|
||||
suggestion::{Suggestions, SuggestionsBuilder},
|
||||
};
|
||||
|
||||
pub type Command<S> = Option<Arc<dyn Fn(&CommandContext<S>) -> i32 + Send + Sync>>;
|
||||
pub type Command<S> =
|
||||
Option<Arc<dyn Fn(&CommandContext<S>) -> Result<i32, CommandSyntaxError> + Send + Sync>>;
|
||||
|
||||
/// An ArgumentBuilder that has been built.
|
||||
#[non_exhaustive]
|
||||
|
@ -149,7 +150,7 @@ impl<S> CommandNode<S> {
|
|||
&self,
|
||||
reader: &mut StringReader,
|
||||
context_builder: &mut CommandContextBuilder<S>,
|
||||
) -> Result<(), CommandSyntaxException> {
|
||||
) -> Result<(), CommandSyntaxError> {
|
||||
match self.value {
|
||||
ArgumentBuilderType::Argument(ref argument) => {
|
||||
let start = reader.cursor();
|
||||
|
@ -176,7 +177,7 @@ impl<S> CommandNode<S> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
Err(BuiltInExceptions::LiteralIncorrect {
|
||||
Err(BuiltInError::LiteralIncorrect {
|
||||
expected: literal.value.clone(),
|
||||
}
|
||||
.create_with_context(reader))
|
||||
|
|
|
@ -131,7 +131,7 @@ impl DispatchStorage {
|
|||
///
|
||||
/// Spawns an entity with the [`SpawnedEntity`] component.
|
||||
fn command_spawn_entity(context: &CommandContext<WorldAccessor>) -> i32 {
|
||||
context.source.lock().spawn(SpawnedEntity);
|
||||
context.source().lock().spawn(SpawnedEntity);
|
||||
|
||||
0
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ impl DispatchStorage {
|
|||
let num = get_integer(context, "entities").unwrap();
|
||||
|
||||
for _ in 0..num {
|
||||
context.source.lock().spawn(SpawnedEntity);
|
||||
context.source().lock().spawn(SpawnedEntity);
|
||||
}
|
||||
|
||||
0
|
||||
|
|
|
@ -5,7 +5,7 @@ use azalea_brigadier::{
|
|||
builder::{literal_argument_builder::literal, required_argument_builder::argument},
|
||||
command_dispatcher::CommandDispatcher,
|
||||
context::CommandContext,
|
||||
exceptions::{BuiltInExceptions, CommandSyntaxException},
|
||||
errors::{BuiltInError, CommandSyntaxError},
|
||||
string_reader::StringReader,
|
||||
};
|
||||
|
||||
|
@ -50,10 +50,7 @@ fn execute_unknown_command() {
|
|||
let execute_result = subject.execute("foo", &CommandSource {});
|
||||
|
||||
let err = execute_result.err().unwrap();
|
||||
match err.type_ {
|
||||
BuiltInExceptions::DispatcherUnknownCommand => {}
|
||||
_ => panic!("Unexpected error"),
|
||||
}
|
||||
assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownCommand);
|
||||
assert_eq!(err.cursor().unwrap(), 0);
|
||||
}
|
||||
|
||||
|
@ -65,10 +62,7 @@ fn execute_impermissible_command() {
|
|||
let execute_result = subject.execute("foo", &CommandSource {});
|
||||
|
||||
let err = execute_result.err().unwrap();
|
||||
match err.type_ {
|
||||
BuiltInExceptions::DispatcherUnknownCommand => {}
|
||||
_ => panic!("Unexpected error"),
|
||||
}
|
||||
assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownCommand);
|
||||
assert_eq!(err.cursor().unwrap(), 0);
|
||||
}
|
||||
|
||||
|
@ -80,10 +74,7 @@ fn execute_empty_command() {
|
|||
let execute_result = subject.execute("", &CommandSource {});
|
||||
|
||||
let err = execute_result.err().unwrap();
|
||||
match err.type_ {
|
||||
BuiltInExceptions::DispatcherUnknownCommand => {}
|
||||
_ => panic!("Unexpected error"),
|
||||
}
|
||||
assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownCommand);
|
||||
assert_eq!(err.cursor().unwrap(), 0);
|
||||
}
|
||||
|
||||
|
@ -95,10 +86,7 @@ fn execute_unknown_subcommand() {
|
|||
let execute_result = subject.execute("foo bar", &CommandSource {});
|
||||
|
||||
let err = execute_result.err().unwrap();
|
||||
match err.type_ {
|
||||
BuiltInExceptions::DispatcherUnknownArgument => {}
|
||||
_ => panic!("Unexpected error"),
|
||||
}
|
||||
assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownArgument);
|
||||
assert_eq!(err.cursor().unwrap(), 4);
|
||||
}
|
||||
|
||||
|
@ -110,10 +98,7 @@ fn execute_incorrect_literal() {
|
|||
let execute_result = subject.execute("foo baz", &CommandSource {});
|
||||
|
||||
let err = execute_result.err().unwrap();
|
||||
match err.type_ {
|
||||
BuiltInExceptions::DispatcherUnknownArgument => {}
|
||||
_ => panic!("Unexpected error"),
|
||||
}
|
||||
assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownArgument);
|
||||
assert_eq!(err.cursor().unwrap(), 4);
|
||||
}
|
||||
|
||||
|
@ -130,10 +115,7 @@ fn execute_ambiguous_incorrect_argument() {
|
|||
let execute_result = subject.execute("foo unknown", &CommandSource {});
|
||||
|
||||
let err = execute_result.err().unwrap();
|
||||
match err.type_ {
|
||||
BuiltInExceptions::DispatcherUnknownArgument => {}
|
||||
_ => panic!("Unexpected error"),
|
||||
}
|
||||
assert_eq!(err.kind(), &BuiltInError::DispatcherUnknownArgument);
|
||||
assert_eq!(err.cursor().unwrap(), 4);
|
||||
}
|
||||
|
||||
|
@ -245,7 +227,7 @@ fn execute_redirected_multiple_times() {
|
|||
);
|
||||
assert_eq!(*child2.unwrap().nodes[0].node.read(), *concrete_node.read());
|
||||
|
||||
assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42);
|
||||
assert_eq!(subject.execute_parsed(parse).unwrap(), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -255,7 +237,7 @@ fn execute_redirected() {
|
|||
let source1 = Arc::new(CommandSource {});
|
||||
let source2 = Arc::new(CommandSource {});
|
||||
|
||||
let modifier = move |_: &CommandContext<CommandSource>| -> Result<Vec<Arc<CommandSource>>, CommandSyntaxException> {
|
||||
let modifier = move |_: &CommandContext<CommandSource>| -> Result<Vec<Arc<CommandSource>>, CommandSyntaxError> {
|
||||
Ok(vec![source1.clone(), source2.clone()])
|
||||
};
|
||||
|
||||
|
@ -281,7 +263,7 @@ fn execute_redirected() {
|
|||
assert_eq!(*parent.nodes[0].node.read(), *concrete_node.read());
|
||||
assert_eq!(*parent.source, CommandSource {});
|
||||
|
||||
assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2);
|
||||
assert_eq!(subject.execute_parsed(parse).unwrap(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -297,10 +279,7 @@ fn execute_orphaned_subcommand() {
|
|||
let result = subject.execute("foo 5", &CommandSource {});
|
||||
assert!(result.is_err());
|
||||
let result = result.unwrap_err();
|
||||
assert_eq!(
|
||||
*result.get_type(),
|
||||
BuiltInExceptions::DispatcherUnknownCommand
|
||||
);
|
||||
assert_eq!(*result.kind(), BuiltInError::DispatcherUnknownCommand);
|
||||
assert_eq!(result.cursor(), Some(5));
|
||||
}
|
||||
|
||||
|
@ -327,10 +306,7 @@ fn parse_no_space_separator() {
|
|||
let result = subject.execute("foo$", &CommandSource {});
|
||||
assert!(result.is_err());
|
||||
let result = result.unwrap_err();
|
||||
assert_eq!(
|
||||
*result.get_type(),
|
||||
BuiltInExceptions::DispatcherUnknownCommand
|
||||
);
|
||||
assert_eq!(*result.kind(), BuiltInError::DispatcherUnknownCommand);
|
||||
assert_eq!(result.cursor(), Some(0));
|
||||
}
|
||||
|
||||
|
@ -348,7 +324,7 @@ fn execute_invalid_subcommand() {
|
|||
assert!(result.is_err());
|
||||
let result = result.unwrap_err();
|
||||
// this fails for some reason, i blame mojang
|
||||
// assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt);
|
||||
// assert_eq!(*result.get_type(), BuiltInError::ReaderExpectedInt);
|
||||
assert_eq!(result.cursor(), Some(4));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use azalea_brigadier::{exceptions::BuiltInExceptions, string_reader::StringReader};
|
||||
use azalea_brigadier::{errors::BuiltInError, string_reader::StringReader};
|
||||
|
||||
#[test]
|
||||
fn can_read() {
|
||||
|
@ -222,7 +222,7 @@ fn read_quoted_string_no_open() {
|
|||
let result = reader.read_quoted_string();
|
||||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedStartOfQuote);
|
||||
assert_eq!(e.kind(), &BuiltInError::ReaderExpectedStartOfQuote);
|
||||
assert_eq!(e.cursor(), Some(0));
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ fn read_quoted_string_no_close() {
|
|||
let result = reader.read_quoted_string();
|
||||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedEndOfQuote);
|
||||
assert_eq!(e.kind(), &BuiltInError::ReaderExpectedEndOfQuote);
|
||||
assert_eq!(e.cursor(), Some(12));
|
||||
}
|
||||
}
|
||||
|
@ -245,8 +245,8 @@ fn read_quoted_string_invalid_escape() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderInvalidEscape { character: 'n' }
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderInvalidEscape { character: 'n' }
|
||||
);
|
||||
assert_eq!(e.cursor(), Some(7));
|
||||
}
|
||||
|
@ -259,8 +259,8 @@ fn read_quoted_string_invalid_quote_escape() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderInvalidEscape { character: '"' }
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderInvalidEscape { character: '"' }
|
||||
);
|
||||
assert_eq!(e.cursor(), Some(7));
|
||||
}
|
||||
|
@ -313,8 +313,8 @@ fn read_int_invalid() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderInvalidInt {
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderInvalidInt {
|
||||
value: "12.34".to_string()
|
||||
}
|
||||
);
|
||||
|
@ -328,7 +328,7 @@ fn read_int_none() {
|
|||
let result = reader.read_int();
|
||||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedInt);
|
||||
assert_eq!(e.kind(), &BuiltInError::ReaderExpectedInt);
|
||||
assert_eq!(e.cursor(), Some(0));
|
||||
}
|
||||
}
|
||||
|
@ -372,8 +372,8 @@ fn read_long_invalid() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderInvalidLong {
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderInvalidLong {
|
||||
value: "12.34".to_string()
|
||||
}
|
||||
);
|
||||
|
@ -387,7 +387,7 @@ fn read_long_none() {
|
|||
let result = reader.read_long();
|
||||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedLong);
|
||||
assert_eq!(e.kind(), &BuiltInError::ReaderExpectedLong);
|
||||
assert_eq!(e.cursor(), Some(0));
|
||||
}
|
||||
}
|
||||
|
@ -439,8 +439,8 @@ fn read_double_invalid() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderInvalidDouble {
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderInvalidDouble {
|
||||
value: "12.34.56".to_string()
|
||||
}
|
||||
);
|
||||
|
@ -454,7 +454,7 @@ fn read_double_none() {
|
|||
let result = reader.read_double();
|
||||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedDouble);
|
||||
assert_eq!(e.kind(), &BuiltInError::ReaderExpectedDouble);
|
||||
assert_eq!(e.cursor(), Some(0));
|
||||
}
|
||||
}
|
||||
|
@ -506,8 +506,8 @@ fn read_float_invalid() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderInvalidFloat {
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderInvalidFloat {
|
||||
value: "12.34.56".to_string()
|
||||
}
|
||||
);
|
||||
|
@ -521,7 +521,7 @@ fn read_float_none() {
|
|||
let result = reader.read_float();
|
||||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedFloat);
|
||||
assert_eq!(e.kind(), &BuiltInError::ReaderExpectedFloat);
|
||||
assert_eq!(e.cursor(), Some(0));
|
||||
}
|
||||
}
|
||||
|
@ -556,8 +556,8 @@ fn expect_incorrect() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' }
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderExpectedSymbol { symbol: 'a' }
|
||||
);
|
||||
assert_eq!(e.cursor(), Some(0));
|
||||
}
|
||||
|
@ -570,8 +570,8 @@ fn expect_none() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderExpectedSymbol { symbol: 'a' }
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderExpectedSymbol { symbol: 'a' }
|
||||
);
|
||||
assert_eq!(e.cursor(), Some(0));
|
||||
}
|
||||
|
@ -591,8 +591,8 @@ fn read_boolean_incorrect() {
|
|||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(
|
||||
e.get_type(),
|
||||
&BuiltInExceptions::ReaderInvalidBool {
|
||||
e.kind(),
|
||||
&BuiltInError::ReaderInvalidBool {
|
||||
value: "tuesday".to_string()
|
||||
}
|
||||
);
|
||||
|
@ -606,7 +606,7 @@ fn read_boolean_none() {
|
|||
let result = reader.read_boolean();
|
||||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e.get_type(), &BuiltInExceptions::ReaderExpectedBool);
|
||||
assert_eq!(e.kind(), &BuiltInError::ReaderExpectedBool);
|
||||
assert_eq!(e.cursor(), Some(0));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue