//! Utilities to convert from typeless instances defined in [`crate::core`]. //! See [`TypelessMentionable::cast`]/[`Point::cast`]. //! //! p.s. the implementation is horrific. use std::convert::identity; use crate::core::*; use super::{typeless::*, *}; struct CastResolver<'a, Ctx: 'a + Context> { 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`] 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 { CastError::Typeless(typeless_error) => { f.write_fmt(format_args!("typeless cast error: {}", typeless_error)) } CastError::AddressIndexOutOfBounds { index, length } => f.write_fmt(format_args!( "cast index out of bound: {}>={}", index, length )), CastError::AddressPointMismatch { expected, received, index, } => f.write_fmt(format_args!( "address mismatch at index {}: {}>={}", index, hex::encode(expected), hex::encode(received), )), } } } impl<'a, Ctx: 'a + Context> Resolver<'a, Ctx> for CastResolver<'a, Ctx> where Ctx::LookupError<'a>: From>, { fn resolve(self: Rc, address: Address) -> HashResolution<'a, Ctx> { let point = match self.points.get(address.index) { Some(point) => point, None => { return Ctx::T::pure(Err(CastError::AddressIndexOutOfBounds { index: address.index, length: self.points.len(), } .into())); } }; if point.point != address.point { return Ctx::T::pure(Err(CastError::AddressPointMismatch { expected: point.point, received: address.point, index: address.index, } .into())); } Ctx::T::fmap( |resolved| match resolved { Ok(mentionable) => { let resolver: Rc> = Rc::new(CastResolver { points: mentionable.points_vec(), }); Ok((mentionable.bytes(), resolver)) } Err(error) => Err(match error { ResolutionError::Lookup(lookup_error) => lookup_error, ResolutionError::Parse(parse_error) => CastError::Typeless(parse_error).into(), }), }, point.resolve(), ) } } /// Returned by [`TypelessMentionable::cast`]. pub type CastResult<'a, Ctx, A> = Result>::Fctr as Factory<'a, Ctx>>::ParseError>; impl<'a, Ctx: Context> TypelessMentionable<'a, Ctx> where Ctx::LookupError<'a>: 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(Rc::new(CastResolver { points: 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) } } impl<'a, Ctx: Context> Point<'a, Ctx, TypelessMentionable<'a, Ctx>> where Ctx::LookupError<'a>: From>, { /// See [`TypelessMentionable::cast`] pub fn cast>(&self, factory: A::Fctr) -> Point<'a, Ctx, A> { let typeless_origin = self.origin.clone(); let origin: Rc> = wrapped_origin(factory.clone(), move || { let factory = factory.clone(); Ctx::T::fmap( 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() } })), }, typeless_origin.clone().resolve(), ) }); Point { point: self.point, origin, } } }