//! This module introduces [`Option`]-like concepts into RADN typesystem using [`Nullable`]. use crate::rcore::*; use super::{inlining::*, point::*, *}; /// Nullable reference type. Made for use as a linking element in data structures. pub enum Nullable<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> { /// Unlike original Python implementation, stores the factory only in the null case. Null(A::Fctr), NotNull(Point<'a, Ctx, A>), } /// Nullable reference factory. #[derive(Clone)] pub struct NullableFactory { /// Factory of the referenced object. factory: F, } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Serializable for Nullable<'a, Ctx, A> { fn serialize(&self, serializer: &mut dyn Serializer) { serializer.write(match self { Nullable::Null(_) => &HASH_ZEROS, Nullable::NotNull(point) => &point.point, }) } } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Mentionable<'a, Ctx> for Nullable<'a, Ctx, A> { type Fctr = NullableFactory; fn factory(&self) -> Self::Fctr { match self { Nullable::Null(factory) => NullableFactory { factory: factory.clone(), }, Nullable::NotNull(point) => NullableFactory { factory: point.origin.factory(), }, } } fn points_typed(&self, points: &mut impl TakesPoints<'a, Ctx>) { match self { Nullable::Null(_) => {} Nullable::NotNull(point) => points.take(point), } } } impl<'a, Ctx: 'a + Context, F: Factory<'a, Ctx>> Factory<'a, Ctx> for NullableFactory { type Mtbl = Nullable<'a, Ctx, F::Mtbl>; type ParseError = PointParseError; fn deserialize( &self, deserializer: &mut dyn Deserializer, resolver: std::rc::Rc>, addresses: &mut Addresses, ) -> ParseResult<'a, Ctx, Self> { let factory = self.factory.clone(); let address = addresses.next(deserializer)?; Ok(if address.point == HASH_ZEROS { Nullable::Null(factory) } else { Nullable::NotNull(Point::from_address(address, factory, resolver)) }) } fn unexpected_tail(&self, tail: &[u8]) -> Self::ParseError { PointParseError::WrongLength(HASH_SIZE + tail.len()) } } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Nullable<'a, Ctx, Nullable<'a, Ctx, A>> { /// Reduce [Nullable] nesting. pub fn join(&self) -> Resolution<'a, Ctx, Nullable<'a, Ctx, A>> { match self { Self::Null(nullable_factory) => { let NullableFactory { factory } = nullable_factory; Ctx::T::pure(Ok(Rc::new(Nullable::Null(factory.clone())))) } Self::NotNull(point) => point.resolve(), } } } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Nullable<'a, Ctx, A> { fn from_mentionable(mentionable: Rc) -> Self { Self::NotNull(mentionable.into()) } } impl NullableFactory { pub fn new(factory: F) -> Self { Self { factory } } } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Clone for Nullable<'a, Ctx, A> { fn clone(&self) -> Self { match self { Self::Null(factory) => Self::Null(factory.clone()), Self::NotNull(point) => Self::NotNull(point.clone()), } } } impl AlwaysConstSize for NullableFactory { const _SIZE: usize = HASH_SIZE; } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> From> for Nullable<'a, Ctx, A> { fn from(value: Rc) -> Self { Self::from_mentionable(value) } } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> From for Nullable<'a, Ctx, A> { fn from(value: A) -> Self { Self::from_mentionable(value.into()) } }