mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
b
This commit is contained in:
parent
30a86e1de5
commit
d9e52f8d96
13 changed files with 280 additions and 200 deletions
|
@ -4,40 +4,35 @@ use crate::{
|
|||
redirect_modifier::RedirectModifier,
|
||||
single_redirect_modifier::SingleRedirectModifier,
|
||||
tree::{
|
||||
command_node::{BaseCommandNode, CommandNode},
|
||||
command_node::{BaseCommandNode, CommandNodeTrait},
|
||||
root_command_node::RootCommandNode,
|
||||
},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct BaseArgumentBuilder<'a, S>
|
||||
where
|
||||
S: Sized,
|
||||
{
|
||||
pub struct BaseArgumentBuilder<'a, S> {
|
||||
arguments: RootCommandNode<'a, S>,
|
||||
command: Option<Box<dyn Command<S>>>,
|
||||
requirement: Box<dyn Fn(&S) -> bool>,
|
||||
target: Option<Box<dyn CommandNode<S>>>,
|
||||
target: Option<Box<dyn CommandNodeTrait<S>>>,
|
||||
modifier: Option<Box<dyn RedirectModifier<S>>>,
|
||||
forks: bool,
|
||||
}
|
||||
|
||||
pub trait ArgumentBuilder<S, T>
|
||||
where
|
||||
T: ArgumentBuilder<S, T>,
|
||||
{
|
||||
fn build(self) -> Box<dyn CommandNode<S>>;
|
||||
pub trait ArgumentBuilder<S> {
|
||||
fn build(self) -> Box<dyn CommandNodeTrait<S>>;
|
||||
}
|
||||
|
||||
impl<'a, S> BaseArgumentBuilder<'a, S> {
|
||||
pub fn then(&mut self, argument: Box<dyn CommandNode<S>>) -> Result<&mut Self, String> {
|
||||
pub fn then(&mut self, argument: Box<dyn ArgumentBuilder<S>>) -> Result<&mut Self, String> {
|
||||
if self.target.is_some() {
|
||||
return Err("Cannot add children to a redirected node".to_string());
|
||||
}
|
||||
|
||||
self.arguments.add_child(argument.build());
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn arguments(&self) -> &Vec<&dyn CommandNode<S>> {
|
||||
pub fn arguments(&self) -> &Vec<&dyn CommandNodeTrait<S>> {
|
||||
&self.arguments.get_children()
|
||||
}
|
||||
|
||||
|
@ -59,13 +54,13 @@ impl<'a, S> BaseArgumentBuilder<'a, S> {
|
|||
self.requirement
|
||||
}
|
||||
|
||||
pub fn redirect(&mut self, target: Box<dyn CommandNode<S>>) -> &mut Self {
|
||||
pub fn redirect(&mut self, target: Box<dyn CommandNodeTrait<S>>) -> &mut Self {
|
||||
self.forward(target, None, false)
|
||||
}
|
||||
|
||||
pub fn redirect_modifier(
|
||||
&mut self,
|
||||
target: &dyn CommandNode<S>,
|
||||
target: &dyn CommandNodeTrait<S>,
|
||||
modifier: &dyn SingleRedirectModifier<S>,
|
||||
) -> &mut Self {
|
||||
// forward(target, modifier == null ? null : o -> Collections.singleton(modifier.apply(o)), false);
|
||||
|
@ -74,7 +69,7 @@ impl<'a, S> BaseArgumentBuilder<'a, S> {
|
|||
|
||||
pub fn fork(
|
||||
&mut self,
|
||||
target: &dyn CommandNode<S>,
|
||||
target: &dyn CommandNodeTrait<S>,
|
||||
modifier: &dyn RedirectModifier<S>,
|
||||
) -> &mut Self {
|
||||
self.forward(target, Some(modifier), true)
|
||||
|
@ -82,20 +77,20 @@ impl<'a, S> BaseArgumentBuilder<'a, S> {
|
|||
|
||||
pub fn forward(
|
||||
&mut self,
|
||||
target: Box<dyn CommandNode<S>>,
|
||||
modifier: Option<Box<dyn RedirectModifier<S>>>,
|
||||
target: Option<Box<dyn CommandNodeTrait<S>>>,
|
||||
modifier: Option<&dyn RedirectModifier<S>>,
|
||||
fork: bool,
|
||||
) -> Result<&mut Self, String> {
|
||||
if !self.arguments.get_children().is_empty() {
|
||||
return Err("Cannot forward a node with children".to_string());
|
||||
}
|
||||
self.target = Some(target);
|
||||
self.target = target;
|
||||
self.modifier = modifier;
|
||||
self.forks = fork;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn get_redirect(&self) -> Option<&dyn CommandNode<S>> {
|
||||
pub fn get_redirect(&self) -> Option<&dyn CommandNodeTrait<S>> {
|
||||
self.target.as_ref()
|
||||
}
|
||||
|
||||
|
|
|
@ -4,27 +4,38 @@ use crate::{
|
|||
command::Command,
|
||||
redirect_modifier::RedirectModifier,
|
||||
tree::{
|
||||
command_node::CommandNode, literal_command_node::LiteralCommandNode,
|
||||
command_node::CommandNodeTrait, literal_command_node::LiteralCommandNode,
|
||||
root_command_node::RootCommandNode,
|
||||
},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct LiteralArgumentBuilder<'a, S> {
|
||||
pub struct LiteralArgumentBuilder<'a, S>
|
||||
where
|
||||
,
|
||||
{
|
||||
arguments: RootCommandNode<'a, S>,
|
||||
command: Option<Box<dyn Command<S>>>,
|
||||
requirement: Box<dyn Fn(&S) -> bool>,
|
||||
target: Option<Box<dyn CommandNode<S>>>,
|
||||
target: Option<Box<dyn CommandNodeTrait<S>>>,
|
||||
modifier: Option<Box<dyn RedirectModifier<S>>>,
|
||||
forks: bool,
|
||||
|
||||
literal: String,
|
||||
}
|
||||
|
||||
impl<'a, S> LiteralArgumentBuilder<'a, S> {
|
||||
impl<'a, S> LiteralArgumentBuilder<'a, S>
|
||||
where
|
||||
,
|
||||
{
|
||||
pub fn new(literal: String) -> Self {
|
||||
Self {
|
||||
literal,
|
||||
base: BaseArgumentBuilder::default(),
|
||||
arguments: RootCommandNode::new(),
|
||||
command: None,
|
||||
requirement: Box::new(|_| true),
|
||||
target: None,
|
||||
modifier: None,
|
||||
forks: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,11 +44,11 @@ impl<'a, S> LiteralArgumentBuilder<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, S, T> ArgumentBuilder<S, T> for LiteralArgumentBuilder<'a, S>
|
||||
impl<'a, S> ArgumentBuilder<S> for LiteralArgumentBuilder<'a, S>
|
||||
where
|
||||
T: ArgumentBuilder<S, T>,
|
||||
,
|
||||
{
|
||||
fn build(self) -> Box<dyn CommandNode<S>> {
|
||||
fn build(self) -> Box<dyn CommandNodeTrait<S>> {
|
||||
let result = LiteralCommandNode::new(self.literal, self.base.build());
|
||||
|
||||
for argument in self.base.arguments() {
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
use super::argument_builder::BaseArgumentBuilder;
|
||||
use crate::{
|
||||
arguments::argument_type::ArgumentType,
|
||||
command::Command,
|
||||
redirect_modifier::RedirectModifier,
|
||||
suggestion::suggestion_provider::SuggestionProvider,
|
||||
tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode},
|
||||
tree::{
|
||||
argument_command_node::ArgumentCommandNode,
|
||||
command_node::{BaseCommandNode, CommandNodeTrait},
|
||||
root_command_node::RootCommandNode,
|
||||
},
|
||||
};
|
||||
use std::any::Any;
|
||||
|
||||
use super::argument_builder::BaseArgumentBuilder;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct RequiredArgumentBuilder<'a, S> {
|
||||
arguments: RootCommandNode<'a, S>,
|
||||
command: Option<Box<dyn Command<S>>>,
|
||||
requirement: Box<dyn Fn(&S) -> bool>,
|
||||
target: Option<Box<dyn CommandNode<S>>>,
|
||||
target: Option<Box<dyn CommandNodeTrait<S>>>,
|
||||
modifier: Option<Box<dyn RedirectModifier<S>>>,
|
||||
forks: bool,
|
||||
|
||||
|
@ -26,7 +32,12 @@ impl<'a, S> RequiredArgumentBuilder<'a, S> {
|
|||
name,
|
||||
type_: type_,
|
||||
suggestions_provider: None,
|
||||
base: BaseArgumentBuilder::default(),
|
||||
arguments: RootCommandNode::new(),
|
||||
command: None,
|
||||
requirement: Box::new(|_| true),
|
||||
target: None,
|
||||
modifier: None,
|
||||
forks: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,15 +73,13 @@ impl<'a, S> RequiredArgumentBuilder<'a, S> {
|
|||
let result = ArgumentCommandNode {
|
||||
name: self.name,
|
||||
type_: self.type_,
|
||||
base: BaseCommandNode {
|
||||
command: self.base.command(),
|
||||
requirement: self.base.requirement(),
|
||||
redirect: self.base.get_redirect(),
|
||||
modifier: self.base.get_redirect_modifier(),
|
||||
forks: self.base.forks,
|
||||
..BaseCommandNode::default()
|
||||
},
|
||||
command: self.base.command(),
|
||||
requirement: self.base.requirement(),
|
||||
redirect: self.base.get_redirect(),
|
||||
modifier: self.base.get_redirect_modifier(),
|
||||
forks: self.base.forks,
|
||||
custom_suggestions: self.base.custom_suggestions,
|
||||
..ArgumentCommandNode::default()
|
||||
};
|
||||
|
||||
for argument in self.base.arguments() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{arguments::argument_type::ArgumentType, tree::root_command_node::RootCommandNode};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// The core command dispatcher, for registering, parsing, and executing commands.
|
||||
/// The `S` generic is a custom "source" type, such as a user or originator of a command
|
||||
|
|
|
@ -4,7 +4,7 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
arguments::argument_type::ArgumentType, command::Command, redirect_modifier::RedirectModifier,
|
||||
tree::command_node::CommandNode,
|
||||
tree::command_node::CommandNodeTrait,
|
||||
};
|
||||
use std::{any::Any, collections::HashMap};
|
||||
|
||||
|
@ -13,7 +13,7 @@ pub struct CommandContext<'a, S> {
|
|||
input: String,
|
||||
command: &'a dyn Command<S>,
|
||||
arguments: HashMap<String, ParsedArgument<Box<dyn Any>>>,
|
||||
root_node: &'a dyn CommandNode<S>,
|
||||
root_node: &'a dyn CommandNodeTrait<S>,
|
||||
nodes: Vec<ParsedCommandNode<S>>,
|
||||
range: StringRange,
|
||||
child: Option<&'a CommandContext<'a, S>>,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::{any::Any, collections::HashMap};
|
||||
|
||||
use crate::{
|
||||
arguments::argument_type::ArgumentType, command::Command,
|
||||
command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier,
|
||||
tree::command_node::CommandNode,
|
||||
tree::command_node::CommandNodeTrait,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use std::{any::Any, collections::HashMap};
|
||||
|
||||
use super::{
|
||||
command_context::CommandContext, parsed_argument::ParsedArgument,
|
||||
|
@ -27,12 +27,12 @@ use super::{
|
|||
#[derive(Clone)]
|
||||
pub struct CommandContextBuilder<'a, S> {
|
||||
arguments: HashMap<String, ParsedArgument<Box<dyn Any>>>,
|
||||
root_node: &'a dyn CommandNode<S>,
|
||||
root_node: &'a dyn CommandNodeTrait<S>,
|
||||
nodes: Vec<ParsedCommandNode<S>>,
|
||||
dispatcher: CommandDispatcher<'a, S>,
|
||||
source: S,
|
||||
command: Box<dyn Command<S>>,
|
||||
child: Option<CommandContextBuilder<'a, S>>,
|
||||
child: Box<Option<CommandContextBuilder<'a, S>>>,
|
||||
range: StringRange,
|
||||
modifier: Option<Box<dyn RedirectModifier<S>>>,
|
||||
forks: bool,
|
||||
|
@ -45,11 +45,14 @@ pub struct CommandContextBuilder<'a, S> {
|
|||
// this.range = StringRange.at(start);
|
||||
// }
|
||||
|
||||
impl<S> CommandContextBuilder<'_, S> {
|
||||
impl<S> CommandContextBuilder<'_, S>
|
||||
where
|
||||
,
|
||||
{
|
||||
pub fn new(
|
||||
dispatcher: CommandDispatcher<S>,
|
||||
source: S,
|
||||
root_node: dyn CommandNode<S>,
|
||||
root_node: dyn CommandNodeTrait<S>,
|
||||
start: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -70,7 +73,7 @@ impl<S> CommandContextBuilder<'_, S> {
|
|||
&self.source
|
||||
}
|
||||
|
||||
pub fn root_node(&self) -> &dyn CommandNode<S> {
|
||||
pub fn root_node(&self) -> &dyn CommandNodeTrait<S> {
|
||||
&self.root_node
|
||||
}
|
||||
|
||||
|
@ -88,7 +91,7 @@ impl<S> CommandContextBuilder<'_, S> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_node(mut self, node: dyn CommandNode<S>, range: StringRange) -> Self {
|
||||
pub fn with_node(mut self, node: dyn CommandNodeTrait<S>, range: StringRange) -> Self {
|
||||
self.nodes.push(ParsedCommandNode::new(node, range));
|
||||
self.range = StringRange::encompassing(&self.range, &range);
|
||||
self.modifier = node.redirect_modifier();
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
use super::string_range::StringRange;
|
||||
use crate::tree::command_node::CommandNode;
|
||||
use crate::tree::command_node::CommandNodeTrait;
|
||||
|
||||
pub struct ParsedCommandNode<S> {
|
||||
node: Box<dyn CommandNode<S>>,
|
||||
node: Box<dyn CommandNodeTrait<S>>,
|
||||
range: StringRange,
|
||||
}
|
||||
|
||||
impl<S> ParsedCommandNode<S> {
|
||||
fn new(node: dyn CommandNode<S>, range: StringRange) -> Self {
|
||||
fn new(node: dyn CommandNodeTrait<S>, range: StringRange) -> Self {
|
||||
Self { node, range }
|
||||
}
|
||||
|
||||
fn node(&self) -> &dyn CommandNode<S> {
|
||||
fn node(&self) -> &dyn CommandNodeTrait<S> {
|
||||
&self.node
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::tree::command_node::CommandNode;
|
||||
use crate::tree::command_node::CommandNodeTrait;
|
||||
|
||||
pub struct SuggestionContext<'a, S> {
|
||||
parent: &'a dyn CommandNode<S>,
|
||||
parent: &'a dyn CommandNodeTrait<S>,
|
||||
start_pos: usize,
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::str::FromStr;
|
|||
#[derive(Clone)]
|
||||
pub struct StringReader<'a> {
|
||||
string: &'a str,
|
||||
cursor: usize,
|
||||
pub cursor: usize,
|
||||
}
|
||||
|
||||
const SYNTAX_ESCAPE: char = '\\';
|
||||
|
@ -18,9 +18,15 @@ const SYNTAX_SINGLE_QUOTE: char = '\'';
|
|||
|
||||
// impl<'a> From<&'a str> for &StringReader<'a> {}
|
||||
|
||||
impl StringReader<'_> {
|
||||
fn from(string: &str) -> StringReader {
|
||||
StringReader { string, cursor: 0 }
|
||||
// impl StringReader<'_> {
|
||||
// fn from(string: &str) -> StringReader {
|
||||
// StringReader { string, cursor: 0 }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<'a> From<&'a str> for StringReader<'a> {
|
||||
fn from(string: &'a str) -> Self {
|
||||
Self { string, cursor: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
use std::{
|
||||
any::Any,
|
||||
fmt::{Display, Formatter},
|
||||
collections::HashMap,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arguments::argument_type::ArgumentType,
|
||||
builder::required_argument_builder::RequiredArgumentBuilder,
|
||||
command::Command,
|
||||
context::{
|
||||
command_context::CommandContext, command_context_builder::CommandContextBuilder,
|
||||
parsed_argument::ParsedArgument,
|
||||
},
|
||||
exceptions::command_syntax_exception::CommandSyntaxException,
|
||||
immutable_string_reader::ImmutableStringReader,
|
||||
redirect_modifier::RedirectModifier,
|
||||
string_reader::StringReader,
|
||||
suggestion::{
|
||||
suggestion_provider::SuggestionProvider, suggestions::Suggestions,
|
||||
|
@ -19,12 +22,16 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
use super::command_node::{BaseCommandNode, CommandNode};
|
||||
use super::{
|
||||
command_node::{BaseCommandNode, CommandNodeTrait},
|
||||
literal_command_node::LiteralCommandNode,
|
||||
root_command_node::RootCommandNode,
|
||||
};
|
||||
|
||||
const USAGE_ARGUMENT_OPEN: &str = "<";
|
||||
const USAGE_ARGUMENT_CLOSE: &str = ">";
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArgumentCommandNode<'a, S> {
|
||||
name: String,
|
||||
type_: Box<dyn ArgumentType<Into = dyn Any>>,
|
||||
|
@ -32,6 +39,15 @@ pub struct ArgumentCommandNode<'a, S> {
|
|||
// custom_suggestions: &'a dyn SuggestionProvider<S>,
|
||||
// Since Rust doesn't have extending, we put the struct this is extending as the "base" field
|
||||
pub base: BaseCommandNode<'a, S>,
|
||||
|
||||
children: HashMap<String, Box<dyn CommandNodeTrait<S>>>,
|
||||
literals: HashMap<String, LiteralCommandNode<'a, S>>,
|
||||
arguments: HashMap<String, ArgumentCommandNode<'a, S>>,
|
||||
pub requirement: Box<dyn Fn(&S) -> bool>,
|
||||
redirect: Option<Box<dyn CommandNodeTrait<S>>>,
|
||||
modifier: Option<Box<dyn RedirectModifier<S>>>,
|
||||
forks: bool,
|
||||
pub command: Option<Box<dyn Command<S>>>,
|
||||
}
|
||||
|
||||
impl<S> ArgumentCommandNode<'_, S> {
|
||||
|
@ -44,10 +60,7 @@ impl<S> ArgumentCommandNode<'_, S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, S> CommandNode<S> for ArgumentCommandNode<'a, S>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
impl<'a, S> CommandNodeTrait<S> for ArgumentCommandNode<'a, S> {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
@ -119,8 +132,44 @@ where
|
|||
self.type_.get_examples()
|
||||
}
|
||||
|
||||
fn base(&self) -> &BaseCommandNode<S> {
|
||||
&self.base
|
||||
fn redirect_modifier(&self) -> Option<&dyn RedirectModifier<S>> {
|
||||
self.modifier.as_ref().map(|modifier| modifier.as_ref())
|
||||
}
|
||||
|
||||
fn can_use(&self, source: S) -> bool {
|
||||
(self.requirement)(&source)
|
||||
}
|
||||
|
||||
fn add_child(&self, node: &Box<dyn CommandNodeTrait<S>>) -> Result<(), String> {
|
||||
let dynamic_node = node as &dyn Any;
|
||||
if dynamic_node.is::<RootCommandNode<S>>() {
|
||||
return Err(String::from(
|
||||
"Cannot add a RootCommandNode as a child to any other CommandNode",
|
||||
));
|
||||
}
|
||||
|
||||
let mut child = self.children.get(node.name());
|
||||
if let Some(child) = child {
|
||||
// We've found something to merge onto
|
||||
if let Some(command) = node.base().command() {
|
||||
child.base_mut().command = Some(*command);
|
||||
}
|
||||
for grandchild in node.base().children().values() {
|
||||
child.base_mut().add_child(&*grandchild)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
self.children.insert(node.name().to_string(), *node);
|
||||
|
||||
if let Some(dynamic_node) = dynamic_node.downcast_ref::<LiteralCommandNode<S>>() {
|
||||
self.literals.insert(node.name().to_string(), *dynamic_node);
|
||||
} else if let Some(dynamic_node) = dynamic_node.downcast_ref::<ArgumentCommandNode<S>>()
|
||||
{
|
||||
self.arguments
|
||||
.insert(node.name().to_string(), *dynamic_node);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,116 +12,111 @@ use crate::{
|
|||
string_reader::StringReader,
|
||||
suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
|
||||
};
|
||||
use dyn_clonable::*;
|
||||
use std::ops::Deref;
|
||||
use std::{any::Any, collections::HashMap, fmt::Debug};
|
||||
|
||||
pub struct BaseCommandNode<'a, S> {
|
||||
children: HashMap<String, Box<dyn CommandNode<S>>>,
|
||||
literals: HashMap<String, LiteralCommandNode<'a, S>>,
|
||||
arguments: HashMap<String, ArgumentCommandNode<'a, S>>,
|
||||
requirement: Box<dyn Fn(&S) -> bool>,
|
||||
redirect: Option<Box<dyn CommandNode<S>>>,
|
||||
modifier: Option<Box<dyn RedirectModifier<S>>>,
|
||||
forks: bool,
|
||||
command: Option<Box<dyn Command<S>>>,
|
||||
enum CommandNodeEnum<'a, S> {
|
||||
Literal(LiteralCommandNode<'a, S>),
|
||||
Argument(ArgumentCommandNode<'a, S>),
|
||||
Root(RootCommandNode<'a, S>),
|
||||
}
|
||||
|
||||
impl<S> BaseCommandNode<'_, S> {
|
||||
pub fn command(&self) -> &Option<Box<dyn Command<S>>> {
|
||||
&self.command
|
||||
impl<S> Deref for CommandNodeEnum<'_, S> {
|
||||
type Target = dyn CommandNodeTrait<S>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
CommandNodeEnum::Literal(node) => node,
|
||||
CommandNodeEnum::Argument(node) => node,
|
||||
CommandNodeEnum::Root(node) => node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<LiteralCommandNode<'_, S>> for CommandNodeEnum<'_, S> {
|
||||
fn from(node: LiteralCommandNode<'_, S>) -> Self {
|
||||
CommandNodeEnum::Literal(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<ArgumentCommandNode<'_, S>> for CommandNodeEnum<'_, S> {
|
||||
fn from(node: ArgumentCommandNode<'_, S>) -> Self {
|
||||
CommandNodeEnum::Argument(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<RootCommandNode<'_, S>> for CommandNodeEnum<'_, S> {
|
||||
fn from(node: RootCommandNode<'_, S>) -> Self {
|
||||
CommandNodeEnum::Root(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> CommandNodeEnum<'_, S> {
|
||||
fn redirect_modifier(&self) -> Option<&dyn RedirectModifier<S>> {
|
||||
(*self).modifier.as_ref().map(|modifier| modifier.as_ref())
|
||||
}
|
||||
|
||||
pub fn children(&self) -> &HashMap<String, Box<dyn CommandNode<S>>> {
|
||||
&self.children
|
||||
}
|
||||
|
||||
pub fn child(&self, name: &str) -> Option<&dyn CommandNode<S>> {
|
||||
self.children.get(name).map(|child| child.as_ref())
|
||||
}
|
||||
|
||||
pub fn redirect(&self) -> Option<&dyn CommandNode<S>> {
|
||||
self.redirect.as_ref().map(|redirect| redirect.as_ref())
|
||||
}
|
||||
|
||||
pub fn redirect_modifier(&self) -> Option<&dyn RedirectModifier<S>> {
|
||||
self.modifier.as_ref().map(|modifier| modifier.as_ref())
|
||||
}
|
||||
|
||||
pub fn can_use(&self, source: S) -> bool {
|
||||
fn can_use(&self, source: S) -> bool {
|
||||
(self.requirement)(&source)
|
||||
}
|
||||
|
||||
// public void addChild(final CommandNode<S> node) {
|
||||
// if (node instanceof RootCommandNode) {
|
||||
// throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode");
|
||||
// }
|
||||
|
||||
// final CommandNode<S> child = children.get(node.getName());
|
||||
// if (child != null) {
|
||||
// // We've found something to merge onto
|
||||
// if (node.getCommand() != null) {
|
||||
// child.command = node.getCommand();
|
||||
// }
|
||||
// for (final CommandNode<S> grandchild : node.getChildren()) {
|
||||
// child.addChild(grandchild);
|
||||
// }
|
||||
// } else {
|
||||
// children.put(node.getName(), node);
|
||||
// if (node instanceof LiteralCommandNode) {
|
||||
// literals.put(node.getName(), (LiteralCommandNode<S>) node);
|
||||
// } else if (node instanceof ArgumentCommandNode) {
|
||||
// arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn add_child(&self, node: &dyn CommandNode<S>) -> Result<(), String> {
|
||||
if (&node as &dyn Any).is::<RootCommandNode<S>>() {
|
||||
fn add_child(&self, node: &Box<dyn CommandNodeTrait<S>>) -> Result<(), String> {
|
||||
let dynamic_node = node as &dyn Any;
|
||||
if dynamic_node.is::<RootCommandNode<S>>() {
|
||||
return Err(String::from(
|
||||
"Cannot add a RootCommandNode as a child to any other CommandNode",
|
||||
));
|
||||
}
|
||||
|
||||
let child = self.children.get(node.name());
|
||||
let mut child = self.children.get(node.name());
|
||||
if let Some(child) = child {
|
||||
// We've found something to merge onto
|
||||
if let Some(command) = node.base.command() {
|
||||
child.command = Some(command);
|
||||
if let Some(command) = node.base().command() {
|
||||
child.base_mut().command = Some(*command);
|
||||
}
|
||||
for grandchild in node.children() {
|
||||
child.add_child(grandchild)?;
|
||||
for grandchild in node.base().children().values() {
|
||||
child.base_mut().add_child(&*grandchild)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
self.children.insert(node.name().to_string(), node);
|
||||
if let Some(literal) =
|
||||
&node.clone_boxed() as &dyn Any as &dyn Any as &LiteralCommandNode<S>
|
||||
{
|
||||
self.literals
|
||||
.insert(node.name().to_string(), literal.clone_boxed());
|
||||
} else if let Some(argument) =
|
||||
&node.clone_boxed() as &dyn Any as &dyn Any as &ArgumentCommandNode<S>
|
||||
self.children.insert(node.name().to_string(), *node);
|
||||
|
||||
if let Some(dynamic_node) = dynamic_node.downcast_ref::<LiteralCommandNode<S>>() {
|
||||
self.literals.insert(node.name().to_string(), *dynamic_node);
|
||||
} else if let Some(dynamic_node) = dynamic_node.downcast_ref::<ArgumentCommandNode<S>>()
|
||||
{
|
||||
self.arguments
|
||||
.insert(node.name().to_string(), argument.clone_boxed());
|
||||
.insert(node.name().to_string(), *dynamic_node);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct BaseCommandNode<'a, S> {
|
||||
children: HashMap<String, Box<dyn CommandNodeTrait<S>>>,
|
||||
literals: HashMap<String, LiteralCommandNode<'a, S>>,
|
||||
arguments: HashMap<String, ArgumentCommandNode<'a, S>>,
|
||||
pub requirement: Box<dyn Fn(&S) -> bool>,
|
||||
redirect: Option<Box<dyn CommandNodeTrait<S>>>,
|
||||
modifier: Option<Box<dyn RedirectModifier<S>>>,
|
||||
forks: bool,
|
||||
pub command: Option<Box<dyn Command<S>>>,
|
||||
}
|
||||
|
||||
impl<S> Clone for BaseCommandNode<'_, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
children: self.children.clone(),
|
||||
literals: self.literals.clone(),
|
||||
arguments: self.arguments.clone(),
|
||||
requirement: self.requirement.clone(),
|
||||
redirect: self.redirect.clone(),
|
||||
modifier: self.modifier.clone(),
|
||||
forks: self.forks.clone(),
|
||||
command: self.command.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl<S> Clone for BaseCommandNode<'_, S> {
|
||||
// fn clone(&self) -> Self {
|
||||
// Self {
|
||||
// children: self.children.clone(),
|
||||
// literals: self.literals.clone(),
|
||||
// arguments: self.arguments.clone(),
|
||||
// requirement: self.requirement.clone(),
|
||||
// redirect: self.redirect.clone(),
|
||||
// modifier: self.modifier.clone(),
|
||||
// forks: self.forks.clone(),
|
||||
// command: self.command.clone(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<S> Debug for BaseCommandNode<'_, S> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
@ -153,7 +148,7 @@ impl<S> Default for BaseCommandNode<'_, S> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait CommandNode<S> {
|
||||
pub trait CommandNodeTrait<S> {
|
||||
fn name(&self) -> &str;
|
||||
fn usage_text(&self) -> &str;
|
||||
fn parse(
|
||||
|
@ -167,7 +162,6 @@ pub trait CommandNode<S> {
|
|||
builder: &SuggestionsBuilder,
|
||||
) -> Result<Suggestions, CommandSyntaxException>;
|
||||
fn is_valid_input(&self, input: &str) -> bool;
|
||||
fn create_builder(&self) -> dyn ArgumentBuilder<S, dyn Any>;
|
||||
fn create_builder(&self) -> Box<dyn ArgumentBuilder<S>>;
|
||||
fn get_examples(&self) -> Vec<String>;
|
||||
fn base(&self) -> &BaseCommandNode<S>;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
use crate::{
|
||||
arguments::argument_type::ArgumentType,
|
||||
builder::literal_argument_builder::LiteralArgumentBuilder,
|
||||
command::Command,
|
||||
builder::{
|
||||
argument_builder::ArgumentBuilder, literal_argument_builder::LiteralArgumentBuilder,
|
||||
},
|
||||
context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
|
||||
exceptions::{
|
||||
builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
|
||||
},
|
||||
redirect_modifier::RedirectModifier,
|
||||
immutable_string_reader::ImmutableStringReader,
|
||||
string_reader::StringReader,
|
||||
suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::command_node::{BaseCommandNode, CommandNode};
|
||||
use super::command_node::{BaseCommandNode, CommandNodeTrait};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LiteralCommandNode<'a, S> {
|
||||
|
@ -22,7 +24,7 @@ pub struct LiteralCommandNode<'a, S> {
|
|||
}
|
||||
|
||||
impl<'a, S> LiteralCommandNode<'a, S> {
|
||||
pub fn new(literal: String, base: BaseCommandNode<S>) -> Self {
|
||||
pub fn new(literal: String, base: BaseCommandNode<'a, S>) -> Self {
|
||||
let literal_lowercase = literal.to_lowercase();
|
||||
Self {
|
||||
literal,
|
||||
|
@ -35,16 +37,16 @@ impl<'a, S> LiteralCommandNode<'a, S> {
|
|||
&self.literal
|
||||
}
|
||||
|
||||
pub fn parse(&self, reader: StringReader) -> i32 {
|
||||
let start = reader.get_cursor();
|
||||
if reader.can_read(self.literal.len()) {
|
||||
pub fn parse(&self, reader: &mut StringReader) -> i32 {
|
||||
let start = reader.cursor();
|
||||
if reader.can_read_length(self.literal.len()) {
|
||||
let end = start + self.literal.len();
|
||||
if reader.get_string()[start..end].eq(&self.literal) {
|
||||
reader.set_cursor(end);
|
||||
if reader.string()[start..end].eq(&self.literal) {
|
||||
reader.cursor = end;
|
||||
if !reader.can_read() || reader.peek() == ' ' {
|
||||
return end as i32;
|
||||
} else {
|
||||
reader.set_cursor(start);
|
||||
reader.cursor = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,27 +54,24 @@ impl<'a, S> LiteralCommandNode<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S> CommandNode<S> for LiteralCommandNode<'_, S>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
impl<S> CommandNodeTrait<S> for LiteralCommandNode<'_, S> {
|
||||
fn name(&self) -> &str {
|
||||
&self.literal
|
||||
}
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
reader: StringReader,
|
||||
reader: &mut StringReader<'_>,
|
||||
context_builder: CommandContextBuilder<S>,
|
||||
) -> Result<(), CommandSyntaxException> {
|
||||
let start = reader.get_cursor();
|
||||
let start = reader.cursor();
|
||||
let end = self.parse(reader);
|
||||
if end > -1 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(BuiltInExceptions::LiteralIncorrect {
|
||||
expected: self.literal(),
|
||||
expected: self.literal().to_string(),
|
||||
}
|
||||
.create_with_context(reader))
|
||||
}
|
||||
|
@ -80,33 +79,41 @@ where
|
|||
fn list_suggestions(
|
||||
&self,
|
||||
context: CommandContext<S>,
|
||||
builder: SuggestionsBuilder,
|
||||
builder: &SuggestionsBuilder,
|
||||
) -> Result<Suggestions, CommandSyntaxException> {
|
||||
if self
|
||||
.literal_lowercase
|
||||
.starts_with(&builder.remaining_lowercase())
|
||||
{
|
||||
builder.suggest(self.literal())
|
||||
Ok(builder.suggest(self.literal()).build())
|
||||
} else {
|
||||
Suggestions::empty()
|
||||
Ok(Suggestions::default())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_input(&self, input: &str) -> bool {
|
||||
self.parse(StringReader::from(input)) > -1
|
||||
self.parse(&mut StringReader::from(input)) > -1
|
||||
}
|
||||
|
||||
fn usage_text(&self) -> &str {
|
||||
self.literal
|
||||
&self.literal
|
||||
}
|
||||
|
||||
fn create_builder(&self) -> LiteralArgumentBuilder<S> {
|
||||
let builder = LiteralArgumentBuilder::literal(self.literal());
|
||||
builder.requires(self.requirement());
|
||||
builder.forward(self.redirect(), self.redirect_modifier(), self.is_fork());
|
||||
fn create_builder(&self) -> Box<dyn ArgumentBuilder<S>> {
|
||||
let mut builder = LiteralArgumentBuilder::literal(self.literal().to_string());
|
||||
builder.base.requires(&self.base().requirement);
|
||||
builder.base.forward(
|
||||
self.base.redirect(),
|
||||
self.base.redirect_modifier(),
|
||||
self.base.is_fork(),
|
||||
);
|
||||
if self.command().is_some() {
|
||||
builder.executes(self.command().unwrap());
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
fn get_examples(&self) -> Vec<String> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,49 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use super::{
|
||||
argument_command_node::ArgumentCommandNode,
|
||||
command_node::{BaseCommandNode, CommandNodeTrait},
|
||||
literal_command_node::LiteralCommandNode,
|
||||
};
|
||||
use crate::{
|
||||
arguments::argument_type::ArgumentType,
|
||||
builder::argument_builder::ArgumentBuilder,
|
||||
context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
|
||||
exceptions::{
|
||||
builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
|
||||
},
|
||||
redirect_modifier::RedirectModifier,
|
||||
string_reader::StringReader,
|
||||
suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
|
||||
};
|
||||
use std::{
|
||||
any::Any,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
};
|
||||
|
||||
use super::command_node::{BaseCommandNode, CommandNode};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct RootCommandNode<'a, S> {
|
||||
// Since Rust doesn't have extending, we put the struct this is extending as the "base" field
|
||||
pub base: BaseCommandNode<'a, S>,
|
||||
}
|
||||
|
||||
impl<S> CommandNode<S> for RootCommandNode<'_, S>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
impl<S> CommandNodeTrait<S> for RootCommandNode<'_, S> {
|
||||
fn name(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
fn parse(
|
||||
&self,
|
||||
reader: StringReader,
|
||||
reader: &mut StringReader<'_>,
|
||||
context_builder: CommandContextBuilder<S>,
|
||||
) -> Result<(), CommandSyntaxException> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn list_suggestions(
|
||||
&self,
|
||||
context: CommandContext<S>,
|
||||
builder: SuggestionsBuilder,
|
||||
builder: &SuggestionsBuilder,
|
||||
) -> Result<Suggestions, CommandSyntaxException> {
|
||||
Suggestions::empty()
|
||||
Ok(Suggestions::default())
|
||||
}
|
||||
|
||||
fn is_valid_input(&self, input: &str) -> bool {
|
||||
|
@ -49,7 +54,7 @@ where
|
|||
""
|
||||
}
|
||||
|
||||
fn create_builder(&self) -> () {
|
||||
fn create_builder(&self) -> Box<dyn ArgumentBuilder<S>> {
|
||||
panic!("Cannot convert root into a builder");
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue