beyond-resp 0.1.0

RESP2/RESP3 wire protocol codec
Documentation
use thiserror::Error;

#[allow(dead_code)]
#[derive(Debug, Error)]
#[must_use = "errors must be handled or explicitly ignored with `let _ =`"]
pub enum RespError {
    #[error("incomplete frame")]
    Incomplete,

    #[error("unknown type byte: 0x{byte:02x}")]
    InvalidTypeByte { byte: u8 },

    #[error("invalid integer")]
    InvalidInteger,

    #[error("invalid double")]
    InvalidDouble,

    #[error("invalid length")]
    InvalidLength,

    #[error("missing CRLF terminator")]
    MissingCrlf,

    #[error("verbatim string encoding separator missing or too short")]
    InvalidVerbatim,

    #[error("invalid big number")]
    InvalidBigNumber,

    #[error("nesting depth limit exceeded")]
    DepthLimitExceeded,

    #[error("frame exceeds size limit of {limit} bytes")]
    FrameTooLarge { limit: usize },

    #[error("I/O error")]
    Io {
        #[source]
        source: std::io::Error,
    },
}

impl RespError {
    pub(crate) fn invalid_type(byte: u8) -> Self {
        Self::InvalidTypeByte { byte }
    }

    pub(crate) fn too_large(limit: usize) -> Self {
        Self::FrameTooLarge { limit }
    }
}

impl From<std::io::Error> for RespError {
    fn from(source: std::io::Error) -> Self {
        Self::Io { source }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io;

    #[test]
    fn io_error_converts_to_resp_error() {
        let io_err = io::Error::new(io::ErrorKind::BrokenPipe, "pipe closed");
        let resp_err: RespError = io_err.into();
        assert!(matches!(resp_err, RespError::Io { .. }));
        assert!(resp_err.to_string().contains("I/O error"));
    }
}