split stuff into more modules

This commit is contained in:
mat 2022-04-18 18:13:15 +00:00
parent 17d9f676cc
commit b3864af9c4
10 changed files with 12 additions and 979 deletions

View file

@ -1,8 +1,5 @@
use super::argument_builder::{ArgumentBuilder, ArgumentBuilderType};
use crate::{
exceptions::command_syntax_exception::CommandSyntaxException, parsers::Parser,
string_reader::StringReader,
};
use crate::{exceptions::CommandSyntaxException, parsers::Parser, string_reader::StringReader};
use std::{any::Any, fmt::Debug, rc::Rc};
/// An argument node type. The `T` type parameter is the type of the argument,

View file

@ -1,202 +0,0 @@
use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
use crate::{
dispatcher::CommandDispatcher,
modifier::RedirectModifier,
string_range::StringRange,
tree::{CommandNode, ParsedCommandNode},
};
pub struct CommandContextBuilder<S> {
pub arguments: HashMap<String, ParsedArgument>,
pub root: Rc<RefCell<CommandNode<S>>>,
pub nodes: Vec<ParsedCommandNode<S>>,
pub dispatcher: Rc<CommandDispatcher<S>>,
pub source: Rc<S>,
pub command: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
pub child: Option<Rc<CommandContextBuilder<S>>>,
pub range: StringRange,
pub modifier: Option<Rc<RedirectModifier<S>>>,
pub forks: bool,
}
impl<S> Clone for CommandContextBuilder<S> {
fn clone(&self) -> Self {
Self {
arguments: self.arguments.clone(),
root: self.root.clone(),
nodes: self.nodes.clone(),
dispatcher: self.dispatcher.clone(),
source: self.source.clone(),
command: self.command.clone(),
child: self.child.clone(),
range: self.range.clone(),
modifier: self.modifier.clone(),
forks: self.forks,
}
}
}
impl<S> CommandContextBuilder<S> {
// CommandDispatcher<S> dispatcher, final S source, final CommandNode<S> rootNode, final int start
pub fn new(
dispatcher: Rc<CommandDispatcher<S>>,
source: Rc<S>,
root_node: Rc<RefCell<CommandNode<S>>>,
start: usize,
) -> Self {
Self {
arguments: HashMap::new(),
root: root_node,
source,
range: StringRange::at(start),
command: None,
dispatcher,
nodes: vec![],
child: None,
modifier: None,
forks: false,
}
}
pub fn with_command(
&mut self,
command: &Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
) -> &Self {
self.command = command.clone();
self
}
pub fn with_child(&mut self, child: Rc<CommandContextBuilder<S>>) -> &Self {
self.child = Some(child);
self
}
pub fn with_argument(&mut self, name: &str, argument: ParsedArgument) -> &Self {
self.arguments.insert(name.to_string(), argument);
self
}
pub fn with_node(&mut self, node: Rc<RefCell<CommandNode<S>>>, range: StringRange) -> &Self {
self.nodes.push(ParsedCommandNode {
node: node.clone(),
range: range.clone(),
});
self.range = StringRange::encompassing(&self.range, &range);
self.modifier = node.borrow().modifier.clone();
self.forks = node.borrow().forks;
self
}
pub fn build(&self, input: &str) -> CommandContext<S> {
CommandContext {
arguments: self.arguments.clone(),
root_node: self.root.clone(),
nodes: self.nodes.clone(),
source: self.source.clone(),
command: self.command.clone(),
child: self.child.clone().map(|c| Rc::new(c.build(input))),
range: self.range.clone(),
forks: self.forks,
modifier: self.modifier.clone(),
input: input.to_string(),
}
}
}
impl<S> Debug for CommandContextBuilder<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CommandContextBuilder")
// .field("arguments", &self.arguments)
.field("root", &self.root)
// .field("nodes", &self.nodes)
// .field("dispatcher", &self.dispatcher)
// .field("source", &self.source)
// .field("command", &self.command)
.field("child", &self.child)
.field("range", &self.range)
// .field("modifier", &self.modifier)
.field("forks", &self.forks)
.finish()
}
}
#[derive(Clone)]
pub struct ParsedArgument {
pub range: StringRange,
pub result: Rc<dyn Any>,
}
/// A built `CommandContextBuilder`.
pub struct CommandContext<S> {
pub source: Rc<S>,
pub input: String,
pub arguments: HashMap<String, ParsedArgument>,
pub command: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
pub root_node: Rc<RefCell<CommandNode<S>>>,
pub nodes: Vec<ParsedCommandNode<S>>,
pub range: StringRange,
pub child: Option<Rc<CommandContext<S>>>,
pub modifier: Option<Rc<RedirectModifier<S>>>,
pub forks: bool,
}
impl<S> Clone for CommandContext<S> {
fn clone(&self) -> Self {
Self {
source: self.source.clone(),
input: self.input.clone(),
arguments: self.arguments.clone(),
command: self.command.clone(),
root_node: self.root_node.clone(),
nodes: self.nodes.clone(),
range: self.range.clone(),
child: self.child.clone(),
modifier: self.modifier.clone(),
forks: self.forks,
}
}
}
impl<S> Debug for CommandContext<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CommandContext")
// .field("source", &self.source)
.field("input", &self.input)
// .field("arguments", &self.arguments)
// .field("command", &self.command)
// .field("root_node", &self.root_node)
// .field("nodes", &self.nodes)
.field("range", &self.range)
.field("child", &self.child)
// .field("modifier", &self.modifier)
.field("forks", &self.forks)
.finish()
}
}
impl<S> CommandContext<S> {
pub fn copy_for(&self, source: Rc<S>) -> Self {
if Rc::ptr_eq(&source, &self.source) {
return self.clone();
}
CommandContext {
source,
input: self.input.clone(),
arguments: self.arguments.clone(),
command: self.command.clone(),
root_node: self.root_node.clone(),
nodes: self.nodes.clone(),
range: self.range.clone(),
child: self.child.clone(),
modifier: self.modifier.clone(),
forks: self.forks,
}
}
pub fn has_nodes(&self) -> bool {
!self.nodes.is_empty()
}
pub fn argument(&self, name: &str) -> Option<Rc<dyn Any>> {
let argument = self.arguments.get(name);
argument.map(|a| a.result.clone())
}
}

