azalea_brigadier/suggestion/
mod.rs1mod suggestion_provider;
2mod suggestions;
3mod suggestions_builder;
4
5#[cfg(feature = "azalea-buf")]
6use std::io::{self, Write};
7use std::{
8 cmp::Ordering,
9 fmt::{self, Display},
10 hash::Hash,
11};
12
13#[cfg(feature = "azalea-buf")]
14use azalea_buf::AzaleaWrite;
15#[cfg(feature = "azalea-buf")]
16use azalea_chat::FormattedText;
17pub use suggestion_provider::SuggestionProvider;
18pub use suggestions::Suggestions;
19pub use suggestions_builder::SuggestionsBuilder;
20
21use crate::context::StringRange;
22
23#[derive(Debug, Clone, Hash, Eq, PartialEq)]
28pub struct Suggestion {
29 pub range: StringRange,
30 value: SuggestionValue,
31 pub tooltip: Option<String>,
32}
33
34#[derive(Debug, Clone, Hash, Eq, PartialEq)]
35pub enum SuggestionValue {
36 Integer(i32),
37 Text(String),
38}
39
40impl Suggestion {
41 pub fn new(range: StringRange, text: &str) -> Suggestion {
42 Suggestion {
43 range,
44 value: SuggestionValue::Text(text.to_string()),
45 tooltip: None,
46 }
47 }
48
49 pub fn new_with_tooltip(range: StringRange, text: &str, tooltip: String) -> Self {
50 Self {
51 range,
52 value: SuggestionValue::Text(text.to_string()),
53 tooltip: Some(tooltip),
54 }
55 }
56
57 pub fn apply(&self, input: &str) -> String {
58 let text = self.value.to_string();
59 if self.range.start() == 0 && self.range.end() == input.len() {
60 return text;
61 }
62 let mut result = String::with_capacity(text.len());
63 if self.range.start() > 0 {
64 result.push_str(&input[0..self.range.start()]);
65 }
66 result.push_str(&text);
67 if self.range.end() < input.len() {
68 result.push_str(&input[self.range.end()..]);
69 }
70
71 result
72 }
73
74 pub fn expand(&self, command: &str, range: StringRange) -> Suggestion {
75 if range == self.range {
76 return self.clone();
77 }
78 let mut result = String::new();
79 if range.start() < self.range.start() {
80 result.push_str(&command[range.start()..self.range.start()]);
81 }
82 result.push_str(&self.value.to_string());
83 if range.end() > self.range.end() {
84 result.push_str(&command[self.range.end()..range.end()]);
85 }
86 Suggestion {
87 range,
88 value: SuggestionValue::Text(result),
89 tooltip: self.tooltip.clone(),
90 }
91 }
92
93 pub fn text(&self) -> String {
94 self.value.to_string()
95 }
96}
97
98impl SuggestionValue {
99 pub fn cmp_ignore_case(&self, other: &Self) -> Ordering {
100 match (self, other) {
101 (SuggestionValue::Text(a), SuggestionValue::Text(b)) => {
102 a.to_lowercase().cmp(&b.to_lowercase())
103 }
104 (SuggestionValue::Integer(a), SuggestionValue::Integer(b)) => a.cmp(b),
105 _ => {
106 let a = self.to_string();
107 let b = other.to_string();
108 a.cmp(&b)
109 }
110 }
111 }
112}
113
114impl Display for SuggestionValue {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 match self {
117 SuggestionValue::Text(text) => write!(f, "{text}"),
118 SuggestionValue::Integer(value) => write!(f, "{value}"),
119 }
120 }
121}
122
123impl Ord for SuggestionValue {
124 fn cmp(&self, other: &Self) -> Ordering {
125 match (self, other) {
126 (SuggestionValue::Text(a), SuggestionValue::Text(b)) => a.cmp(b),
127 (SuggestionValue::Integer(a), SuggestionValue::Integer(b)) => a.cmp(b),
128 _ => {
129 let a = self.to_string();
130 let b = other.to_string();
131 a.cmp(&b)
132 }
133 }
134 }
135}
136impl PartialOrd for SuggestionValue {
137 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
138 Some(self.cmp(other))
139 }
140}
141
142#[cfg(feature = "azalea-buf")]
143impl AzaleaWrite for Suggestion {
144 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
145 self.value.to_string().azalea_write(buf)?;
146 self.tooltip
147 .clone()
148 .map(FormattedText::from)
149 .azalea_write(buf)?;
150 Ok(())
151 }
152}