From 12370ab07609bf78baef4ec2302fa4ba44317dae Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 5 May 2023 23:09:57 -0500 Subject: change some things to be Arc+RwLock in brigadier --- azalea-brigadier/src/tree/mod.rs | 47 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'azalea-brigadier/src/tree') diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs index bb6af68d..902e288b 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,7 +10,7 @@ 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, rc::Rc, sync::Arc}; pub type Command = Option) -> i32>>; @@ -17,13 +19,13 @@ pub type Command = Option) -> i32>>; pub struct CommandNode { pub value: ArgumentBuilderType, - pub children: HashMap>>>, - pub literals: HashMap>>>, - pub arguments: HashMap>>>, + pub children: HashMap>>>, + pub literals: HashMap>>>, + pub arguments: HashMap>>>, pub command: Command, pub requirement: Rc) -> bool>, - pub redirect: Option>>>, + pub redirect: Option>>>, pub forks: bool, pub modifier: Option>>, } @@ -62,7 +64,7 @@ impl CommandNode { } } - pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { + pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec>>> { let literals = &self.literals; if literals.is_empty() { @@ -92,20 +94,20 @@ impl CommandNode { (self.requirement)(source) } - pub fn add_child(&mut self, node: &Rc>>) { - let child = self.children.get(node.borrow().name()); + pub fn add_child(&mut self, node: &Arc>>) { + 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 CommandNode { } } - pub fn child(&self, name: &str) -> Option>>> { + pub fn child(&self, name: &str) -> Option>>> { self.children.get(name).cloned() } @@ -142,7 +144,7 @@ impl CommandNode { }; 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 CommandNode { 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(()); @@ -232,7 +234,7 @@ impl Hash for CommandNode { // 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,9 +243,16 @@ impl Hash for CommandNode { impl PartialEq for CommandNode { 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 { -- cgit v1.2.3 From f825544e2776ab545ff0a9c674b68183120695cb Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 5 May 2023 23:23:11 -0500 Subject: CommandDispatcher is now Send+Sync --- azalea-brigadier/src/builder/argument_builder.rs | 20 ++++---- .../src/builder/required_argument_builder.rs | 13 ++++-- azalea-brigadier/src/command_dispatcher.rs | 10 ++-- azalea-brigadier/src/context/command_context.rs | 10 ++-- .../src/context/command_context_builder.rs | 14 +++--- azalea-brigadier/src/modifier.rs | 4 +- azalea-brigadier/src/tree/mod.rs | 14 +++--- azalea-brigadier/tests/command_dispatcher_test.rs | 54 +++++++++++----------- 8 files changed, 71 insertions(+), 68 deletions(-) (limited to 'azalea-brigadier/src/tree') diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index a1f3d6ae..c6f2146a 100755 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -7,7 +7,7 @@ use crate::{ }; use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; -use std::{fmt::Debug, rc::Rc, sync::Arc}; +use std::{fmt::Debug, sync::Arc}; #[derive(Debug, Clone)] pub enum ArgumentBuilderType { @@ -20,11 +20,11 @@ pub struct ArgumentBuilder { arguments: CommandNode, command: Command, - requirement: Rc) -> bool>, + requirement: Arc) -> bool + Send + Sync>, target: Option>>>, forks: bool, - modifier: Option>>, + modifier: Option>>, } /// A node that isn't yet built. @@ -36,7 +36,7 @@ impl ArgumentBuilder { ..Default::default() }, command: None, - requirement: Rc::new(|_| true), + requirement: Arc::new(|_| true), forks: false, modifier: None, target: None, @@ -54,17 +54,17 @@ impl ArgumentBuilder { pub fn executes(mut self, f: F) -> Self where - F: Fn(&CommandContext) -> i32 + 'static, + F: Fn(&CommandContext) -> i32 + Send + Sync + 'static, { - self.command = Some(Rc::new(f)); + self.command = Some(Arc::new(f)); self } pub fn requires(mut self, requirement: F) -> Self where - F: Fn(Rc) -> bool + 'static, + F: Fn(Arc) -> bool + Send + Sync + 'static, { - self.requirement = Rc::new(requirement); + self.requirement = Arc::new(requirement); self } @@ -75,7 +75,7 @@ impl ArgumentBuilder { pub fn fork( self, target: Arc>>, - modifier: Rc>, + modifier: Arc>, ) -> Self { self.forward(target, Some(modifier), true) } @@ -83,7 +83,7 @@ impl ArgumentBuilder { pub fn forward( mut self, target: Arc>>, - modifier: Option>>, + modifier: Option>>, fork: bool, ) -> Self { if !self.arguments.children.is_empty() { 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, + parser: Arc, } impl Argument { - pub fn new(name: &str, parser: Rc) -> Self { + pub fn new(name: &str, parser: Arc) -> 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(name: &str, parser: impl ArgumentType + 'static) -> ArgumentBuilder { - ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into()) +pub fn argument( + name: &str, + parser: impl ArgumentType + Send + Sync + 'static, +) -> ArgumentBuilder { + 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 0d84171e..384a28dd 100755 --- a/azalea-brigadier/src/command_dispatcher.rs +++ b/azalea-brigadier/src/command_dispatcher.rs @@ -13,8 +13,8 @@ use std::{cmp::Ordering, collections::HashMap, mem, rc::Rc, sync::Arc}; /// The root of the command tree. You need to make this to register commands. #[derive(Default)] pub struct CommandDispatcher -// where -// Self: Sync + Send, +where + Self: Sync + Send, { pub root: Arc>>, } @@ -32,7 +32,7 @@ impl CommandDispatcher { build } - pub fn parse(&self, command: StringReader, source: Rc) -> ParseResults { + pub fn parse(&self, command: StringReader, source: Arc) -> ParseResults { let context = CommandContextBuilder::new(self, source, self.root.clone(), command.cursor()); self.parse_nodes(&self.root, &command, context).unwrap() } @@ -91,7 +91,7 @@ impl CommandDispatcher { 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, @@ -144,7 +144,7 @@ impl CommandDispatcher { pub fn execute( &self, input: StringReader, - source: Rc, + source: Arc, ) -> Result { let parse = self.parse(input, source); Self::execute_parsed(parse) diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs index 88af2002..1734bb05 100755 --- a/azalea-brigadier/src/context/command_context.rs +++ b/azalea-brigadier/src/context/command_context.rs @@ -9,15 +9,15 @@ use std::{any::Any, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc}; /// A built `CommandContextBuilder`. pub struct CommandContext { - pub source: Rc, + pub source: Arc, pub input: String, pub arguments: HashMap, pub command: Command, pub root_node: Arc>>, pub nodes: Vec>, pub range: StringRange, - pub child: Option>>, - pub modifier: Option>>, + pub child: Option>>, + pub modifier: Option>>, pub forks: bool, } @@ -56,8 +56,8 @@ impl Debug for CommandContext { } impl CommandContext { - pub fn copy_for(&self, source: Rc) -> Self { - if Rc::ptr_eq(&source, &self.source) { + pub fn copy_for(&self, source: Arc) -> 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 54063879..78088941 100755 --- a/azalea-brigadier/src/context/command_context_builder.rs +++ b/azalea-brigadier/src/context/command_context_builder.rs @@ -9,18 +9,18 @@ use crate::{ modifier::RedirectModifier, tree::{Command, CommandNode}, }; -use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc}; +use std::{collections::HashMap, fmt::Debug, sync::Arc}; pub struct CommandContextBuilder<'a, S> { pub arguments: HashMap, pub root: Arc>>, pub nodes: Vec>, pub dispatcher: &'a CommandDispatcher, - pub source: Rc, + pub source: Arc, pub command: Command, - pub child: Option>>, + pub child: Option>>, pub range: StringRange, - pub modifier: Option>>, + pub modifier: Option>>, pub forks: bool, } @@ -44,7 +44,7 @@ impl Clone for CommandContextBuilder<'_, S> { impl<'a, S> CommandContextBuilder<'a, S> { pub fn new( dispatcher: &'a CommandDispatcher, - source: Rc, + source: Arc, root_node: Arc>>, start: usize, ) -> Self { @@ -66,7 +66,7 @@ impl<'a, S> CommandContextBuilder<'a, S> { self.command = command.clone(); self } - pub fn with_child(&mut self, child: Rc>) -> &Self { + pub fn with_child(&mut self, child: Arc>) -> &Self { self.child = Some(child); self } @@ -92,7 +92,7 @@ impl<'a, S> CommandContextBuilder<'a, 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(), 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 = - dyn Fn(&CommandContext) -> Result>, CommandSyntaxException>; + dyn Fn(&CommandContext) -> Result>, CommandSyntaxException> + Send + Sync; diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs index 902e288b..cec972dc 100755 --- a/azalea-brigadier/src/tree/mod.rs +++ b/azalea-brigadier/src/tree/mod.rs @@ -10,9 +10,9 @@ use crate::{ modifier::RedirectModifier, string_reader::StringReader, }; -use std::{collections::HashMap, fmt::Debug, hash::Hash, ptr, rc::Rc, sync::Arc}; +use std::{collections::HashMap, fmt::Debug, hash::Hash, ptr, sync::Arc}; -pub type Command = Option) -> i32>>; +pub type Command = Option) -> i32 + Send + Sync>>; /// An ArgumentBuilder that has been built. #[non_exhaustive] @@ -24,10 +24,10 @@ pub struct CommandNode { pub arguments: HashMap>>>, pub command: Command, - pub requirement: Rc) -> bool>, + pub requirement: Arc) -> bool + Send + Sync>, pub redirect: Option>>>, pub forks: bool, - pub modifier: Option>>, + pub modifier: Option>>, } impl Clone for CommandNode { @@ -90,7 +90,7 @@ impl CommandNode { } } - pub fn can_use(&self, source: Rc) -> bool { + pub fn can_use(&self, source: Arc) -> bool { (self.requirement)(source) } @@ -221,7 +221,7 @@ impl Default for CommandNode { arguments: HashMap::new(), command: None, - requirement: Rc::new(|_| true), + requirement: Arc::new(|_| true), redirect: None, forks: false, modifier: None, @@ -257,7 +257,7 @@ impl PartialEq for CommandNode { // 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 { diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs index fb0085ba..4479cfa2 100755 --- a/azalea-brigadier/tests/command_dispatcher_test.rs +++ b/azalea-brigadier/tests/command_dispatcher_test.rs @@ -1,4 +1,4 @@ -use std::rc::Rc; +use std::sync::Arc; use azalea_brigadier::{ arguments::integer_argument_type::integer, @@ -25,7 +25,7 @@ fn create_and_execute_command() { assert_eq!( subject - .execute("foo".into(), Rc::new(CommandSource {})) + .execute("foo".into(), Arc::new(CommandSource {})) .unwrap(), 42 ); @@ -38,7 +38,7 @@ fn create_and_execute_offset_command() { assert_eq!( subject - .execute(input_with_offset("/foo", 1), Rc::new(CommandSource {})) + .execute(input_with_offset("/foo", 1), Arc::new(CommandSource {})) .unwrap(), 42 ); @@ -52,13 +52,13 @@ fn create_and_merge_commands() { assert_eq!( subject - .execute("base foo".into(), Rc::new(CommandSource {})) + .execute("base foo".into(), Arc::new(CommandSource {})) .unwrap(), 42 ); assert_eq!( subject - .execute("base bar".into(), Rc::new(CommandSource {})) + .execute("base bar".into(), Arc::new(CommandSource {})) .unwrap(), 42 ); @@ -70,7 +70,7 @@ fn execute_unknown_command() { subject.register(literal("bar")); subject.register(literal("baz")); - let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {})); + let execute_result = subject.execute("foo".into(), Arc::new(CommandSource {})); let err = execute_result.err().unwrap(); match err.type_ { @@ -85,7 +85,7 @@ 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 execute_result = subject.execute("foo".into(), Arc::new(CommandSource {})); let err = execute_result.err().unwrap(); match err.type_ { @@ -100,7 +100,7 @@ fn execute_empty_command() { let mut subject = CommandDispatcher::new(); subject.register(literal("")); - let execute_result = subject.execute("".into(), Rc::new(CommandSource {})); + let execute_result = subject.execute("".into(), Arc::new(CommandSource {})); let err = execute_result.err().unwrap(); match err.type_ { @@ -115,7 +115,7 @@ 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 execute_result = subject.execute("foo bar".into(), Arc::new(CommandSource {})); let err = execute_result.err().unwrap(); match err.type_ { @@ -130,7 +130,7 @@ 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 execute_result = subject.execute("foo baz".into(), Arc::new(CommandSource {})); let err = execute_result.err().unwrap(); match err.type_ { @@ -150,7 +150,7 @@ fn execute_ambiguous_incorrect_argument() { .then(literal("baz")), ); - let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {})); + let execute_result = subject.execute("foo unknown".into(), Arc::new(CommandSource {})); let err = execute_result.err().unwrap(); match err.type_ { @@ -174,7 +174,7 @@ fn execute_subcommand() { assert_eq!( subject - .execute("foo =".into(), Rc::new(CommandSource {})) + .execute("foo =".into(), Arc::new(CommandSource {})) .unwrap(), 100 ); @@ -185,7 +185,7 @@ 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 {})); + let parse = subject.parse("foo ".into(), Arc::new(CommandSource {})); assert_eq!(parse.reader.remaining(), " "); assert_eq!(parse.context.nodes.len(), 1); } @@ -195,7 +195,7 @@ 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 {})); + let parse = subject.parse("foo ".into(), Arc::new(CommandSource {})); assert_eq!(parse.reader.remaining(), " "); assert_eq!(parse.context.nodes.len(), 1); } @@ -212,7 +212,7 @@ fn execute_ambiguious_parent_subcommand() { assert_eq!( subject - .execute("test 1 2".into(), Rc::new(CommandSource {})) + .execute("test 1 2".into(), Arc::new(CommandSource {})) .unwrap(), 100 ); @@ -232,7 +232,7 @@ fn execute_ambiguious_parent_subcommand_via_redirect() { assert_eq!( subject - .execute("redirect 1 2".into(), Rc::new(CommandSource {})) + .execute("redirect 1 2".into(), Arc::new(CommandSource {})) .unwrap(), 100 ); @@ -248,7 +248,7 @@ fn execute_redirected_multiple_times() { let input = "redirected redirected actual"; - let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + let parse = subject.parse(input.into(), Arc::new(CommandSource {})); assert_eq!(parse.context.range.get(input), "redirected"); assert_eq!(parse.context.nodes.len(), 1); assert_eq!(*parse.context.root.read(), *root.read()); @@ -287,19 +287,19 @@ fn execute_redirected_multiple_times() { fn execute_redirected() { let mut subject = CommandDispatcher::new(); - let source1 = Rc::new(CommandSource {}); - let source2 = Rc::new(CommandSource {}); + let source1 = Arc::new(CommandSource {}); + let source2 = Arc::new(CommandSource {}); - let modifier = move |_: &CommandContext| -> Result>, CommandSyntaxException> { + 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))); + subject.register(literal("redirected").fork(subject.root.clone(), Arc::new(modifier))); let input = "redirected actual"; - let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + let parse = subject.parse(input.into(), Arc::new(CommandSource {})); assert_eq!(parse.context.range.get(input), "redirected"); assert_eq!(parse.context.nodes.len(), 1); assert_eq!(*parse.context.root.read(), *subject.root.read()); @@ -314,7 +314,7 @@ fn execute_redirected() { assert_eq!(*parse.context.root.read(), *subject.root.read()); assert_eq!(parent.nodes[0].range, parent.range); assert_eq!(*parent.nodes[0].node.read(), *concrete_node.read()); - assert_eq!(parent.source, Rc::new(CommandSource {})); + assert_eq!(parent.source, Arc::new(CommandSource {})); assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2); } @@ -329,7 +329,7 @@ fn execute_orphaned_subcommand() { .executes(|_| 42), ); - let result = subject.execute("foo 5".into(), Rc::new(CommandSource {})); + let result = subject.execute("foo 5".into(), Arc::new(CommandSource {})); assert!(result.is_err()); let result = result.unwrap_err(); assert_eq!( @@ -348,7 +348,7 @@ fn execute_invalid_other() { assert_eq!( subject - .execute("world".into(), Rc::new(CommandSource {})) + .execute("world".into(), Arc::new(CommandSource {})) .unwrap(), 42 ); @@ -364,7 +364,7 @@ fn parse_no_space_separator() { .executes(|_| 42), ); - let result = subject.execute("foo$".into(), Rc::new(CommandSource {})); + let result = subject.execute("foo$".into(), Arc::new(CommandSource {})); assert!(result.is_err()); let result = result.unwrap_err(); assert_eq!( @@ -384,7 +384,7 @@ fn execute_invalid_subcommand() { .executes(|_| 42), ); - let result = subject.execute("foo bar".into(), Rc::new(CommandSource {})); + let result = subject.execute("foo bar".into(), Arc::new(CommandSource {})); assert!(result.is_err()); let result = result.unwrap_err(); // this fails for some reason, i blame mojang -- cgit v1.2.3