use crate::{error::*, marshalling::*};
use std::borrow::Cow;
pub trait Field: FromFieldStr + AsBytes {
const NAME: &'static str;
const KEY: u8;
}
#[macro_export]
macro_rules! make_field {
($type:ident, $key_enum:ty) => {
impl $crate::marshalling::Field for $type {
const NAME: &'static str = stringify!($type);
const KEY: u8 = <$key_enum>::$type as u8;
}
};
}
pub trait FromFieldStr: Sized {
fn from_field_str(value: FieldStr) -> Result<Self, AnonLocErr>;
}
pub trait AsBytes {
fn as_bytes(&self) -> Cow<'_, [u8]>;
}
pub trait Serialize<W: std::io::Write> {
fn serialize(&self, w: &mut W) -> Result<(), AnonLocErr>;
}
impl<F: Field, W: std::io::Write> Serialize<W> for F {
fn serialize(&self, w: &mut W) -> Result<(), AnonLocErr> {
let bytes = self.as_bytes();
if bytes.is_empty() {
return Ok(());
}
|| -> std::io::Result<()> {
w.write_all(&[F::KEY])?;
w.write_all(bytes.as_ref())?;
w.write_all(b"\0")
}()
.map_err(AnonLocErr::Write)
}
}
pub trait Deserialize {
fn deserialize(bytes: &[u8]) -> Result<Self, AnonLocErr>
where
Self: std::marker::Sized;
}
impl<F: Field> Deserialize for F {
fn deserialize(bytes: &[u8]) -> Result<Self, AnonLocErr>
where
Self: std::marker::Sized,
{
if bytes.is_empty() {
return Err(AnonLocErr::FieldMissing(Self::NAME));
}
let (key, value) = bytes.split_at(1);
if key != [Self::KEY] {
return Err(AnonLocErr::FieldInvalid(
Self::NAME,
format!(
"Expected key '{}' but found key '{}'",
Self::KEY as char,
key.first().map(|&c| c as char).unwrap_or('?'),
),
));
}
FieldStr::try_from(value)
.map_err(|e| e.field(Self::NAME))
.and_then(Self::from_field_str)
}
}