ibind/IState
This commit is contained in:
parent
8a3f9ac25f
commit
eb153d6396
22
src/core.rs
22
src/core.rs
@ -19,20 +19,21 @@ pub trait Context {
|
||||
}
|
||||
|
||||
/// Failure yielded by [`Origin`].
|
||||
#[derive(Debug)]
|
||||
pub enum ResolutionError<L, P> {
|
||||
Lookup(L),
|
||||
Parse(P),
|
||||
}
|
||||
|
||||
/// Result yielded by [`Origin`].
|
||||
pub type ResolutionResult<'a, Ctx, A> = Result<
|
||||
Rc<A>,
|
||||
ResolutionError<
|
||||
<Ctx as Context>::LookupError<'a>,
|
||||
<<A as Mentionable<'a, Ctx>>::Fctr as Factory<'a, Ctx>>::ParseError,
|
||||
>,
|
||||
/// See [`ResolutionResult`].
|
||||
pub type ResolutionFailure<'a, Ctx, A> = ResolutionError<
|
||||
<Ctx as Context>::LookupError<'a>,
|
||||
<<A as Mentionable<'a, Ctx>>::Fctr as Factory<'a, Ctx>>::ParseError,
|
||||
>;
|
||||
|
||||
/// Result yielded by [`Origin`].
|
||||
pub type ResolutionResult<'a, Ctx, A> = Result<Rc<A>, ResolutionFailure<'a, Ctx, A>>;
|
||||
|
||||
/// Helper alias for [`WeakFunctor::F`] of [`Context::T`].
|
||||
pub type Wrapped<'a, Ctx, A> = <<Ctx as Context>::T as WeakFunctor>::F<'a, A>;
|
||||
|
||||
@ -98,8 +99,11 @@ pub trait Mentionable<'a, Ctx: 'a + Context>: 'a + Serializable {
|
||||
}
|
||||
|
||||
/// Short-hand for the type of values returned by [`Resolver::resolve`].
|
||||
pub type HashResolution<'a, Ctx> =
|
||||
Wrapped<'a, Ctx, Result<(Vec<u8>, Rc<dyn Resolver<'a, Ctx>>), <Ctx as Context>::LookupError<'a>>>;
|
||||
pub type HashResolution<'a, Ctx> = Wrapped<
|
||||
'a,
|
||||
Ctx,
|
||||
Result<(Vec<u8>, Rc<dyn Resolver<'a, Ctx>>), <Ctx as Context>::LookupError<'a>>,
|
||||
>;
|
||||
|
||||
/// Value accepted by [`Resolver::resolve`]. Includes index to make it order-sensitive.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
20
src/func.rs
20
src/func.rs
@ -147,6 +147,14 @@ pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTu
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents iteration state.
|
||||
pub enum IState<A, B> {
|
||||
/// Loop running.
|
||||
Pending(A),
|
||||
/// Loop finished.
|
||||
Done(B),
|
||||
}
|
||||
|
||||
/// Equivalent of Haskell's `Monad`.
|
||||
///
|
||||
/// <https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Monad.html>
|
||||
@ -159,6 +167,18 @@ pub trait Monad: Applicative {
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Kleisli category composition special case used to represent loops.
|
||||
/// Included for optimisation and clarity.
|
||||
/// Generally, [`Monad::bind`] should be enough implement it.
|
||||
/// See [`classes::stackless::StacklessClass::ibind`] for a generic, though less-than ideal, blanket implementation.
|
||||
/// On practice, you shouldn't be using [`Monad::bind`]/[`Applicative::pure`]/[`Functor::fmap`] here.
|
||||
fn ibind<'a, A: 'a, B: 'a>(
|
||||
a: A,
|
||||
f: impl 'a + FnMut(A) -> Self::F<'a, IState<A, B>>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Equivalent of Haskell's `join`.
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A>
|
||||
where
|
||||
|
@ -84,6 +84,23 @@ impl Monad for FutureClass {
|
||||
Box::pin(async { f(fa.await).await })
|
||||
}
|
||||
|
||||
fn ibind<'a, A: 'a, B: 'a>(
|
||||
mut a: A,
|
||||
mut f: impl 'a + FnMut(A) -> Self::F<'a, IState<A, B>>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Box::pin(async move {
|
||||
loop {
|
||||
match f(a).await {
|
||||
IState::Pending(next_a) => a = next_a,
|
||||
IState::Done(b) => return b,
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> {
|
||||
Box::pin(async { ffa.await.await })
|
||||
}
|
||||
|
@ -83,6 +83,21 @@ impl Monad for LazyClass {
|
||||
Box::new(|| f(fa())())
|
||||
}
|
||||
|
||||
fn ibind<'a, A: 'a, B: 'a>(
|
||||
mut a: A,
|
||||
mut f: impl 'a + FnMut(A) -> Self::F<'a, IState<A, B>>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Box::new(move || loop {
|
||||
match f(a)() {
|
||||
IState::Pending(next_a) => a = next_a,
|
||||
IState::Done(b) => return b,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> {
|
||||
Box::new(|| ffa()())
|
||||
}
|
||||
|
@ -77,6 +77,21 @@ impl Monad for OptionClass {
|
||||
f(fa?)
|
||||
}
|
||||
|
||||
fn ibind<'a, A: 'a, B: 'a>(
|
||||
mut a: A,
|
||||
mut f: impl 'a + FnMut(A) -> Self::F<'a, IState<A, B>>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
loop {
|
||||
match f(a)? {
|
||||
IState::Pending(next_a) => a = next_a,
|
||||
IState::Done(b) => return Self::pure(b),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> {
|
||||
ffa?
|
||||
}
|
||||
|
@ -77,6 +77,21 @@ impl<E> Monad for ResultClass<E> {
|
||||
f(fa?)
|
||||
}
|
||||
|
||||
fn ibind<'a, A: 'a, B: 'a>(
|
||||
mut a: A,
|
||||
mut f: impl 'a + FnMut(A) -> Self::F<'a, IState<A, B>>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
loop {
|
||||
match f(a)? {
|
||||
IState::Pending(next_a) => a = next_a,
|
||||
IState::Done(b) => return Self::pure(b),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> {
|
||||
ffa?
|
||||
}
|
||||
|
@ -77,6 +77,21 @@ impl Monad for SoloClass {
|
||||
f(fa)
|
||||
}
|
||||
|
||||
fn ibind<'a, A: 'a, B: 'a>(
|
||||
mut a: A,
|
||||
mut f: impl 'a + FnMut(A) -> Self::F<'a, IState<A, B>>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
loop {
|
||||
match f(a) {
|
||||
IState::Pending(next_a) => a = next_a,
|
||||
IState::Done(b) => return b,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> {
|
||||
ffa
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ impl<'a, A: 'a> From<A> for Stackless<'a, A> {
|
||||
}
|
||||
}
|
||||
|
||||
struct StacklessClass;
|
||||
pub struct StacklessClass;
|
||||
|
||||
impl WeakFunctor for StacklessClass {
|
||||
type F<'a, A: 'a> = Stackless<'a, A>;
|
||||
@ -221,6 +221,22 @@ impl Monad for StacklessClass {
|
||||
{
|
||||
fa.bind(f)
|
||||
}
|
||||
|
||||
fn ibind<'a, A: 'a, B: 'a>(
|
||||
a: A,
|
||||
mut f: impl 'a + FnMut(A) -> Self::F<'a, IState<A, B>>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Self::pure(a).bind(move |a| {
|
||||
f(a).bind(|state| match state {
|
||||
IState::Pending(a) => Self::ibind(a, f),
|
||||
IState::Done(b) => Self::pure(b),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A>
|
||||
where
|
||||
Self::F<'a, A>: 'a,
|
||||
|
@ -344,11 +344,11 @@ impl Display for Address {
|
||||
}
|
||||
|
||||
pub trait ExtSerializable: Serializable {
|
||||
fn to_vec(&self) -> Vec<u8>;
|
||||
fn bytes(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl<S: Serializable> ExtSerializable for S {
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
fn bytes(&self) -> Vec<u8> {
|
||||
let mut vec = Vec::new();
|
||||
self.serialize(&mut vec);
|
||||
vec
|
||||
|
@ -3,12 +3,12 @@ use std::fmt::Display;
|
||||
use crate::std::atomic::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Plain {
|
||||
pub data: Vec<u8>,
|
||||
pub struct Plain {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PlainParseError {}
|
||||
pub enum PlainParseError {}
|
||||
|
||||
impl Display for PlainParseError {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@ -28,9 +28,7 @@ impl Atomic for Plain {
|
||||
type ParseError = PlainParseError;
|
||||
|
||||
fn deserialize(deserializer: &mut dyn Deserializer) -> Result<Self, Self::ParseError> {
|
||||
Ok(Plain {
|
||||
data: deserializer.read_all().into(),
|
||||
})
|
||||
Ok(Plain::from_slice(deserializer.read_all()))
|
||||
}
|
||||
|
||||
fn unexpected_tail(tail: &[u8]) -> Self::ParseError {
|
||||
@ -41,15 +39,21 @@ impl Atomic for Plain {
|
||||
}
|
||||
}
|
||||
|
||||
impl Plain {
|
||||
pub fn from_slice(slice: &[u8]) -> Self {
|
||||
Plain { data: slice.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::std::*;
|
||||
use super::*;
|
||||
use crate::std::*;
|
||||
|
||||
#[test]
|
||||
fn test_plain() -> Result<(), PlainParseError> {
|
||||
let plain = Plain::parse_slice(b"slice")?;
|
||||
assert_eq!(plain.to_vec().as_slice(), b"slice");
|
||||
assert_eq!(plain.bytes().as_slice(), b"slice");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,13 @@ struct CastResolver<'a, Ctx: 'a + Context> {
|
||||
points: Vec<Point<'a, Ctx, TypelessMentionable<'a, Ctx>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CastError<'a> {
|
||||
Typeless(TypelessError<'a>),
|
||||
AddressIndexOutOfBounds(usize),
|
||||
AddressIndexOutOfBounds {
|
||||
index: usize,
|
||||
length: usize,
|
||||
},
|
||||
AddressPointMismatch {
|
||||
expected: Hash,
|
||||
received: Hash,
|
||||
@ -15,6 +19,30 @@ pub enum CastError<'a> {
|
||||
},
|
||||
}
|
||||
|
||||
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<CastError<'a>>,
|
||||
@ -23,7 +51,11 @@ where
|
||||
let point = match self.points.get(address.index) {
|
||||
Some(point) => point,
|
||||
None => {
|
||||
return Ctx::T::pure(Err(CastError::AddressIndexOutOfBounds(address.index).into()));
|
||||
return Ctx::T::pure(Err(CastError::AddressIndexOutOfBounds {
|
||||
index: address.index,
|
||||
length: self.points.len(),
|
||||
}
|
||||
.into()));
|
||||
}
|
||||
};
|
||||
if point.point != address.point {
|
||||
@ -40,7 +72,7 @@ where
|
||||
let resolver: Rc<dyn Resolver<'a, Ctx>> = Rc::new(CastResolver {
|
||||
points: mentionable.points(),
|
||||
});
|
||||
Ok((mentionable.to_vec(), resolver))
|
||||
Ok((mentionable.bytes(), resolver))
|
||||
}
|
||||
Err(error) => Err(match error {
|
||||
ResolutionError::Lookup(lookup_error) => lookup_error,
|
||||
@ -60,7 +92,7 @@ where
|
||||
{
|
||||
pub fn cast<A: Mentionable<'a, Ctx>>(&self, factory: A::Fctr) -> CastResult<'a, Ctx, A> {
|
||||
factory.parse_slice(
|
||||
&self.to_vec(),
|
||||
&self.bytes(),
|
||||
Rc::new(CastResolver {
|
||||
points: self.points(),
|
||||
}),
|
||||
|
@ -4,7 +4,7 @@ use crate::std::*;
|
||||
|
||||
pub struct StackNode<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> {
|
||||
pub rest: Stack<'a, Ctx, A>,
|
||||
pub element: A,
|
||||
pub element: Rc<A>,
|
||||
}
|
||||
|
||||
type Stack<'a, Ctx, A> = Nullable<'a, Ctx, StackNode<'a, Ctx, A>>;
|
||||
@ -90,12 +90,14 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx>
|
||||
return Err(StackParseError::Point(ppe));
|
||||
}
|
||||
};
|
||||
let element = match self.element_factory.deserialize(deserializer, resolver) {
|
||||
Ok(element) => element,
|
||||
Err(epe) => {
|
||||
return Err(StackParseError::Element(epe));
|
||||
}
|
||||
};
|
||||
let element = Rc::new(
|
||||
match self.element_factory.deserialize(deserializer, resolver) {
|
||||
Ok(element) => element,
|
||||
Err(epe) => {
|
||||
return Err(StackParseError::Element(epe));
|
||||
}
|
||||
},
|
||||
);
|
||||
Ok(StackNode { rest, element })
|
||||
}
|
||||
|
||||
@ -104,10 +106,17 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx>
|
||||
}
|
||||
}
|
||||
|
||||
pub type StackFaiure<'a, Ctx, A> = ResolutionFailure<'a, Ctx, StackNode<'a, Ctx, A>>;
|
||||
|
||||
pub type StackVecResult<'a, Ctx, A> = Result<Vec<Rc<A>>, StackFaiure<'a, Ctx, A>>;
|
||||
|
||||
pub type StackVecWrapped<'a, Ctx, A> = Wrapped<'a, Ctx, StackVecResult<'a, Ctx, A>>;
|
||||
|
||||
pub trait ExtStack<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>>: Mentionable<'a, Ctx> {
|
||||
fn empty(factory: A::Fctr) -> Self;
|
||||
fn f(factory: A::Fctr) -> Self::Fctr;
|
||||
fn add(self, element: A) -> Self;
|
||||
fn vec(self) -> StackVecWrapped<'a, Ctx, A>;
|
||||
}
|
||||
|
||||
impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> ExtStack<'a, Ctx, A> for Stack<'a, Ctx, A> {
|
||||
@ -121,9 +130,77 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> ExtStack<'a, Ctx, A> for St
|
||||
Nullable::NotNull(
|
||||
StackNode {
|
||||
rest: self,
|
||||
element,
|
||||
element: Rc::new(element),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
fn vec(self) -> StackVecWrapped<'a, Ctx, A> {
|
||||
Ctx::T::ibind((vec![], self), |(mut vec, stack)| match stack {
|
||||
Nullable::Null(_) => Ctx::T::pure(IState::Done(Ok(vec))),
|
||||
Nullable::NotNull(point) => Ctx::T::fmap(
|
||||
|resolved| {
|
||||
let node = match resolved {
|
||||
Ok(node) => node,
|
||||
Err(error) => {
|
||||
return IState::Done(Err(error));
|
||||
}
|
||||
};
|
||||
vec.push(node.element.clone());
|
||||
IState::Pending((vec, node.rest.clone()))
|
||||
},
|
||||
point.resolve(),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::std::atomic::plain::*;
|
||||
use crate::std::atomic::*;
|
||||
use crate::testing::*;
|
||||
|
||||
type T = Stack<'static, TestContext, Plain>;
|
||||
|
||||
fn unstack(stack: &T) -> Result<Vec<Vec<u8>>, StackFaiure<'static, TestContext, Plain>> {
|
||||
Ok(stack
|
||||
.clone()
|
||||
.vec()?
|
||||
.iter()
|
||||
.map(|plain| plain.bytes())
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn make_stack() -> T {
|
||||
let stack: T = Stack::empty(Plain::f());
|
||||
|
||||
let stack: T = stack.add(Plain::from_slice(b"A0"));
|
||||
let stack: T = stack.add(Plain::from_slice(b"B1"));
|
||||
let stack: T = stack.add(Plain::from_slice(b"C2"));
|
||||
|
||||
stack
|
||||
}
|
||||
|
||||
fn validate_stack(stack: &T) {
|
||||
let mut vec: Vec<Vec<u8>> = unstack(stack).unwrap();
|
||||
vec.reverse();
|
||||
assert_eq!(vec, vec![b"A0".to_vec(), b"B1".to_vec(), b"C2".to_vec()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stack() -> Result<(), StackFaiure<'static, TestContext, Plain>> {
|
||||
let stack: T = make_stack();
|
||||
|
||||
validate_stack(&stack);
|
||||
|
||||
let stack: T = TypelessMentionable::from_typed(stack.into())
|
||||
.cast(Stack::f(Plain::f()))
|
||||
.expect("cast failed");
|
||||
|
||||
validate_stack(&stack);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -65,13 +65,13 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx>
|
||||
deserializer: &mut dyn Deserializer,
|
||||
resolver: std::rc::Rc<dyn Resolver<'a, Ctx>>,
|
||||
) -> ParseResult<'a, Ctx, Self> {
|
||||
let point = deserializer.read_n_const::<HASH_SIZE>()?;
|
||||
let mut addresses = Addresses::start();
|
||||
let factory = self.factory.clone();
|
||||
Ok(if point == HASH_ZEROS {
|
||||
let address = addresses.next(deserializer)?;
|
||||
Ok(if address.point == HASH_ZEROS {
|
||||
Nullable::Null(factory)
|
||||
} else {
|
||||
let mut addresses = Addresses::start();
|
||||
Nullable::NotNull(addresses.next_point(deserializer, resolver, factory)?)
|
||||
Nullable::NotNull(Point::from_point(address, factory, resolver))
|
||||
})
|
||||
}
|
||||
|
||||
@ -86,10 +86,8 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Nullable<'a, Ctx, Nullable<
|
||||
Nullable::Null(nullable_factory) => {
|
||||
let NullableFactory { factory } = nullable_factory;
|
||||
Ctx::T::pure(Ok(Rc::new(Nullable::Null(factory.clone()))))
|
||||
},
|
||||
Nullable::NotNull(point) => {
|
||||
point.resolve()
|
||||
},
|
||||
}
|
||||
Nullable::NotNull(point) => point.resolve(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,3 +97,12 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> NullableFactory<'a, Ctx, A>
|
||||
Self { factory }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Ctx: 'a + Context, A: Mentionable<'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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use std::fmt::Display;
|
||||
|
||||
use crate::core::*;
|
||||
use crate::func::*;
|
||||
use crate::std::cast::CastError;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub struct TestContext;
|
||||
@ -10,6 +11,7 @@ pub struct TestContext;
|
||||
#[derive(Debug)]
|
||||
pub enum TestLookupError<'a> {
|
||||
Typeless(TypelessError<'a>),
|
||||
Cast(CastError<'a>),
|
||||
EmptyResolverAccess(Address),
|
||||
}
|
||||
|
||||
@ -19,12 +21,21 @@ impl<'a> From<TypelessError<'a>> for TestLookupError<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<CastError<'a>> for TestLookupError<'a> {
|
||||
fn from(value: CastError<'a>) -> Self {
|
||||
Self::Cast(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for TestLookupError<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TestLookupError::Typeless(typeless_error) => {
|
||||
f.write_fmt(format_args!("typeless lookup failure: {}", typeless_error))
|
||||
}
|
||||
TestLookupError::Cast(cast_error) => {
|
||||
f.write_fmt(format_args!("cast failure: {}", cast_error))
|
||||
}
|
||||
TestLookupError::EmptyResolverAccess(address) => f.write_fmt(format_args!(
|
||||
"accessed an empty resolved at address {}.",
|
||||
address
|
||||
|
Loading…
Reference in New Issue
Block a user