//! This module introduces [`Option`]-like concepts into RADN typesystem using [`Nullable`]. use crate::func::context::*; 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: Context<'a>, 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: Context<'a>, A: Mentionable<'a, Ctx>> Serializable for Nullable<'a, Ctx, A> { fn serialize(&self, serializer: &mut dyn Serializer) { serializer.write(match self { Self::Null(_) => &HASH_ZEROS, Self::NotNull(point) => &point.point, }) } } impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> Mentionable<'a, Ctx> for Nullable<'a, Ctx, A> { type Fctr = NullableFactory; fn factory(&self) -> Self::Fctr { match self { Self::Null(factory) => NullableFactory { factory: factory.clone(), }, Self::NotNull(point) => NullableFactory { factory: point.origin.factory(), }, } } fn points_typed(&self, points: &mut impl TakesPoints<'a, Ctx>) { match self { Self::Null(_) => {} Self::NotNull(point) => points.take(point), } } } impl<'a, Ctx: Context<'a>, 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 extend( &self, _mentionable: Self::Mtbl, tail: &[u8], ) -> Result { Err(PointParseError::WrongLength(HASH_SIZE + tail.len())) } } impl<'a, Ctx: Context<'a>, 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::pure(Ok(Rc::new(Nullable::Null(factory.clone())))) } Self::NotNull(point) => point.resolve(), } } } impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> Nullable<'a, Ctx, A> { fn from_mentionable(mentionable: Rc) -> Self { Self::NotNull(mentionable.into()) } pub fn as_ref(&self) -> Option<&Point<'a, Ctx, A>> { match self { Nullable::Null(_) => None, Nullable::NotNull(point) => Some(point), } } } impl NullableFactory { pub fn new(factory: F) -> Self { Self { factory } } } impl<'a, Ctx: Context<'a>, 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: Context<'a>, A: Mentionable<'a, Ctx>> From> for Nullable<'a, Ctx, A> { fn from(value: Rc) -> Self { Self::from_mentionable(value) } } impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> From for Nullable<'a, Ctx, A> { fn from(value: A) -> Self { Self::from_mentionable(value.into()) } }