aboutsummaryrefslogtreecommitdiff
path: root/azalea-brigadier/src/tree
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2022-04-18 18:14:25 +0000
committermat <github@matdoes.dev>2022-04-18 18:14:25 +0000
commit8d71fbf813391783531a9f7c70e75e105fabaf03 (patch)
treedd1d70370d3068c4aecd8aaf25174278dfbc1b29 /azalea-brigadier/src/tree
parentb3864af9c4af83552e37fd71a46262967572f9e6 (diff)
downloadazalea-drasl-8d71fbf813391783531a9f7c70e75e105fabaf03.tar.xz
change a BTreeMap to a HashMap
Diffstat (limited to 'azalea-brigadier/src/tree')
-rw-r--r--azalea-brigadier/src/tree/mod.rs259
1 files changed, 259 insertions, 0 deletions
diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs
new file mode 100644
index 00000000..b6181c73
--- /dev/null
+++ b/azalea-brigadier/src/tree/mod.rs
@@ -0,0 +1,259 @@
+use crate::{
+ builder::{
+ argument_builder::ArgumentBuilderType, literal_argument_builder::Literal,
+ required_argument_builder::Argument,
+ },
+ context::{CommandContext, CommandContextBuilder, ParsedArgument, StringRange},
+ exceptions::{BuiltInExceptions, CommandSyntaxException},
+ modifier::RedirectModifier,
+ string_reader::StringReader,
+};
+use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, ptr, rc::Rc};
+
+/// 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 command: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
+ pub requirement: Rc<dyn Fn(Rc<S>) -> bool>,
+ pub redirect: Option<Rc<RefCell<CommandNode<S>>>>,
+ pub forks: bool,
+ pub modifier: Option<Rc<RedirectModifier<S>>>,
+}
+
+impl<S> Clone for CommandNode<S> {
+ fn clone(&self) -> Self {
+ Self {
+ value: self.value.clone(),
+ children: self.children.clone(),
+ literals: self.literals.clone(),
+ arguments: self.arguments.clone(),
+ command: self.command.clone(),
+ requirement: self.requirement.clone(),
+ redirect: self.redirect.clone(),
+ forks: self.forks,
+ modifier: self.modifier.clone(),
+ }
+ }
+}
+
+impl<S> CommandNode<S> {
+ /// Gets the literal, or panics. You should use match if you're not certain about the type.
+ pub fn literal(&self) -> &Literal {
+ match self.value {
+ ArgumentBuilderType::Literal(ref literal) => literal,
+ _ => panic!("CommandNode::literal() called on non-literal node"),
+ }
+ }
+ /// Gets the argument, or panics. You should use match if you're not certain about the type.
+ pub fn argument(&self) -> &Argument {
+ match self.value {
+ ArgumentBuilderType::Argument(ref argument) => argument,
+ _ => panic!("CommandNode::argument() called on non-argument node"),
+ }
+ }
+
+ pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec<Rc<RefCell<CommandNode<S>>>> {
+ let literals = &self.literals;
+
+ if !literals.is_empty() {
+ let cursor = input.cursor();
+ while input.can_read() && input.peek() != ' ' {
+ input.skip();
+ }
+ let text: String = input
+ .string()
+ .chars()
+ .skip(cursor)
+ .take(input.cursor() - cursor)
+ .collect();
+ input.cursor = cursor;
+ let literal = literals.get(&text);
+ if let Some(literal) = literal {
+ return vec![literal.clone()];
+ } else {
+ return self.arguments.values().cloned().collect();
+ }
+ } else {
+ self.arguments.values().cloned().collect()
+ }
+ }
+
+ pub fn can_use(&self, source: Rc<S>) -> bool {
+ (self.requirement)(source)
+ }
+
+ pub fn add_child(&mut self, node: &Rc<RefCell<CommandNode<S>>>) {
+ let child = self.children.get(node.borrow().name());
+ if let Some(child) = child {
+ // We've found something to merge onto
+ if let Some(command) = &node.borrow().command {
+ child.borrow_mut().command = Some(command.clone());
+ }
+ for grandchild in node.borrow().children.values() {
+ child.borrow_mut().add_child(grandchild);
+ }
+ } else {
+ self.children
+ .insert(node.borrow().name().to_string(), node.clone());
+ match &node.borrow().value {
+ ArgumentBuilderType::Literal(literal) => {
+ self.literals.insert(literal.value.clone(), node.clone());
+ }
+ ArgumentBuilderType::Argument(argument) => {
+ self.arguments.insert(argument.name.clone(), node.clone());
+ }
+ }
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ match &self.value {
+ ArgumentBuilderType::Argument(argument) => &argument.name,
+ ArgumentBuilderType::Literal(literal) => &literal.value,
+ }
+ }
+
+ pub fn child(&self, name: &str) -> Option<Rc<RefCell<CommandNode<S>>>> {
+ self.children.get(name).cloned()
+ }
+
+ pub fn parse_with_context(
+ &self,
+ reader: &mut StringReader,
+ context_builder: &mut CommandContextBuilder<S>,
+ ) -> Result<(), CommandSyntaxException> {
+ match self.value {
+ ArgumentBuilderType::Argument(ref argument) => {
+ let start = reader.cursor();
+ let result = argument.parse(reader)?;
+ let parsed = ParsedArgument {
+ range: StringRange::between(start, reader.cursor()),
+ result,
+ };
+
+ context_builder.with_argument(&argument.name, parsed.clone());
+ context_builder.with_node(Rc::new(RefCell::new(self.clone())), parsed.range);
+
+ Ok(())
+ }
+ ArgumentBuilderType::Literal(ref literal) => {
+ let start = reader.cursor();
+ let end = self.parse(reader);
+
+ if let Some(end) = end {
+ context_builder.with_node(
+ Rc::new(RefCell::new(self.clone())),
+ StringRange::between(start, end),
+ );
+ return Ok(());
+ }
+
+ Err(BuiltInExceptions::LiteralIncorrect {
+ expected: literal.value.clone(),
+ }
+ .create_with_context(reader))
+ }
+ }
+ }
+
+ fn parse(&self, reader: &mut StringReader) -> Option<usize> {
+ match self.value {
+ ArgumentBuilderType::Argument(_) => {
+ panic!("Can't parse argument.")
+ }
+ ArgumentBuilderType::Literal(ref literal) => {
+ let start = reader.cursor();
+ if reader.can_read_length(literal.value.len()) {
+ let end = start + literal.value.len();
+ if reader
+ .string()
+ .get(start..end)
+ .expect("Couldn't slice reader correctly?")
+ == literal.value
+ {
+ reader.cursor = end;
+ if !reader.can_read() || reader.peek() == ' ' {
+ return Some(end);
+ } else {
+ reader.cursor = start;
+ }
+ }
+ }
+ }
+ }
+ None
+ }
+}
+
+impl<S> Debug for CommandNode<S> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CommandNode")
+ .field("value", &self.value)
+ .field("children", &self.children)
+ .field("command", &self.command.is_some())
+ // .field("requirement", &self.requirement)
+ .field("redirect", &self.redirect)
+ .field("forks", &self.forks)
+ // .field("modifier", &self.modifier)
+ .finish()
+ }
+}
+
+impl<S> Default for CommandNode<S> {
+ fn default() -> Self {
+ Self {
+ value: ArgumentBuilderType::Literal(Literal::default()),
+
+ children: HashMap::new(),
+ literals: HashMap::new(),
+ arguments: HashMap::new(),
+
+ command: None,
+ requirement: Rc::new(|_| true),
+ redirect: None,
+ forks: false,
+ modifier: None,
+ }
+ }
+}
+
+impl<S> Hash for CommandNode<S> {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ // hash the children
+ for (k, v) in &self.children {
+ k.hash(state);
+ v.borrow().hash(state);
+ }
+ // i hope this works because if doesn't then that'll be a problem
+ ptr::hash(&self.command, state);
+ }
+}
+
+impl<S> PartialEq for CommandNode<S> {
+ fn eq(&self, other: &Self) -> bool {
+ if self.children != other.children {
+ return false;
+ }
+ if let Some(selfexecutes) = &self.command {
+ // idk how to do this better since we can't compare `dyn Fn`s
+ if let Some(otherexecutes) = &other.command {
+ #[allow(clippy::vtable_address_comparisons)]
+ if !Rc::ptr_eq(selfexecutes, otherexecutes) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else if other.command.is_some() {
+ return false;
+ }
+ true
+ }
+}
+impl<S> Eq for CommandNode<S> {}