use crate::error::{Error as VortexError, Result as VortexResult};
use bytes::{BufMut, Bytes, BytesMut};
const CODE_SIZE: usize = 1;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Code {
Unknown = 0,
InvalidArgument = 1,
NotFound = 2,
Internal = 3,
Reserved(u8), }
impl TryFrom<u8> for Code {
type Error = VortexError;
fn try_from(value: u8) -> VortexResult<Self> {
match value {
0 => Ok(Code::Unknown),
1 => Ok(Code::InvalidArgument),
2 => Ok(Code::NotFound),
3 => Ok(Code::Internal),
4..=255 => Ok(Code::Reserved(value)),
}
}
}
impl From<Code> for u8 {
fn from(code: Code) -> u8 {
match code {
Code::Unknown => 0,
Code::InvalidArgument => 1,
Code::NotFound => 2,
Code::Internal => 3,
Code::Reserved(value) => value,
}
}
}
#[derive(Debug, Clone)]
pub struct Error {
code: Code,
message: String,
}
impl Error {
pub fn new(code: Code, message: String) -> Self {
Self { code, message }
}
pub fn code(&self) -> Code {
self.code
}
pub fn message(&self) -> &str {
&self.message
}
pub fn len(&self) -> usize {
self.message.len() + CODE_SIZE
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl From<Error> for Bytes {
fn from(err: Error) -> Self {
let mut bytes = BytesMut::with_capacity(err.len());
bytes.put_u8(err.code.into());
bytes.extend_from_slice(err.message.as_bytes());
bytes.freeze()
}
}
impl TryFrom<Bytes> for Error {
type Error = VortexError;
fn try_from(bytes: Bytes) -> VortexResult<Self> {
if bytes.len() < CODE_SIZE {
return Err(VortexError::InvalidLength(format!(
"expected at least {} bytes for Error, got {}",
CODE_SIZE,
bytes.len()
)));
}
Ok(Error {
code: Code::try_from(
bytes
.first()
.ok_or(VortexError::InvalidPacket(
"insufficient bytes for code".to_string(),
))?
.to_owned(),
)?,
message: String::from_utf8(
bytes
.get(CODE_SIZE..)
.ok_or(VortexError::InvalidPacket(
"insufficient bytes for message".to_string(),
))?
.to_vec(),
)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use bytes::Bytes;
#[test]
fn test_new() {
let code = Code::InvalidArgument;
let message = "Invalid argument".to_string();
let error = Error::new(code, message.clone());
assert_eq!(error.code(), code);
assert_eq!(error.message(), message);
assert_eq!(error.len(), message.len() + CODE_SIZE);
}
#[test]
fn test_is_non_empty() {
let error_non_empty = Error::new(Code::Unknown, "Error message".to_string());
assert!(!error_non_empty.is_empty());
}
#[test]
fn test_to_bytes_and_from_bytes() {
let code = Code::NotFound;
let message = "Resource not found".to_string();
let error = Error::new(code, message.clone());
let bytes: Bytes = error.into();
let error = Error::try_from(bytes).unwrap();
assert_eq!(error.code(), code);
assert_eq!(error.message(), message);
}
#[test]
fn test_from_bytes_invalid_input() {
let invalid_bytes = Bytes::from("");
assert!(Error::try_from(invalid_bytes).is_err());
}
}