//! Utilities to convert from typeless instances defined in [`crate::rcore`]. //! See [`TypelessMentionable::cast`]/[`Point::cast`]. //! //! p.s. the implementation is horrific. 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 [`Factory::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 [`Mentionable::points_typed`] and/or [`Mentionable::topology`]. AddressIndexOutOfBounds { index: usize, length: usize, }, /// See [`CastError::AddressIndexOutOfBounds`]. AddressPointMismatch { expected: Hash, received: Hash, index: usize, }, } 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> where Ctx::LookupError: From>, { Ctx::pure(Err(self.into())) } } impl<'a, Ctx: Context<'a>> CastResolver<'a, Ctx> { fn rc(points: Vec>>) -> Rc> where Ctx::LookupError: From>, { 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 cast_resolved<'a, Ctx: Context<'a>>( resolved: ResolutionResult<'a, Ctx, TypelessMentionable<'a, Ctx>>, ) -> HashResolutionResult<'a, Ctx> where Ctx::LookupError: From>, { match resolved { Ok(mentionable) => Ok(( mentionable.bytes(), CastResolver::rc(mentionable.points_vec()), )), Err(error) => Err(match error { ResolutionError::Lookup(lookup_error) => lookup_error, ResolutionError::Parse(parse_error) => CastError::Typeless(parse_error).into(), }), } } impl<'a, Ctx: Context<'a>> Resolver<'a, Ctx> for CastResolver<'a, Ctx> where Ctx::LookupError: From>, { 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(cast_resolved) } } /// Returned by [`TypelessMentionable::cast`]. pub type CastResult<'a, Ctx, A> = Result>; impl<'a, Ctx: Context<'a>> TypelessMentionable<'a, Ctx> where Ctx::LookupError: From>, { pub fn cast_full>( &self, factory: A::Fctr, map_resolver: impl FnOnce(Rc>) -> Rc>, ) -> CastResult<'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) -> CastResult<'a, Ctx, A> { self.cast_full(factory, identity) } } fn cast_resolve<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>>( typeless_origin: Rc>>, factory: A::Fctr, ) -> Resolution<'a, Ctx, A> where Ctx::LookupError: From>, { Ctx::fmap( typeless_origin.clone().resolve(), move |resolved| match resolved { Ok(typeless_mentionable) => match typeless_mentionable.cast(factory) { Ok(mentionable) => Ok(Rc::new(mentionable)), Err(parse_error) => Err(ResolutionError::Parse(parse_error)), }, Err(error) => Err(ResolutionError::Lookup(match error { ResolutionError::Lookup(lookup_error) => lookup_error, ResolutionError::Parse(parse_error) => CastError::Typeless(parse_error).into(), })), }, ) } fn cast_origin<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>>( typeless_origin: Rc>>, factory: A::Fctr, ) -> Rc> where Ctx::LookupError: From>, { 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: Context<'a>> Point<'a, Ctx, TypelessMentionable<'a, Ctx>> where Ctx::LookupError: From>, { 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), } } }