use std::{
convert::{TryFrom, TryInto},
fmt,
io::{self, Read, Write},
};
use crate::{
io::{ReadFrom, WriteTo},
var_type::VarInt,
};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Encoding {
Ignore = 0,
Trivial = 1,
Simple = 2,
Extended = 3,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct InvalidEncoding(u64);
impl fmt::Display for InvalidEncoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid encoding: {}", self.0)
}
}
impl std::error::Error for InvalidEncoding {}
impl TryFrom<u64> for Encoding {
type Error = InvalidEncoding;
fn try_from(value: u64) -> Result<Self, <Self as TryFrom<u64>>::Error> {
match value {
0 => Ok(Encoding::Ignore),
1 => Ok(Encoding::Trivial),
2 => Ok(Encoding::Simple),
3 => Ok(Encoding::Extended),
_ => Err(InvalidEncoding(value)),
}
}
}
impl WriteTo for Encoding {
fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
let v: VarInt = (*self as u64).into();
v.write_to(w)?;
Ok(())
}
}
impl ReadFrom for Encoding {
fn read_from(r: &mut dyn Read) -> io::Result<Self>
where
Self: Sized,
{
let v = VarInt::read_from(r)?;
match v.as_u64().try_into() {
Ok(encoding) => Ok(encoding),
Err(err) => Err(io::Error::new(io::ErrorKind::Other, err)),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum SimpleError {
SubjectContainsLf,
}
impl fmt::Display for SimpleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SubjectContainsLf => "subject contains LF".fmt(f),
}
}
}
impl std::error::Error for SimpleError {}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Simple {
subject: Vec<u8>,
body: Vec<u8>,
}
impl Simple {
pub fn new(subject: Vec<u8>, body: Vec<u8>) -> Result<Self, SimpleError> {
if let Some(_item) = subject.iter().find(|b| **b == b'\n') {
return Err(SimpleError::SubjectContainsLf);
}
Ok(Self { subject, body })
}
pub fn subject(&self) -> &[u8] {
&self.subject
}
pub fn body(&self) -> &[u8] {
&self.body
}
}
impl WriteTo for Simple {
fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
b"Subject:".write_to(w)?;
self.subject.write_to(w)?;
b'\n'.write_to(w)?;
b"Body:".write_to(w)?;
self.body.write_to(w)?;
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TryIntoSimpleError {
SubjectNotFound,
BodyNotFound,
}
const SUBJECT: &[u8] = b"Subject:";
const BODY: &[u8] = b"Body:";
impl TryFrom<&[u8]> for Simple {
type Error = TryIntoSimpleError;
fn try_from(bytes: &[u8]) -> Result<Self, <Self as TryFrom<&[u8]>>::Error> {
if bytes.len() < SUBJECT.len() {
return Err(TryIntoSimpleError::SubjectNotFound);
}
if &bytes[..SUBJECT.len()] != SUBJECT {
return Err(TryIntoSimpleError::SubjectNotFound);
}
let lf_pos = bytes[SUBJECT.len()..].iter().position(|b| *b == b'\n');
if lf_pos.is_none() {
return Err(TryIntoSimpleError::SubjectNotFound);
}
let lf_pos = SUBJECT.len() + lf_pos.unwrap();
let subject = &bytes[SUBJECT.len()..lf_pos];
if bytes.len() < lf_pos + 1 + BODY.len() {
return Err(TryIntoSimpleError::BodyNotFound);
}
if &bytes[lf_pos + 1..lf_pos + 1 + BODY.len()] != BODY {
return Err(TryIntoSimpleError::BodyNotFound);
}
let body = &bytes[lf_pos + 1 + BODY.len()..];
Ok(Self {
subject: subject.to_vec(),
body: body.to_vec(),
})
}
}