//! Traits to better express parsing semantics. pub mod static_pair; use crate::rcore::*; use super::{ atomic::{atomic_object::*, *}, point::*, *, }; /// This factory should return an error on EOF. pub trait InlineableFactory<'a, Ctx: Context<'a>>: Factory<'a, Ctx> { fn extension_error(&self, tail: &[u8]) -> Self::ParseError; } /// This factory always reads the same amount of bytes or returns error. pub trait FixedSizeFactory<'a, Ctx: Context<'a>>: InlineableFactory<'a, Ctx> { /// For [`ConstSizeFactory`] this must return [`ConstSizeFactory::SIZE`]. fn size(&self) -> usize; } /// Compile-time analogue of [`FixedSizeFactory`]. pub trait ConstSizeFactory<'a, Ctx: Context<'a>>: FixedSizeFactory<'a, Ctx> { /// Must be equal to [`FixedSizeFactory::size()`]. const SIZE: usize; } /// Object analogue of [`InlineableFactory`]. pub trait InlineableObject<'a, Ctx: Context<'a>>: Mentionable<'a, Ctx> {} /// Object analogue of [`FixedSizeFactory`]. pub trait FixedSizeObject<'a, Ctx: Context<'a>>: InlineableObject<'a, Ctx> { /// For [`ConstSizeObject`] this must return [`ConstSizeObject::SIZE`]. fn size(&self) -> usize; } /// Object analogue of [`ConstSizeFactory`]. pub trait ConstSizeObject<'a, Ctx: Context<'a>>: FixedSizeObject<'a, Ctx> { /// Must be equal to [`FixedSizeObject::size()`]. const SIZE: usize; } /// Atomic analogue of [`InlineableFactory`]/[`InlineableObject`]. pub trait InlineableAtomic: Atomic { fn a_extension_error(tail: &[u8]) -> Self::AParseError; } /// Atomic analogue of [`ConstSizeFactory`]/[`ConstSizeObject`]. /// /// Note: `FixedSizeAtomic` doesn't exist because it would /// either be const anyway /// or have a very undesireable implementation. pub trait ConstSizeAtomic: InlineableAtomic { const SIZE: usize; } impl<'a, Ctx: Context<'a>, A: InlineableAtomic> InlineableFactory<'a, Ctx> for AtomicFactory { fn extension_error(&self, tail: &[u8]) -> Self::ParseError { A::a_extension_error(tail) } } impl<'a, Ctx: Context<'a>, A: ConstSizeAtomic> FixedSizeFactory<'a, Ctx> for AtomicFactory { fn size(&self) -> usize { A::SIZE } } impl<'a, Ctx: Context<'a>, A: ConstSizeAtomic> ConstSizeFactory<'a, Ctx> for AtomicFactory { const SIZE: usize = A::SIZE; } impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> InlineableObject<'a, Ctx> for A where A::Fctr: InlineableFactory<'a, Ctx> { } impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> FixedSizeObject<'a, Ctx> for A where A::Fctr: FixedSizeFactory<'a, Ctx>, { fn size(&self) -> usize { self.factory().size() } } impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> ConstSizeObject<'a, Ctx> for A where A::Fctr: ConstSizeFactory<'a, Ctx>, { const SIZE: usize = A::Fctr::SIZE; } /// Error returned by [`CheckedParse::deserialize_checked`]/[`CheckedSerialize::serialize_checked`]. #[derive(Debug)] pub struct SizeError { expected: usize, received: usize, } impl Display for SizeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "expected {} bytes, read/wrote {} instead.", self.expected, self.received ) } } impl Error for SizeError {} /// Wrapper for [`SizeError`]/[`Factory::ParseError`]. #[derive(Debug)] pub enum CheckedParseError { Parse(P), Size(SizeError), } impl Display for CheckedParseError

{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Parse(parse_error) => write!(f, "{}", parse_error), Self::Size(size_error) => { write!(f, "size check failed: {}", size_error) } } } } impl From

for CheckedParseError

{ fn from(value: P) -> Self { Self::Parse(value) } } /// Returned by [`CheckedParse::deserialize_checked`]. pub type CheckedParseResult<'a, Ctx, F> = Result, CheckedParseError>>; /// Extension trait for factories that ensures fixed size read. pub trait CheckedParse<'a, Ctx: Context<'a>>: FixedSizeFactory<'a, Ctx> { /// Verify proper read length using [`Deserializer::tell`]. fn deserialize_checked( &self, deserializer: &mut dyn Deserializer, resolver: &Rc>, addresses: &mut Addresses, ) -> CheckedParseResult<'a, Ctx, Self> { let expected_size = self.size(); let start = deserializer.tell(); let result = self.deserialize(deserializer, resolver, addresses)?; let end = deserializer.tell(); let received_size = end - start; if received_size == expected_size { Ok(result) } else { Err(CheckedParseError::Size(SizeError { expected: expected_size, received: received_size, })) } } } /// Extension trait for factories that ensures fixed size write. pub trait CheckedSerialize<'a, Ctx: Context<'a>>: Serializable + FixedSizeObject<'a, Ctx> { /// Verify proper write length using [`Serializer::tell`]. fn serialize_checked(&self, serializer: &mut dyn Serializer) -> Result<(), SizeError> { let expected_size = self.size(); let start = serializer.tell(); self.serialize(serializer); let end = serializer.tell(); let received_size = end - start; if received_size == expected_size { Ok(()) } else { Err(SizeError { expected: expected_size, received: received_size, }) } } } impl<'a, Ctx: Context<'a>, F: FixedSizeFactory<'a, Ctx>> CheckedParse<'a, Ctx> for F {} impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx> + FixedSizeObject<'a, Ctx>> CheckedSerialize<'a, Ctx> for A { } /// Trait useful for objects which aren't influenced by /// whether some other type (for example, a generic parameter type) /// is fixed-size or not. pub trait AlwaysFixedSize { fn _size(&self) -> usize; } impl<'a, Ctx: Context<'a>, F: AlwaysFixedSize + InlineableFactory<'a, Ctx>> FixedSizeFactory<'a, Ctx> for F { fn size(&self) -> usize { self._size() } } /// Compile-time analogue of [`AlwaysFixedSize`]. pub trait AlwaysConstSize { const _SIZE: usize; } impl AlwaysFixedSize for F { fn _size(&self) -> usize { Self::_SIZE } } impl<'a, Ctx: Context<'a>, F: AlwaysConstSize + InlineableFactory<'a, Ctx>> ConstSizeFactory<'a, Ctx> for F { const SIZE: usize = Self::_SIZE; } impl<'a, Ctx: Context<'a>, F: Factory<'a, Ctx>> InlineableFactory<'a, Ctx> for PointFactory { fn extension_error(&self, tail: &[u8]) -> Self::ParseError { PointParseError::WrongLength(HASH_SIZE + tail.len()) } } impl AlwaysConstSize for PointFactory { const _SIZE: usize = HASH_SIZE; }