radn-rs/src/rstd/nullable.rs

146 lines
4.3 KiB
Rust

//! 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<F> {
/// 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<A::Fctr>;
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<F> {
type Mtbl = Nullable<'a, Ctx, F::Mtbl>;
type ParseError = PointParseError;
}
impl<F> ImplMode for NullableFactory<F> {
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<A>) -> 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<F> NullableFactory<F> {
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<F>
{
fn cextension_error(&self, tail: &[u8]) -> Self::ParseError {
PointParseError::WrongLength(HASH_SIZE + tail.len())
}
fn cideserialize<I: InCtx<'a, Ctx>>(&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<F> AlwaysConstSize for NullableFactory<F> {
const _SIZE: usize = HASH_SIZE;
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> From<Rc<A>> for Nullable<'a, Ctx, A> {
fn from(value: Rc<A>) -> Self {
Self::from_mentionable(value)
}
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> From<A> for Nullable<'a, Ctx, A> {
fn from(value: A) -> Self {
Self::from_mentionable(value.into())
}
}