mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
a
This commit is contained in:
parent
cc4fe62fc8
commit
270507736a
20 changed files with 307 additions and 43 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -71,6 +71,7 @@ dependencies = [
|
||||||
name = "azalea-brigadier"
|
name = "azalea-brigadier"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dyn-clonable",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -336,6 +337,33 @@ version = "2.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clonable"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4"
|
||||||
|
dependencies = [
|
||||||
|
"dyn-clonable-impl",
|
||||||
|
"dyn-clone",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clonable-impl"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
|
|
@ -7,3 +7,4 @@ version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "^1.4"
|
lazy_static = "^1.4"
|
||||||
|
dyn-clonable = "^0.9"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
# Azalea Brigadier
|
# Azalea Brigadier
|
||||||
|
|
||||||
A Rustier port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library.
|
A Rustier port of Mojang's [Brigadier](https://github.com/Mojang/brigadier) command parsing and dispatching library.
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,19 @@ use crate::{
|
||||||
string_reader::StringReader,
|
string_reader::StringReader,
|
||||||
suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
|
suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
|
||||||
};
|
};
|
||||||
|
use dyn_clonable::*;
|
||||||
|
|
||||||
pub trait Types {
|
#[clonable]
|
||||||
|
// This should be applied to an Enum
|
||||||
|
pub trait Types: Clone {
|
||||||
fn bool(value: bool) -> Self
|
fn bool(value: bool) -> Self
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
|
/// Get the less specific ArgumentType from this enum
|
||||||
|
fn inner<T>(&self) -> Box<dyn ArgumentType<T>>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -25,12 +33,21 @@ enum BrigadierTypes {
|
||||||
|
|
||||||
Entity(EntityArgumentType)
|
Entity(EntityArgumentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Types for BrigadierTypes {
|
||||||
|
fn inner(&self) -> dyn ArgumentType<dyn Types> {
|
||||||
|
match self {
|
||||||
|
Bool(t) => t,
|
||||||
|
Entity(t) => t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub trait ArgumentType<T>
|
#[clonable]
|
||||||
|
pub trait ArgumentType<T: ?Sized>: Clone
|
||||||
where
|
where
|
||||||
Self: Sized,
|
T: Types,
|
||||||
T: Types + ?Sized,
|
|
||||||
{
|
{
|
||||||
// T parse(StringReader reader) throws CommandSyntaxException;
|
// T parse(StringReader reader) throws CommandSyntaxException;
|
||||||
|
|
||||||
|
@ -42,7 +59,7 @@ where
|
||||||
// return Collections.emptyList();
|
// return Collections.emptyList();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
fn parse(&self, reader: &mut StringReader) -> Result<T, CommandSyntaxException>;
|
fn parse(&self, reader: &mut StringReader) -> Result<Box<T>, CommandSyntaxException>;
|
||||||
|
|
||||||
fn list_suggestions<S>(
|
fn list_suggestions<S>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -50,6 +67,7 @@ where
|
||||||
builder: &mut SuggestionsBuilder,
|
builder: &mut SuggestionsBuilder,
|
||||||
) -> Result<Suggestions, CommandSyntaxException>
|
) -> Result<Suggestions, CommandSyntaxException>
|
||||||
where
|
where
|
||||||
|
Self: Sized,
|
||||||
S: Sized,
|
S: Sized,
|
||||||
T: Sized;
|
T: Sized;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
|
|
||||||
use super::argument_type::{ArgumentType, Types};
|
use super::argument_type::{ArgumentType, Types};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct BoolArgumentType {}
|
pub struct BoolArgumentType {}
|
||||||
|
|
||||||
impl<T> ArgumentType<T> for BoolArgumentType
|
impl<T> ArgumentType<T> for BoolArgumentType
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
arguments::argument_type::{ArgumentType, Types},
|
||||||
command::Command,
|
command::Command,
|
||||||
redirect_modifier::RedirectModifier,
|
redirect_modifier::RedirectModifier,
|
||||||
single_redirect_modifier::SingleRedirectModifier,
|
single_redirect_modifier::SingleRedirectModifier,
|
||||||
|
@ -8,7 +9,7 @@ use crate::{
|
||||||
pub struct BaseArgumentBuilder<'a, S, T>
|
pub struct BaseArgumentBuilder<'a, S, T>
|
||||||
where
|
where
|
||||||
S: Sized,
|
S: Sized,
|
||||||
T: Sized,
|
T: Sized + ArgumentType<dyn Types>,
|
||||||
{
|
{
|
||||||
arguments: RootCommandNode<'a, S, T>,
|
arguments: RootCommandNode<'a, S, T>,
|
||||||
command: Option<&'a dyn Command<S, T>>,
|
command: Option<&'a dyn Command<S, T>>,
|
||||||
|
@ -22,7 +23,10 @@ pub trait ArgumentBuilder<S, T> {
|
||||||
fn build(self) -> dyn CommandNode<S, T>;
|
fn build(self) -> dyn CommandNode<S, T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> BaseArgumentBuilder<'_, S, T> {
|
impl<S, T> BaseArgumentBuilder<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
pub fn then(&mut self, command: dyn CommandNode<S, T>) -> Result<&mut T, String> {
|
pub fn then(&mut self, command: dyn CommandNode<S, T>) -> Result<&mut T, String> {
|
||||||
if self.target.is_some() {
|
if self.target.is_some() {
|
||||||
return Err("Cannot add children to a redirected node".to_string());
|
return Err("Cannot add children to a redirected node".to_string());
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
use crate::tree::literal_command_node::LiteralCommandNode;
|
use crate::{
|
||||||
|
arguments::argument_type::{ArgumentType, Types},
|
||||||
|
tree::literal_command_node::LiteralCommandNode,
|
||||||
|
};
|
||||||
|
|
||||||
use super::argument_builder::BaseArgumentBuilder;
|
use super::argument_builder::BaseArgumentBuilder;
|
||||||
|
|
||||||
pub struct LiteralArgumentBuilder<'a, S, T> {
|
pub struct LiteralArgumentBuilder<'a, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
literal: String,
|
literal: String,
|
||||||
|
|
||||||
pub base: BaseArgumentBuilder<'a, S, T>,
|
pub base: BaseArgumentBuilder<'a, S, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, T> LiteralArgumentBuilder<'a, S, T> {
|
impl<'a, S, T> LiteralArgumentBuilder<'a, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
pub fn new(literal: String) -> Self {
|
pub fn new(literal: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
literal,
|
literal,
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
arguments::argument_type::{ArgumentType, Types},
|
||||||
suggestion::suggestion_provider::SuggestionProvider,
|
suggestion::suggestion_provider::SuggestionProvider,
|
||||||
tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode},
|
tree::{argument_command_node::ArgumentCommandNode, command_node::BaseCommandNode},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::argument_builder::BaseArgumentBuilder;
|
use super::argument_builder::BaseArgumentBuilder;
|
||||||
|
|
||||||
pub struct RequiredArgumentBuilder<'a, S, T> {
|
pub struct RequiredArgumentBuilder<'a, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
// private final String name;
|
// private final String name;
|
||||||
// private final ArgumentType<T> type;
|
// private final ArgumentType<T> type;
|
||||||
// private SuggestionProvider<S> suggestionsProvider = null;
|
// private SuggestionProvider<S> suggestionsProvider = null;
|
||||||
|
@ -16,7 +20,10 @@ pub struct RequiredArgumentBuilder<'a, S, T> {
|
||||||
pub base: BaseArgumentBuilder<'a, S, T>,
|
pub base: BaseArgumentBuilder<'a, S, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, T> RequiredArgumentBuilder<'a, S, T> {
|
impl<'a, S, T> RequiredArgumentBuilder<'a, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
pub fn new(name: String, type_: T) -> Self {
|
pub fn new(name: String, type_: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -2,9 +2,11 @@ use crate::{
|
||||||
context::command_context::CommandContext,
|
context::command_context::CommandContext,
|
||||||
exceptions::command_syntax_exception::CommandSyntaxException,
|
exceptions::command_syntax_exception::CommandSyntaxException,
|
||||||
};
|
};
|
||||||
|
use dyn_clonable::*;
|
||||||
|
|
||||||
pub const SINGLE_SUCCESS: i32 = 1;
|
pub const SINGLE_SUCCESS: i32 = 1;
|
||||||
|
|
||||||
pub trait Command<S, T> {
|
#[clonable]
|
||||||
|
pub trait Command<S, T>: Clone {
|
||||||
fn run(&self, context: &mut CommandContext<S, T>) -> Result<i32, CommandSyntaxException>;
|
fn run(&self, context: &mut CommandContext<S, T>) -> Result<i32, CommandSyntaxException>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
use crate::tree::root_command_node::RootCommandNode;
|
use crate::{
|
||||||
|
arguments::argument_type::{ArgumentType, Types},
|
||||||
|
tree::root_command_node::RootCommandNode,
|
||||||
|
};
|
||||||
|
|
||||||
/// The core command dispatcher, for registering, parsing, and executing commands.
|
/// 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
|
/// The `S` generic is a custom "source" type, such as a user or originator of a command
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
pub struct CommandDispatcher<'a, S, T> {
|
pub struct CommandDispatcher<'a, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
root: RootCommandNode<'a, S, T>,
|
root: RootCommandNode<'a, S, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> CommandDispatcher<'_, S, T> {
|
impl<S, T> CommandDispatcher<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
/// The string required to separate individual arguments in an input string
|
/// The string required to separate individual arguments in an input string
|
||||||
///
|
///
|
||||||
/// See: [`ARGUMENT_SEPARATOR_CHAR`]
|
/// See: [`ARGUMENT_SEPARATOR_CHAR`]
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arguments::argument_type::ArgumentType, command::Command,
|
arguments::argument_type::{ArgumentType, Types},
|
||||||
command_dispatcher::CommandDispatcher, redirect_modifier::RedirectModifier,
|
command::Command,
|
||||||
|
command_dispatcher::CommandDispatcher,
|
||||||
|
redirect_modifier::RedirectModifier,
|
||||||
tree::command_node::CommandNode,
|
tree::command_node::CommandNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,7 +27,10 @@ use super::{
|
||||||
// private boolean forks;
|
// private boolean forks;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CommandContextBuilder<'a, S, T> {
|
pub struct CommandContextBuilder<'a, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
arguments: HashMap<String, ParsedArgument<T>>,
|
arguments: HashMap<String, ParsedArgument<T>>,
|
||||||
root_node: &'a dyn CommandNode<S, T>,
|
root_node: &'a dyn CommandNode<S, T>,
|
||||||
nodes: Vec<ParsedCommandNode<S, T>>,
|
nodes: Vec<ParsedCommandNode<S, T>>,
|
||||||
|
@ -45,7 +50,10 @@ pub struct CommandContextBuilder<'a, S, T> {
|
||||||
// this.range = StringRange.at(start);
|
// this.range = StringRange.at(start);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl<S, T> CommandContextBuilder<'_, S, T> {
|
impl<S, T> CommandContextBuilder<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
dispatcher: CommandDispatcher<S, T>,
|
dispatcher: CommandDispatcher<S, T>,
|
||||||
source: S,
|
source: S,
|
||||||
|
|
|
@ -19,3 +19,12 @@ impl<S, T> ParsedCommandNode<S, T> {
|
||||||
&self.range
|
&self.range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S, T> Clone for ParsedCommandNode<S, T> {
|
||||||
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
Self {
|
||||||
|
node: self.node.clone(),
|
||||||
|
range: self.range.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
use dyn_clonable::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::command_context::CommandContext,
|
context::command_context::CommandContext,
|
||||||
exceptions::command_syntax_exception::CommandSyntaxException,
|
exceptions::command_syntax_exception::CommandSyntaxException,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait RedirectModifier<S, T> {
|
#[clonable]
|
||||||
|
pub trait RedirectModifier<S, T>: Clone {
|
||||||
fn apply(&self, context: CommandContext<S, T>) -> Result<Vec<S>, CommandSyntaxException>;
|
fn apply(&self, context: CommandContext<S, T>) -> Result<Vec<S>, CommandSyntaxException>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
pub struct IntegerSuggestion {}
|
|
@ -1,8 +1,90 @@
|
||||||
use std::cmp;
|
use std::{cmp, collections::HashSet};
|
||||||
|
|
||||||
use crate::{context::string_range::StringRange, message::Message};
|
use crate::{context::string_range::StringRange, message::Message};
|
||||||
|
|
||||||
pub struct Suggestions {}
|
use super::suggestion::Suggestion;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Default)]
|
||||||
|
pub struct Suggestions {
|
||||||
|
range: StringRange,
|
||||||
|
suggestions: Vec<Suggestions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Suggestions {
|
||||||
|
fn range(&self) -> &StringRange {
|
||||||
|
&self.range
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(&self) -> &Vec<Suggestions> {
|
||||||
|
&self.suggestions
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.suggestions.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge(command: &str, input: &Vec<Suggestions>) {
|
||||||
|
if input.is_empty() {
|
||||||
|
return Self::default();
|
||||||
|
} else if input.len() == 1 {
|
||||||
|
return input.iter().next();
|
||||||
|
}
|
||||||
|
let texts = HashSet::new();
|
||||||
|
for suggestions in input {
|
||||||
|
texts.extend(suggestions.list())
|
||||||
|
}
|
||||||
|
Self::new(command, texts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// public static Suggestions create(final String command, final Collection<Suggestion> suggestions) {
|
||||||
|
// if (suggestions.isEmpty()) {
|
||||||
|
// return EMPTY;
|
||||||
|
// }
|
||||||
|
// int start = Integer.MAX_VALUE;
|
||||||
|
// int end = Integer.MIN_VALUE;
|
||||||
|
// for (final Suggestion suggestion : suggestions) {
|
||||||
|
// start = Math.min(suggestion.getRange().getStart(), start);
|
||||||
|
// end = Math.max(suggestion.getRange().getEnd(), end);
|
||||||
|
// }
|
||||||
|
// final StringRange range = new StringRange(start, end);
|
||||||
|
// final Set<Suggestion> texts = new HashSet<>();
|
||||||
|
// for (final Suggestion suggestion : suggestions) {
|
||||||
|
// texts.add(suggestion.expand(command, range));
|
||||||
|
// }
|
||||||
|
// final List<Suggestion> sorted = new ArrayList<>(texts);
|
||||||
|
// sorted.sort((a, b) -> a.compareToIgnoreCase(b));
|
||||||
|
// return new Suggestions(range, sorted);
|
||||||
|
pub fn new(command: String, suggestions: Vec<Suggestion>) -> Self {
|
||||||
|
if suggestions.is_empty() {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
let mut start = usize::MAX;
|
||||||
|
let mut end = usize::MIN;
|
||||||
|
for suggestion in suggestions {
|
||||||
|
let start = cmp::min(suggestion.range().start(), start);
|
||||||
|
let end = cmp::max(suggestion.range().end(), end);
|
||||||
|
}
|
||||||
|
let range = StringRange::new(start, end);
|
||||||
|
let texts = HashSet::new();
|
||||||
|
for suggestion in suggestions {
|
||||||
|
texts.insert(suggestion.expand(command, range));
|
||||||
|
}
|
||||||
|
let sorted = texts.sort_by(|a, b| a.compare_ignore_case(b));
|
||||||
|
Suggestions {
|
||||||
|
range,
|
||||||
|
suggestions: sorted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Suggestions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
range: StringRange::at(0),
|
||||||
|
suggestions: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// mod tests {
|
// mod tests {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::context::string_range::StringRange;
|
use crate::context::string_range::StringRange;
|
||||||
|
|
||||||
use super::{suggestion::Suggestion, suggestions::Suggestions};
|
use super::{
|
||||||
|
integer_suggestion::IntegerSuggestion, suggestion::Suggestion, suggestions::Suggestions,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct SuggestionsBuilder {
|
pub struct SuggestionsBuilder {
|
||||||
input: String,
|
input: String,
|
||||||
|
|
|
@ -21,6 +21,7 @@ use super::command_node::{BaseCommandNode, CommandNode};
|
||||||
const USAGE_ARGUMENT_OPEN: &str = "<";
|
const USAGE_ARGUMENT_OPEN: &str = "<";
|
||||||
const USAGE_ARGUMENT_CLOSE: &str = ">";
|
const USAGE_ARGUMENT_CLOSE: &str = ">";
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ArgumentCommandNode<'a, S, T>
|
pub struct ArgumentCommandNode<'a, S, T>
|
||||||
where
|
where
|
||||||
// each argument command node has its own different type
|
// each argument command node has its own different type
|
||||||
|
@ -34,7 +35,10 @@ where
|
||||||
pub base: BaseCommandNode<'a, S, T>,
|
pub base: BaseCommandNode<'a, S, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> ArgumentCommandNode<'_, S, T> {
|
impl<S, T> ArgumentCommandNode<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
fn get_type(&self) -> &T {
|
fn get_type(&self) -> &T {
|
||||||
&self.type_
|
&self.type_
|
||||||
}
|
}
|
||||||
|
@ -44,7 +48,11 @@ impl<S, T> ArgumentCommandNode<'_, S, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, T> CommandNode<S, T> for ArgumentCommandNode<'a, S, T> {
|
impl<'a, S, T> CommandNode<S, T> for ArgumentCommandNode<'a, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types> + Clone,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
@ -117,7 +125,10 @@ impl<'a, S, T> CommandNode<S, T> for ArgumentCommandNode<'a, S, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ArgumentCommandNode<'_, (), (), ()> {
|
impl<S, T> Display for ArgumentCommandNode<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "<argument {}: {}>", self.name, self.type_)
|
write!(f, "<argument {}: {}>", self.name, self.type_)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arguments::argument_type::ArgumentType,
|
arguments::argument_type::{ArgumentType, Types},
|
||||||
builder::argument_builder::ArgumentBuilder,
|
builder::argument_builder::ArgumentBuilder,
|
||||||
command::Command,
|
command::Command,
|
||||||
context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
|
context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
|
||||||
|
@ -10,10 +9,14 @@ use crate::{
|
||||||
string_reader::StringReader,
|
string_reader::StringReader,
|
||||||
suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
|
suggestion::{suggestions::Suggestions, suggestions_builder::SuggestionsBuilder},
|
||||||
};
|
};
|
||||||
|
use dyn_clonable::*;
|
||||||
|
use std::{collections::HashMap, fmt::Debug};
|
||||||
|
|
||||||
use super::{argument_command_node::ArgumentCommandNode, literal_command_node::LiteralCommandNode};
|
#[derive(Default)]
|
||||||
|
pub struct BaseCommandNode<'a, S, T>
|
||||||
pub struct BaseCommandNode<'a, S, T> {
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
children: HashMap<String, &'a dyn CommandNode<S, T>>,
|
children: HashMap<String, &'a dyn CommandNode<S, T>>,
|
||||||
literals: HashMap<String, LiteralCommandNode<'a, S, T>>,
|
literals: HashMap<String, LiteralCommandNode<'a, S, T>>,
|
||||||
arguments: HashMap<String, ArgumentCommandNode<'a, S, T>>,
|
arguments: HashMap<String, ArgumentCommandNode<'a, S, T>>,
|
||||||
|
@ -24,9 +27,49 @@ pub struct BaseCommandNode<'a, S, T> {
|
||||||
command: Option<&'a dyn Command<S, T>>,
|
command: Option<&'a dyn Command<S, T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> BaseCommandNode<'_, S, T> {}
|
impl<S, T> BaseCommandNode<'_, S, T> where T: ArgumentType<dyn Types> {}
|
||||||
|
|
||||||
pub trait CommandNode<S, T> {
|
impl<S, T> Clone for BaseCommandNode<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
|
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, T> Debug for BaseCommandNode<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("BaseCommandNode")
|
||||||
|
.field("children", &self.children)
|
||||||
|
.field("literals", &self.literals)
|
||||||
|
.field("arguments", &self.arguments)
|
||||||
|
.field("requirement", &self.requirement)
|
||||||
|
.field("redirect", &self.redirect)
|
||||||
|
.field("modifier", &self.modifier)
|
||||||
|
.field("forks", &self.forks)
|
||||||
|
.field("command", &self.command)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[clonable]
|
||||||
|
pub trait CommandNode<S, T>: Clone
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
fn name(&self) -> &str;
|
fn name(&self) -> &str;
|
||||||
fn usage_text(&self) -> &str;
|
fn usage_text(&self) -> &str;
|
||||||
fn parse(
|
fn parse(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
arguments::argument_type::{ArgumentType, Types},
|
||||||
builder::literal_argument_builder::LiteralArgumentBuilder,
|
builder::literal_argument_builder::LiteralArgumentBuilder,
|
||||||
command::Command,
|
command::Command,
|
||||||
context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
|
context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
|
||||||
|
@ -12,15 +13,22 @@ use crate::{
|
||||||
|
|
||||||
use super::command_node::{BaseCommandNode, CommandNode};
|
use super::command_node::{BaseCommandNode, CommandNode};
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LiteralCommandNode<'a, S, T> {
|
pub struct LiteralCommandNode<'a, S, T>
|
||||||
|
where
|
||||||
|
// each argument command node has its own different type
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
literal: String,
|
literal: String,
|
||||||
literal_lowercase: String,
|
literal_lowercase: String,
|
||||||
// Since Rust doesn't have extending, we put the struct this is extending as the "base" field
|
// Since Rust doesn't have extending, we put the struct this is extending as the "base" field
|
||||||
pub base: BaseCommandNode<'a, S, T>,
|
pub base: BaseCommandNode<'a, S, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, T> LiteralCommandNode<'a, S, T> {
|
impl<'a, S, T> LiteralCommandNode<'a, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
pub fn new(literal: String, base: BaseCommandNode<S, T>) -> Self {
|
pub fn new(literal: String, base: BaseCommandNode<S, T>) -> Self {
|
||||||
let literal_lowercase = literal.to_lowercase();
|
let literal_lowercase = literal.to_lowercase();
|
||||||
Self {
|
Self {
|
||||||
|
@ -51,7 +59,11 @@ impl<'a, S, T> LiteralCommandNode<'a, S, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> CommandNode<S, T> for LiteralCommandNode<'_, S, T> {
|
impl<S, T> CommandNode<S, T> for LiteralCommandNode<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types> + Clone,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.literal
|
&self.literal
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
arguments::argument_type::{ArgumentType, Types},
|
||||||
context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
|
context::{command_context::CommandContext, command_context_builder::CommandContextBuilder},
|
||||||
exceptions::{
|
exceptions::{
|
||||||
builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
|
builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
|
||||||
|
@ -11,12 +12,21 @@ use crate::{
|
||||||
|
|
||||||
use super::command_node::{BaseCommandNode, CommandNode};
|
use super::command_node::{BaseCommandNode, CommandNode};
|
||||||
|
|
||||||
pub struct RootCommandNode<'a, S, T> {
|
#[derive(Clone, Default)]
|
||||||
|
pub struct RootCommandNode<'a, S, T>
|
||||||
|
where
|
||||||
|
// each argument command node has its own different type
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
// Since Rust doesn't have extending, we put the struct this is extending as the "base" field
|
// Since Rust doesn't have extending, we put the struct this is extending as the "base" field
|
||||||
pub base: BaseCommandNode<'a, S, T>,
|
pub base: BaseCommandNode<'a, S, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> CommandNode<S, T> for RootCommandNode<'_, S, T> {
|
impl<S, T> CommandNode<S, T> for RootCommandNode<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types> + Clone,
|
||||||
|
S: Clone,
|
||||||
|
{
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
@ -53,7 +63,10 @@ impl<S, T> CommandNode<S, T> for RootCommandNode<'_, S, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> Display for RootCommandNode<'_, S, T> {
|
impl<S, T> Display for RootCommandNode<'_, S, T>
|
||||||
|
where
|
||||||
|
T: ArgumentType<dyn Types>,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "<root>")
|
write!(f, "<root>")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue