use crate::Frame; use bytes::Bytes; use std::{error, fmt, io, str, vec}; /// Utility for parsing a command #[derive(Debug)] pub(crate) struct Parse { parts: vec::IntoIter>, } #[derive(Debug)] pub(crate) enum ParseError { EndOfStream, Invalid, UnknownCommand(String), } impl Parse { pub(crate) fn new(frame: Frame) -> Result { let array = match frame { Frame::Array(array) => array, _ => return Err(ParseError::Invalid), }; Ok(Parse { parts: array.into_iter(), }) } fn next(&mut self) -> Result { self.parts .next() .map(|frame| *frame) .ok_or(ParseError::EndOfStream) } pub(crate) fn next_string(&mut self) -> Result { match self.next()? { Frame::Simple(s) => Ok(s), Frame::Bulk(data) => str::from_utf8(&data[..]) .map(|s| s.to_string()) .map_err(|_| ParseError::Invalid), _ => Err(ParseError::Invalid), } } pub(crate) fn next_bytes(&mut self) -> Result { match self.next()? { Frame::Simple(s) => Ok(Bytes::from(s.into_bytes())), Frame::Bulk(data) => Ok(data), _ => Err(ParseError::Invalid), } } pub(crate) fn next_int(&mut self) -> Result { use atoi::atoi; match self.next()? { Frame::Integer(v) => Ok(v), Frame::Simple(data) => atoi::(data.as_bytes()).ok_or(ParseError::Invalid), Frame::Bulk(data) => atoi::(&data).ok_or(ParseError::Invalid), _ => Err(ParseError::Invalid), } } /// Ensure there are no more entries in the array pub(crate) fn finish(&mut self) -> Result<(), ParseError> { if self.parts.next().is_none() { Ok(()) } else { Err(ParseError::Invalid) } } } impl From for io::Error { fn from(src: ParseError) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("{}", src)) } } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let msg = match self { ParseError::EndOfStream => "end of stream".to_string(), ParseError::Invalid => "invalid".to_string(), ParseError::UnknownCommand(cmd) => format!("unknown command `{}`", cmd), }; write!(f, "{}", &msg) } } impl std::error::Error for ParseError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { None } }