Diffstat (limited to 'src/protocol.rs')
| -rw-r--r-- | src/protocol.rs | 295 |
1 files changed, 105 insertions, 190 deletions
diff --git a/src/protocol.rs b/src/protocol.rs index a74e791..617cc4d 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,10 +1,10 @@ -use core::any::Any; +use core::any::{type_name, Any}; pub use any_hint::AnyHint; pub use any_visit::AnyVisit; pub use id::ProtocolId; -use crate::{Visitor, WalkerHints, WalkStatus}; +use crate::{ControlFlow, Visitor, WalkerHints}; /// A protocol between a walker and visitor. /// @@ -38,62 +38,15 @@ pub trait Protocol: Any { type Accessor<'walking, 'ctx: 'walking>; } -#[derive(Copy, Clone)] -pub struct ProtocolDescription { - id: fn() -> ProtocolId, - name: fn() -> &'static str, -} - -impl ProtocolDescription { - pub const fn of<P: Protocol>() -> Self { - Self { - id: || ProtocolId::of::<P>(), - name: || core::any::type_name::<P>(), - } - } - - pub fn id(&self) -> ProtocolId { - (self.id)() - } - - pub fn name(&self) -> &'static str { - (self.name)() - } - -} - -impl core::fmt::Display for ProtocolDescription { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.name()) - } -} - -impl core::fmt::Debug for ProtocolDescription { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("ProtocolDescription") - .field("id", &self.id()) - .field("name", &self.name()) - .finish() - } -} - -pub type HintOps<'walking, 'ctx, P> = - &'walking mut dyn Hint<'ctx, P>; - -pub type VisitOps<'walking, 'ctx, P> = - &'walking mut dyn Visit<'ctx, P>; - /// Protocol specific hint for a walker. +/// +/// A walker can implement any number of these for any protocols it supports. pub trait Hint<'ctx, P: Protocol> { /// Hint that protocol `P` should be used. /// /// After hinting a protocol, a walker should invoke only the same protocol on the visitor. /// This is not forced though. - fn hint( - &mut self, - visitor: &mut dyn Visitor<'ctx>, - hint: P::Hint<'ctx>, - ) -> WalkStatus; + fn hint(&mut self, visitor: &mut dyn Visitor<'ctx>, hint: P::Hint<'ctx>) -> ControlFlow; /// Any information the walker has for the protocol. /// @@ -103,123 +56,96 @@ pub trait Hint<'ctx, P: Protocol> { /// /// Most protocols will allow returning a value representing no knowledge is known by the /// walker. - fn known( - &mut self, - hint: &P::Hint<'ctx>, - ) -> P::Known<'ctx>; + fn known(&mut self, hint: &P::Hint<'ctx>) -> P::Known<'ctx>; } /// Protocol specific visit for a visitor. +/// +/// A visitor can implement any number of these for any protocols it supports. pub trait Visit<'ctx, P: Protocol> { /// Visit a value from the walker. - fn visit<'walking>( - &'walking mut self, - accessor: P::Accessor<'walking, 'ctx>, - ); + /// + /// The `accessor` will either be a concrete value or another walker. + /// This is dependant on the protocol `P`. The visitor can do whatever it wants + /// with the `accessor` during the function call. + fn visit(&mut self, accessor: P::Accessor<'_, 'ctx>) -> ControlFlow; } -pub trait ErrorWrongProtocol { - fn error_wrong_protocol<Expected: Protocol>(&mut self, got: ProtocolDescription); +#[derive(thiserror::Error, Debug)] +#[error("Expected Hint to be for `{expected}` but got one for `{got}`.")] +pub struct WalkerWrongProtocol { + pub got: &'static str, + pub expected: &'static str, } -pub trait ErrorMissingProtocol { - fn error_missing_protocol<P: Protocol>(&mut self); +#[derive(thiserror::Error, Debug)] +pub enum WalkerMissingProtocol { + #[error("Walker doesn't support the protocol `{0}`.")] + Missing(&'static str), + + #[error(transparent)] + Wrong(#[from] WalkerWrongProtocol), } -pub fn try_lookup_hint< - 'a, - 'ctx, - P: Protocol, - E: ErrorWrongProtocol, - H: ?Sized + WalkerHints<'ctx>, ->( - hints: &'a mut H, - error_report: &mut E, -) -> Option<HintOps<'a, 'ctx, P>> { - match hints.protocol(ProtocolId::of::<P>()) { - Some(protocol) => match protocol.downcast::<P>() { - Ok(hint) => Some(hint), - Err(hint) => { - error_report.error_wrong_protocol::<P>(hint.description()); - None - } - }, - None => None, - } +#[derive(thiserror::Error, Debug)] +#[error("Expected Visit to be for `{expected}` but got one for `{got}`.")] +pub struct VisitorWrongProtocol { + pub got: &'static str, + pub expected: &'static str, } -pub fn lookup_hint< - 'a, - 'ctx, - P: Protocol, - E: ErrorWrongProtocol + ErrorMissingProtocol, - H: ?Sized + WalkerHints<'ctx>, ->( - hints: &'a mut H, - error_report: &mut E, -) -> Option<HintOps<'a, 'ctx, P>> { +#[derive(thiserror::Error, Debug)] +pub enum VisitorMissingProtocol { + #[error("Visitor doesn't support the protocol `{0}`.")] + Missing(&'static str), + + #[error(transparent)] + Wrong(#[from] VisitorWrongProtocol), +} + +/// Try to lookup a [`Hint`] on a walker. +pub fn try_lookup_hint<'ctx, P: Protocol, H: ?Sized + WalkerHints<'ctx>>( + hints: &mut H, +) -> Result<Option<&mut dyn Hint<'ctx, P>>, WalkerWrongProtocol> { match hints.protocol(ProtocolId::of::<P>()) { Some(protocol) => match protocol.downcast::<P>() { - Ok(hint) => Some(hint), - Err(hint) => { - error_report.error_wrong_protocol::<P>(hint.description()); - None - } - }, - None => { - error_report.error_missing_protocol::<P>(); - None + Ok(hint) => Ok(Some(hint)), + Err(hint) => Err(WalkerWrongProtocol { + got: hint.protocol_type_name(), + expected: type_name::<P>(), + }), }, + None => Ok(None), } } -pub fn try_lookup_visit< - 'a, - 'ctx, - P: Protocol, - E: ErrorWrongProtocol, - V: ?Sized + Visitor<'ctx>, ->( - visitor: &'a mut V, - error_report: &mut E, -) -> Option<VisitOps<'a, 'ctx, P>> { - match visitor.protocol(ProtocolId::of::<P>()) { - Some(protocol) => match protocol.downcast::<P>() { - Ok(visit) => Some(visit), - Err(visit) => { - error_report.error_wrong_protocol::<P>(visit.description()); - None - } - }, - None => None, - } +pub fn lookup_hint<'ctx, P: Protocol, H: ?Sized + WalkerHints<'ctx>>( + hints: &mut H, +) -> Result<&mut dyn Hint<'ctx, P>, WalkerMissingProtocol> { + try_lookup_hint(hints)?.ok_or(WalkerMissingProtocol::Missing(type_name::<P>())) } -pub fn lookup_visit< - 'a, - 'ctx, - P: Protocol, - E: ErrorWrongProtocol + ErrorMissingProtocol, - V: ?Sized + Visitor<'ctx>, ->( - visitor: &'a mut V, - error_report: &mut E, -) -> Option<VisitOps<'a, 'ctx, P>> { +pub fn try_lookup_visit<'ctx, P: Protocol, V: ?Sized + Visitor<'ctx>>( + visitor: &mut V, +) -> Result<Option<&mut dyn Visit<'ctx, P>>, VisitorWrongProtocol> { match visitor.protocol(ProtocolId::of::<P>()) { Some(protocol) => match protocol.downcast::<P>() { - Ok(visit) => Some(visit), - Err(visit) => { - error_report.error_wrong_protocol::<P>(visit.description()); - None - } - }, - None => { - error_report.error_missing_protocol::<P>(); - None + Ok(visit) => Ok(Some(visit)), + Err(visit) => Err(VisitorWrongProtocol { + got: visit.protocol_type_name(), + expected: type_name::<P>(), + }), }, + None => Ok(None), } } +pub fn lookup_visit<'ctx, P: Protocol, V: ?Sized + Visitor<'ctx>>( + visitor: &mut V, +) -> Result<&mut dyn Visit<'ctx, P>, VisitorMissingProtocol> { + try_lookup_visit(visitor)?.ok_or(VisitorMissingProtocol::Missing(type_name::<P>())) +} + mod id { use super::Protocol; use core::any::TypeId; @@ -241,23 +167,27 @@ mod id { } mod any_hint { - use core::{any::Any, marker::PhantomData, mem::MaybeUninit}; + use core::{ + any::{type_name, Any}, + marker::PhantomData, + mem::MaybeUninit, + }; use crate::Hint; - use super::{Protocol, ProtocolDescription, ProtocolId}; + use super::{Protocol, ProtocolId}; /// Form of `Hint` without `P`. trait ErasedHint<'ctx>: Any {} /// Get the size of pointers to trait objects for this target. - const DYN_PTR_SIZE: usize = - core::mem::size_of::<&mut dyn ErasedHint<'static>>(); + const DYN_PTR_SIZE: usize = core::mem::size_of::<&mut dyn ErasedHint<'static>>(); /// Type erased form of `&'walking mut dyn Hint<'value, P, Err>` where `P` is erased. pub struct AnyHint<'walking, 'ctx> { /// ID of `P`. - id: ProtocolDescription, + id: ProtocolId, + name: &'static str, /// This field stores a `&'walking mut dyn Hint<'value, P, Err>`. fat_ptr: MaybeUninit<[u8; DYN_PTR_SIZE]>, @@ -266,17 +196,14 @@ mod any_hint { _marker: PhantomData<&'walking mut dyn ErasedHint<'ctx>>, } - impl<'walking, 'ctx> - AnyHint<'walking, 'ctx> - { + impl<'walking, 'ctx> AnyHint<'walking, 'ctx> { /// Erase the `P` in a hint. /// /// This allows returning a hint from a object safe method. - pub fn new<P: Protocol>( - visit: &'walking mut dyn Hint<'ctx, P>, - ) -> Self { + pub fn new<P: Protocol>(visit: &'walking mut dyn Hint<'ctx, P>) -> Self { Self { - id: ProtocolDescription::of::<P>(), + id: ProtocolId::of::<P>(), + name: type_name::<P>(), // SAFETY: A maybe uninit array of bytes can hold any pointer. // Additionally, transmute makes sure the size is correct. fat_ptr: unsafe { core::mem::transmute(visit) }, @@ -287,11 +214,8 @@ mod any_hint { /// Try to downcast the hint for the given protocol. /// /// If the hint is of the wrong type then `None` is returned. - pub fn downcast<P: Protocol>( - self, - ) -> Result<&'walking mut dyn Hint<'ctx, P>, Self> - { - if self.id.id() == ProtocolId::of::<P>() { + pub fn downcast<P: Protocol>(self) -> Result<&'walking mut dyn Hint<'ctx, P>, Self> { + if self.id == ProtocolId::of::<P>() { // SAFETY: Only `new` can make a value of this type, and it stores the ID of `P`. // If the IDs are equal then we can act like any and downcast back to the real // type. @@ -304,30 +228,34 @@ mod any_hint { } } - pub fn description(&self) -> ProtocolDescription { - self.id + pub fn protocol_type_name(&self) -> &'static str { + self.name } } } mod any_visit { - use core::{any::Any, marker::PhantomData, mem::MaybeUninit}; + use core::{ + any::{type_name, Any}, + marker::PhantomData, + mem::MaybeUninit, + }; use crate::Visit; - use super::{Protocol, ProtocolDescription, ProtocolId}; + use super::{Protocol, ProtocolId}; /// Form of `Visit` without `P`. trait ErasedVisit<'ctx>: Any {} /// Get the size of pointers to trait objects for this target. - const DYN_PTR_SIZE: usize = - core::mem::size_of::<&mut dyn ErasedVisit<'static>>(); + const DYN_PTR_SIZE: usize = core::mem::size_of::<&mut dyn ErasedVisit<'static>>(); /// Type erased form of `&'walking mut dyn Visit<'value, P, Err>` where `P` is erased. pub struct AnyVisit<'walking, 'ctx> { /// ID of `P`. - id: ProtocolDescription, + id: ProtocolId, + name: &'static str, /// This field stores a `&'walking mut dyn Visit<'value, P, Err>`. fat_ptr: MaybeUninit<[u8; DYN_PTR_SIZE]>, @@ -336,17 +264,14 @@ mod any_visit { _marker: PhantomData<&'walking mut dyn ErasedVisit<'ctx>>, } - impl<'walking, 'ctx> - AnyVisit<'walking, 'ctx> - { + impl<'walking, 'ctx> AnyVisit<'walking, 'ctx> { /// Erase the `P` in a Visit. /// /// This allows returning a Visit from a object safe method. - pub fn new<P: Protocol>( - visit: &'walking mut dyn Visit<'ctx, P>, - ) -> Self { + pub fn new<P: Protocol>(visit: &'walking mut dyn Visit<'ctx, P>) -> Self { Self { - id: ProtocolDescription::of::<P>(), + id: ProtocolId::of::<P>(), + name: type_name::<P>(), // SAFETY: A maybe uninit array of bytes can hold any pointer. // Additionally, transmute makes sure the size is correct. fat_ptr: unsafe { core::mem::transmute(visit) }, @@ -357,11 +282,8 @@ mod any_visit { /// Try to downcast the Visit for the given protocol. /// /// If the Visit is of the wrong type then `None` is returned. - pub fn downcast<P: Protocol>( - self, - ) -> Result<&'walking mut dyn Visit<'ctx, P>, Self> - { - if self.id.id() == ProtocolId::of::<P>() { + pub fn downcast<P: Protocol>(self) -> Result<&'walking mut dyn Visit<'ctx, P>, Self> { + if self.id == ProtocolId::of::<P>() { // SAFETY: Only `new` can make a value of this type, and it stores the ID of `P`. // If the IDs are equal then we can act like any and downcast back to the real // type. @@ -374,8 +296,8 @@ mod any_visit { } } - pub fn description(&self) -> ProtocolDescription { - self.id + pub fn protocol_type_name(&self) -> &'static str { + self.name } } } @@ -394,22 +316,15 @@ mod generic_example { fat_ptr: &'walking mut dyn Hint<'ctx, P>, } - impl<'walking, 'ctx, P: Protocol> - Generic<'walking, 'ctx, P> - { - pub fn new( - visit: &'walking mut dyn Hint<'ctx, P>, - ) -> Self { + impl<'walking, 'ctx, P: Protocol> Generic<'walking, 'ctx, P> { + pub fn new(visit: &'walking mut dyn Hint<'ctx, P>) -> Self { Self { id: ProtocolId::of::<P>(), fat_ptr: visit, } } - pub fn downcast( - self, - ) -> Result<&'walking mut dyn Hint<'ctx, P>, Self> - { + pub fn downcast(self) -> Result<&'walking mut dyn Hint<'ctx, P>, Self> { if self.id == ProtocolId::of::<P>() { // Notice how this is valid. Ok(self.fat_ptr) |