View file

@ -1,713 +0,0 @@
use crate::{
builder::argument_builder::ArgumentBuilder,
context::{CommandContext, CommandContextBuilder},
exceptions::{
builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
},
parse_results::ParseResults,
string_reader::StringReader,
tree::CommandNode,
};
use std::{cell::RefCell, cmp::Ordering, collections::HashMap, marker::PhantomData, mem, rc::Rc};
#[derive(Default)]
pub struct CommandDispatcher<S> {
root: Rc<RefCell<CommandNode<S>>>,
_marker: PhantomData<S>,
}
impl<S> CommandDispatcher<S> {
pub fn new() -> Self {
Self {
root: Rc::new(RefCell::new(CommandNode::default())),
_marker: PhantomData,
}
}
pub fn register(&mut self, node: ArgumentBuilder<S>) -> Rc<RefCell<CommandNode<S>>> {
let build = Rc::new(RefCell::new(node.build()));
self.root.borrow_mut().add_child(&build);
build
}
pub fn parse(&self, command: StringReader, source: Rc<S>) -> ParseResults<S> {
let context = CommandContextBuilder::new(
Rc::new(self.clone()),
source,
self.root.clone(),
command.cursor(),
);
self.parse_nodes(&self.root, &command, context).unwrap()
}
fn parse_nodes(
&self,
node: &Rc<RefCell<CommandNode<S>>>,
original_reader: &StringReader,
context_so_far: CommandContextBuilder<S>,
) -> Result<ParseResults<S>, CommandSyntaxException> {
let source = context_so_far.source.clone();
let mut errors = HashMap::<Rc<CommandNode<S>>, CommandSyntaxException>::new();
let mut potentials: Vec<ParseResults<S>> = vec![];
let cursor = original_reader.cursor();
for child in node
.borrow()
.get_relevant_nodes(&mut original_reader.clone())
{
if !child.borrow().can_use(source.clone()) {
continue;
}
let mut context = context_so_far.clone();
let mut reader = original_reader.clone();
let parse_with_context_result =
child.borrow().parse_with_context(&mut reader, &mut context);
if let Err(ex) = parse_with_context_result {
errors.insert(
Rc::new((*child.borrow()).clone()),
BuiltInExceptions::DispatcherParseException {
message: ex.message(),
}
.create_with_context(&reader),
);
reader.cursor = cursor;
continue;
}
if reader.can_read() && reader.peek() != ' ' {
errors.insert(
Rc::new((*child.borrow()).clone()),
BuiltInExceptions::DispatcherExpectedArgumentSeparator
.create_with_context(&reader),
);
reader.cursor = cursor;
continue;
}
context.with_command(&child.borrow().command);
if reader.can_read_length(if child.borrow().redirect.is_none() {
2
} else {
1
}) {
reader.skip();
if let Some(redirect) = &child.borrow().redirect {
let child_context = CommandContextBuilder::new(
Rc::new(self.clone()),
source,
redirect.clone(),
reader.cursor,
);
let parse = self
.parse_nodes(redirect, &reader, child_context)
.expect("Parsing nodes failed");
context.with_child(Rc::new(parse.context));
return Ok(ParseResults {
context,
reader: parse.reader,
exceptions: parse.exceptions,
});
} else {
let parse = self
.parse_nodes(&child, &reader, context)
.expect("Parsing nodes failed");
potentials.push(parse);
}
} else {
potentials.push(ParseResults {
context,
reader,
exceptions: HashMap::new(),
});
}
}
if !potentials.is_empty() {
if potentials.len() > 1 {
potentials.sort_by(|a, b| {
if !a.reader.can_read() && b.reader.can_read() {
return Ordering::Less;
};
if a.reader.can_read() && !b.reader.can_read() {
return Ordering::Greater;
};
if a.exceptions.is_empty() && !b.exceptions.is_empty() {
return Ordering::Less;
};
if !a.exceptions.is_empty() && b.exceptions.is_empty() {
return Ordering::Greater;
};
Ordering::Equal
})
}
let best_potential = potentials.into_iter().next().unwrap();
return Ok(best_potential);
}
Ok(ParseResults {
context: context_so_far,
reader: original_reader.clone(),
exceptions: errors,
})
}
pub fn execute(
&self,
input: StringReader,
source: Rc<S>,
) -> Result<i32, CommandSyntaxException> {
let parse = self.parse(input, source);
Self::execute_parsed(parse)
}
pub fn add_paths(
&self,
node: Rc<RefCell<CommandNode<S>>>,
result: &mut Vec<Vec<Rc<RefCell<CommandNode<S>>>>>,
parents: Vec<Rc<RefCell<CommandNode<S>>>>,
) {
let mut current = parents;
current.push(node.clone());
result.push(current.clone());
for child in node.borrow().children.values() {
self.add_paths(child.clone(), result, current.clone());
}
}
pub fn get_path(&self, target: CommandNode<S>) -> Vec<String> {
let rc_target = Rc::new(RefCell::new(target));
let mut nodes: Vec<Vec<Rc<RefCell<CommandNode<S>>>>> = Vec::new();
self.add_paths(self.root.clone(), &mut nodes, vec![]);
for list in nodes {
if *list.last().expect("Nothing in list").borrow() == *rc_target.borrow() {
let mut result: Vec<String> = Vec::with_capacity(list.len());
for node in list {
if node != self.root {
result.push(node.borrow().name().to_string());
}
}
return result;
}
}
vec![]
}
pub fn find_node(&self, path: &[&str]) -> Option<Rc<RefCell<CommandNode<S>>>> {
let mut node = self.root.clone();
for name in path {
if let Some(child) = node.clone().borrow().child(name) {
node = child
} else {
return None;
}
}
Some(node)
}
/// Executes a given pre-parsed command.
pub fn execute_parsed(parse: ParseResults<S>) -> Result<i32, CommandSyntaxException> {
if parse.reader.can_read() {
if parse.exceptions.len() == 1 {
return Err(parse.exceptions.values().next().unwrap().clone());
}
if parse.context.range.is_empty() {
return Err(
BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader)
);
}
return Err(
BuiltInExceptions::DispatcherUnknownArgument.create_with_context(&parse.reader)
);
}
let mut result = 0i32;
let mut successful_forks = 0;
let mut forked = false;
let mut found_command = false;
let command = parse.reader.string();
let original = parse.context.build(command);
let mut contexts = vec![original];
let mut next: Vec<CommandContext<S>> = vec![];
while !contexts.is_empty() {
for context in contexts.iter() {
let child = &context.child;
if let Some(child) = child {
println!("aaaaaaa {:?}", child);
forked |= child.forks;
if child.has_nodes() {
found_command = true;
let modifier = &context.modifier;
if let Some(modifier) = modifier {
let results = modifier(context);
if let Ok(results) = results {
if !results.is_empty() {
next.extend(results.iter().map(|s| child.copy_for(s.clone())));
}
} else {
// TODO
// self.consumer.on_command_complete(context, false, 0);
if !forked {
return Err(results.err().unwrap());
}
}
} else {
next.push(child.copy_for(context.source.clone()));
}
}
} else if let Some(context_command) = &context.command {
found_command = true;
let value = context_command(context);
result += value;
// consumer.on_command_complete(context, true, value);
successful_forks += 1;
// TODO: allow context_command to error and handle those errors
}
}
// move next into contexts and clear next
mem::swap(&mut contexts, &mut next);
next.clear();
}
if !found_command {
// consumer.on_command_complete(original, false, 0);
return Err(
BuiltInExceptions::DispatcherUnknownCommand.create_with_context(&parse.reader)
);
}
// TODO: this is not how vanilla does it but it works
Ok(if successful_forks >= 2 {
successful_forks
} else {
result
})
// Ok(if forked { successful_forks } else { result })
}
}
impl<S> Clone for CommandDispatcher<S> {
fn clone(&self) -> Self {
Self {
root: self.root.clone(),
_marker: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
builder::{literal_argument_builder::literal, required_argument_builder::argument},
parsers::integer,
};
#[derive(Debug, PartialEq)]
struct CommandSource {}
fn input_with_offset(input: &str, offset: usize) -> StringReader {
let mut result: StringReader = input.into();
result.cursor = offset;
result
}
#[test]
fn create_and_execute_command() {
let mut subject = CommandDispatcher::new();
subject.register(literal("foo").executes(|_| 42));
assert_eq!(
subject
.execute("foo".into(), Rc::new(CommandSource {}))
.unwrap(),
42
);
}
#[test]
fn create_and_execute_offset_command() {
let mut subject = CommandDispatcher::new();
subject.register(literal("foo").executes(|_| 42));
assert_eq!(
subject
.execute(input_with_offset("/foo", 1), Rc::new(CommandSource {}))
.unwrap(),
42
);
}
#[test]
fn create_and_merge_commands() {
let mut subject = CommandDispatcher::new();
subject.register(literal("base").then(literal("foo").executes(|_| 42)));
subject.register(literal("base").then(literal("bar").executes(|_| 42)));
assert_eq!(
subject
.execute("base foo".into(), Rc::new(CommandSource {}))
.unwrap(),
42
);
assert_eq!(
subject
.execute("base bar".into(), Rc::new(CommandSource {}))
.unwrap(),
42
);
}
#[test]
fn execute_unknown_command() {
let mut subject = CommandDispatcher::new();
subject.register(literal("bar"));
subject.register(literal("baz"));
let execute_result = subject.execute("foo".into(), Rc::new(CommandSource {}));
let err = execute_result.err().unwrap();
match err.type_ {
BuiltInExceptions::DispatcherUnknownCommand => {}
_ => panic!("Unexpected error"),
}
assert_eq!(err.cursor().unwrap(), 0);
}
#[test]
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 err = execute_result.err().unwrap();
match err.type_ {
BuiltInExceptions::DispatcherUnknownCommand => {}
_ => panic!("Unexpected error"),
}
assert_eq!(err.cursor().unwrap(), 0);
}
#[test]
fn execute_empty_command() {
let mut subject = CommandDispatcher::new();
subject.register(literal(""));
let execute_result = subject.execute("".into(), Rc::new(CommandSource {}));
let err = execute_result.err().unwrap();
match err.type_ {
BuiltInExceptions::DispatcherUnknownCommand => {}
_ => panic!("Unexpected error"),
}
assert_eq!(err.cursor().unwrap(), 0);
}
#[test]
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 err = execute_result.err().unwrap();
match err.type_ {
BuiltInExceptions::DispatcherUnknownArgument => {}
_ => panic!("Unexpected error"),
}
assert_eq!(err.cursor().unwrap(), 4);
}
#[test]
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 err = execute_result.err().unwrap();
match err.type_ {
BuiltInExceptions::DispatcherUnknownArgument => {}
_ => panic!("Unexpected error"),
}
assert_eq!(err.cursor().unwrap(), 4);
}
#[test]
fn execute_ambiguous_incorrect_argument() {
let mut subject = CommandDispatcher::new();
subject.register(
literal("foo")
.executes(|_| 42)
.then(literal("bar"))
.then(literal("baz")),
);
let execute_result = subject.execute("foo unknown".into(), Rc::new(CommandSource {}));
let err = execute_result.err().unwrap();
match err.type_ {
BuiltInExceptions::DispatcherUnknownArgument => {}
_ => panic!("Unexpected error"),
}
assert_eq!(err.cursor().unwrap(), 4);
}
#[test]
fn execute_subcommand() {
let mut subject = CommandDispatcher::new();
subject.register(
literal("foo")
.then(literal("a"))
.then(literal("=").executes(|_| 100))
.then(literal("c"))
.executes(|_| 42),
);
assert_eq!(
subject
.execute("foo =".into(), Rc::new(CommandSource {}))
.unwrap(),
100
);
}
#[test]
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 {}));
assert_eq!(parse.reader.remaining(), " ");
assert_eq!(parse.context.nodes.len(), 1);
}
#[test]
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 {}));
assert_eq!(parse.reader.remaining(), " ");
assert_eq!(parse.context.nodes.len(), 1);
}
#[test]
fn execute_ambiguious_parent_subcommand() {
let mut subject = CommandDispatcher::new();
subject.register(
literal("test")
.then(argument("incorrect", integer()).executes(|_| 42))
.then(
argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)),
),
);
assert_eq!(
subject
.execute("test 1 2".into(), Rc::new(CommandSource {}))
.unwrap(),
100
);
}
#[test]
fn execute_ambiguious_parent_subcommand_via_redirect() {
let mut subject = CommandDispatcher::new();
let real = subject.register(
literal("test")
.then(argument("incorrect", integer()).executes(|_| 42))
.then(
argument("right", integer()).then(argument("sub", integer()).executes(|_| 100)),
),
);
subject.register(literal("redirect").redirect(real));
assert_eq!(
subject
.execute("redirect 1 2".into(), Rc::new(CommandSource {}))
.unwrap(),
100
);
}
#[test]
fn execute_redirected_multiple_times() {
let mut subject = CommandDispatcher::new();
let concrete_node = subject.register(literal("actual").executes(|_| 42));
let root = subject.root.clone();
let redirect_node = subject.register(literal("redirected").redirect(root.clone()));
let input = "redirected redirected actual";
let parse = subject.parse(input.into(), Rc::new(CommandSource {}));
assert_eq!(parse.context.range.get(input), "redirected");
assert_eq!(parse.context.nodes.len(), 1);
assert_eq!(parse.context.root, root);
assert_eq!(parse.context.nodes[0].range, parse.context.range);
assert_eq!(parse.context.nodes[0].node, redirect_node);
let child1 = parse.context.child.clone();
assert!(child1.is_some());
assert_eq!(child1.clone().unwrap().range.get(input), "redirected");
assert_eq!(child1.clone().unwrap().nodes.len(), 1);
assert_eq!(child1.clone().unwrap().root, root);
assert_eq!(
child1.clone().unwrap().nodes[0].range,
child1.clone().unwrap().range
);
assert_eq!(child1.clone().unwrap().nodes[0].node, redirect_node);
let child2 = child1.unwrap().child.clone();
assert!(child2.is_some());
assert_eq!(child2.clone().unwrap().range.get(input), "actual");
assert_eq!(child2.clone().unwrap().nodes.len(), 1);
assert_eq!(child2.clone().unwrap().root, root);
assert_eq!(
child2.clone().unwrap().nodes[0].range,
child2.clone().unwrap().range
);
assert_eq!(child2.clone().unwrap().nodes[0].node, concrete_node);
assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42);
}
#[test]
fn execute_redirected() {
let mut subject = CommandDispatcher::new();
let source1 = Rc::new(CommandSource {});
let source2 = Rc::new(CommandSource {});
let modifier = move |_: &CommandContext<CommandSource>| -> Result<Vec<Rc<CommandSource>>, 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)));
let input = "redirected actual";
let parse = subject.parse(input.into(), Rc::new(CommandSource {}));
assert_eq!(parse.context.range.get(input), "redirected");
assert_eq!(parse.context.nodes.len(), 1);
assert_eq!(parse.context.root, subject.root);
assert_eq!(parse.context.nodes[0].range, parse.context.range);
assert_eq!(parse.context.nodes[0].node, redirect_node);
let parent = parse.context.child.clone();
assert!(parent.is_some());
let parent = parent.unwrap();
assert_eq!(parent.range.get(input), "actual");
assert_eq!(parent.nodes.len(), 1);
assert_eq!(parse.context.root, subject.root);
assert_eq!(parent.nodes[0].range, parent.range);
assert_eq!(parent.nodes[0].node, concrete_node);
assert_eq!(parent.source, Rc::new(CommandSource {}));
assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 2);
}
#[test]
fn execute_orphaned_subcommand() {
let mut subject = CommandDispatcher::new();
subject.register(
literal("foo")
.then(argument("bar", integer()))
.executes(|_| 42),
);
let result = subject.execute("foo 5".into(), Rc::new(CommandSource {}));
assert!(result.is_err());
let result = result.unwrap_err();
assert_eq!(
*result.get_type(),
BuiltInExceptions::DispatcherUnknownCommand
);
assert_eq!(result.cursor(), Some(5));
}
#[test]
fn execute_invalid_other() {
let mut subject = CommandDispatcher::new();
subject.register(literal("w").executes(|_| panic!("This should not run")));
subject.register(literal("world").executes(|_| 42));
assert_eq!(
subject
.execute("world".into(), Rc::new(CommandSource {}))
.unwrap(),
42
);
}
#[test]
fn parse_no_space_separator() {
let mut subject = CommandDispatcher::new();
subject.register(
literal("foo")
.then(argument("bar", integer()))
.executes(|_| 42),
);
let result = subject.execute("foo$".into(), Rc::new(CommandSource {}));
assert!(result.is_err());
let result = result.unwrap_err();
assert_eq!(
*result.get_type(),
BuiltInExceptions::DispatcherUnknownCommand
);
assert_eq!(result.cursor(), Some(0));
}
#[test]
fn execute_invalid_subcommand() {
let mut subject = CommandDispatcher::new();
subject.register(
literal("foo")
.then(argument("bar", integer()))
.executes(|_| 42),
);
let result = subject.execute("foo bar".into(), Rc::new(CommandSource {}));
assert!(result.is_err());
let result = result.unwrap_err();
// this fails for some reason, i blame mojang
// assert_eq!(*result.get_type(), BuiltInExceptions::ReaderExpectedInt);
assert_eq!(result.cursor(), Some(4));
}
#[test]
fn get_path() {
let mut subject = CommandDispatcher::<()>::new();
let bar = literal("bar").build();
subject.register(literal("foo").then_built(bar.clone()));
assert_eq!(
subject.get_path(bar),
vec!["foo".to_string(), "bar".to_string()]
);
}
#[test]
fn find_node_doesnt_exist() {
let subject = CommandDispatcher::<()>::new();
assert_eq!(subject.find_node(&vec!["foo", "bar"]), None)
}
}

