azalea_brigadier/
string_reader.rs1use std::str::FromStr;
2
3use crate::errors::{BuiltInError, CommandSyntaxError};
4
5#[derive(Clone)]
6pub struct StringReader {
7 string: String,
8 pub cursor: usize,
9}
10
11const SYNTAX_ESCAPE: char = '\\';
12const SYNTAX_DOUBLE_QUOTE: char = '"';
13const SYNTAX_SINGLE_QUOTE: char = '\'';
14
15impl From<String> for StringReader {
16 fn from(string: String) -> Self {
17 Self { string, cursor: 0 }
18 }
19}
20impl From<&str> for StringReader {
21 fn from(string: &str) -> Self {
22 Self {
23 string: string.to_string(),
24 cursor: 0,
25 }
26 }
27}
28
29impl StringReader {
30 pub fn string(&self) -> &str {
31 &self.string
32 }
33
34 pub fn remaining_length(&self) -> usize {
35 self.string.len() - self.cursor
36 }
37
38 pub fn total_length(&self) -> usize {
39 self.string.len()
40 }
41
42 pub fn get_read(&self) -> &str {
43 &self.string[..self.cursor]
44 }
45
46 pub fn remaining(&self) -> &str {
47 &self.string[self.cursor..]
48 }
49
50 pub fn can_read_length(&self, length: usize) -> bool {
51 self.cursor + length <= self.string.len()
52 }
53
54 pub fn can_read(&self) -> bool {
55 self.can_read_length(1)
56 }
57
58 pub fn peek(&self) -> char {
59 self.string.chars().nth(self.cursor).unwrap()
60 }
61
62 pub fn peek_offset(&self, offset: usize) -> char {
63 self.string.chars().nth(self.cursor + offset).unwrap()
64 }
65
66 pub fn cursor(&self) -> usize {
67 self.cursor
68 }
69
70 pub fn read(&mut self) -> char {
71 let c = self.peek();
72 self.cursor += 1;
73 c
74 }
75
76 pub fn skip(&mut self) {
77 self.cursor += 1;
78 }
79
80 pub fn is_allowed_number(c: char) -> bool {
81 c.is_ascii_digit() || c == '.' || c == '-'
82 }
83
84 pub fn is_quoted_string_start(c: char) -> bool {
85 c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE
86 }
87
88 pub fn skip_whitespace(&mut self) {
89 while self.can_read() && self.peek().is_whitespace() {
90 self.skip();
91 }
92 }
93
94 pub fn read_int(&mut self) -> Result<i32, CommandSyntaxError> {
95 let start = self.cursor;
96 while self.can_read() && StringReader::is_allowed_number(self.peek()) {
97 self.skip();
98 }
99 let number = &self.string[start..self.cursor];
100 if number.is_empty() {
101 return Err(BuiltInError::ReaderExpectedInt.create_with_context(self));
102 }
103 let result = i32::from_str(number);
104 if result.is_err() {
105 self.cursor = start;
106 return Err(BuiltInError::ReaderInvalidInt {
107 value: number.to_string(),
108 }
109 .create_with_context(self));
110 }
111
112 Ok(result.unwrap())
113 }
114
115 pub fn read_long(&mut self) -> Result<i64, CommandSyntaxError> {
116 let start = self.cursor;
117 while self.can_read() && StringReader::is_allowed_number(self.peek()) {
118 self.skip();
119 }
120 let number = &self.string[start..self.cursor];
121 if number.is_empty() {
122 return Err(BuiltInError::ReaderExpectedLong.create_with_context(self));
123 }
124 let result = i64::from_str(number);
125 if result.is_err() {
126 self.cursor = start;
127 return Err(BuiltInError::ReaderInvalidLong {
128 value: number.to_string(),
129 }
130 .create_with_context(self));
131 }
132
133 Ok(result.unwrap())
134 }
135
136 pub fn read_double(&mut self) -> Result<f64, CommandSyntaxError> {
137 let start = self.cursor;
138 while self.can_read() && StringReader::is_allowed_number(self.peek()) {
139 self.skip();
140 }
141 let number = &self.string[start..self.cursor];
142 if number.is_empty() {
143 return Err(BuiltInError::ReaderExpectedDouble.create_with_context(self));
144 }
145 let result = f64::from_str(number);
146 if result.is_err() {
147 self.cursor = start;
148 return Err(BuiltInError::ReaderInvalidDouble {
149 value: number.to_string(),
150 }
151 .create_with_context(self));
152 }
153
154 Ok(result.unwrap())
155 }
156
157 pub fn read_float(&mut self) -> Result<f32, CommandSyntaxError> {
158 let start = self.cursor;
159 while self.can_read() && StringReader::is_allowed_number(self.peek()) {
160 self.skip();
161 }
162 let number = &self.string[start..self.cursor];
163 if number.is_empty() {
164 return Err(BuiltInError::ReaderExpectedFloat.create_with_context(self));
165 }
166 let result = f32::from_str(number);
167 if result.is_err() {
168 self.cursor = start;
169 return Err(BuiltInError::ReaderInvalidFloat {
170 value: number.to_string(),
171 }
172 .create_with_context(self));
173 }
174
175 Ok(result.unwrap())
176 }
177
178 pub fn is_allowed_in_unquoted_string(c: char) -> bool {
179 c.is_ascii_digit()
180 || c.is_ascii_uppercase()
181 || c.is_ascii_lowercase()
182 || c == '_'
183 || c == '-'
184 || c == '.'
185 || c == '+'
186 }
187
188 pub fn read_unquoted_string(&mut self) -> &str {
189 let start = self.cursor;
190 while self.can_read() && StringReader::is_allowed_in_unquoted_string(self.peek()) {
191 self.skip();
192 }
193 &self.string[start..self.cursor]
194 }
195
196 pub fn read_quoted_string(&mut self) -> Result<String, CommandSyntaxError> {
197 if !self.can_read() {
198 return Ok(String::new());
199 }
200 let next = self.peek();
201 if !StringReader::is_quoted_string_start(next) {
202 return Err(BuiltInError::ReaderExpectedStartOfQuote.create_with_context(self));
203 }
204 self.skip();
205 self.read_string_until(next)
206 }
207
208 pub fn read_string_until(&mut self, terminator: char) -> Result<String, CommandSyntaxError> {
209 let mut result = String::new();
210 let mut escaped = false;
211 while self.can_read() {
212 let c = self.read();
213 if escaped {
214 if c == terminator || c == SYNTAX_ESCAPE {
215 result.push(c);
216 escaped = false;
217 } else {
218 self.cursor -= 1;
219 return Err(BuiltInError::ReaderInvalidEscape { character: c }
220 .create_with_context(self));
221 }
222 } else if c == SYNTAX_ESCAPE {
223 escaped = true;
224 } else if c == terminator {
225 return Ok(result);
226 } else {
227 result.push(c);
228 }
229 }
230
231 Err(BuiltInError::ReaderExpectedEndOfQuote.create_with_context(self))
232 }
233
234 pub fn read_string(&mut self) -> Result<String, CommandSyntaxError> {
235 if !self.can_read() {
236 return Ok(String::new());
237 }
238 let next = self.peek();
239 if StringReader::is_quoted_string_start(next) {
240 self.skip();
241 return self.read_string_until(next);
242 }
243 Ok(self.read_unquoted_string().to_string())
244 }
245
246 pub fn read_boolean(&mut self) -> Result<bool, CommandSyntaxError> {
247 let start = self.cursor;
248 let value = self.read_string()?;
249 if value.is_empty() {
250 return Err(BuiltInError::ReaderExpectedBool.create_with_context(self));
251 }
252
253 if value == "true" {
254 Ok(true)
255 } else if value == "false" {
256 Ok(false)
257 } else {
258 self.cursor = start;
259 Err(BuiltInError::ReaderInvalidBool { value }.create_with_context(self))
260 }
261 }
262
263 pub fn expect(&mut self, c: char) -> Result<(), CommandSyntaxError> {
264 if !self.can_read() || self.peek() != c {
265 return Err(BuiltInError::ReaderExpectedSymbol { symbol: c }.create_with_context(self));
266 }
267 self.skip();
268 Ok(())
269 }
270}