Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 272 |
1 files changed, 63 insertions, 209 deletions
@@ -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) } |