use std::{ collections::{BTreeMap, HashMap}, fmt::{self, Debug}, hash::{Hash, Hasher}, ptr, sync::Arc, }; use parking_lot::RwLock; use crate::{ builder::{ argument_builder::ArgumentBuilderType, literal_argument_builder::Literal, required_argument_builder::Argument, }, context::{CommandContext, CommandContextBuilder, ParsedArgument, StringRange}, errors::{BuiltInError, CommandSyntaxError}, modifier::RedirectModifier, string_reader::StringReader, suggestion::{Suggestions, SuggestionsBuilder}, }; pub type Command = Option) -> Result + Send + Sync>>; /// An ArgumentBuilder that has been built. #[non_exhaustive] pub struct CommandNode { pub value: ArgumentBuilderType, // this is a BTreeMap because children need to be ordered when getting command suggestions pub children: BTreeMap>>>, pub literals: HashMap>>>, pub arguments: HashMap>>>, pub command: Command, pub requirement: Arc bool + Send + Sync>, pub redirect: Option>>>, pub forks: bool, pub modifier: Option>>, } impl Clone for CommandNode { 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 CommandNode { /// Returns the value as a literal from this command node, assuming it's /// already been checked. /// /// # Panics /// /// Will panic if this node is not a literal. Consider using a match /// statement instead. pub fn literal(&self) -> &Literal { match self.value { ArgumentBuilderType::Literal(ref literal) => literal, _ => panic!("CommandNode::literal() called on non-literal node"), } } /// Returns the value as an argument from this command node, assuming it's /// already been checked. /// /// # Panics /// /// Will panic if this node is not an argument. Consider using a match /// statement instead. 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>>> { 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: &S) -> bool { (self.requirement)(source) } 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.read().command { child.write().command = Some(command.clone()); } for grandchild in node.read().children.values() { child.write().add_child(grandchild); } } else { self.children .insert(node.read().name().to_owned(), node.clone()); match &node.read().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 usage_text(&self) -> String { match &self.value { ArgumentBuilderType::Argument(argument) => format!("<{}>", argument.name), ArgumentBuilderType::Literal(literal) => literal.value.to_owned(), } } pub fn child(&self, name: &str) -> Option>>> { self.children.get(name).cloned() } pub fn parse_with_context( &self, reader: &mut StringReader, context_builder: &mut CommandContextBuilder, ) -> Result<(), CommandSyntaxError> { 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(Arc::new(RwLock::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( Arc::new(RwLock::new(self.clone())), StringRange::between(start, end), ); return Ok(()); } Err(BuiltInError::LiteralIncorrect { expected: literal.value.clone(), } .create_with_context(reader)) } } } fn parse(&self, reader: &mut StringReader) -> Option { 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 } pub fn list_suggestions( &self, context: CommandContext, builder: SuggestionsBuilder, ) -> Suggestions { match &self.value { ArgumentBuilderType::Literal(literal) => { if literal .value .to_lowercase() .starts_with(builder.remaining_lowercase()) { builder.suggest(&literal.value).build() } else { Suggestions::default() } } ArgumentBuilderType::Argument(argument) => argument.list_suggestions(context, builder), } } } impl Debug for CommandNode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> 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 Default for CommandNode { fn default() -> Self { Self { value: ArgumentBuilderType::Literal(Literal::default()), children: BTreeMap::new(), literals: HashMap::new(), arguments: HashMap::new(), command: None, requirement: Arc::new(|_| true), redirect: None, forks: false, modifier: None, } } } impl Hash for CommandNode { fn hash(&self, state: &mut H) { // hash the children for (k, v) in &self.children { k.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); } } impl PartialEq for CommandNode { fn eq(&self, other: &Self) -> bool { 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; } } match &self.command { Some(selfexecutes) => { // idk how to do this better since we can't compare `dyn Fn`s match &other.command { Some(otherexecutes) => { if !Arc::ptr_eq(selfexecutes, otherexecutes) { return false; } } _ => { return false; } } } _ => { if other.command.is_some() { return false; } } } true } } impl Eq for CommandNode {}