View file

@ -1,2 +1,5 @@
pub mod builtin_exceptions;
pub mod command_syntax_exception;
mod builtin_exceptions;
mod command_syntax_exception;
pub use builtin_exceptions::BuiltInExceptions;
pub use command_syntax_exception::CommandSyntaxException;

View file

@ -1,12 +1,11 @@
pub mod builder;
pub mod command_dispatcher;
pub mod context;
pub mod dispatcher;
pub mod exceptions;
pub mod message;
pub mod modifier;
pub mod parse_results;
pub mod parsers;
pub mod string_range;
pub mod string_reader;
pub mod tree;
@ -17,8 +16,8 @@ mod tests {
use crate::{
builder::{literal_argument_builder::literal, required_argument_builder::argument},
command_dispatcher::CommandDispatcher,
context::CommandContext,
dispatcher::CommandDispatcher,
parsers::{get_integer, integer},
};

View file

@ -1,8 +1,6 @@
use std::rc::Rc;
use crate::{
context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException,
};
use crate::{context::CommandContext, exceptions::CommandSyntaxException};
pub type RedirectModifier<S> =
dyn Fn(&CommandContext<S>) -> Result<Vec<Rc<S>>, CommandSyntaxException>;

View file

@ -1,5 +1,5 @@
use crate::{
context::CommandContextBuilder, exceptions::command_syntax_exception::CommandSyntaxException,
context::CommandContextBuilder, exceptions::CommandSyntaxException,
string_reader::StringReader, tree::CommandNode,
};
use std::{collections::HashMap, fmt::Debug, rc::Rc};

View file

@ -2,9 +2,7 @@ use std::{any::Any, rc::Rc};
use crate::{
context::CommandContext,
exceptions::{
builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
},
exceptions::{BuiltInExceptions, CommandSyntaxException},
string_reader::StringReader,
};

View file

@ -1,45 +0,0 @@
use std::cmp;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct StringRange {
start: usize,
end: usize,
}
impl StringRange {
pub fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
pub fn at(pos: usize) -> Self {
Self::new(pos, pos)
}
pub fn between(start: usize, end: usize) -> Self {
Self::new(start, end)
}
pub fn encompassing(a: &Self, b: &Self) -> Self {
Self::new(cmp::min(a.start, b.start), cmp::max(a.end, b.end))
}
pub fn start(&self) -> usize {
self.start
}
pub fn end(&self) -> usize {
self.end
}
pub fn get<'a>(&self, reader: &'a str) -> &'a str {
&reader[self.start..self.end]
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
pub fn length(&self) -> usize {
self.end - self.start
}
}

View file

@ -1,6 +1,4 @@
use crate::exceptions::{
builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
};
use crate::exceptions::{BuiltInExceptions, CommandSyntaxException};
use std::str::FromStr;
#[derive(Clone)]