azalea_brigadier/
string_reader.rs

1use 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}