azalea_brigadier/
string_reader.rs

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