use crate::{BufMut, Bytes, BytesMut};
use alloc::{borrow::Cow, boxed::Box, rc::Rc, sync::Arc};
pub trait Serialize {
fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
where
Buffer: BufMut + ?Sized;
fn size(&self) -> Option<usize>;
fn to_bytes(&self) -> Result<Bytes, SerializeError> {
let Some(size) = self.size() else {
return Err(SerializeError::SizeOverflow);
};
let mut buffer = BytesMut::with_capacity(size);
self.serialize(&mut buffer).map(|_total| buffer.freeze())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
#[non_exhaustive]
pub enum SerializeError {
#[error("buffer would overflow")]
BufferOverflow,
#[error("invariant failed: {0}")]
InvariantFailed(Cow<'static, str>),
#[error("length exceeds capacity of length field")]
LengthOverflow,
#[error("size doesn't match expected value")]
SizeMismatch,
#[error("size exceeds capacity of `usize`")]
SizeOverflow,
}
impl SerializeError {
#[inline]
#[must_use]
pub fn invariant(message: impl Into<Cow<'static, str>>) -> Self {
Self::InvariantFailed(message.into())
}
}
macro_rules! impl_serialize_forward {
($name:ty) => {
impl<T: Serialize + ?Sized> Serialize for $name {
fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
where
Buffer: BufMut + ?Sized,
{
(**self).serialize(buffer)
}
fn size(&self) -> Option<usize> {
(**self).size()
}
}
};
}
impl_serialize_forward!(&T);
impl_serialize_forward!(&mut T);
impl_serialize_forward!(Box<T>);
impl_serialize_forward!(Rc<T>);
impl_serialize_forward!(Arc<T>);
macro_rules! impl_serialize_tuple {
($( $name:ident )+) => {
#[expect(non_snake_case, reason = "generic parameters")]
#[expect(clippy::min_ident_chars, reason = "generic parameters")]
impl<$($name: Serialize),+> Serialize for ($($name,)+) {
fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
where
Buffer: BufMut + ?Sized
{
let &($(ref $name,)+) = self;
Ok(0_usize)
$(
.and_then(|total| {
$name.serialize(buffer).and_then(|size|
total.checked_add(size).ok_or(SerializeError::SizeOverflow)
)
})
)+
}
fn size(&self) -> Option<usize> {
let &($(ref $name,)+) = self;
Some(0_usize)
$(
.and_then(|total| {
$name.size().and_then(|size| total.checked_add(size))
})
)+
}
}
};
}
impl_serialize_tuple! { A }
impl_serialize_tuple! { A B }
impl_serialize_tuple! { A B C }
impl_serialize_tuple! { A B C D }
impl_serialize_tuple! { A B C D E }
impl_serialize_tuple! { A B C D E F }
impl_serialize_tuple! { A B C D E F G }
impl_serialize_tuple! { A B C D E F G H }
impl_serialize_tuple! { A B C D E F G H I }
impl_serialize_tuple! { A B C D E F G H I J }
impl_serialize_tuple! { A B C D E F G H I J K }
impl_serialize_tuple! { A B C D E F G H I J K L }
macro_rules! impl_serialize_basic_type {
($name:ty, $method:ident) => {
impl Serialize for $name {
fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
where
Buffer: BufMut + ?Sized,
{
buffer.$method(*self);
Ok(size_of::<$name>())
}
fn size(&self) -> Option<usize> {
Some(size_of::<$name>())
}
}
};
}
impl_serialize_basic_type!(u8, put_u8);
impl_serialize_basic_type!(u16, put_u16);
impl_serialize_basic_type!(u32, put_u32);
impl_serialize_basic_type!(u64, put_u64);
impl_serialize_basic_type!(i8, put_i8);
impl_serialize_basic_type!(i16, put_i16);
impl_serialize_basic_type!(i32, put_i32);
impl_serialize_basic_type!(i64, put_i64);
impl_serialize_basic_type!(f32, put_f32);
impl_serialize_basic_type!(f64, put_f64);
impl Serialize for bool {
fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
where
Buffer: BufMut + ?Sized,
{
if *self {
buffer.put_u8(1);
} else {
buffer.put_u8(0);
}
Ok(size_of::<u8>())
}
fn size(&self) -> Option<usize> {
Some(1)
}
}
impl<T: Serialize, const N: usize> Serialize for [T; N] {
fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
where
Buffer: BufMut + ?Sized,
{
let mut iterator = self.iter();
iterator.try_fold(0_usize, |acc, elem| {
elem.serialize(buffer)
.and_then(|elem| acc.checked_add(elem).ok_or(SerializeError::SizeOverflow))
})
}
fn size(&self) -> Option<usize> {
let mut iterator = self.iter();
iterator.try_fold(0_usize, |acc, elem| {
elem.size().and_then(|elem| acc.checked_add(elem))
})
}
}
impl Serialize for Bytes {
fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
where
Buffer: BufMut + ?Sized,
{
buffer.put_slice(self);
Ok(self.len())
}
fn size(&self) -> Option<usize> {
Some(self.len())
}
}
pub struct SerializeWithFn<'value, Value, SerializeFn, SizeFn> {
value: &'value Value,
serialize: SerializeFn,
size: SizeFn,
}
impl<'value, Value, SerializeFn, SizeFn> SerializeWithFn<'value, Value, SerializeFn, SizeFn>
where
for<'any> SerializeFn: Fn(&Value, &mut dyn BufMut) -> Result<usize, SerializeError>,
for<'any> SizeFn: Fn(&Value) -> Option<usize>,
{
#[inline]
#[must_use]
pub const fn new(value: &'value Value, serialize: SerializeFn, size: SizeFn) -> Self {
Self {
value,
serialize,
size,
}
}
}
impl<Value, SerializeFn, SizeFn> Serialize for SerializeWithFn<'_, Value, SerializeFn, SizeFn>
where
for<'any> SerializeFn: Fn(&Value, &mut dyn BufMut) -> Result<usize, SerializeError>,
for<'any> SizeFn: Fn(&Value) -> Option<usize>,
{
fn serialize<Buffer>(&self, mut buffer: &mut Buffer) -> Result<usize, SerializeError>
where
Buffer: BufMut + ?Sized,
{
(self.serialize)(self.value, &mut buffer)
}
fn size(&self) -> Option<usize> {
(self.size)(self.value)
}
}
#[cfg(test)]
#[expect(clippy::inline_modules, reason = "rust-clippy#17342")]
mod tests {
use super::*;
macro_rules! test_serialize_basic_type {
($t:ty, $name:ident) => {
#[test]
fn $name() {
let mut buffer = BytesMut::with_capacity(size_of::<$t>());
let result = <$t>::MAX.serialize(&mut buffer);
assert_eq!(result, Ok(size_of::<$t>()));
assert_eq!(result.ok(), <$t>::MAX.size());
assert_eq!(buffer.freeze(), <$t>::MAX.to_be_bytes().as_slice());
}
};
}
test_serialize_basic_type!(u8, serialize_u8);
test_serialize_basic_type!(u16, serialize_u16);
test_serialize_basic_type!(u32, serialize_u32);
test_serialize_basic_type!(u64, serialize_u64);
test_serialize_basic_type!(i8, serialize_i8);
test_serialize_basic_type!(i16, serialize_i16);
test_serialize_basic_type!(i32, serialize_i32);
test_serialize_basic_type!(i64, serialize_i64);
test_serialize_basic_type!(f32, serialize_f32);
test_serialize_basic_type!(f64, serialize_f64);
#[test]
fn serialize_bool() {
let mut buffer = BytesMut::with_capacity(2);
for value in [true, false] {
let size = value
.serialize(&mut buffer)
.expect("should serialize the bool");
assert_eq!(size, 1);
assert_eq!(value.size(), Some(1));
}
assert_eq!(buffer.freeze(), [1_u8, 0_u8].as_slice());
}
#[test]
fn serialize_array() {
let mut buffer = BytesMut::with_capacity(2);
let array = [1_u8, 2_u8];
let size = array
.serialize(&mut buffer)
.expect("should serialize the array");
assert_eq!(size, 2);
assert_eq!(Some(size), array.size());
assert_eq!(buffer.freeze(), [1_u8, 2_u8].as_slice());
}
#[test]
fn serialize_tuple() {
let mut buffer = BytesMut::with_capacity(2);
let tuple = (1_u8, 2_u8);
let size = tuple
.serialize(&mut buffer)
.expect("should serialize the tuple");
assert_eq!(size, 2);
assert_eq!(Some(size), tuple.size());
assert_eq!(buffer.freeze(), [1_u8, 2_u8].as_slice());
}
#[test]
fn serialize_bytes() {
let mut buffer = BytesMut::with_capacity(2);
let bytes = Bytes::copy_from_slice(&[1_u8, 2_u8]);
let size = bytes
.serialize(&mut buffer)
.expect("should serialize the buffer");
assert_eq!(size, 2);
assert_eq!(Some(size), bytes.size());
assert_eq!(buffer.freeze(), [1_u8, 2].as_slice());
}
#[test]
fn serialize_box() {
let mut buffer = BytesMut::with_capacity(2);
let value = Box::new(0x0102_u16);
let size = value
.serialize(&mut buffer)
.expect("should serialize the buffer");
assert_eq!(size, 2);
assert_eq!(Some(size), value.size());
assert_eq!(buffer.freeze(), [1_u8, 2].as_slice());
}
}