1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00
azalea/azalea-brigadier/src/tree/mod.rs

263 lines
8.8 KiB
Rust
Executable file

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};
pub type Command<S> = Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>;
/// 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: Command<S>,
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() {
self.arguments.values().cloned().collect()
} else {
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 {
vec![literal.clone()]
} 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> {}