aboutsummaryrefslogtreecommitdiff
path: root/src/server/parser.rs
diff options
context:
space:
mode:
authorevuez <julien@mulga.net>2022-11-26 15:38:06 -0500
committerevuez <julien@mulga.net>2024-04-03 22:44:12 +0200
commit86098797034cbc7eb6db0cee54e17f8dcaedbc5d (patch)
tree29b6225ead843eb9022296a54657bbadfa1c4da0 /src/server/parser.rs
downloadblom-main.tar.gz
Initial commitHEADmain
Diffstat (limited to 'src/server/parser.rs')
-rw-r--r--src/server/parser.rs118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/server/parser.rs b/src/server/parser.rs
new file mode 100644
index 0000000..e9c2aad
--- /dev/null
+++ b/src/server/parser.rs
@@ -0,0 +1,118 @@
+use crate::common::sized_buffer::Error as SizedBufferError;
+use crate::common::sized_buffer::SizedBuffer;
+use std::fmt;
+
+#[derive(Debug)]
+pub enum Error {
+ ExpectedArray(char),
+ ExpectedString(char),
+ InvalidLength,
+ Buffer(SizedBufferError),
+}
+
+#[derive(Debug)]
+pub enum State {
+ Wait,
+ Done(Vec<Vec<u8>>),
+}
+
+pub struct Parser {
+ pub argv: Vec<Vec<u8>>,
+ argc_rem: u64,
+ arg_len: i64,
+}
+
+impl Parser {
+ pub fn new() -> Self {
+ Self {
+ argv: Vec::new(),
+ argc_rem: 0,
+ arg_len: -1,
+ }
+ }
+
+ pub fn parse<const N: usize>(
+ &mut self,
+ buffer: &mut SizedBuffer<[u8; N]>,
+ ) -> Result<State, Error> {
+ if self.argc_rem == 0 {
+ assert_eq!(self.arg_len, -1);
+
+ /* argc_rem wasn't initialized yet. The current buffer was never read, we need to
+ * figure out the number of args. */
+ if buffer.peek() != b'*' {
+ return Err(Error::ExpectedArray(buffer.peek() as char));
+ }
+ let arr_lf_offset = match buffer.find_offset(b'\n') {
+ Some(offset) => offset,
+ /* Couldn't find \n, wait for more data */
+ None => return Ok(State::Wait),
+ };
+ self.argc_rem = read_data_len(buffer, arr_lf_offset)? as u64;
+ buffer.skip_byte(b'\n').map_err(Error::Buffer)?;
+ }
+
+ /* We already started reading a command. We can process the arguments. */
+ while self.argc_rem > 0 {
+ /* arg_len wasn't initialized yet. We need to find the size of the string argument. */
+ if self.arg_len == -1 {
+ if buffer.peek() != b'$' {
+ return Err(Error::ExpectedString(buffer.peek() as char));
+ }
+
+ let str_lf_offset = match buffer.find_offset(b'\n') {
+ Some(offset) => offset,
+ /* Couldn't find \n, wait for more data */
+ None => return Ok(State::Wait),
+ };
+ self.arg_len = read_data_len(buffer, str_lf_offset)? as i64;
+ buffer.skip_byte(b'\n').map_err(Error::Buffer)?;
+ } else {
+ match buffer.read_count(self.arg_len as usize) {
+ /* Not enough data, wait for more */
+ Err(_) => return Ok(State::Wait),
+ /* Push the new string onto the args list */
+ Ok(arg) => self.argv.push(arg.to_vec()),
+ }
+ buffer.skip_byte(b'\n').map_err(Error::Buffer)?;
+
+ self.argc_rem -= 1;
+ self.arg_len = -1;
+ }
+ }
+
+ let argv = self.argv.clone();
+ self.argv.clear();
+ Ok(State::Done(argv))
+ }
+}
+
+fn read_data_len<const N: usize>(
+ buffer: &mut SizedBuffer<[u8; N]>,
+ n: usize,
+) -> Result<usize, Error> {
+ /* Read from 1 to skip the data tag */
+ let mut len: usize = 0;
+
+ for (i, x) in buffer.read_count_unchecked(n)[1..].iter().rev().enumerate() {
+ if *x < 48 || *x > 57 {
+ return Err(Error::InvalidLength);
+ }
+ len += (x - 48) as usize * 10usize.pow(i as u32);
+ }
+
+ Ok(len)
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let res = match self {
+ Error::ExpectedArray(got) => format!("Expected array, got {:?}.", *got as char),
+ Error::ExpectedString(got) => format!("Expected string, got {:?}.", *got as char),
+ Error::InvalidLength => "Invalid header size.".to_string(),
+ Error::Buffer(err) => format!("Error read or writing to/from the buffer: {err:?}"),
+ };
+
+ write!(f, "{}", res)
+ }
+}