diff options
Diffstat (limited to 'azalea-brigadier/src')
17 files changed, 473 insertions, 160 deletions
diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs new file mode 100644 index 00000000..57fa8a03 --- /dev/null +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -0,0 +1,21 @@ +use std::{any::Any, rc::Rc}; + +use crate::{ + context::CommandContext, exceptions::CommandSyntaxException, string_reader::StringReader, +}; + +use super::ArgumentType; + +impl ArgumentType for bool { + fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> { + Ok(Rc::new(reader.read_boolean())) + } +} + +pub fn get_bool<S>(context: &CommandContext<S>, name: &str) -> Option<bool> { + context + .argument(name) + .unwrap() + .downcast_ref::<bool>() + .cloned() +} diff --git a/azalea-brigadier/src/arguments/double_argument_type.rs b/azalea-brigadier/src/arguments/double_argument_type.rs new file mode 100644 index 00000000..d83bfb2a --- /dev/null +++ b/azalea-brigadier/src/arguments/double_argument_type.rs @@ -0,0 +1,54 @@ +use std::{any::Any, rc::Rc}; + +use crate::{ + context::CommandContext, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + string_reader::StringReader, +}; + +use super::ArgumentType; + +#[derive(Default)] +struct Double { + pub minimum: Option<f64>, + pub maximum: Option<f64>, +} + +impl ArgumentType for Double { + fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> { + let start = reader.cursor; + let result = reader.read_double()?; + if let Some(minimum) = self.minimum { + if result < minimum { + reader.cursor = start; + return Err(BuiltInExceptions::DoubleTooSmall { + found: result, + min: minimum, + } + .create_with_context(reader)); + } + } + if let Some(maximum) = self.maximum { + if result > maximum { + reader.cursor = start; + return Err(BuiltInExceptions::DoubleTooBig { + found: result, + max: maximum, + } + .create_with_context(reader)); + } + } + Ok(Rc::new(result)) + } +} + +pub fn double() -> impl ArgumentType { + Double::default() +} +pub fn get_double<S>(context: &CommandContext<S>, name: &str) -> Option<f64> { + context + .argument(name) + .unwrap() + .downcast_ref::<f64>() + .copied() +} diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs new file mode 100644 index 00000000..0d194efe --- /dev/null +++ b/azalea-brigadier/src/arguments/float_argument_type.rs @@ -0,0 +1,54 @@ +use std::{any::Any, rc::Rc}; + +use crate::{ + context::CommandContext, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + string_reader::StringReader, +}; + +use super::ArgumentType; + +#[derive(Default)] +struct Float { + pub minimum: Option<f32>, + pub maximum: Option<f32>, +} + +impl ArgumentType for Float { + fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> { + let start = reader.cursor; + let result = reader.read_float()?; + if let Some(minimum) = self.minimum { + if result < minimum { + reader.cursor = start; + return Err(BuiltInExceptions::FloatTooSmall { + found: result, + min: minimum, + } + .create_with_context(reader)); + } + } + if let Some(maximum) = self.maximum { + if result > maximum { + reader.cursor = start; + return Err(BuiltInExceptions::FloatTooBig { + found: result, + max: maximum, + } + .create_with_context(reader)); + } + } + Ok(Rc::new(result)) + } +} + +pub fn float() -> impl ArgumentType { + Float::default() +} +pub fn get_float<S>(context: &CommandContext<S>, name: &str) -> Option<f32> { + context + .argument(name) + .unwrap() + .downcast_ref::<f32>() + .copied() +} diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs index 336046a7..336046a7 100755..100644 --- a/azalea-brigadier/src/arguments/integer_argument_type.rs +++ b/azalea-brigadier/src/arguments/integer_argument_type.rs diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs new file mode 100644 index 00000000..9b3469b6 --- /dev/null +++ b/azalea-brigadier/src/arguments/long_argument_type.rs @@ -0,0 +1,54 @@ +use std::{any::Any, rc::Rc}; + +use crate::{ + context::CommandContext, + exceptions::{BuiltInExceptions, CommandSyntaxException}, + string_reader::StringReader, +}; + +use super::ArgumentType; + +#[derive(Default)] +struct Long { + pub minimum: Option<i64>, + pub maximum: Option<i64>, +} + +impl ArgumentType for Long { + fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> { + let start = reader.cursor; + let result = reader.read_long()?; + if let Some(minimum) = self.minimum { + if result < minimum { + reader.cursor = start; + return Err(BuiltInExceptions::LongTooSmall { + found: result, + min: minimum, + } + .create_with_context(reader)); + } + } + if let Some(maximum) = self.maximum { + if result > maximum { + reader.cursor = start; + return Err(BuiltInExceptions::LongTooBig { + found: result, + max: maximum, + } + .create_with_context(reader)); + } + } + Ok(Rc::new(result)) + } +} + +pub fn long() -> impl ArgumentType { + Long::default() +} +pub fn get_long<S>(context: &CommandContext<S>, name: &str) -> Option<i64> { + context + .argument(name) + .unwrap() + .downcast_ref::<i64>() + .copied() +} diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs index dec39297..9d2c2cd0 100755 --- a/azalea-brigadier/src/arguments/mod.rs +++ b/azalea-brigadier/src/arguments/mod.rs @@ -1,4 +1,9 @@ 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; pub use argument_type::ArgumentType; diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs new file mode 100644 index 00000000..27363bd4 --- /dev/null +++ b/azalea-brigadier/src/arguments/string_argument_type.rs @@ -0,0 +1,53 @@ +use std::{any::Any, rc::Rc}; + +use crate::{ + context::CommandContext, exceptions::CommandSyntaxException, string_reader::StringReader, +}; + +use super::ArgumentType; + +pub enum StringArgument { + /// Match up until the next space. + SingleWord, + /// Same as single word unless the argument is wrapped in quotes, in which + /// case it can contain spaces. + QuotablePhrase, + /// Match the rest of the input. + GreedyPhrase, +} + +impl ArgumentType for StringArgument { + fn parse(&self, reader: &mut StringReader) -> Result<Rc<dyn Any>, CommandSyntaxException> { + let result = match self { + StringArgument::SingleWord => reader.read_unquoted_string().to_string(), + StringArgument::QuotablePhrase => reader.read_string()?, + StringArgument::GreedyPhrase => { + let text = reader.remaining().to_string(); + reader.cursor = reader.total_length(); + text + } + }; + Ok(Rc::new(result)) + } +} + +/// Match up until the next space. +pub fn word() -> impl ArgumentType { + StringArgument::SingleWord +} +/// Same as single word unless the argument is wrapped in quotes, in which case +/// it can contain spaces. +pub fn string() -> impl ArgumentType { + StringArgument::QuotablePhrase +} +/// Match the rest of the input. +pub fn greedy_string() -> impl ArgumentType { + StringArgument::GreedyPhrase +} +pub fn get_string<S>(context: &CommandContext<S>, name: &str) -> Option<String> { + context + .argument(name) + .unwrap() + .downcast_ref::<String>() + .cloned() +} diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 38ccc98c..643a3bd0 100755 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,3 +1,5 @@ +use parking_lot::RwLock; + use crate::{ context::CommandContext, modifier::RedirectModifier, @@ -5,7 +7,7 @@ use crate::{ }; use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; -use std::{cell::RefCell, fmt::Debug, rc::Rc}; +use std::{fmt::Debug, sync::Arc}; #[derive(Debug, Clone)] pub enum ArgumentBuilderType { @@ -18,24 +20,11 @@ pub struct ArgumentBuilder<S> { arguments: CommandNode<S>, command: Command<S>, - requirement: Rc<dyn Fn(Rc<S>) -> bool>, - target: Option<Rc<RefCell<CommandNode<S>>>>, + requirement: Arc<dyn Fn(Arc<S>) -> bool + Send + Sync>, + target: Option<Arc<RwLock<CommandNode<S>>>>, forks: bool, - modifier: Option<Rc<RedirectModifier<S>>>, -} - -impl<S> Clone for ArgumentBuilder<S> { - fn clone(&self) -> Self { - Self { - arguments: self.arguments.clone(), - command: self.command.clone(), - requirement: self.requirement.clone(), - target: self.target.clone(), - forks: self.forks, - modifier: self.modifier.clone(), - } - } + modifier: Option<Arc<RedirectModifier<S>>>, } /// A node that isn't yet built. @@ -47,54 +36,93 @@ impl<S> ArgumentBuilder<S> { ..Default::default() }, command: None, - requirement: Rc::new(|_| true), + requirement: Arc::new(|_| true), forks: false, modifier: None, target: None, } } - pub fn then(&mut self, argument: ArgumentBuilder<S>) -> Self { + /// Continue building this node with a child node. + /// + /// ``` + /// # use azalea_brigadier::prelude::*; + /// # let mut subject = CommandDispatcher::<()>::new(); + /// literal("foo").then( + /// literal("bar").executes(|ctx: &CommandContext<()>| 42) + /// ) + /// # ; + /// ``` + pub fn then(self, argument: ArgumentBuilder<S>) -> Self { self.then_built(argument.build()) } - pub fn then_built(&mut self, argument: CommandNode<S>) -> Self { - self.arguments.add_child(&Rc::new(RefCell::new(argument))); - self.clone() + /// Add an already built child node to this node. + /// + /// You should usually use [`Self::then`] instead. + pub fn then_built(mut self, argument: CommandNode<S>) -> Self { + self.arguments.add_child(&Arc::new(RwLock::new(argument))); + self } - pub fn executes<F>(&mut self, f: F) -> Self + /// Set the command to be executed when this node is reached. If this is not + /// present on a node, it is not a valid command. + /// + /// ``` + /// # use azalea_brigadier::prelude::*; + /// # let mut subject = CommandDispatcher::<()>::new(); + /// # subject.register( + /// literal("foo").executes(|ctx: &CommandContext<()>| 42) + /// # ); + /// ``` + pub fn executes<F>(mut self, f: F) -> Self where - F: Fn(&CommandContext<S>) -> i32 + 'static, + F: Fn(&CommandContext<S>) -> i32 + Send + Sync + 'static, { - self.command = Some(Rc::new(f)); - self.clone() + self.command = Some(Arc::new(f)); + self } - pub fn requires<F>(&mut self, requirement: F) -> Self + /// Set the requirement for this node to be considered. If this is not + /// present on a node, it is considered to always pass. + /// + /// ``` + /// # use azalea_brigadier::prelude::*; + /// # use std::sync::Arc; + /// # pub struct CommandSource { + /// # pub opped: bool, + /// # } + /// # let mut subject = CommandDispatcher::<CommandSource>::new(); + /// # subject.register( + /// literal("foo") + /// .requires(|s: Arc<CommandSource>| s.opped) + /// // ... + /// # .executes(|ctx: &CommandContext<CommandSource>| 42) + /// # ); + pub fn requires<F>(mut self, requirement: F) -> Self where - F: Fn(Rc<S>) -> bool + 'static, + F: Fn(Arc<S>) -> bool + Send + Sync + 'static, { - self.requirement = Rc::new(requirement); - self.clone() + self.requirement = Arc::new(requirement); + self } - pub fn redirect(&mut self, target: Rc<RefCell<CommandNode<S>>>) -> Self { + pub fn redirect(self, target: Arc<RwLock<CommandNode<S>>>) -> Self { self.forward(target, None, false) } pub fn fork( - &mut self, - target: Rc<RefCell<CommandNode<S>>>, - modifier: Rc<RedirectModifier<S>>, + self, + target: Arc<RwLock<CommandNode<S>>>, + modifier: Arc<RedirectModifier<S>>, ) -> Self { self.forward(target, Some(modifier), true) } pub fn forward( - &mut self, - target: Rc<RefCell<CommandNode<S>>>, - modifier: Option<Rc<RedirectModifier<S>>>, + mut self, + target: Arc<RwLock<CommandNode<S>>>, + modifier: Option<Arc<RedirectModifier<S>>>, fork: bool, ) -> Self { if !self.arguments.children.is_empty() { @@ -103,9 +131,11 @@ impl<S> ArgumentBuilder<S> { self.target = Some(target); self.modifier = modifier; self.forks = fork; - self.clone() + self } + /// Manually build this node into a [`CommandNode`]. You probably don't need + /// to do this yourself. pub fn build(self) -> CommandNode<S> { let mut result = CommandNode { value: self.arguments.value, diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 9d4d9e0a..0363d204 100755 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -2,17 +2,17 @@ use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType}; use crate::{ arguments::ArgumentType, exceptions::CommandSyntaxException, string_reader::StringReader, }; -use std::{any::Any, fmt::Debug, rc::Rc}; +use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc}; /// 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<dyn ArgumentType>, + parser: Arc<dyn ArgumentType + Send + Sync>, } impl Argument { - pub fn new(name: &str, parser: Rc<dyn ArgumentType>) -> Self { + pub fn new(name: &str, parser: Arc<dyn ArgumentType + Send + Sync>) -> Self { Self { name: name.to_string(), parser, @@ -40,6 +40,9 @@ impl Debug for Argument { } /// Shortcut for creating a new argument builder node. -pub fn argument<S>(name: &str, parser: impl ArgumentType + 'static) -> ArgumentBuilder<S> { - ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into()) +pub fn argument<S>( + name: &str, + parser: impl ArgumentType + Send + Sync + 'static, +) -> ArgumentBuilder<S> { + ArgumentBuilder::new(Argument::new(name, Arc::new(parser)).into()) } diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs index 273b1681..595bc57d 100755 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -1,3 +1,5 @@ +use parking_lot::RwLock; + use crate::{ builder::argument_builder::ArgumentBuilder, context::{CommandContext, CommandContextBuilder}, @@ -6,64 +8,72 @@ use crate::{ string_reader::StringReader, tree::CommandNode, }; -use std::{cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomData, mem, rc::Rc}; +use std::{cmp::Ordering, collections::HashMap, mem, rc::Rc, sync::Arc}; -#[derive(Default)] -pub struct CommandDispatcher<S> { - pub root: Rc<RefCell<CommandNode<S>>>, - _marker: PhantomData<S>, +/// The root of the command tree. You need to make this to register commands. +/// +/// ``` +/// # use azalea_brigadier::prelude::*; +/// # struct CommandSource; +/// let mut subject = CommandDispatcher::<CommandSource>::new(); +/// ``` +pub struct CommandDispatcher<S> +where + Self: Sync + Send, +{ + pub root: Arc<RwLock<CommandNode<S>>>, } impl<S> CommandDispatcher<S> { pub fn new() -> Self { Self { - root: Rc::new(RefCell::new(CommandNode::default())), - _marker: PhantomData, + root: Arc::new(RwLock::new(CommandNode::default())), } } - pub fn register(&mut self, node: ArgumentBuilder<S>) -> Rc<RefCell<CommandNode<S>>> { - let build = Rc::new(RefCell::new(node.build())); - self.root.borrow_mut().add_child(&build); + /// Add a new node to the root. + /// + /// ``` + /// # use azalea_brigadier::prelude::*; + /// # let mut subject = CommandDispatcher::<()>::new(); + /// subject.register(literal("foo").executes(|_| 42)); + /// ``` + pub fn register(&mut self, node: ArgumentBuilder<S>) -> Arc<RwLock<CommandNode<S>>> { + let build = Arc::new(RwLock::new(node.build())); + self.root.write().add_child(&build); build } - pub fn parse(&self, command: StringReader, source: Rc<S>) -> ParseResults<S> { - let context = CommandContextBuilder::new( - Rc::new(self.clone()), - source, - self.root.clone(), - command.cursor(), - ); + pub fn parse(&self, command: StringReader, source: S) -> ParseResults<S> { + let source = Arc::new(source); + + let context = CommandContextBuilder::new(self, source, self.root.clone(), command.cursor()); self.parse_nodes(&self.root, &command, context).unwrap() } - fn parse_nodes( - &self, - node: &Rc<RefCell<CommandNode<S>>>, + fn parse_nodes<'a>( + &'a self, + node: &Arc<RwLock<CommandNode<S>>>, original_reader: &StringReader, - context_so_far: CommandContextBuilder<S>, - ) -> Result<ParseResults<S>, CommandSyntaxException> { + context_so_far: CommandContextBuilder<'a, S>, + ) -> Result<ParseResults<'a, S>, CommandSyntaxException> { let source = context_so_far.source.clone(); let mut errors = HashMap::<Rc<CommandNode<S>>, CommandSyntaxException>::new(); let mut potentials: Vec<ParseResults<S>> = 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()) { + for child in node.read().get_relevant_nodes(&mut original_reader.clone()) { + if !child.read().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); + child.read().parse_with_context(&mut reader, &mut context); if let Err(ex) = parse_with_context_result { errors.insert( - Rc::new((*child.borrow()).clone()), + Rc::new((*child.read()).clone()), BuiltInExceptions::DispatcherParseException { message: ex.message(), } @@ -74,7 +84,7 @@ impl<S> CommandDispatcher<S> { } if reader.can_read() && reader.peek() != ' ' { errors.insert( - Rc::new((*child.borrow()).clone()), + Rc::new((*child.read()).clone()), BuiltInExceptions::DispatcherExpectedArgumentSeparator .create_with_context(&reader), ); @@ -82,24 +92,20 @@ impl<S> CommandDispatcher<S> { continue; } - context.with_command(&child.borrow().command); - if reader.can_read_length(if child.borrow().redirect.is_none() { + context.with_command(&child.read().command); + if reader.can_read_length(if child.read().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, - ); + if let Some(redirect) = &child.read().redirect { + let child_context = + CommandContextBuilder::new(self, 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)); + context.with_child(Arc::new(parse.context)); return Ok(ParseResults { context, reader: parse.reader, @@ -149,40 +155,46 @@ impl<S> CommandDispatcher<S> { }) } + /// Parse and execute the command using the given input and context. The + /// number returned depends on the command, and may not be of significance. + /// + /// This is a shortcut for `Self::parse` and `Self::execute_parsed`. pub fn execute( &self, - input: StringReader, - source: Rc<S>, + input: impl Into<StringReader>, + source: S, ) -> Result<i32, CommandSyntaxException> { + let input = input.into(); + let parse = self.parse(input, source); Self::execute_parsed(parse) } pub fn add_paths( - node: Rc<RefCell<CommandNode<S>>>, - result: &mut Vec<Vec<Rc<RefCell<CommandNode<S>>>>>, - parents: Vec<Rc<RefCell<CommandNode<S>>>>, + node: Arc<RwLock<CommandNode<S>>>, + result: &mut Vec<Vec<Arc<RwLock<CommandNode<S>>>>>, + parents: Vec<Arc<RwLock<CommandNode<S>>>>, ) { let mut current = parents; current.push(node.clone()); result.push(current.clone()); - for child in node.borrow().children.values() { + for child in node.read().children.values() { Self::add_paths(child.clone(), result, current.clone()); } } pub fn get_path(&self, target: CommandNode<S>) -> Vec<String> { - let rc_target = Rc::new(RefCell::new(target)); - let mut nodes: Vec<Vec<Rc<RefCell<CommandNode<S>>>>> = Vec::new(); + let rc_target = Arc::new(RwLock::new(target)); + let mut nodes: Vec<Vec<Arc<RwLock<CommandNode<S>>>>> = 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() { + if *list.last().expect("Nothing in list").read() == *rc_target.read() { let mut result: Vec<String> = Vec::with_capacity(list.len()); for node in list { - if node != self.root { - result.push(node.borrow().name().to_string()); + if !Arc::ptr_eq(&node, &self.root) { + result.push(node.read().name().to_string()); } } return result; @@ -191,10 +203,10 @@ impl<S> CommandDispatcher<S> { vec![] } - pub fn find_node(&self, path: &[&str]) -> Option<Rc<RefCell<CommandNode<S>>>> { + pub fn find_node(&self, path: &[&str]) -> Option<Arc<RwLock<CommandNode<S>>>> { let mut node = self.root.clone(); for name in path { - if let Some(child) = node.clone().borrow().child(name) { + if let Some(child) = node.clone().read().child(name) { node = child; } else { return None; @@ -287,11 +299,8 @@ impl<S> CommandDispatcher<S> { } } -impl<S> Clone for CommandDispatcher<S> { - fn clone(&self) -> Self { - Self { - root: self.root.clone(), - _marker: PhantomData, - } +impl<S> Default for CommandDispatcher<S> { + fn default() -> Self { + Self::new() } } diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 98609a6e..1734bb05 100755 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -1,21 +1,23 @@ +use parking_lot::RwLock; + use super::{parsed_command_node::ParsedCommandNode, string_range::StringRange, ParsedArgument}; use crate::{ modifier::RedirectModifier, tree::{Command, CommandNode}, }; -use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; +use std::{any::Any, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc}; /// A built `CommandContextBuilder`. pub struct CommandContext<S> { - pub source: Rc<S>, + pub source: Arc<S>, pub input: String, pub arguments: HashMap<String, ParsedArgument>, pub command: Command<S>, - pub root_node: Rc<RefCell<CommandNode<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<Rc<RedirectModifier<S>>>, + pub child: Option<Arc<CommandContext<S>>>, + pub modifier: Option<Arc<RedirectModifier<S>>>, pub forks: bool, } @@ -54,8 +56,8 @@ impl<S> Debug for CommandContext<S> { } impl<S> CommandContext<S> { - pub fn copy_for(&self, source: Rc<S>) -> Self { - if Rc::ptr_eq(&source, &self.source) { + pub fn copy_for(&self, source: Arc<S>) -> Self { + if Arc::ptr_eq(&source, &self.source) { return self.clone(); } CommandContext { diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs index 7516ab9e..78088941 100755 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -1,3 +1,5 @@ +use parking_lot::RwLock; + use super::{ command_context::CommandContext, parsed_command_node::ParsedCommandNode, string_range::StringRange, ParsedArgument, @@ -7,28 +9,28 @@ use crate::{ modifier::RedirectModifier, tree::{Command, CommandNode}, }; -use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; +use std::{collections::HashMap, fmt::Debug, sync::Arc}; -pub struct CommandContextBuilder<S> { +pub struct CommandContextBuilder<'a, S> { pub arguments: HashMap<String, ParsedArgument>, - pub root: Rc<RefCell<CommandNode<S>>>, + pub root: Arc<RwLock<CommandNode<S>>>, pub nodes: Vec<ParsedCommandNode<S>>, - pub dispatcher: Rc<CommandDispatcher<S>>, - pub source: Rc<S>, + pub dispatcher: &'a CommandDispatcher<S>, + pub source: Arc<S>, pub command: Command<S>, - pub child: Option<Rc<CommandContextBuilder<S>>>, + pub child: Option<Arc<CommandContextBuilder<'a, S>>>, pub range: StringRange, - pub modifier: Option<Rc<RedirectModifier<S>>>, + pub modifier: Option<Arc<RedirectModifier<S>>>, pub forks: bool, } -impl<S> Clone for CommandContextBuilder<S> { +impl<S> Clone for CommandContextBuilder<'_, S> { fn clone(&self) -> Self { Self { arguments: self.arguments.clone(), root: self.root.clone(), nodes: self.nodes.clone(), - dispatcher: self.dispatcher.clone(), + dispatcher: self.dispatcher, source: self.source.clone(), command: self.command.clone(), child: self.child.clone(), @@ -39,11 +41,11 @@ impl<S> Clone for CommandContextBuilder<S> { } } -impl<S> CommandContextBuilder<S> { +impl<'a, S> CommandContextBuilder<'a, S> { pub fn new( - dispatcher: Rc<CommandDispatcher<S>>, - source: Rc<S>, - root_node: Rc<RefCell<CommandNode<S>>>, + dispatcher: &'a CommandDispatcher<S>, + source: Arc<S>, + root_node: Arc<RwLock<CommandNode<S>>>, start: usize, ) -> Self { Self { @@ -64,7 +66,7 @@ impl<S> CommandContextBuilder<S> { self.command = command.clone(); self } - pub fn with_child(&mut self, child: Rc<CommandContextBuilder<S>>) -> &Self { + pub fn with_child(&mut self, child: Arc<CommandContextBuilder<'a, S>>) -> &Self { self.child = Some(child); self } @@ -72,14 +74,14 @@ impl<S> CommandContextBuilder<S> { self.arguments.insert(name.to_string(), argument); self } - pub fn with_node(&mut self, node: Rc<RefCell<CommandNode<S>>>, range: StringRange) -> &Self { + pub fn with_node(&mut self, node: Arc<RwLock<CommandNode<S>>>, 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.modifier = node.read().modifier.clone(); + self.forks = node.read().forks; self } @@ -90,7 +92,7 @@ impl<S> CommandContextBuilder<S> { 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| Arc::new(c.build(input))), range: self.range.clone(), forks: self.forks, modifier: self.modifier.clone(), @@ -99,7 +101,7 @@ impl<S> CommandContextBuilder<S> { } } -impl<S> Debug for CommandContextBuilder<S> { +impl<S> Debug for CommandContextBuilder<'_, S> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CommandContextBuilder") // .field("arguments", &self.arguments) diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs index ed49928d..bba5d121 100755 --- a/azalea-brigadier/src/context/parsed_command_node.rs +++ b/azalea-brigadier/src/context/parsed_command_node.rs @@ -1,10 +1,12 @@ +use parking_lot::RwLock; + use super::string_range::StringRange; use crate::tree::CommandNode; -use std::{cell::RefCell, rc::Rc}; +use std::sync::Arc; #[derive(Debug)] pub struct ParsedCommandNode<S> { - pub node: Rc<RefCell<CommandNode<S>>>, + pub node: Arc<RwLock<CommandNode<S>>>, pub range: StringRange, } diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index eb670643..161ef83a 100755 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -10,3 +10,18 @@ pub mod parse_results; pub mod string_reader; pub mod suggestion; pub mod tree; + +pub mod prelude { + pub use crate::{ + arguments::{ + double_argument_type::{double, get_double}, + float_argument_type::{float, get_float}, + integer_argument_type::{get_integer, integer}, + long_argument_type::{get_long, long}, + string_argument_type::{get_string, greedy_string, string, word}, + }, + builder::{literal_argument_builder::literal, required_argument_builder::argument}, + command_dispatcher::CommandDispatcher, + context::CommandContext, + }; +} diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs index d40d59f9..bebdd0cb 100755 --- a/azalea-brigadier/src/modifier.rs +++ b/azalea-brigadier/src/modifier.rs @@ -1,6 +1,6 @@ -use std::rc::Rc; +use std::sync::Arc; use crate::{context::CommandContext, exceptions::CommandSyntaxException}; pub type RedirectModifier<S> = - dyn Fn(&CommandContext<S>) -> Result<Vec<Rc<S>>, CommandSyntaxException>; + dyn Fn(&CommandContext<S>) -> Result<Vec<Arc<S>>, CommandSyntaxException> + Send + Sync; diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index 3698ae82..aa7d79ea 100755 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -4,13 +4,13 @@ use crate::{ }; use std::{collections::HashMap, fmt::Debug, rc::Rc}; -pub struct ParseResults<S> { - pub context: CommandContextBuilder<S>, +pub struct ParseResults<'a, S> { + pub context: CommandContextBuilder<'a, S>, pub reader: StringReader, pub exceptions: HashMap<Rc<CommandNode<S>>, CommandSyntaxException>, } -impl<S> Debug for ParseResults<S> { +impl<S> Debug for ParseResults<'_, S> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ParseResults") .field("context", &self.context) diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs index bb6af68d..cec972dc 100755 --- a/azalea-brigadier/src/tree/mod.rs +++ b/azalea-brigadier/src/tree/mod.rs @@ -1,3 +1,5 @@ +use parking_lot::RwLock; + use crate::{ builder::{ argument_builder::ArgumentBuilderType, literal_argument_builder::Literal, @@ -8,24 +10,24 @@ use crate::{ modifier::RedirectModifier, string_reader::StringReader, }; -use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; +use std::{collections::HashMap, fmt::Debug, hash::Hash, ptr, sync::Arc}; -pub type Command<S> = Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>; +pub type Command<S> = Option<Arc<dyn Fn(&CommandContext<S>) -> i32 + Send + Sync>>; /// An ArgumentBuilder that has been built. #[non_exhaustive] pub struct CommandNode<S> { pub value: ArgumentBuilderType, - pub children: HashMap<String, Rc<RefCell<CommandNode<S>>>>, - pub literals: HashMap<String, Rc<RefCell<CommandNode<S>>>>, - pub arguments: HashMap<String, Rc<RefCell<CommandNode<S>>>>, + pub children: HashMap<String, Arc<RwLock<CommandNode<S>>>>, + pub literals: HashMap<String, Arc<RwLock<CommandNode<S>>>>, + pub arguments: HashMap<String, Arc<RwLock<CommandNode<S>>>>, pub command: Command<S>, - pub requirement: Rc<dyn Fn(Rc<S>) -> bool>, - pub redirect: Option<Rc<RefCell<CommandNode<S>>>>, + pub requirement: Arc<dyn Fn(Arc<S>) -> bool + Send + Sync>, + pub redirect: Option<Arc<RwLock<CommandNode<S>>>>, pub forks: bool, - pub modifier: Option<Rc<RedirectModifier<S>>>, + pub modifier: Option<Arc<RedirectModifier<S>>>, } impl<S> Clone for CommandNode<S> { @@ -62,7 +64,7 @@ impl<S> CommandNode<S> { } } - pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec<Rc<RefCell<CommandNode<S>>>> { + pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec<Arc<RwLock<CommandNode<S>>>> { let literals = &self.literals; if literals.is_empty() { @@ -88,24 +90,24 @@ impl<S> CommandNode<S> { } } - pub fn can_use(&self, source: Rc<S>) -> bool { + pub fn can_use(&self, source: Arc<S>) -> bool { (self.requirement)(source) } - pub fn add_child(&mut self, node: &Rc<RefCell<CommandNode<S>>>) { - let child = self.children.get(node.borrow().name()); + pub fn add_child(&mut self, node: &Arc<RwLock<CommandNode<S>>>) { + let child = self.children.get(node.read().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()); + if let Some(command) = &node.read().command { + child.write().command = Some(command.clone()); } - for grandchild in node.borrow().children.values() { - child.borrow_mut().add_child(grandchild); + for grandchild in node.read().children.values() { + child.write().add_child(grandchild); } } else { self.children - .insert(node.borrow().name().to_string(), node.clone()); - match &node.borrow().value { + .insert(node.read().name().to_string(), node.clone()); + match &node.read().value { ArgumentBuilderType::Literal(literal) => { self.literals.insert(literal.value.clone(), node.clone()); } @@ -123,7 +125,7 @@ impl<S> CommandNode<S> { } } - pub fn child(&self, name: &str) -> Option<Rc<RefCell<CommandNode<S>>>> { + pub fn child(&self, name: &str) -> Option<Arc<RwLock<CommandNode<S>>>> { self.children.get(name).cloned() } @@ -142,7 +144,7 @@ impl<S> CommandNode<S> { }; context_builder.with_argument(&argument.name, parsed.clone()); - context_builder.with_node(Rc::new(RefCell::new(self.clone())), parsed.range); + context_builder.with_node(Arc::new(RwLock::new(self.clone())), parsed.range); Ok(()) } @@ -152,7 +154,7 @@ impl<S> CommandNode<S> { if let Some(end) = end { context_builder.with_node( - Rc::new(RefCell::new(self.clone())), + Arc::new(RwLock::new(self.clone())), StringRange::between(start, end), ); return Ok(()); @@ -219,7 +221,7 @@ impl<S> Default for CommandNode<S> { arguments: HashMap::new(), command: None, - requirement: Rc::new(|_| true), + requirement: Arc::new(|_| true), redirect: None, forks: false, modifier: None, @@ -232,7 +234,7 @@ impl<S> Hash for CommandNode<S> { // hash the children for (k, v) in &self.children { k.hash(state); - v.borrow().hash(state); + v.read().hash(state); } // i hope this works because if doesn't then that'll be a problem ptr::hash(&self.command, state); @@ -241,14 +243,21 @@ impl<S> Hash for CommandNode<S> { impl<S> PartialEq for CommandNode<S> { fn eq(&self, other: &Self) -> bool { - if self.children != other.children { + if self.children.len() != other.children.len() { return false; } + for (k, v) in &self.children { + let other_child = other.children.get(k).unwrap(); + if !Arc::ptr_eq(v, other_child) { + 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) { + if !Arc::ptr_eq(selfexecutes, otherexecutes) { return false; } } else { |
