diff --git a/src/core.rs b/src/core.rs index 932e9c2..0b048a2 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,20 +1,30 @@ +//! Core module for ADN functionality. +//! Brings [`Mentionable`]/[`Factory`]/[`Origin`] concepts from the original implementation in Python. +//! Allows for more generic behaviour via [`Context`], as opposed to original async-only. + use std::{error::Error, fmt::Display, rc::Rc}; use crate::func::*; +/// Execution context. pub trait Context { + /// Type to provide for [Monad]ic representation of computation, mostly that of resolution ([`Resolution`]). type T: Monad; + /// Type to represent resolution errors mainly arising in [`Resolver::resolve`]. type LookupError: Error; + /// Get [type@Hash] of a slice, mostly for use in [`Point`]. fn hash(s: &[u8]) -> Hash; } +/// Failure yielded by [`Origin`]. pub enum ResolutionError { Lookup(L), Parse(P), } +/// Result yielded by [`Origin`]. pub type ResolutionResult<'a, Ctx, A> = Result< Rc, ResolutionError< @@ -23,18 +33,30 @@ pub type ResolutionResult<'a, Ctx, A> = Result< >, >; +/// Helper alias for [`WeakFunctor::F`] of [`Context::T`]. pub type Wrapped<'a, Ctx, A> = <::T as WeakFunctor>::F<'a, A>; +/// Wrapped result returned by [`Origin`]. pub type Resolution<'a, Ctx, A> = Wrapped<'a, Ctx, ResolutionResult<'a, Ctx, A>>; +/// Fixed [type@Hash] length. pub const HASH_SIZE: usize = 32; +/// Zeroed out array of the same length as [`type@Hash`]. +/// Used in [`crate::std::nullable`]. pub const HASH_ZEROS: [u8; HASH_SIZE] = [0; HASH_SIZE]; +/// For use in [`Point`]/[`Address`]. pub type Hash = [u8; HASH_SIZE]; +/// Serialisation mechanism that is chosen over bytestring concatenation +/// both for performance and simplicity. +/// +/// See [`Serializable`]. pub trait Serializer { + /// Writes bytes from a slice. Should advance value of [`Serializer::tell()`] by `buf.len()`. fn write(&mut self, buf: &[u8]); + /// Current position of the stream. It's expected to be used by [`crate::std::inlining`] fn tell(&self) -> usize; } @@ -48,14 +70,22 @@ impl Serializer for Vec { } } +/// See [`Serializer`]. pub trait Serializable { + /// Expected to use [`Serializer::write`]. fn serialize(&self, serializer: &mut dyn Serializer); } +/// Fundamental trait for ADN objects. pub trait Mentionable<'a, Ctx: 'a + Context>: 'a + Serializable { + /// Type of the associated factory. type Fctr: Factory<'a, Ctx, Mtbl = Self>; + /// Value of the associated factory. fn factory(&self) -> Self::Fctr; + /// See implementation for the definition. + /// Hash of all the references' points concatenated, ordered, non-unique. + /// Used for walking over object trees to ensure two objects with different references don't collide. fn topology(&self) -> Hash { let mut vec = Vec::new(); for point in self.points() { @@ -63,47 +93,70 @@ pub trait Mentionable<'a, Ctx: 'a + Context>: 'a + Serializable { } Ctx::hash(&vec) } + /// References ([Point]s) to other objects. fn points(&self) -> Vec>>; } +/// Short-hand for the type of values returned by [`Resolver::resolve`]. pub type HashResolution<'a, Ctx> = Wrapped<'a, Ctx, Result<(Vec, Rc>), ::LookupError>>; +/// Value accepted by [`Resolver::resolve`]. Includes index to make it order-sensitive. #[derive(Clone, Copy)] pub struct Address { pub point: Hash, + /// Index of the point in the [`Mentionable::points()`]. pub index: usize, } +/// Trait representing the "rainbow table" behaviour. pub trait Resolver<'a, Ctx: 'a + Context>: 'a { + /// Successfully returned value should be the inverse of the point passed + /// with topology header ([`Mentionable::topology()`]) omitted. fn resolve(self: Rc, address: Address) -> HashResolution<'a, Ctx>; } +/// Trait representing a readable stream used for parsing. +/// +/// See [`Serializer`], [`Factory::deserialize`]. pub trait Deserializer { + /// Read at most `n` bytes. fn read_n(&mut self, n: usize) -> &[u8]; + /// Read til the end of the stream. fn read_all(&mut self) -> &[u8]; + /// See [`Serializer::tell`]. fn tell(&self) -> usize; } +/// Short-hand for the type of vaalues returned by [`Factory::deserialize`]. pub type ParseResult<'a, Ctx, F> = Result<>::Mtbl, >::ParseError>; +/// Trait representing deserialisation rules for [Mentionable]s. +/// Crucial for [`TypelessMentionable`] and therefore [`Mentionable::points`]. pub trait Factory<'a, Ctx: 'a + Context>: Clone + 'a { + /// Type of the associated objects. type Mtbl: Mentionable<'a, Ctx, Fctr = Self>; + /// Type of an error that [`Factory::deserialize`] can fail with. type ParseError: 'a + Error; + /// See [`Deserializer`], [`Resolver`]. fn deserialize( &self, deserializer: &mut dyn Deserializer, resolver: Rc>, ) -> ParseResult<'a, Ctx, Self>; - + /// Called by finite stream parsers if there's any data left. fn unexpected_tail(&self, tail: &[u8]) -> Self::ParseError; } +/// Represents a potentially resolvable [`Mentionable`]. pub trait Origin<'a, Ctx: 'a + Context>: 'a { + /// Type of the associated object. type Mtbl: Mentionable<'a, Ctx>; + /// Value of the associated factory. fn factory(&self) -> >::Fctr; + /// Try resolving the value. fn resolve(self: Rc) -> Resolution<'a, Ctx, Self::Mtbl>; } @@ -127,6 +180,7 @@ impl From for LocalOrigin { } } +/// The main way to represent a reference in ADN. pub struct Point<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> { pub point: Hash, pub origin: Rc>, @@ -156,22 +210,28 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Point<'a, Ctx, A> { Self::from_fields(point, Rc::new(origin)) } + /// See [`Origin::resolve`]. pub fn resolve(&self) -> Resolution<'a, Ctx, A> { self.origin.clone().resolve() } + + fn from_mentionable(mentionable: A) -> Self { + Self::from_values( + Ctx::hash(Self::prepare_bytes_for_hashing(&mentionable).as_slice()), + LocalOrigin::from(mentionable), + ) + } } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> From for Point<'a, Ctx, A> { fn from(value: A) -> Self { - Self::from_values( - Ctx::hash(Self::prepare_bytes_for_hashing(&value).as_slice()), - LocalOrigin::from(value), - ) + Self::from_mentionable(value) } } type TypelessSerialize<'a> = dyn 'a + Fn(&mut dyn Serializer); +/// See [`Mentionable::points`]. pub struct TypelessMentionable<'a, Ctx: 'a + Context> { t_serialize: Box>, t_factory: TypelessFactory<'a, Ctx>, @@ -186,6 +246,7 @@ type TypelessDeserialize<'a, Ctx> = type TypelessUnexpectedTail<'a> = dyn 'a + Fn(&[u8]) -> TypelessError<'a>; +/// See [`Mentionable::points`]/[`TypelessMentionable`]. pub struct TypelessFactory<'a, Ctx: 'a + Context> { t_deserialize: Rc>, t_unexpected_tail: Rc>, @@ -222,6 +283,7 @@ impl<'a, Ctx: 'a + Context> Clone for TypelessFactory<'a, Ctx> { } } +/// See [`Mentionable::points`]/[`TypelessFactory`]. #[derive(Debug)] pub struct TypelessError<'a>(Box); diff --git a/src/std.rs b/src/std.rs index 57bab19..c3866b3 100644 --- a/src/std.rs +++ b/src/std.rs @@ -84,6 +84,7 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Origin<'a, Ctx> for Wrapped } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Point<'a, Ctx, A> { + /// Typeless version of the point. Useful for [Mentionable::points] implementaion. pub fn typeless(&self) -> Point<'a, Ctx, TypelessMentionable<'a, Ctx>> { Point { point: self.point,