Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs272
1 files changed, 63 insertions, 209 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 0fdad32..f74cd44 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,56 +1,34 @@
-#![no_std]
+#![cfg_attr(not(feature = "std"), no_std)]
+#[cfg(feature = "alloc")]
+extern crate alloc;
+
+pub mod build;
+pub mod error;
pub mod impls;
pub mod protocol;
pub mod protocols;
+pub mod transform;
+pub mod walk;
-use core::fmt::Display;
+// use buildable::Buildable;
+use error::{UniError, WalkerError};
+use protocol::{AnyHint, AnyVisit, Hint, ProtocolDescription, ProtocolId, Visit};
-use protocol::{AnyHint, AnyVisit, Protocol, ProtocolId};
+// pub use buildable::Buildable;
+// pub use walkable::{Walkable, WalkableMut, WalkableRef};
-/// Trait for all walker error types.
+/// Token value showing a hint was given.
///
-/// This gives an interface for visitors to generate their own errors.
-/// `'value` is the lifetime of the source value which the error may borrow from.
-pub trait Error<'value>: Sized + Display + 'value {
- /// Create a custom error.
- fn custom<T: Display>(message: T) -> Self;
-
- /// Create a custom error from a static string message.
- ///
- /// This is useful for error types that can't store arbitrary strings.
- /// The default impl forwards to `Self::custom`.
- fn custom_static_str(message: &'static str) -> Self {
- Self::custom(message)
- }
-
- /// Helper for making an error when a visitor is missing the
- /// visit for a protocol it hinted.
- fn missing_visit<P: Protocol<'value>>() -> Self {
- Self::custom_static_str("visitor is missing expected protocol")
- }
-}
-
+/// This is used by [`Visitor::request_hint`] to tell the walker if a hint
+/// was given or not. A `Some(HintGiven)` means the visitor gave a hint by
+/// calling a [`Hint::hint`] method. A `None` means the visitor did not
+/// give a hint to the walker for what protocol to use.
+///
+/// [`Hint::hint`] methods return an instance of this type so [`Visitor::request_hint`]
+/// can just return that instance.
+#[must_use]
#[derive(Debug)]
-pub struct BasicError(pub &'static str);
-
-impl Display for BasicError {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- f.write_str(self.0)
- }
-}
-
-impl<'value> Error<'value> for BasicError {
- fn custom<T: Display>(_message: T) -> Self {
- Self("basic error")
- }
-
- fn custom_static_str(message: &'static str) -> Self {
- Self(message)
- }
-}
-
-/// Token value showing a hint was given.
pub struct HintGiven;
/// Status of a walker after walking using a visitor.
@@ -68,9 +46,9 @@ pub enum WalkStatus {
}
/// Walker over a value with lifetime `'value`.
-pub trait Walker<'value> {
+pub trait Walker<'value, 'ctx: 'value, VisitorErr> {
/// Error type the walker generates.
- type Error: Error<'value>;
+ type Error;
/// Walk the walker over the value.
///
@@ -83,37 +61,49 @@ pub trait Walker<'value> {
/// [`Visitor::request_hint`] before using walker specific logic to pick a protocol.
fn walk(
&mut self,
- visitor: &mut dyn Visitor<'value, Self::Error>,
- ) -> Result<WalkStatus, Self::Error> {
- // Request that the visitor give us a hint of what protocol to use.
- match visitor.request_hint(self.hints())? {
- Some(HintGiven) => Ok(WalkStatus::Done),
- None => Err(<Self::Error as Error>::custom("walker needs a hint")),
- }
- }
+ visitor: &mut dyn Visitor<'value, 'ctx, Self::Error, Error = VisitorErr>,
+ ) -> Result<WalkStatus, UniError<Self::Error, VisitorErr>>;
- /// Get the hints the walker supports.
- ///
- /// The hint lookup is seperate from [`Walker`] so that [`Visitor::request_hint`] can't
- /// easily cause a infinite loop be calling [`Walker::walk`].
- fn hints(&mut self) -> &mut dyn Hints<'value, Self::Error>;
+ fn all_protocols() -> impl Iterator<Item = ProtocolDescription>
+ where
+ Self: Sized;
+}
+
+pub fn walk_with_hints<'value, 'ctx: 'value, VisitorErr, H: WalkerHints<'value, 'ctx, VisitorErr>>(
+ hints: &mut H,
+ visitor: &mut dyn Visitor<'value, 'ctx, H::Error, Error = VisitorErr>,
+) -> Result<WalkStatus, UniError<H::Error, VisitorErr>>
+where
+ H::Error: WalkerError<'value, 'ctx>,
+{
+ // Request that the visitor give us a hint of what protocol to use.
+ match visitor.request_hint(hints, true)? {
+ Some(HintGiven) => Ok(WalkStatus::Done),
+ None => Err(UniError::Walker(
+ <H::Error as WalkerError<'value, 'ctx>>::needs_hint(),
+ )),
+ }
}
/// Hint lookup for a walker.
-pub trait Hints<'value, Err> {
+pub trait WalkerHints<'value, 'ctx: 'value, VisitorErr> {
+ type Error;
+
/// Query the walker for a given protocol.
///
/// If the walker doesn't support the protocol then a `None` is returned.
/// Note, a walker can return `None` if it can't handle a hint of the protocol for the given
/// value.
- fn protocol(&mut self, id: ProtocolId) -> Option<AnyHint<'_, 'value, Err>> {
- let _ = id;
- None
- }
+ fn protocol(
+ &mut self,
+ id: ProtocolId,
+ ) -> Option<AnyHint<'_, 'value, 'ctx, Self::Error, VisitorErr>>;
}
/// Visitor over a value to be built.
-pub trait Visitor<'value, Err> {
+pub trait Visitor<'value, 'ctx: 'value, WalkerErr> {
+ type Error;
+
/// Request the visitor hint what protocol to use.
///
/// It is not recommended to call this while in a protocol hint as a walker.
@@ -125,159 +115,23 @@ pub trait Visitor<'value, Err> {
/// A return value of `Ok(None)` means no hint was given to the walker.
fn request_hint(
&mut self,
- hints: &mut dyn Hints<'value, Err>,
- ) -> Result<Option<HintGiven>, Err> {
+ hints: &mut dyn WalkerHints<'value, 'ctx, Self::Error, Error = WalkerErr>,
+ need_hint: bool,
+ ) -> Result<Option<HintGiven>, UniError<WalkerErr, Self::Error>> {
let _ = hints;
+ let _ = need_hint;
Ok(None)
}
/// Query the visitor for a given protocol.
///
/// If the visitor doesn't support the protocol then a `None` is returned.
- fn protocol(&mut self, id: ProtocolId) -> Option<AnyVisit<'_, 'value, Err>> {
- let _ = id;
- None
- }
-}
-
-pub type HintOps<'walking, 'value, P, Err> = &'walking mut dyn Hint<'value, P, Err>;
-pub type VisitOps<'walking, 'value, P, Err> = &'walking mut dyn Visit<'value, P, Err>;
-
-/// Protocol specific hint for a walker.
-pub trait Hint<'value, P: Protocol<'value>, Err> {
- /// 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(
+ fn protocol(
&mut self,
- visitor: &mut dyn Visitor<'value, Err>,
- hint: P::Hint,
- ) -> Result<HintGiven, Err>;
-
- /// Any information the walker has for the protocol.
- ///
- /// This information should be easy to get inside the walker, and should
- /// only be used when making a decision of what protocol to hint as a visitor.
- /// This can be helpful for doing things like preallocating space in the visitor.
- ///
- /// 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, Err>;
-}
-
-/// Protocol specific visit for a visitor.
-pub trait Visit<'value, P: Protocol<'value>, Err: Error<'value>> {
- /// Visit a value from the walker.
- fn visit<'walking>(&'walking mut self, accessor: P::Accessor<'walking, Err>)
- -> Result<(), Err>;
-}
-
-/// A type buildable from a walker.
-pub trait Buildable<'value, Err>: Sized {
- /// The builder that can be used to build a value.
- type Builder: Builder<'value, Err, Value = Self>;
-}
+ id: ProtocolId,
+ ) -> Option<AnyVisit<'_, 'value, 'ctx, WalkerErr, Self::Error>>;
-/// Build a [`Buildable`] type from a walker.
-///
-/// This calls [`Walker::walk`] on the walker.
-pub fn build<'value, T: Buildable<'value, Err>, Err, W: ?Sized + Walker<'value, Error = Err>>(
- walker: &mut W,
-) -> Result<(T, WalkStatus), W::Error> {
- let mut builder = T::Builder::init();
- let status = walker.walk(builder.as_visitor())?;
- Ok((builder.finish()?, status))
-}
-
-/// Extension to [`Visitor`] that allows constructing and finishing the visitor.
-pub trait Builder<'value, Err> {
- /// Type to be built.
- type Value: Sized;
-
- /// Init a new builder.
- fn init() -> Self
+ fn all_protocols() -> impl Iterator<Item = ProtocolDescription>
where
Self: Sized;
-
- /// As a visitor.
- fn as_visitor(&mut self) -> &mut dyn Visitor<'value, Err>;
-
- /// Finish the value.
- fn finish(self) -> Result<Self::Value, Err>
- where
- Self: Sized;
-}
-
-/// A walkable type.
-pub trait WalkableRef<'walking, 'value>: Walkable<'walking, 'value> {
- /// Create a walker for a value.
- fn walker_ref(&'walking self) -> Self::Walker;
-}
-
-/// A walkable type.
-pub trait Walkable<'walking, 'value> {
- /// The walker for the type.
- type Walker: Walker<'value>;
-
- /// Create a walker for a value.
- ///
- /// In general, calling this multiple times will result in different walks.
- fn walker(&'walking mut self) -> Self::Walker;
-}
-
-/// 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,
- P: Protocol<'value>,
- Err: Error<'value>,
- H: ?Sized + Hints<'value, Err>,
->(
- hints: &mut H,
-) -> Result<Option<HintOps<'_, 'value, P, Err>>, Err> {
- match hints.protocol(ProtocolId::of::<P>()) {
- Some(hint) => match hint.downcast::<P>() {
- Ok(hint) => Ok(Some(hint)),
- Err(_) => Err(Err::custom_static_str(
- "unexpected protocol hint instance from walker",
- )),
- },
- 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 lookup_visit<
- 'value,
- P: Protocol<'value>,
- Err: Error<'value>,
- V: ?Sized + Visitor<'value, Err>,
->(
- visitor: &mut V,
-) -> Result<Option<VisitOps<'_, 'value, P, Err>>, Err> {
- match visitor.protocol(ProtocolId::of::<P>()) {
- Some(hint) => match hint.downcast::<P>() {
- Ok(hint) => Ok(Some(hint)),
- Err(_) => Err(Err::custom_static_str(
- "unexpected protocol visit instance from visitor",
- )),
- },
- None => Ok(None),
- }
-}
-
-pub fn walking_clone<'walking, 'value, T>(
- value: &'walking T,
-) -> Result<T, <T::Walker as Walker<'value>>::Error>
-where
- T: Buildable<'value, <T::Walker as Walker<'value>>::Error> + WalkableRef<'walking, 'value>,
-{
- let (value, _) = build(&mut value.walker_ref())?;
- Ok(value)
}