azalea_brigadier/
string_reader.rs1use std::str::FromStr;
2
3use crate::exceptions::{BuiltInExceptions, CommandSyntaxException};
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, CommandSyntaxException> {
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(BuiltInExceptions::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(BuiltInExceptions::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, CommandSyntaxException> {
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(BuiltInExceptions::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(BuiltInExceptions::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, CommandSyntaxException> {
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(BuiltInExceptions::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(BuiltInExceptions::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, CommandSyntaxException> {
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(BuiltInExceptions::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(BuiltInExceptions::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, CommandSyntaxException> {
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(BuiltInExceptions::ReaderExpectedStartOfQuote.create_with_context(self));
203 }
204 self.skip();
205 self.read_string_until(next)
206 }
207
208 pub fn read_string_until(
209 &mut self,
210 terminator: char,
211 ) -> Result<String, CommandSyntaxException> {
212 let mut result = String::new();
213 let mut escaped = false;
214 while self.can_read() {
215 let c = self.read();
216 if escaped {
217 if c == terminator || c == SYNTAX_ESCAPE {
218 result.push(c);
219 escaped = false;
220 } else {
221 self.cursor -= 1;
222 return Err(BuiltInExceptions::ReaderInvalidEscape { character: c }
223 .create_with_context(self));
224 }
225 } else if c == SYNTAX_ESCAPE {
226 escaped = true;
227 } else if c == terminator {
228 return Ok(result);
229 } else {
230 result.push(c);
231 }
232 }
233
234 Err(BuiltInExceptions::ReaderExpectedEndOfQuote.create_with_context(self))
235 }
236
237 pub fn read_string(&mut self) -> Result<String, CommandSyntaxException> {
238 if !self.can_read() {
239 return Ok(String::new());
240 }
241 let next = self.peek();
242 if StringReader::is_quoted_string_start(next) {
243 self.skip();
244 return self.read_string_until(next);
245 }
246 Ok(self.read_unquoted_string().to_string())
247 }
248
249 pub fn read_boolean(&mut self) -> Result<bool, CommandSyntaxException> {
250 let start = self.cursor;
251 let value = self.read_string()?;
252 if value.is_empty() {
253 return Err(BuiltInExceptions::ReaderExpectedBool.create_with_context(self));
254 }
255
256 if value == "true" {
257 Ok(true)
258 } else if value == "false" {
259 Ok(false)
260 } else {
261 self.cursor = start;
262 Err(BuiltInExceptions::ReaderInvalidBool { value }.create_with_context(self))
263 }
264 }
265
266 pub fn expect(&mut self, c: char) -> Result<(), CommandSyntaxException> {
267 if !self.can_read() || self.peek() != c {
268 return Err(
269 BuiltInExceptions::ReaderExpectedSymbol { symbol: c }.create_with_context(self)
270 );
271 }
272 self.skip();
273 Ok(())
274 }
275}