#![no_std]
pub mod impls;
pub mod protocol;
pub mod protocols;
use core::fmt::Display;
use protocol::{AnyHint, AnyVisit, Protocol, ProtocolId};
/// Trait for all walker error types.
///
/// 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")
}
}
#[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.
///
/// Some walkers can be walked multiple times to extract multiple
/// values.
pub enum WalkStatus {
/// The walker is done.
///
/// Attemping to call `walk` is likely to result in an error.
Done,
/// The walker can continue.
Continue,
}
/// Walker over a value with lifetime `'value`.
pub trait Walker<'value> {
/// Error type the walker generates.
type Error: Error<'value>;
/// Walk the walker over the value.
///
/// This is the main entrypoint for walking a value.
/// The walker should call [`Visit::visit`] on the provided visitor as it walks.
///
/// The default impl calls [`Visitor::request_hint`] then returns an error if no hint
/// was given. Self describing formats can replace the default impl to fall back to
/// their own logic if no hint is given. It is recommended to keep the call to
/// [`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")),
}
}
/// 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>;
}
/// Hint lookup for a walker.
pub trait Hints<'value, Err> {
/// 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
}
}
/// Visitor over a value to be built.
pub trait Visitor<'value, Err> {
/// Request the visitor hint what protocol to use.
///
/// It is not recommended to call this while in a protocol hint as a walker.
/// Calling this method when already processing a hint can cause a infinite loop.
///
/// The visitor will hint the protocol by calling the [`Hint::hint`] method on the
/// the walker's returned hint instance for the protocol.
///
/// 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> {
let _ = hints;
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(
&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>;
}
/// 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
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)
}