diff options
author | evuez <julien@mulga.net> | 2022-11-26 15:38:06 -0500 |
---|---|---|
committer | evuez <julien@mulga.net> | 2024-04-03 22:44:12 +0200 |
commit | 86098797034cbc7eb6db0cee54e17f8dcaedbc5d (patch) | |
tree | 29b6225ead843eb9022296a54657bbadfa1c4da0 /src/server/parser.rs | |
download | blom-86098797034cbc7eb6db0cee54e17f8dcaedbc5d.tar.gz |
Diffstat (limited to 'src/server/parser.rs')
-rw-r--r-- | src/server/parser.rs | 118 |
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) + } +} |