Diffstat (limited to 'src/protocol.rs')
-rw-r--r--src/protocol.rs295
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)