add documentation
This commit is contained in:
parent
bfb3840b94
commit
d221f8900f
@ -18,7 +18,10 @@ pub(crate) struct CommandDispatcher<'a> {
|
|||||||
pub(crate) commands: HashMap<&'a str, CommandTree<'a>>
|
pub(crate) commands: HashMap<&'a str, CommandTree<'a>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stores registered [CommandTree]s and dispatches commands to them.
|
||||||
impl <'a> CommandDispatcher<'a> {
|
impl <'a> CommandDispatcher<'a> {
|
||||||
|
|
||||||
|
/// Execute a command using its corresponding [CommandTree].
|
||||||
pub(crate) fn dispatch(&'a self, src: &mut CommandSender, cmd: &str) -> Result<(), String> {
|
pub(crate) fn dispatch(&'a self, src: &mut CommandSender, cmd: &str) -> Result<(), String> {
|
||||||
|
|
||||||
let mut parts = cmd.split_ascii_whitespace();
|
let mut parts = cmd.split_ascii_whitespace();
|
||||||
@ -27,8 +30,9 @@ impl <'a> CommandDispatcher<'a> {
|
|||||||
|
|
||||||
let tree = self.commands.get(key).ok_or("Command not found")?;
|
let tree = self.commands.get(key).ok_or("Command not found")?;
|
||||||
|
|
||||||
|
// try paths until fitting path is found
|
||||||
for path in tree.iter_paths() {
|
for path in tree.iter_paths() {
|
||||||
match Self::try_path(src, path, tree, raw_args.clone()) {
|
match Self::try_is_fitting_path(src, path, tree, raw_args.clone()) {
|
||||||
Err(InvalidConsumptionError(s)) => {
|
Err(InvalidConsumptionError(s)) => {
|
||||||
println!("Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed");
|
println!("Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed");
|
||||||
return Err("Internal Error (See logs for details)".into())
|
return Err("Internal Error (See logs for details)".into())
|
||||||
@ -37,8 +41,8 @@ impl <'a> CommandDispatcher<'a> {
|
|||||||
println!("Error while parsing command \"{cmd}\": a requirement that was expected was not met.");
|
println!("Error while parsing command \"{cmd}\": a requirement that was expected was not met.");
|
||||||
return Err("Internal Error (See logs for details)".into())
|
return Err("Internal Error (See logs for details)".into())
|
||||||
},
|
},
|
||||||
Ok(fitting_path) => {
|
Ok(is_fitting_path) => {
|
||||||
if fitting_path { return Ok(()) }
|
if is_fitting_path { return Ok(()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +50,7 @@ impl <'a> CommandDispatcher<'a> {
|
|||||||
Err(format!("Invalid Syntax. Usage:{}", tree.paths_formatted(key)))
|
Err(format!("Invalid Syntax. Usage:{}", tree.paths_formatted(key)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_path(src: &mut CommandSender, path: Vec<usize>, tree: &CommandTree, mut raw_args: RawArgs) -> Result<bool, InvalidTreeError> {
|
fn try_is_fitting_path(src: &mut CommandSender, path: Vec<usize>, tree: &CommandTree, mut raw_args: RawArgs) -> Result<bool, InvalidTreeError> {
|
||||||
|
|
||||||
let mut parsed_args: ConsumedArgs = HashMap::new();
|
let mut parsed_args: ConsumedArgs = HashMap::new();
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ mod tree;
|
|||||||
mod tree_builder;
|
mod tree_builder;
|
||||||
mod dispatcher;
|
mod dispatcher;
|
||||||
mod arg_player;
|
mod arg_player;
|
||||||
mod cmd_teleport;
|
|
||||||
mod cmd_help;
|
mod cmd_help;
|
||||||
|
|
||||||
pub enum CommandSender<'a> {
|
pub enum CommandSender<'a> {
|
||||||
@ -63,9 +62,13 @@ impl<'a> CommandSender<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: reconsider using constant
|
/// todo: reconsider using constant
|
||||||
|
///
|
||||||
|
/// Central point from which commands are dispatched. Should always be initialized using
|
||||||
|
/// [dispatcher_init].
|
||||||
const DISPATCHER: OnceLock<CommandDispatcher> = OnceLock::new();
|
const DISPATCHER: OnceLock<CommandDispatcher> = OnceLock::new();
|
||||||
|
|
||||||
|
/// create [CommandDispatcher] instance for [DISPATCHER]
|
||||||
fn dispatcher_init<'a>() -> CommandDispatcher<'a> {
|
fn dispatcher_init<'a>() -> CommandDispatcher<'a> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,15 @@ use std::collections::{HashMap, VecDeque};
|
|||||||
|
|
||||||
use crate::commands::CommandSender;
|
use crate::commands::CommandSender;
|
||||||
use crate::commands::dispatcher::InvalidTreeError;
|
use crate::commands::dispatcher::InvalidTreeError;
|
||||||
|
use crate::commands::tree_builder::{argument, NonLeafNodeBuilder};
|
||||||
|
|
||||||
|
/// see [argument]
|
||||||
pub(crate) type RawArgs<'a> = Vec<&'a str>;
|
pub(crate) type RawArgs<'a> = Vec<&'a str>;
|
||||||
|
|
||||||
|
/// see [argument] and [CommandTree::execute]/[NonLeafNodeBuilder::execute]
|
||||||
pub(crate) type ConsumedArgs<'a> = HashMap<&'a str, String>;
|
pub(crate) type ConsumedArgs<'a> = HashMap<&'a str, String>;
|
||||||
|
|
||||||
|
/// see [argument]
|
||||||
pub(crate) type ArgumentConsumer<'a> = fn(&CommandSender, &mut RawArgs) -> Option<String>;
|
pub(crate) type ArgumentConsumer<'a> = fn(&CommandSender, &mut RawArgs) -> Option<String>;
|
||||||
|
|
||||||
pub(crate) struct Node<'a> {
|
pub(crate) struct Node<'a> {
|
||||||
@ -51,6 +55,8 @@ impl <'a> CommandTree<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// format possible paths as [String], using ```name``` as the command name
|
||||||
pub(crate) fn paths_formatted(&'a self, name: &str) -> String {
|
pub(crate) fn paths_formatted(&'a self, name: &str) -> String {
|
||||||
let paths: Vec<Vec<&NodeType>> = self.iter_paths()
|
let paths: Vec<Vec<&NodeType>> = self.iter_paths()
|
||||||
.map(|path| path.iter().map(|&i| &self.nodes[i].node_type).collect())
|
.map(|path| path.iter().map(|&i| &self.nodes[i].node_type).collect())
|
||||||
|
|||||||
@ -3,6 +3,8 @@ use crate::commands::dispatcher::InvalidTreeError;
|
|||||||
use crate::commands::tree::{ArgumentConsumer, ConsumedArgs, Node, NodeType, CommandTree};
|
use crate::commands::tree::{ArgumentConsumer, ConsumedArgs, Node, NodeType, CommandTree};
|
||||||
|
|
||||||
impl <'a> CommandTree<'a> {
|
impl <'a> CommandTree<'a> {
|
||||||
|
|
||||||
|
/// Add a child [Node] to the root of this [CommandTree].
|
||||||
pub fn with_child(mut self, child: impl NodeBuilder<'a>) -> Self {
|
pub fn with_child(mut self, child: impl NodeBuilder<'a>) -> Self {
|
||||||
let node = child.build(&mut self);
|
let node = child.build(&mut self);
|
||||||
self.children.push(self.nodes.len());
|
self.children.push(self.nodes.len());
|
||||||
@ -19,6 +21,12 @@ impl <'a> CommandTree<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Executes if a command terminates at this [Node], i.e. without any arguments.
|
/// Executes if a command terminates at this [Node], i.e. without any arguments.
|
||||||
|
///
|
||||||
|
/// [ConsumedArgs] maps the names of all
|
||||||
|
/// arguments to the result of their consumption, i.e. a string that can be parsed to the
|
||||||
|
/// desired type.
|
||||||
|
///
|
||||||
|
/// Also see [NonLeafNodeBuilder::execute].
|
||||||
pub fn execute(mut self, run: &'a dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError>) -> Self {
|
pub fn execute(mut self, run: &'a dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError>) -> Self {
|
||||||
let node = Node {
|
let node = Node {
|
||||||
node_type: NodeType::ExecuteLeaf {
|
node_type: NodeType::ExecuteLeaf {
|
||||||
@ -81,12 +89,20 @@ impl <'a>NodeBuilder<'a> for NonLeafNodeBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl <'a>NonLeafNodeBuilder<'a> {
|
impl <'a>NonLeafNodeBuilder<'a> {
|
||||||
|
|
||||||
|
/// Add a child [Node] to this one.
|
||||||
pub fn with_child(mut self, child: NonLeafNodeBuilder<'a>) -> Self {
|
pub fn with_child(mut self, child: NonLeafNodeBuilder<'a>) -> Self {
|
||||||
self.child_nodes.push(child);
|
self.child_nodes.push(child);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes if a command terminates at this [Node].
|
/// Executes if a command terminates at this [Node].
|
||||||
|
///
|
||||||
|
/// [ConsumedArgs] maps the names of all
|
||||||
|
/// arguments to the result of their consumption, i.e. a string that can be parsed to the
|
||||||
|
/// desired type.
|
||||||
|
///
|
||||||
|
/// Also see [CommandTree::execute].
|
||||||
pub fn execute(mut self, run: &'a dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError>) -> Self {
|
pub fn execute(mut self, run: &'a dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError>) -> Self {
|
||||||
self.leaf_nodes.push(LeafNodeBuilder {
|
self.leaf_nodes.push(LeafNodeBuilder {
|
||||||
node_type: NodeType::ExecuteLeaf {
|
node_type: NodeType::ExecuteLeaf {
|
||||||
@ -109,8 +125,14 @@ pub fn literal(string: &str) -> NonLeafNodeBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```name``` identifies this argument.
|
/// ```name``` identifies this argument in [ConsumedArgs].
|
||||||
/// ```consumer: [ArgumentConsumer]``` has the purpose of validating arguments. Conversion may start here, as long as the result remains a [String] (e.g. convert offset to absolute position actual coordinates). It must remove consumed arg(s) from [RawArgs] and return them. It must return None if [RawArgs] are invalid. [RawArgs] is reversed, so [Vec::pop] can be used to obtain args in ltr order.
|
///
|
||||||
|
/// ```consumer: ArgumentConsumer``` has the purpose of validating arguments. Conversion may start
|
||||||
|
/// here, as long as the result remains a [String] (e.g. convert offset to absolute position actual
|
||||||
|
/// coordinates), because the result of this function will be passed to following
|
||||||
|
/// [NonLeafNodeBuilder::execute] nodes in a [ConsumedArgs] instance. It must remove consumed arg(s)
|
||||||
|
/// from [RawArgs] and return them. It must return None if [RawArgs] are invalid. [RawArgs] is
|
||||||
|
/// reversed, so [Vec::pop] can be used to obtain args in ltr order.
|
||||||
pub fn argument<'a>(name: &'a str, consumer: ArgumentConsumer) -> NonLeafNodeBuilder<'a> {
|
pub fn argument<'a>(name: &'a str, consumer: ArgumentConsumer) -> NonLeafNodeBuilder<'a> {
|
||||||
NonLeafNodeBuilder {
|
NonLeafNodeBuilder {
|
||||||
node_type: NodeType::Argument {
|
node_type: NodeType::Argument {
|
||||||
@ -122,7 +144,8 @@ pub fn argument<'a>(name: &'a str, consumer: ArgumentConsumer) -> NonLeafNodeBui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```predicate``` should return ```false``` if requirement for reaching following [Node]s is not met.
|
/// ```predicate``` should return ```false``` if requirement for reaching following [Node]s is not
|
||||||
|
/// met.
|
||||||
pub fn require(predicate: &dyn Fn(&CommandSender) -> bool) -> NonLeafNodeBuilder {
|
pub fn require(predicate: &dyn Fn(&CommandSender) -> bool) -> NonLeafNodeBuilder {
|
||||||
NonLeafNodeBuilder {
|
NonLeafNodeBuilder {
|
||||||
node_type: NodeType::Require {
|
node_type: NodeType::Require {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user