aboutsummaryrefslogtreecommitdiff
path: root/azalea-brigadier/src/string_reader.rs
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2022-01-09 00:14:39 -0600
committermat <github@matdoes.dev>2022-01-09 00:14:39 -0600
commitd959fb2d0cc4d8ad97eae86666876e22b2e50613 (patch)
tree96d80723a1e07de8aed36b26cd2a5c2eb6bca614 /azalea-brigadier/src/string_reader.rs
parente3ecb607b826abd64d0ef0cd3e1e00f64fddfbdf (diff)
downloadazalea-drasl-d959fb2d0cc4d8ad97eae86666876e22b2e50613.tar.xz
add string_reader
Diffstat (limited to 'azalea-brigadier/src/string_reader.rs')
-rw-r--r--azalea-brigadier/src/string_reader.rs277
1 files changed, 277 insertions, 0 deletions
diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs
index e69de29b..376cc711 100644
--- a/azalea-brigadier/src/string_reader.rs
+++ b/azalea-brigadier/src/string_reader.rs
@@ -0,0 +1,277 @@
+use crate::immutable_string_reader::ImmutableStringReader;
+use std::str::FromStr;
+
+#[derive(Clone)]
+struct StringReader<'a> {
+ pub string: &'a str,
+ pub cursor: usize,
+}
+
+const SYNTAX_ESCAPE: char = '\\';
+const SYNTAX_DOUBLE_QUOTE: char = '"';
+const SYNTAX_SINGLE_QUOTE: char = '\'';
+
+impl<'a> From<&'a str> for &StringReader<'a> {
+ fn from(string: &'a str) -> &StringReader<'a> {
+ &StringReader { string, cursor: 0 }
+ }
+}
+
+impl ImmutableStringReader for StringReader<'_> {
+ fn remaining_length(&self) -> usize {
+ self.string.len() - self.cursor
+ }
+
+ fn total_length(&self) -> usize {
+ self.string.len()
+ }
+
+ fn get_read(&self) -> &str {
+ &self.string[self.cursor..]
+ }
+
+ fn remaining(&self) -> &str {
+ &self.string[self.cursor..]
+ }
+
+ fn can_read_length(&self, length: usize) -> bool {
+ self.cursor + length <= self.string.len()
+ }
+
+ fn can_read(&self) -> bool {
+ self.can_read_length(1)
+ }
+
+ fn peek(&self) -> char {
+ self.string.chars().nth(self.cursor).unwrap()
+ }
+
+ fn peek_offset(&self, offset: usize) -> char {
+ self.string.chars().nth(self.cursor + offset).unwrap()
+ }
+}
+
+impl StringReader<'_> {
+ fn read(&mut self) -> char {
+ let c = self.peek();
+ self.cursor += 1;
+ c
+ }
+
+ fn skip(&mut self) {
+ self.cursor += 1;
+ }
+
+ fn is_allowed_number(c: char) -> bool {
+ c >= '0' && c <= '9' || c == '.' || c == '-'
+ }
+
+ fn is_quoted_string_start(c: char) -> bool {
+ c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE
+ }
+
+ fn skip_whitespace(&mut self) {
+ while self.can_read() && self.peek().is_whitespace() {
+ self.skip();
+ }
+ }
+
+ fn read_int(&self) -> Result<(), CommandSyntaxException> {
+ let start = self.cursor;
+ while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) {
+ self.skip();
+ }
+ let number = &self.string[start..self.cursor];
+ if number.is_empty() {
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_expected_int()
+ .create_with_context(self));
+ }
+ let result = i32::from_str(number);
+ if result.is_err() {
+ self.cursor = start;
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_invalid_int()
+ .create_with_context(self, number));
+ }
+
+ Ok(())
+ }
+
+ fn read_long(&self) -> Result<(), CommandSyntaxException> {
+ let start = self.cursor;
+ while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) {
+ self.skip();
+ }
+ let number = &self.string[start..self.cursor];
+ if number.is_empty() {
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_expected_long()
+ .create_with_context(self));
+ }
+ let result = i64::from_str(number);
+ if result.is_err() {
+ self.cursor = start;
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_invalid_long()
+ .create_with_context(self, number));
+ }
+
+ Ok(())
+ }
+
+ fn read_double(&self) -> Result<(), CommandSyntaxException> {
+ let start = self.cursor;
+ while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) {
+ self.skip();
+ }
+ let number = &self.string[start..self.cursor];
+ if number.is_empty() {
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_expected_double()
+ .create_with_context(self));
+ }
+ let result = f64::from_str(number);
+ if result.is_err() {
+ self.cursor = start;
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_invalid_double()
+ .create_with_context(self, number));
+ }
+
+ Ok(())
+ }
+
+ fn read_float(&self) -> Result<(), CommandSyntaxException> {
+ let start = self.cursor;
+ while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) {
+ self.skip();
+ }
+ let number = &self.string[start..self.cursor];
+ if number.is_empty() {
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_expected_float()
+ .create_with_context(self));
+ }
+ let result = f32::from_str(number);
+ if result.is_err() {
+ self.cursor = start;
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_invalid_float()
+ .create_with_context(self, number));
+ }
+
+ Ok(())
+ }
+
+ fn is_allowed_in_unquoted_string(c: char) -> bool {
+ c >= '0' && c <= '9'
+ || c >= 'A' && c <= 'Z'
+ || c >= 'a' && c <= 'z'
+ || c == '_'
+ || c == '-'
+ || c == '.'
+ || c == '+'
+ }
+
+ fn read_unquoted_string(&self) -> &str {
+ let start = self.cursor;
+ while self.can_read() && StringReader::<'_>::is_allowed_in_unquoted_string(self.peek()) {
+ self.skip();
+ }
+ &self.string[start..self.cursor]
+ }
+
+ fn read_quoted_string(&self) -> Result<&str, CommandSyntaxException> {
+ if !self.can_read() {
+ return "";
+ }
+ let next = self.peek();
+ if !StringReader::<'_>::is_quoted_string_start(next) {
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_expected_start_of_quote()
+ .create_with_context(self));
+ }
+ self.skip();
+ self.read_string_until(next)
+ }
+
+ fn read_string_until(&self, terminator: char) -> Result<String, CommandSynatxException> {
+ let result = String::new();
+ let mut escaped = false;
+ while self.can_read() {
+ let c = self.read();
+ if escaped {
+ if c == terminator || c == SYNTAX_ESCAPE {
+ result.push(c);
+ escaped = false;
+ } else {
+ self.cursor -= 1;
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_invalid_escape()
+ .create_with_context(self, c));
+ }
+ } else if c == SYNTAX_ESCAPE {
+ escaped = true;
+ } else if c == terminator {
+ return Ok(result);
+ } else {
+ result.push(c);
+ }
+ }
+
+ Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_expected_end_of_quote()
+ .create_with_context(self))
+ }
+
+ fn read_string(&self) -> Result<String, CommandSyntaxException> {
+ // if (!canRead()) {
+ // return "";
+ // }
+ // final char next = peek();
+ // if (isQuotedStringStart(next)) {
+ // skip();
+ // return readStringUntil(next);
+ // }
+ // return readUnquotedString();
+ if !self.can_read() {
+ return Ok(String::new());
+ }
+ let next = self.peek();
+ if StringReader::<'_>::is_quoted_string_start(next) {
+ self.skip();
+ return self.read_string_until(next);
+ }
+ Ok(self.read_unquoted_string().to_string())
+ }
+
+ fn read_boolean(&self) -> Result<bool, CommandSyntaxException> {
+ let start = self.cursor;
+ let value = self.read_string()?;
+ if value.is_empty() {
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_expected_bool()
+ .create_with_context(self));
+ }
+
+ if value == "true" {
+ return Ok(true);
+ } else if value == "false" {
+ return Ok(false);
+ } else {
+ self.cursor = start;
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_invalid_bool()
+ .create_with_context(self, value));
+ }
+ }
+
+ fn expect(&self, c: char) -> Result<(), CommandSyntaxException> {
+ if !self.can_read() || self.peek() != c {
+ return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS
+ .reader_expected_symbol()
+ .create_with_context(self, c));
+ }
+ self.skip();
+ }