Diffstat (limited to 'src/protocol.rs')
| -rw-r--r-- | src/protocol.rs | 282 |
1 files changed, 157 insertions, 125 deletions
diff --git a/src/protocol.rs b/src/protocol.rs index 01336db..a74e791 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -4,7 +4,7 @@ pub use any_hint::AnyHint; pub use any_visit::AnyVisit; pub use id::ProtocolId; -use crate::{error::VisitorError, HintGiven, UniError, Visitor, WalkerError, WalkerHints}; +use crate::{Visitor, WalkerHints, WalkStatus}; /// A protocol between a walker and visitor. /// @@ -16,18 +16,18 @@ use crate::{error::VisitorError, HintGiven, UniError, Visitor, WalkerError, Walk /// /// A protocol never needs to be a value, so it's recommended to use an uninhabited type /// like an empty enum to represent them. -pub trait Protocol<'value, 'ctx: 'value>: Any { +pub trait Protocol: Any { /// Arbitrary hint metadata for the protocol. /// /// This allows a visitor to give extra information to a walker when hinting to /// use the protocol. - type Hint; + type Hint<'ctx>; /// Data known about the protocol before hinting. /// /// This allows a walker to give extra information to a visitor to make a /// better decision when selecting a hint. - type Known; + type Known<'ctx>; /// The visit data the walker provides to the visitor. /// @@ -35,27 +35,20 @@ pub trait Protocol<'value, 'ctx: 'value>: Any { /// The '`walking` lifetime is only alive while the walker is walking. /// As such, a visitor cannot borrow from a `'walking` lifetime containing type /// for it's output. - type Accessor<'walking, WalkerErr: 'value, VisitorErr: 'value> - where - 'value: 'walking; - - fn description() -> Option<&'static str> { - None - } + type Accessor<'walking, 'ctx: 'walking>; } +#[derive(Copy, Clone)] pub struct ProtocolDescription { id: fn() -> ProtocolId, name: fn() -> &'static str, - description: fn() -> Option<&'static str>, } impl ProtocolDescription { - pub const fn of<'value, 'ctx: 'value, P: Protocol<'value, 'ctx>>() -> Self { + pub const fn of<P: Protocol>() -> Self { Self { id: || ProtocolId::of::<P>(), name: || core::any::type_name::<P>(), - description: P::description, } } @@ -67,17 +60,11 @@ impl ProtocolDescription { (self.name)() } - pub fn description(&self) -> Option<&'static str> { - (self.description)() - } } impl core::fmt::Display for ProtocolDescription { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self.description() { - Some(description) => write!(f, "{} - {}", self.name(), description), - None => write!(f, "{}", self.name()), - } + write!(f, "{}", self.name()) } } @@ -86,43 +73,27 @@ impl core::fmt::Debug for ProtocolDescription { f.debug_struct("ProtocolDescription") .field("id", &self.id()) .field("name", &self.name()) - .field("description", &self.description()) .finish() } } -#[macro_export] -#[doc(hidden)] -macro_rules! protocol_list { - ($($protocol:ty),* $(,)?) => {{ - [ - $($crate::protocol::ProtocolDescription::of::<$protocol>()),* - ].into_iter() - }} -} - -#[doc(inline)] -pub use protocol_list; - -pub type HintOps<'walking, 'value, 'ctx, P, WalkerErr, VisitorErr> = - &'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>; +pub type HintOps<'walking, 'ctx, P> = + &'walking mut dyn Hint<'ctx, P>; -pub type VisitOps<'walking, 'value, 'ctx, P, WalkerErr, VisitorErr> = - &'walking mut dyn Visit<'value, 'ctx, P, WalkerErr, Error = VisitorErr>; +pub type VisitOps<'walking, 'ctx, P> = + &'walking mut dyn Visit<'ctx, P>; /// Protocol specific hint for a walker. -pub trait Hint<'value, 'ctx: 'value, P: Protocol<'value, 'ctx>, VisitorErr> { - type Error; - +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<'value, 'ctx, Self::Error, Error = VisitorErr>, - hint: P::Hint, - ) -> Result<HintGiven, UniError<Self::Error, VisitorErr>>; + visitor: &mut dyn Visitor<'ctx>, + hint: P::Hint<'ctx>, + ) -> WalkStatus; /// Any information the walker has for the protocol. /// @@ -132,61 +103,120 @@ pub trait Hint<'value, 'ctx: 'value, P: Protocol<'value, 'ctx>, VisitorErr> { /// /// Most protocols will allow returning a value representing no knowledge is known by the /// walker. - fn known(&mut self, hint: &P::Hint) -> Result<P::Known, Self::Error>; + fn known( + &mut self, + hint: &P::Hint<'ctx>, + ) -> P::Known<'ctx>; } /// Protocol specific visit for a visitor. -pub trait Visit<'value, 'ctx: 'value, P: Protocol<'value, 'ctx>, WalkerErr> { - type Error; - +pub trait Visit<'ctx, P: Protocol> { /// Visit a value from the walker. fn visit<'walking>( &'walking mut self, - accessor: P::Accessor<'walking, WalkerErr, Self::Error>, - ) -> Result<(), UniError<WalkerErr, Self::Error>>; + accessor: P::Accessor<'walking, 'ctx>, + ); +} + +pub trait ErrorWrongProtocol { + fn error_wrong_protocol<Expected: Protocol>(&mut self, got: ProtocolDescription); +} + +pub trait ErrorMissingProtocol { + fn error_missing_protocol<P: Protocol>(&mut self); +} + +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, + } } -/// Helper to lookup a protocol's hint on a walker. -/// -/// A `Ok(None)` is returned if the walker doesn't support the protocol. -/// A `Err(...)` is returned if the walker returns a hint instance not for the protocol. pub fn lookup_hint< - 'value, - 'ctx: 'value, - P: Protocol<'value, 'ctx>, - VisitorErr: VisitorError<'value, 'ctx>, - H: ?Sized + WalkerHints<'value, 'ctx, VisitorErr>, + 'a, + 'ctx, + P: Protocol, + E: ErrorWrongProtocol + ErrorMissingProtocol, + H: ?Sized + WalkerHints<'ctx>, >( - hints: &mut H, -) -> Result<Option<HintOps<'_, 'value, 'ctx, P, H::Error, VisitorErr>>, VisitorErr> { + hints: &'a mut H, + error_report: &mut E, +) -> Option<HintOps<'a, 'ctx, P>> { match hints.protocol(ProtocolId::of::<P>()) { - Some(hint) => match hint.downcast::<P>() { - Ok(hint) => Ok(Some(hint)), - Err(_) => Err(VisitorErr::wrong_hint::<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 }, - None => Ok(None), } } -/// Helper to lookup a protocol's visit on a visitor. -/// -/// A `Ok(None)` is returned if the visitor doesn't support the protocol. -/// A `Err(...)` is returned if the visitor returns a visit instance not for the protocol. +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_visit< - 'value, + 'a, 'ctx, - P: Protocol<'value, 'ctx>, - WalkerErr: WalkerError<'value, 'ctx>, - V: ?Sized + Visitor<'value, 'ctx, WalkerErr>, + P: Protocol, + E: ErrorWrongProtocol + ErrorMissingProtocol, + V: ?Sized + Visitor<'ctx>, >( - visitor: &mut V, -) -> Result<Option<VisitOps<'_, 'value, 'ctx, P, WalkerErr, V::Error>>, WalkerErr> { + visitor: &'a mut V, + error_report: &mut E, +) -> Option<VisitOps<'a, 'ctx, P>> { match visitor.protocol(ProtocolId::of::<P>()) { - Some(visit) => match visit.downcast::<P>() { - Ok(visit) => Ok(Some(visit)), - Err(_) => Err(WalkerError::wrong_visit::<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 }, - None => Ok(None), } } @@ -204,7 +234,7 @@ mod id { /// Get the ID of a protocol. /// /// The ID is unique per protocol. - pub fn of<'value, 'ctx: 'value, P: Protocol<'value, 'ctx>>() -> Self { + pub fn of<P: Protocol>() -> Self { Self(TypeId::of::<P>()) } } @@ -215,38 +245,38 @@ mod any_hint { use crate::Hint; - use super::{Protocol, ProtocolId}; + use super::{Protocol, ProtocolDescription, ProtocolId}; /// Form of `Hint` without `P`. - trait ErasedHint<'value, 'ctx: 'value, WalkerErr, VisitorErr>: Any {} + 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, 'static, (), ()>>(); + 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, 'value: 'walking, 'ctx: 'value, WalkerErr, VisitorErr> { + pub struct AnyHint<'walking, 'ctx> { /// ID of `P`. - id: ProtocolId, + id: ProtocolDescription, /// This field stores a `&'walking mut dyn Hint<'value, P, Err>`. fat_ptr: MaybeUninit<[u8; DYN_PTR_SIZE]>, /// Mimick what we actually store with a trait without `P`. - _marker: PhantomData<&'walking mut dyn ErasedHint<'value, 'ctx, VisitorErr, WalkerErr>>, + _marker: PhantomData<&'walking mut dyn ErasedHint<'ctx>>, } - impl<'walking, 'value, 'ctx: 'value, WalkerErr, VisitorErr> - AnyHint<'walking, 'value, 'ctx, WalkerErr, VisitorErr> + 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<'value, 'ctx>>( - visit: &'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, + pub fn new<P: Protocol>( + visit: &'walking mut dyn Hint<'ctx, P>, ) -> Self { Self { - id: ProtocolId::of::<P>(), + id: ProtocolDescription::of::<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) }, @@ -257,11 +287,11 @@ 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<'value, 'ctx>>( + pub fn downcast<P: Protocol>( self, - ) -> Result<&'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, Self> + ) -> Result<&'walking mut dyn Hint<'ctx, P>, Self> { - if self.id == ProtocolId::of::<P>() { + if self.id.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. @@ -273,6 +303,10 @@ mod any_hint { Err(self) } } + + pub fn description(&self) -> ProtocolDescription { + self.id + } } } @@ -281,38 +315,38 @@ mod any_visit { use crate::Visit; - use super::{Protocol, ProtocolId}; + use super::{Protocol, ProtocolDescription, ProtocolId}; /// Form of `Visit` without `P`. - trait ErasedVisit<'value, 'ctx: 'value, WalkerErr, VisitorErr>: Any {} + 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, 'static, (), ()>>(); + 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, 'value: 'walking, 'ctx: 'value, WalkerErr, VisitorErr> { + pub struct AnyVisit<'walking, 'ctx> { /// ID of `P`. - id: ProtocolId, + id: ProtocolDescription, /// This field stores a `&'walking mut dyn Visit<'value, P, Err>`. fat_ptr: MaybeUninit<[u8; DYN_PTR_SIZE]>, /// Mimick what we actually store with a trait without `P`. - _marker: PhantomData<&'walking mut dyn ErasedVisit<'value, 'ctx, WalkerErr, VisitorErr>>, + _marker: PhantomData<&'walking mut dyn ErasedVisit<'ctx>>, } - impl<'walking, 'value: 'walking, 'ctx: 'value, WalkerErr, VisitorErr> - AnyVisit<'walking, 'value, 'ctx, WalkerErr, VisitorErr> + impl<'walking, 'ctx> + AnyVisit<'walking, 'ctx> { - /// Erase the `P` in a visit. + /// Erase the `P` in a Visit. /// - /// This allows returning a visit from a object safe method. - pub fn new<P: Protocol<'value, 'ctx>>( - visit: &'walking mut dyn Visit<'value, 'ctx, P, WalkerErr, Error = VisitorErr>, + /// This allows returning a Visit from a object safe method. + pub fn new<P: Protocol>( + visit: &'walking mut dyn Visit<'ctx, P>, ) -> Self { Self { - id: ProtocolId::of::<P>(), + id: ProtocolDescription::of::<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) }, @@ -320,14 +354,14 @@ mod any_visit { } } - /// Try to downcast the visit for the given protocol. + /// 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<'value, 'ctx>>( + /// If the Visit is of the wrong type then `None` is returned. + pub fn downcast<P: Protocol>( self, - ) -> Result<&'walking mut dyn Visit<'value, 'ctx, P, WalkerErr, Error = VisitorErr>, Self> + ) -> Result<&'walking mut dyn Visit<'ctx, P>, Self> { - if self.id == ProtocolId::of::<P>() { + if self.id.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. @@ -339,6 +373,10 @@ mod any_visit { Err(self) } } + + pub fn description(&self) -> ProtocolDescription { + self.id + } } } @@ -351,22 +389,16 @@ mod generic_example { use super::{Protocol, ProtocolId}; - pub struct Generic<'walking, 'value: 'walking, 'ctx: 'value, P, WalkerErr, VisitorErr> { + pub struct Generic<'walking, 'ctx, P> { id: ProtocolId, - fat_ptr: &'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, + fat_ptr: &'walking mut dyn Hint<'ctx, P>, } - impl< - 'walking, - 'value: 'walking, - 'ctx: 'value, - P: Protocol<'value, 'ctx>, - WalkerErr, - VisitorErr, - > Generic<'walking, 'value, 'ctx, P, WalkerErr, VisitorErr> + impl<'walking, 'ctx, P: Protocol> + Generic<'walking, 'ctx, P> { pub fn new( - visit: &'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, + visit: &'walking mut dyn Hint<'ctx, P>, ) -> Self { Self { id: ProtocolId::of::<P>(), @@ -376,7 +408,7 @@ mod generic_example { pub fn downcast( self, - ) -> Result<&'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, Self> + ) -> Result<&'walking mut dyn Hint<'ctx, P>, Self> { if self.id == ProtocolId::of::<P>() { // Notice how this is valid. |