//! Generic implementation for objects that are structured //! like a pair of two other objects. use std::ops::Deref; use crate::rstd::*; use super::*; /// Trait to represent serialisation of object's data. /// Due to serialisation being [Context]-free in RADN, /// this functionality is in a separate trait. pub trait StaticPairSerializable { /// First element's type. Must equal [`StaticPair::A`]. type SA: Serializable; /// Second element's type. Must equal [`StaticPair::B`]. type SB: Serializable; /// Borrow both elements. fn elements(&self) -> (&Self::SA, &Self::SB); fn into_elements(self) -> (Self::SA, Self::SB); } /// Trait to be implemented on object data. /// /// [`StaticPair::FA`]/[`StaticPair::FB`] are required members /// for clarity and [`StaticPair`]'s simpler implementation /// at the cost of having to specify two extra fields. /// /// Note: [`StaticPair::FA`] requires [`InliningFactory`] be implemented. pub trait StaticPair<'a, Ctx: Context<'a>>: 'a + StaticPairSerializable { /// [Factory] data. May, depending on the usecase, include factory (factories) on the element(s). type FactoryData: 'a + Send + Sync + Clone; /// First element's type. Must equal [`StaticPairSerializable::SA`]. type A: MentionableBase<'a, Ctx, Fctr = Self::FA>; /// Second element's type. Must equal [`StaticPairSerializable::SB`]. type B: MentionableBase<'a, Ctx, Fctr = Self::FB>; /// First element's factory. type FA: FactoryBase<'a, Ctx, Mtbl = Self::A> + InliningFactory<'a, Ctx>; /// Second element's factory. type FB: FactoryBase<'a, Ctx, Mtbl = Self::B> + ParseMode; /// See [`FactoryBase::ParseError`]. type ParseError: 'a + Error; /// Borrow both elements' factories. fn factories(factory_data: &Self::FactoryData) -> (&Self::FA, &Self::FB); /// Construct the object from the elements. fn from_parsed(factory_data: &Self::FactoryData, a: Self::A, b: Self::B) -> Self; /// Regularise the error returned while parsing the first element. fn from_error_a( factory_data: &Self::FactoryData, error: ParseError<'a, Ctx, Self::FA>, ) -> Self::ParseError; /// Regularise the error returned while parsing the second element. fn from_error_b( factory_data: &Self::FactoryData, error: ParseError<'a, Ctx, Self::FB>, ) -> Self::ParseError; /// Derive factory data from the object data. fn factory_data(&self) -> Self::FactoryData; } /// Generic implementation of a [Mentionable] for [StaticPair]s. pub struct StaticPairObject { pair: SP, } impl From for StaticPairObject { fn from(value: SP) -> Self { StaticPairObject { pair: value } } } impl AsRef for StaticPairObject { fn as_ref(&self) -> &SP { &self.pair } } impl Deref for StaticPairObject { type Target = SP; fn deref(&self) -> &Self::Target { &self.pair } } /// Generic implementation of a [Factory] for [StaticPair]s. pub struct StaticPairFactory<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> { factory_data: SP::FactoryData, } impl Serializable for StaticPairObject { fn serialize(&self, serializer: &mut dyn Serializer) { let (a, b) = self.pair.elements(); a.serialize(serializer); b.serialize(serializer); } } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> MentionableBase<'a, Ctx> for StaticPairObject { type Fctr = StaticPairFactory<'a, Ctx, SP>; fn factory(&self) -> Self::Fctr { StaticPairFactory { factory_data: self.pair.factory_data(), } } } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> MentionableTop<'a, Ctx> for StaticPairObject where SP::A: Mentionable<'a, Ctx>, SP::B: Mentionable<'a, Ctx>, { fn points_typed(&self, points: &mut impl PointsVisitor<'a, Ctx>) { let (a, b) = self.pair.elements(); a.points_typed(points); b.points_typed(points); } } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> Clone for StaticPairFactory<'a, Ctx, SP> { fn clone(&self) -> Self { Self { factory_data: self.factory_data.clone(), } } } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> FactoryBase<'a, Ctx> for StaticPairFactory<'a, Ctx, SP> { type Mtbl = StaticPairObject; type ParseError = SP::ParseError; } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> ImplMode for StaticPairFactory<'a, Ctx, SP> { type Mode = ::Mode; } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> QRegularFactory<'a, Ctx> for StaticPairFactory<'a, Ctx, SP> where SP::FB: RegularFactory<'a, Ctx>, { fn qrdeserialize(&self, inctx: impl InCtx<'a, Ctx>) -> ParseResult<'a, Ctx, Self> { let (fa, fb) = SP::factories(&self.factory_data); let (a, inctx) = fa .ideserialize(inctx) .map_err(|e| SP::from_error_a(&self.factory_data, e))?; let b = fb .rdeserialize(inctx) .map_err(|e| SP::from_error_b(&self.factory_data, e))?; Ok(StaticPairObject { pair: SP::from_parsed(&self.factory_data, a, b), }) } fn qrextend(&self, mentionable: Self::Mtbl, tail: &[u8]) -> ParseResult<'a, Ctx, Self> { let (_, fb) = SP::factories(&self.factory_data); let (a, b) = mentionable.pair.into_elements(); match fb.rextend(b, tail) { Ok(b) => Ok(SP::from_parsed(&self.factory_data, a, b).into()), Err(e) => Err(SP::from_error_b(&self.factory_data, e)), } } } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> InliningFactory<'a, Ctx> for StaticPairFactory<'a, Ctx, SP> where SP::FB: InliningFactory<'a, Ctx>, { fn extension_error(&self, tail: &[u8]) -> Self::ParseError { let (_, fb) = SP::factories(&self.factory_data); SP::from_error_b(&self.factory_data, fb.extension_error(tail)) } fn ideserialize>(&self, inctx: I) -> IParseResult<'a, Ctx, Self, I> { let (fa, fb) = SP::factories(&self.factory_data); let (a, inctx) = fa .ideserialize(inctx) .map_err(|e| SP::from_error_a(&self.factory_data, e))?; let (b, inctx) = fb .ideserialize(inctx) .map_err(|e| SP::from_error_b(&self.factory_data, e))?; Ok(( StaticPairObject { pair: SP::from_parsed(&self.factory_data, a, b), }, inctx, )) } } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> FixedSizeFactory<'a, Ctx> for StaticPairFactory<'a, Ctx, SP> where SP::FA: FixedSizeFactory<'a, Ctx>, SP::FB: FixedSizeFactory<'a, Ctx>, { fn size(&self) -> usize { let (fa, fb) = SP::factories(&self.factory_data); fa.size() + fb.size() } } impl<'a, Ctx: Context<'a>, SP: StaticPair<'a, Ctx>> ConstSizeFactory<'a, Ctx> for StaticPairFactory<'a, Ctx, SP> where SP::FA: ConstSizeFactory<'a, Ctx>, SP::FB: ConstSizeFactory<'a, Ctx>, { const SIZE: usize = SP::FA::SIZE + SP::FB::SIZE; }