//! 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: MentionableBase<'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: MentionableBase<'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: MentionableBase<'a, Ctx>> MentionableBase<'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(), }, } } } impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> MentionableTop<'a, Ctx> for Nullable<'a, Ctx, A> { fn points_typed(&self, points: &mut impl PointsVisitor<'a, Ctx>) { match self { Self::Null(_) => {} Self::NotNull(point) => points.visit(point), } } } impl<'a, Ctx: Context<'a>, F: FactoryBase<'a, Ctx>> FactoryBase<'a, Ctx> for NullableFactory { type Mtbl = Nullable<'a, Ctx, F::Mtbl>; type ParseError = PointParseError; } impl ImplMode for NullableFactory { type Mode = InliningMode; } 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: MentionableBase<'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<'a, Ctx: Context<'a>, F: FactoryBase<'a, Ctx>> CInliningFactory<'a, Ctx> for NullableFactory { fn cextension_error(&self, tail: &[u8]) -> Self::ParseError { PointParseError::WrongLength(HASH_SIZE + tail.len()) } fn cideserialize>(&self, inctx: I) -> IParseResult<'a, Ctx, Self, I> { let factory = self.factory.clone(); let (address, inctx) = inctx.icnext_address(|slice| PointParseError::from(slice))?; Ok(( if address.point == HASH_ZEROS { Nullable::Null(factory) } else { Nullable::NotNull(Point::from_address(address, factory, inctx.iresolver())) }, inctx, )) } } 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()) } }