use crate::tagged::Tagged;
use std::{
borrow::{Borrow, Cow},
collections::{HashMap, HashSet},
io::Write,
marker::PhantomData,
sync::Arc,
};
pub const GLOBAL_TAG: &str = "midnight:";
pub fn tagged_serialize<T: Serializable + Tagged>(
value: &T,
mut writer: impl Write,
) -> std::io::Result<()> {
let tag = T::tag();
write!(&mut writer, "{GLOBAL_TAG}{tag}:")?;
value.serialize(&mut writer)
}
pub fn tagged_serialized_size<T: Serializable + Tagged>(value: &T) -> usize {
T::tag().len() + GLOBAL_TAG.len() + 1 + T::serialized_size(value)
}
pub trait Serializable {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()>;
fn serialized_size(&self) -> usize;
}
impl<T: Serializable> Serializable for Vec<T> {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
(self.len() as u32).serialize(writer)?;
for elem in self {
elem.serialize(writer)?;
}
Ok(())
}
fn serialized_size(&self) -> usize {
self.iter()
.fold((self.len() as u64).serialized_size(), |acc, x| {
acc + x.serialized_size()
})
}
}
impl<K: Serializable + Ord, V: Serializable> Serializable for HashMap<K, V> {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
(self.len() as u32).serialize(writer)?;
let mut kvs = self.iter().collect::<Vec<_>>();
kvs.sort_by_key(|(k1, _)| *k1);
for (k, v) in kvs.into_iter() {
k.serialize(writer)?;
v.serialize(writer)?;
}
Ok(())
}
fn serialized_size(&self) -> usize {
self.iter().fold((self.len() as u32).serialized_size(), |acc, (k, v)| {
acc + k.serialized_size() + v.serialized_size()
})
}
}
impl<T: Serializable + Ord> Serializable for HashSet<T> {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
(self.len() as u32).serialize(writer)?;
let mut elems = self.iter().collect::<Vec<_>>();
elems.sort();
for elem in elems.into_iter() {
elem.serialize(writer)?;
}
Ok(())
}
fn serialized_size(&self) -> usize {
self.iter()
.fold((self.len() as u32).serialized_size(), |acc, elem| acc + elem.serialized_size())
}
}
impl<'a, T> Serializable for &'a T
where
T: Serializable + 'a,
Self: Borrow<T>,
{
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
T::serialize(self, writer)
}
fn serialized_size(&self) -> usize {
T::serialized_size(self)
}
}
impl<T: Serializable> Serializable for Option<T> {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
match self {
Some(v) => {
1u8.serialize(writer)?;
v.serialize(writer)?;
Ok(())
}
None => {
0u8.serialize(writer)?;
Ok(())
}
}
}
fn serialized_size(&self) -> usize {
match self {
Some(v) => 1 + v.serialized_size(),
None => 1,
}
}
}
impl Serializable for str {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
(self.len() as u64).serialize(writer)?;
writer.write_all(self.as_bytes())
}
fn serialized_size(&self) -> usize {
let len = self.len();
(len as u64).serialized_size() + len
}
}
impl Serializable for String {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
str::serialize(self, writer)
}
fn serialized_size(&self) -> usize {
str::serialized_size(self)
}
}
impl Serializable for &str {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
str::serialize(self, writer)
}
fn serialized_size(&self) -> usize {
str::serialized_size(self)
}
}
impl<const N: usize> Serializable for [u8; N] {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
writer.write_all(&self[..])
}
fn serialized_size(&self) -> usize {
N
}
}
impl<T: ?Sized> Serializable for PhantomData<T> {
fn serialize(&self, _writer: &mut impl Write) -> std::io::Result<()> {
Ok(())
}
fn serialized_size(&self) -> usize {
0
}
}
impl<T: ?Sized> Tagged for PhantomData<T> {
fn tag() -> Cow<'static, str> {
Cow::Borrowed("()")
}
fn tag_unique_factor() -> String {
"()".into()
}
}
impl<T: Serializable> Serializable for Box<T> {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
T::serialize(self, writer)
}
fn serialized_size(&self) -> usize {
T::serialized_size(self)
}
}
impl<T: Serializable> Serializable for Arc<T> {
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
T::serialize(self, writer)
}
fn serialized_size(&self) -> usize {
T::serialized_size(self)
}
}
impl<'a, T: ToOwned + ?Sized> Serializable for Cow<'a, T>
where
T: Serializable,
{
fn serialize(&self, writer: &mut impl Write) -> std::io::Result<()> {
T::serialize(self, writer)
}
fn serialized_size(&self) -> usize {
T::serialized_size(self)
}
}
impl<'a, T: ToOwned + ?Sized + Tagged> Tagged for Cow<'a, T> {
fn tag() -> Cow<'static, str> {
T::tag()
}
fn tag_unique_factor() -> String {
T::tag_unique_factor()
}
}