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