steel_core/command/brigadier/
argument.rs1use super::{
4 ArgumentSuggestionContext, CommandSyntaxError, CommandSyntaxErrorKind, StringReader,
5 SuggestionsBuilder,
6};
7
8pub(crate) trait CommandArgumentParser<S>: PartialEq + Send + Sync + 'static {
10 type Value: Clone + Send + Sync + 'static;
12
13 fn parse(
15 &self,
16 reader: &mut StringReader<'_>,
17 source: &S,
18 ) -> Result<Self::Value, CommandSyntaxError>;
19
20 fn list_suggestions(
22 &self,
23 context: &ArgumentSuggestionContext<'_, S, Self::Value>,
24 builder: &mut SuggestionsBuilder<'_>,
25 );
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub(crate) enum StringType {
31 Word,
32 QuotablePhrase,
33 GreedyPhrase,
34}
35
36#[derive(Clone, Debug, PartialEq)]
38pub(crate) enum ArgumentType {
39 Bool,
41 Integer { minimum: i32, maximum: i32 },
43 Long { minimum: i64, maximum: i64 },
45 Float { minimum: f32, maximum: f32 },
47 Double { minimum: f64, maximum: f64 },
49 String(StringType),
51}
52
53impl ArgumentType {
54 pub(crate) const fn bool() -> Self {
56 Self::Bool
57 }
58
59 pub(crate) const fn integer(minimum: i32, maximum: i32) -> Self {
61 Self::Integer { minimum, maximum }
62 }
63
64 pub(crate) const fn long(minimum: i64, maximum: i64) -> Self {
66 Self::Long { minimum, maximum }
67 }
68
69 pub(crate) const fn float(minimum: f32, maximum: f32) -> Self {
71 Self::Float { minimum, maximum }
72 }
73
74 pub(crate) const fn double(minimum: f64, maximum: f64) -> Self {
76 Self::Double { minimum, maximum }
77 }
78
79 pub(crate) const fn word() -> Self {
81 Self::String(StringType::Word)
82 }
83
84 pub(crate) const fn string() -> Self {
86 Self::String(StringType::QuotablePhrase)
87 }
88
89 pub(crate) const fn greedy_string() -> Self {
91 Self::String(StringType::GreedyPhrase)
92 }
93
94 pub(crate) fn parse_value(
95 &self,
96 reader: &mut StringReader<'_>,
97 ) -> Result<PrimitiveArgumentValue, CommandSyntaxError> {
98 match *self {
99 Self::Bool => reader.read_boolean().map(PrimitiveArgumentValue::Bool),
100 Self::Integer { minimum, maximum } => {
101 let start = reader.checkpoint();
102 let value = reader.read_int()?;
103 if value < minimum {
104 reader.restore(start);
105 return Err(reader.error(CommandSyntaxErrorKind::IntegerTooLow {
106 found: value,
107 minimum,
108 }));
109 }
110 if value > maximum {
111 reader.restore(start);
112 return Err(reader.error(CommandSyntaxErrorKind::IntegerTooHigh {
113 found: value,
114 maximum,
115 }));
116 }
117 Ok(PrimitiveArgumentValue::Integer(value))
118 }
119 Self::Long { minimum, maximum } => {
120 let start = reader.checkpoint();
121 let value = reader.read_long()?;
122 if value < minimum {
123 reader.restore(start);
124 return Err(reader.error(CommandSyntaxErrorKind::LongTooLow {
125 found: value,
126 minimum,
127 }));
128 }
129 if value > maximum {
130 reader.restore(start);
131 return Err(reader.error(CommandSyntaxErrorKind::LongTooHigh {
132 found: value,
133 maximum,
134 }));
135 }
136 Ok(PrimitiveArgumentValue::Long(value))
137 }
138 Self::Float { minimum, maximum } => {
139 let start = reader.checkpoint();
140 let value = reader.read_float()?;
141 if value < minimum {
142 reader.restore(start);
143 return Err(reader.error(CommandSyntaxErrorKind::FloatTooLow {
144 found: value,
145 minimum,
146 }));
147 }
148 if value > maximum {
149 reader.restore(start);
150 return Err(reader.error(CommandSyntaxErrorKind::FloatTooHigh {
151 found: value,
152 maximum,
153 }));
154 }
155 Ok(PrimitiveArgumentValue::Float(value))
156 }
157 Self::Double { minimum, maximum } => {
158 let start = reader.checkpoint();
159 let value = reader.read_double()?;
160 if value < minimum {
161 reader.restore(start);
162 return Err(reader.error(CommandSyntaxErrorKind::DoubleTooLow {
163 found: value,
164 minimum,
165 }));
166 }
167 if value > maximum {
168 reader.restore(start);
169 return Err(reader.error(CommandSyntaxErrorKind::DoubleTooHigh {
170 found: value,
171 maximum,
172 }));
173 }
174 Ok(PrimitiveArgumentValue::Double(value))
175 }
176 Self::String(StringType::Word) => Ok(PrimitiveArgumentValue::String(
177 reader.read_unquoted_string().into(),
178 )),
179 Self::String(StringType::QuotablePhrase) => reader
180 .read_string()
181 .map(String::into_boxed_str)
182 .map(PrimitiveArgumentValue::String),
183 Self::String(StringType::GreedyPhrase) => Ok(PrimitiveArgumentValue::String(
184 reader.read_remaining().into(),
185 )),
186 }
187 }
188
189 pub(crate) fn suggest(&self, builder: &mut SuggestionsBuilder<'_>) {
190 if *self != Self::Bool {
191 return;
192 }
193
194 let remaining = builder.remaining_lowercase();
195 let suggest_true = "true".starts_with(remaining);
196 let suggest_false = "false".starts_with(remaining);
197 if suggest_true {
198 builder.suggest("true");
199 }
200 if suggest_false {
201 builder.suggest("false");
202 }
203 }
204}
205
206impl<S> CommandArgumentParser<S> for ArgumentType {
207 type Value = PrimitiveArgumentValue;
208
209 fn parse(
210 &self,
211 reader: &mut StringReader<'_>,
212 _source: &S,
213 ) -> Result<Self::Value, CommandSyntaxError> {
214 self.parse_value(reader)
215 }
216
217 fn list_suggestions(
218 &self,
219 _context: &ArgumentSuggestionContext<'_, S, Self::Value>,
220 builder: &mut SuggestionsBuilder<'_>,
221 ) {
222 self.suggest(builder);
223 }
224}
225
226#[derive(Clone, Debug, PartialEq)]
227pub(crate) enum PrimitiveArgumentValue {
228 Bool(bool),
229 Integer(i32),
230 Long(i64),
231 Float(f32),
232 Double(f64),
233 String(Box<str>),
234}
235
236pub(crate) trait ContainsPrimitiveArgumentValue {
238 fn primitive_value(&self) -> Option<&PrimitiveArgumentValue>;
240}
241
242impl ContainsPrimitiveArgumentValue for PrimitiveArgumentValue {
243 fn primitive_value(&self) -> Option<&PrimitiveArgumentValue> {
244 Some(self)
245 }
246}