//! Utilities to convert from typeless instances defined in [`rcore`]. //! See [`TypelessMentionable::cast`]/[`Point::cast`]. //! //! p.s. the implementation is horrific. //! //! [`rcore`]: crate::rcore use std::convert::identity; use crate::func::context::*; use crate::rcore::*; use super::{typeless::*, wrapped_origin::*, *}; struct CastResolver<'a, Ctx: Context<'a>> { points: Vec>>, } /// Used to represent errors that might arise during resolution/parsion. /// Is expected to be classified as [`Context::LookupError`] rather than [`FactoryBase::ParseError`]. /// [`CastError::AddressIndexOutOfBounds`] and [`CastError::AddressPointMismatch`] /// variants generally shouldn't happen. #[derive(Debug)] pub enum CastError<'a> { Typeless(TypelessError<'a>), /// If you don't know what that means, it's a good idea to [`panic!`]. /// Happens due to internal resolver using indices rather than `point`s. /// This error usually indicates inconsistent behaviour /// of [`MentionableTop::points_typed`] and/or [`MentionableTop::topology`]. AddressIndexOutOfBounds { index: usize, length: usize, }, /// See [`CastError::AddressIndexOutOfBounds`]. AddressPointMismatch { expected: Hash, received: Hash, index: usize, }, } pub trait CastCtx<'a>: Context<'a> { fn from_cast(cast_error: CastError<'a>) -> Self::LookupError; } impl<'a, Ctx: Context<'a>> CastCtx<'a> for Ctx where Self::LookupError: From>, { fn from_cast(cast_error: CastError<'a>) -> Self::LookupError { cast_error.into() } } impl<'a> Display for CastError<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Typeless(typeless_error) => { write!(f, "typeless cast error: {}", typeless_error) } Self::AddressIndexOutOfBounds { index, length } => { write!(f, "cast index out of bound: {}>={}", index, length) } Self::AddressPointMismatch { expected, received, index, } => write!( f, "address mismatch at index {}: {}!={}", index, hex::encode(expected), hex::encode(received), ), } } } impl<'a> CastError<'a> { fn pure>(self) -> HashResolution<'a, Ctx> { Ctx::pure(Err(Ctx::from_cast(self))) } } impl<'a, Ctx: CastCtx<'a>> CastResolver<'a, Ctx> { fn rc(points: Vec>>) -> Rc> { Rc::new(Self { points }) } fn _get_point( &self, index: usize, ) -> Result<&Point<'a, Ctx, TypelessMentionable<'a, Ctx>>, CastError<'a>> { match self.points.get(index) { Some(point) => Ok(point), None => Err(CastError::AddressIndexOutOfBounds { index, length: self.points.len(), }), } } fn _validate_point( &self, point: &Point<'a, Ctx, TypelessMentionable<'a, Ctx>>, address: Address, ) -> Result<(), CastError<'a>> { if point.point == address.point { Ok(()) } else { Err(CastError::AddressPointMismatch { expected: point.point, received: address.point, index: address.index, }) } } fn get_point( &self, address: Address, ) -> Result<&Point<'a, Ctx, TypelessMentionable<'a, Ctx>>, CastError<'a>> { let point = self._get_point(address.index)?; self._validate_point(point, address)?; Ok(point) } } fn map_resolved<'a, Ctx: CastCtx<'a>>( resolved: ResolutionResult<'a, Ctx, TypelessMentionable<'a, Ctx>>, ) -> HashResolutionResult<'a, Ctx> { resolved.map_err(lookup_error::).map(|mentionable| { ( mentionable.bytes(), CastResolver::rc(mentionable.points_vec()), ) }) } impl<'a, Ctx: CastCtx<'a>> Resolver<'a, Ctx> for CastResolver<'a, Ctx> { fn resolve(self: Rc, address: Address) -> HashResolution<'a, Ctx> { let point = match self.get_point(address) { Ok(point) => point, Err(cast_error) => return cast_error.pure::(), }; point.resolve_map(map_resolved) } } impl<'a, Ctx: CastCtx<'a>> TypelessMentionable<'a, Ctx> { pub fn cast_full>( &self, factory: A::Fctr, map_resolver: impl FnOnce(Rc>) -> Rc>, ) -> ParseResultA<'a, Ctx, A> { factory.parse_slice( &self.bytes(), &map_resolver(CastResolver::rc(self.points_vec())), ) } /// Re-parse the object as if it is of a specific type. Has potential to break topology. pub fn cast>(&self, factory: A::Fctr) -> ParseResultA<'a, Ctx, A> { self.cast_full(factory, identity) } } fn lookup_error<'a, Ctx: CastCtx<'a>>( error: ResolutionFailure<'a, Ctx, TypelessMentionable<'a, Ctx>>, ) -> Ctx::LookupError { match error { ResolutionError::Lookup(lookup_error) => lookup_error, ResolutionError::Parse(parse_error) => Ctx::from_cast(CastError::Typeless(parse_error)), } } fn cast_resolved<'a, Ctx: CastCtx<'a>, A: Mentionable<'a, Ctx>>( resolved: ResolutionResult<'a, Ctx, TypelessMentionable<'a, Ctx>>, factory: A::Fctr, ) -> ResolutionResult<'a, Ctx, A> { resolved .map_err(lookup_error::) .map_err(ResolutionError::Lookup)? .cast(factory) .map_err(ResolutionError::Parse) .map(Rc::new) } fn cast_resolve<'a, Ctx: CastCtx<'a>, A: Mentionable<'a, Ctx>>( typeless_origin: Rc>>, factory: A::Fctr, ) -> Resolution<'a, Ctx, A> { typeless_origin.resolve_map(|resolved| cast_resolved(resolved, factory)) } fn cast_origin<'a, Ctx: CastCtx<'a>, A: Mentionable<'a, Ctx>>( typeless_origin: Rc>>, factory: A::Fctr, ) -> Rc> { let origin_rb = typeless_origin.clone(); wrapped_origin( factory.clone(), move || cast_resolve(typeless_origin.clone(), factory.clone()), move || origin_rb.clone().resolve_bytes(), ) } impl<'a, Ctx: CastCtx<'a>> Point<'a, Ctx, TypelessMentionable<'a, Ctx>> { fn cast_origin>( &self, factory: A::Fctr, ) -> Rc> { let typeless_origin = self.origin.clone(); cast_origin(typeless_origin, factory) } /// See [`TypelessMentionable::cast`] pub fn cast>(&self, factory: A::Fctr) -> Point<'a, Ctx, A> { Point { point: self.point, origin: self.cast_origin(factory), } } }