aboutsummaryrefslogtreecommitdiff
path: root/azalea-brigadier/src
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-brigadier/src')
-rwxr-xr-xazalea-brigadier/src/builder/argument_builder.rs5
-rwxr-xr-xazalea-brigadier/src/command_dispatcher.rs184
-rwxr-xr-xazalea-brigadier/src/context/command_context.rs4
-rwxr-xr-xazalea-brigadier/src/context/command_context_builder.rs6
-rwxr-xr-xazalea-brigadier/src/context/parsed_command_node.rs2
-rwxr-xr-xazalea-brigadier/src/context/string_range.rs2
-rwxr-xr-xazalea-brigadier/src/suggestion/mod.rs102
-rwxr-xr-xazalea-brigadier/src/suggestion/suggestions.rs39
-rwxr-xr-xazalea-brigadier/src/suggestion/suggestions_builder.rs50
-rwxr-xr-xazalea-brigadier/src/tree/mod.rs20
10 files changed, 370 insertions, 44 deletions
diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs
index 643a3bd0..10191463 100755
--- a/azalea-brigadier/src/builder/argument_builder.rs
+++ b/azalea-brigadier/src/builder/argument_builder.rs
@@ -16,6 +16,7 @@ pub enum ArgumentBuilderType {
}
/// A node that hasn't yet been built.
+#[derive(Clone)]
pub struct ArgumentBuilder<S> {
arguments: CommandNode<S>,
@@ -134,6 +135,10 @@ impl<S> ArgumentBuilder<S> {
self
}
+ pub fn arguments(&self) -> &CommandNode<S> {
+ &self.arguments
+ }
+
/// Manually build this node into a [`CommandNode`]. You probably don't need
/// to do this yourself.
pub fn build(self) -> CommandNode<S> {
diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs
index 672a250b..42737fba 100755
--- a/azalea-brigadier/src/command_dispatcher.rs
+++ b/azalea-brigadier/src/command_dispatcher.rs
@@ -8,7 +8,13 @@ use crate::{
string_reader::StringReader,
tree::CommandNode,
};
-use std::{cmp::Ordering, collections::HashMap, mem, rc::Rc, sync::Arc};
+use std::{
+ cmp::Ordering,
+ collections::{HashMap, HashSet},
+ mem,
+ rc::Rc,
+ sync::Arc,
+};
/// The root of the command tree. You need to make this to register commands.
///
@@ -297,6 +303,182 @@ impl<S> CommandDispatcher<S> {
})
// Ok(if forked { successful_forks } else { result })
}
+
+ pub fn get_all_usage(
+ &self,
+ node: &CommandNode<S>,
+ source: Arc<S>,
+ restricted: bool,
+ ) -> Vec<String> {
+ let mut result = vec![];
+ self.get_all_usage_recursive(node, source, &mut result, "", restricted);
+ result
+ }
+
+ fn get_all_usage_recursive(
+ &self,
+ node: &CommandNode<S>,
+ source: Arc<S>,
+ result: &mut Vec<String>,
+ prefix: &str,
+ restricted: bool,
+ ) {
+ if restricted && !node.can_use(source.clone()) {
+ return;
+ }
+ if node.command.is_some() {
+ result.push(prefix.to_owned());
+ }
+ if let Some(redirect) = &node.redirect {
+ let redirect = if redirect.data_ptr() == self.root.data_ptr() {
+ "...".to_string()
+ } else {
+ format!("-> {}", redirect.read().usage_text())
+ };
+ if prefix.is_empty() {
+ result.push(format!("{} {redirect}", node.usage_text()));
+ } else {
+ result.push(format!("{prefix} {redirect}"));
+ }
+ } else {
+ for child in node.children.values() {
+ let child = child.read();
+ self.get_all_usage_recursive(
+ &child,
+ Arc::clone(&source),
+ result,
+ if prefix.is_empty() {
+ child.usage_text()
+ } else {
+ format!("{prefix} {}", child.usage_text())
+ }
+ .as_str(),
+ restricted,
+ );
+ }
+ }
+ }
+
+ /// Gets the possible executable commands from a specified node.
+ ///
+ /// You may use [`Self::root`] as a target to get usage data for the entire
+ /// command tree.
+ pub fn get_smart_usage(
+ &self,
+ node: &CommandNode<S>,
+ source: Arc<S>,
+ ) -> Vec<(Arc<RwLock<CommandNode<S>>>, String)> {
+ let mut result = Vec::new();
+
+ let optional = node.command.is_some();
+ for child in node.children.values() {
+ let usage =
+ self.get_smart_usage_recursive(&child.read(), source.clone(), optional, false);
+ if let Some(usage) = usage {
+ result.push((child.clone(), usage));
+ }
+ }
+
+ result
+ }
+
+ fn get_smart_usage_recursive(
+ &self,
+ node: &CommandNode<S>,
+ source: Arc<S>,
+ optional: bool,
+ deep: bool,
+ ) -> Option<String> {
+ if !node.can_use(source.clone()) {
+ return None;
+ }
+
+ let this = if optional {
+ format!("[{}]", node.usage_text())
+ } else {
+ node.usage_text()
+ };
+ let child_optional = node.command.is_some();
+ let open = if child_optional { "[" } else { "(" };
+ let close = if child_optional { "]" } else { ")" };
+
+ if deep {
+ return Some(this);
+ }
+
+ if let Some(redirect) = &node.redirect {
+ let redirect = if redirect.data_ptr() == self.root.data_ptr() {
+ "...".to_string()
+ } else {
+ format!("-> {}", redirect.read().usage_text())
+ };
+ return Some(format!("{this} {redirect}"));
+ }
+
+ let children = node
+ .children
+ .values()
+ .filter(|child| child.read().can_use(source.clone()))
+ .collect::<Vec<_>>();
+ match children.len().cmp(&1) {
+ Ordering::Less => {}
+ Ordering::Equal => {
+ let usage = self.get_smart_usage_recursive(
+ &children[0].read(),
+ source.clone(),
+ child_optional,
+ child_optional,
+ );
+ if let Some(usage) = usage {
+ return Some(format!("{this} {usage}"));
+ }
+ }
+ Ordering::Greater => {
+ let mut child_usage = HashSet::new();
+ for child in &children {
+ let usage = self.get_smart_usage_recursive(
+ &child.read(),
+ source.clone(),
+ child_optional,
+ true,
+ );
+ if let Some(usage) = usage {
+ child_usage.insert(usage);
+ }
+ }
+ match child_usage.len().cmp(&1) {
+ Ordering::Less => {}
+ Ordering::Equal => {
+ let usage = child_usage.into_iter().next().unwrap();
+ let usage = if child_optional {
+ format!("[{}]", usage)
+ } else {
+ usage
+ };
+ return Some(format!("{this} {usage}"));
+ }
+ Ordering::Greater => {
+ let mut builder = String::new();
+ builder.push_str(open);
+ let mut count = 0;
+ for child in children {
+ if count > 0 {
+ builder.push('|');
+ }
+ builder.push_str(&child.read().usage_text());
+ count += 1;
+ }
+ if count > 0 {
+ builder.push_str(close);
+ return Some(format!("{this} {builder}"));
+ }
+ }
+ }
+ }
+ }
+
+ Some(this)
+ }
}
impl<S> Default for CommandDispatcher<S> {
diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs
index f78fe758..4d93006e 100755
--- a/azalea-brigadier/src/context/command_context.rs
+++ b/azalea-brigadier/src/context/command_context.rs
@@ -30,7 +30,7 @@ impl<S> Clone for CommandContext<S> {
command: self.command.clone(),
root_node: self.root_node.clone(),
nodes: self.nodes.clone(),
- range: self.range.clone(),
+ range: self.range,
child: self.child.clone(),
modifier: self.modifier.clone(),
forks: self.forks,
@@ -67,7 +67,7 @@ impl<S> CommandContext<S> {
command: self.command.clone(),
root_node: self.root_node.clone(),
nodes: self.nodes.clone(),
- range: self.range.clone(),
+ range: self.range,
child: self.child.clone(),
modifier: self.modifier.clone(),
forks: self.forks,
diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs
index 2fc8d4ac..99c40dac 100755
--- a/azalea-brigadier/src/context/command_context_builder.rs
+++ b/azalea-brigadier/src/context/command_context_builder.rs
@@ -34,7 +34,7 @@ impl<S> Clone for CommandContextBuilder<'_, S> {
source: self.source.clone(),
command: self.command.clone(),
child: self.child.clone(),
- range: self.range.clone(),
+ range: self.range,
modifier: self.modifier.clone(),
forks: self.forks,
}
@@ -77,7 +77,7 @@ impl<'a, S> CommandContextBuilder<'a, S> {
pub fn with_node(&mut self, node: Arc<RwLock<CommandNode<S>>>, range: StringRange) -> &Self {
self.nodes.push(ParsedCommandNode {
node: node.clone(),
- range: range.clone(),
+ range,
});
self.range = StringRange::encompassing(&self.range, &range);
self.modifier = node.read().modifier.clone();
@@ -93,7 +93,7 @@ impl<'a, S> CommandContextBuilder<'a, S> {
source: self.source.clone(),
command: self.command.clone(),
child: self.child.clone().map(|c| Rc::new(c.build(input))),
- range: self.range.clone(),
+ range: self.range,
forks: self.forks,
modifier: self.modifier.clone(),
input: input.to_string(),
diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs
index bba5d121..2d69c72e 100755
--- a/azalea-brigadier/src/context/parsed_command_node.rs
+++ b/azalea-brigadier/src/context/parsed_command_node.rs
@@ -14,7 +14,7 @@ impl<S> Clone for ParsedCommandNode<S> {
fn clone(&self) -> Self {
Self {
node: self.node.clone(),
- range: self.range.clone(),
+ range: self.range,
}
}
}
diff --git a/azalea-brigadier/src/context/string_range.rs b/azalea-brigadier/src/context/string_range.rs
index 8ca88624..75163405 100755
--- a/azalea-brigadier/src/context/string_range.rs
+++ b/azalea-brigadier/src/context/string_range.rs
@@ -1,6 +1,6 @@
use std::cmp;
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Copy)]
pub struct StringRange {
start: usize,
end: usize,
diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs
index c404adc7..bc0e7608 100755
--- a/azalea-brigadier/src/suggestion/mod.rs
+++ b/azalea-brigadier/src/suggestion/mod.rs
@@ -8,6 +8,10 @@ use azalea_buf::McBufWritable;
use azalea_chat::FormattedText;
#[cfg(feature = "azalea-buf")]
use std::io::Write;
+use std::{
+ fmt::{self, Display},
+ hash::Hash,
+};
pub use suggestions::Suggestions;
pub use suggestions_builder::SuggestionsBuilder;
@@ -16,22 +20,50 @@ pub use suggestions_builder::SuggestionsBuilder;
/// The `M` generic is the type of the tooltip, so for example a `String` or
/// just `()` if you don't care about it.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
-pub struct Suggestion<M = String> {
- pub text: String,
+pub struct Suggestion<M = ()>
+where
+ M: Clone,
+{
pub range: StringRange,
+ value: SuggestionValue,
pub tooltip: Option<M>,
}
+#[derive(Debug, Clone, Hash, Eq, PartialEq)]
+pub enum SuggestionValue {
+ Integer(i32),
+ Text(String),
+}
+
+impl Suggestion<()> {
+ pub fn new(range: StringRange, text: &str) -> Suggestion<()> {
+ Suggestion {
+ range,
+ value: SuggestionValue::Text(text.to_string()),
+ tooltip: None,
+ }
+ }
+}
+
impl<M: Clone> Suggestion<M> {
+ pub fn new_with_tooltip(range: StringRange, text: &str, tooltip: M) -> Self {
+ Self {
+ range,
+ value: SuggestionValue::Text(text.to_string()),
+ tooltip: Some(tooltip),
+ }
+ }
+
pub fn apply(&self, input: &str) -> String {
+ let text = self.value.to_string();
if self.range.start() == 0 && self.range.end() == input.len() {
- return input.to_string();
+ return text;
}
- let mut result = String::with_capacity(self.text.len());
+ let mut result = String::with_capacity(text.len());
if self.range.start() > 0 {
result.push_str(&input[0..self.range.start()]);
}
- result.push_str(&self.text);
+ result.push_str(&text);
if self.range.end() < input.len() {
result.push_str(&input[self.range.end()..]);
}
@@ -39,30 +71,78 @@ impl<M: Clone> Suggestion<M> {
result
}
- pub fn expand(&self, command: &str, range: &StringRange) -> Suggestion<M> {
- if range == &self.range {
+ pub fn expand(&self, command: &str, range: StringRange) -> Suggestion<M> {
+ if range == self.range {
return self.clone();
}
let mut result = String::new();
if range.start() < self.range.start() {
result.push_str(&command[range.start()..self.range.start()]);
}
- result.push_str(&self.text);
+ result.push_str(&self.value.to_string());
if range.end() > self.range.end() {
result.push_str(&command[self.range.end()..range.end()]);
}
Suggestion {
- range: range.clone(),
- text: result,
+ range,
+ value: SuggestionValue::Text(result),
tooltip: self.tooltip.clone(),
}
}
+
+ pub fn text(&self) -> String {
+ self.value.to_string()
+ }
+}
+
+impl SuggestionValue {
+ pub fn cmp_ignore_case(&self, other: &Self) -> std::cmp::Ordering {
+ match (self, other) {
+ (SuggestionValue::Text(a), SuggestionValue::Text(b)) => {
+ a.to_lowercase().cmp(&b.to_lowercase())
+ }
+ (SuggestionValue::Integer(a), SuggestionValue::Integer(b)) => a.cmp(b),
+ _ => {
+ let a = self.to_string();
+ let b = other.to_string();
+ a.cmp(&b)
+ }
+ }
+ }
+}
+
+impl Display for SuggestionValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ SuggestionValue::Text(text) => write!(f, "{text}"),
+ SuggestionValue::Integer(value) => write!(f, "{value}"),
+ }
+ }
+}
+
+impl Ord for SuggestionValue {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ match (self, other) {
+ (SuggestionValue::Text(a), SuggestionValue::Text(b)) => a.cmp(b),
+ (SuggestionValue::Integer(a), SuggestionValue::Integer(b)) => a.cmp(b),
+ _ => {
+ let a = self.to_string();
+ let b = other.to_string();
+ a.cmp(&b)
+ }
+ }
+ }
+}
+impl PartialOrd for SuggestionValue {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
}
#[cfg(feature = "azalea-buf")]
impl McBufWritable for Suggestion<FormattedText> {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
- self.text.write_into(buf)?;
+ self.value.to_string().write_into(buf)?;
self.tooltip.write_into(buf)?;
Ok(())
}
diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs
index 2a8b5e9e..69877786 100755
--- a/azalea-brigadier/src/suggestion/suggestions.rs
+++ b/azalea-brigadier/src/suggestion/suggestions.rs
@@ -1,6 +1,8 @@
use super::Suggestion;
use crate::context::StringRange;
#[cfg(feature = "azalea-buf")]
+use crate::suggestion::SuggestionValue;
+#[cfg(feature = "azalea-buf")]
use azalea_buf::{
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
};
@@ -11,12 +13,19 @@ use std::io::{Cursor, Write};
use std::{collections::HashSet, hash::Hash};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Suggestions<M = String> {
- pub range: StringRange,
- pub suggestions: Vec<Suggestion<M>>,
+pub struct Suggestions<M>
+where
+ M: Clone + PartialEq + Hash,
+{
+ range: StringRange,
+ suggestions: Vec<Suggestion<M>>,
}
impl<M: Clone + Eq + Hash> Suggestions<M> {
+ pub fn new(range: StringRange, suggestions: Vec<Suggestion<M>>) -> Self {
+ Self { range, suggestions }
+ }
+
pub fn merge(command: &str, input: &[Suggestions<M>]) -> Self {
if input.is_empty() {
return Suggestions::default();
@@ -45,20 +54,34 @@ impl<M: Clone + Eq + Hash> Suggestions<M> {
let range = StringRange::new(start, end);
let mut texts = HashSet::new();
for suggestion in suggestions {
- texts.insert(suggestion.expand(command, &range));
+ texts.insert(suggestion.expand(command, range));
}
let mut sorted = texts.into_iter().collect::<Vec<_>>();
- sorted.sort_by(|a, b| a.text.cmp(&b.text));
+
+ sorted.sort_by(|a, b| a.value.cmp_ignore_case(&b.value));
+
Suggestions {
range,
suggestions: sorted,
}
}
+
+ pub fn is_empty(&self) -> bool {
+ self.suggestions.is_empty()
+ }
+
+ pub fn list(&self) -> &[Suggestion<M>] {
+ &self.suggestions
+ }
+
+ pub fn range(&self) -> StringRange {
+ self.range
+ }
}
// this can't be derived because that'd require the generic to have `Default`
// too even if it's not actually necessary
-impl<M> Default for Suggestions<M> {
+impl<M: Clone + Hash + Eq> Default for Suggestions<M> {
fn default() -> Self {
Self {
range: StringRange::default(),
@@ -85,12 +108,12 @@ impl McBufReadable for Suggestions<FormattedText> {
let mut suggestions = Vec::<StandaloneSuggestion>::read_from(buf)?
.into_iter()
.map(|s| Suggestion {
- text: s.text,
+ value: SuggestionValue::Text(s.text),
tooltip: s.tooltip,
range: range.clone(),
})
.collect::<Vec<_>>();
- suggestions.sort_by(|a, b| a.text.cmp(&b.text));
+ suggestions.sort_by(|a, b| a.value.cmp(&b.value));
Ok(Suggestions { range, suggestions })
}
diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs
index 66f17fb1..469a7b98 100755
--- a/azalea-brigadier/src/suggestion/suggestions_builder.rs
+++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs
@@ -1,19 +1,24 @@
use std::collections::HashSet;
+use std::hash::Hash;
use crate::context::StringRange;
-use super::{Suggestion, Suggestions};
+use super::{Suggestion, SuggestionValue, Suggestions};
-pub struct SuggestionsBuilder {
+#[derive(PartialEq, Debug)]
+pub struct SuggestionsBuilder<M = ()>
+where
+ M: Clone + Eq + Hash,
+{
input: String,
input_lowercase: String,
start: usize,
remaining: String,
remaining_lowercase: String,
- result: HashSet<Suggestion>,
+ result: HashSet<Suggestion<M>>,
}
-impl SuggestionsBuilder {
+impl SuggestionsBuilder<()> {
pub fn new(input: &str, start: usize) -> Self {
Self::new_with_lowercase(input, input.to_lowercase().as_str(), start)
}
@@ -28,7 +33,9 @@ impl SuggestionsBuilder {
result: HashSet::new(),
}
}
+}
+impl<M: Clone + Eq + Hash> SuggestionsBuilder<M> {
pub fn input(&self) -> &str {
&self.input
}
@@ -37,7 +44,7 @@ impl SuggestionsBuilder {
self.start
}
- pub fn remianing(&self) -> &str {
+ pub fn remaining(&self) -> &str {
&self.remaining
}
@@ -45,7 +52,7 @@ impl SuggestionsBuilder {
&self.remaining_lowercase
}
- pub fn build(&self) -> Suggestions {
+ pub fn build(&self) -> Suggestions<M> {
Suggestions::create(&self.input, &self.result)
}
@@ -55,38 +62,53 @@ impl SuggestionsBuilder {
}
self.result.insert(Suggestion {
range: StringRange::between(self.start, self.input.len()),
- text: text.to_string(),
+ value: SuggestionValue::Text(text.to_string()),
tooltip: None,
});
self
}
- pub fn suggest_with_tooltip(mut self, text: &str, tooltip: String) -> Self {
+ pub fn suggest_with_tooltip(mut self, text: &str, tooltip: M) -> Self {
if text == self.remaining {
return self;
}
self.result.insert(Suggestion {
range: StringRange::between(self.start, self.input.len()),
- text: text.to_string(),
+ value: SuggestionValue::Text(text.to_string()),
tooltip: Some(tooltip),
});
self
}
- // TODO: integer suggestions
- // https://github.com/Mojang/brigadier/blob/master/src/main/java/com/mojang/brigadier/suggestion/SuggestionsBuilder.java#L74
+ pub fn suggest_integer(mut self, value: i32) -> Self {
+ self.result.insert(Suggestion {
+ range: StringRange::between(self.start, self.input.len()),
+ value: SuggestionValue::Integer(value),
+ tooltip: None,
+ });
+ self
+ }
+
+ pub fn suggest_integer_with_tooltip(mut self, value: i32, tooltip: M) -> Self {
+ self.result.insert(Suggestion {
+ range: StringRange::between(self.start, self.input.len()),
+ value: SuggestionValue::Integer(value),
+ tooltip: Some(tooltip),
+ });
+ self
+ }
#[allow(clippy::should_implement_trait)]
- pub fn add(mut self, other: SuggestionsBuilder) -> Self {
+ pub fn add(mut self, other: SuggestionsBuilder<M>) -> Self {
self.result.extend(other.result);
self
}
- pub fn create_offset(&self, start: usize) -> Self {
+ pub fn create_offset(&self, start: usize) -> SuggestionsBuilder<()> {
SuggestionsBuilder::new_with_lowercase(&self.input, &self.input_lowercase, start)
}
- pub fn restart(self) -> Self {
+ pub fn restart(&self) -> SuggestionsBuilder<()> {
self.create_offset(self.start)
}
}
diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs
index cec972dc..8ab3526e 100755
--- a/azalea-brigadier/src/tree/mod.rs
+++ b/azalea-brigadier/src/tree/mod.rs
@@ -10,7 +10,13 @@ use crate::{
modifier::RedirectModifier,
string_reader::StringReader,
};
-use std::{collections::HashMap, fmt::Debug, hash::Hash, ptr, sync::Arc};
+use std::{
+ collections::{BTreeMap, HashMap},
+ fmt::Debug,
+ hash::Hash,
+ ptr,
+ sync::Arc,
+};
pub type Command<S> = Option<Arc<dyn Fn(&CommandContext<S>) -> i32 + Send + Sync>>;
@@ -19,7 +25,8 @@ pub type Command<S> = Option<Arc<dyn Fn(&CommandContext<S>) -> i32 + Send + Sync
pub struct CommandNode<S> {
pub value: ArgumentBuilderType,
- pub children: HashMap<String, Arc<RwLock<CommandNode<S>>>>,
+ // this is a BTreeMap because children need to be ordered when getting command suggestions
+ pub children: BTreeMap<String, Arc<RwLock<CommandNode<S>>>>,
pub literals: HashMap<String, Arc<RwLock<CommandNode<S>>>>,
pub arguments: HashMap<String, Arc<RwLock<CommandNode<S>>>>,
@@ -125,6 +132,13 @@ impl<S> CommandNode<S> {
}
}
+ pub fn usage_text(&self) -> String {
+ match &self.value {
+ ArgumentBuilderType::Argument(argument) => format!("<{}>", argument.name),
+ ArgumentBuilderType::Literal(literal) => literal.value.to_owned(),
+ }
+ }
+
pub fn child(&self, name: &str) -> Option<Arc<RwLock<CommandNode<S>>>> {
self.children.get(name).cloned()
}
@@ -216,7 +230,7 @@ impl<S> Default for CommandNode<S> {
Self {
value: ArgumentBuilderType::Literal(Literal::default()),
- children: HashMap::new(),
+ children: BTreeMap::new(),
literals: HashMap::new(),
arguments: HashMap::new(),