added visitor error type and added blanked impls for references
| -rw-r--r-- | Cargo.lock | 58 | ||||
| -rw-r--r-- | Cargo.toml | 6 | ||||
| -rw-r--r-- | src/build.rs | 61 | ||||
| -rw-r--r-- | src/error.rs | 93 | ||||
| -rw-r--r-- | src/impls.rs | 143 | ||||
| -rw-r--r-- | src/impls/alloc.rs | 1 | ||||
| -rw-r--r-- | src/impls/alloc/string.rs | 213 | ||||
| -rw-r--r-- | src/impls/core.rs | 2 | ||||
| -rw-r--r-- | src/impls/core/reference.rs | 199 | ||||
| -rw-r--r-- | src/impls/core/str.rs | 112 | ||||
| -rw-r--r-- | src/lib.rs | 272 | ||||
| -rw-r--r-- | src/protocol.rs | 225 | ||||
| -rw-r--r-- | src/protocols.rs | 47 | ||||
| -rw-r--r-- | src/transform.rs | 86 | ||||
| -rw-r--r-- | src/walk.rs | 67 | ||||
| -rw-r--r-- | tests/demo.rs | 16 |
16 files changed, 1207 insertions, 394 deletions
@@ -3,5 +3,63 @@ version = 3 [[package]] +name = "no-std-thiserror" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd51a908f34bf466b13228b1f47639bf281c5580f6ef96a70cd6ddde90ce0380" +dependencies = [ + "no-std-thiserror-impl", +] + +[[package]] +name = "no-std-thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db3f7230e5876bd71547b860ac196513a1e38f0a854eea0cca293ccf055f554" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] name = "uniserde" version = "0.1.0" +dependencies = [ + "no-std-thiserror", +] @@ -6,3 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +no-std-thiserror = { version = "0.1.0", default-features = false } + +[features] +default = ["alloc"] +std = ["alloc", "no-std-thiserror/std"] +alloc = [] diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..6342309 --- /dev/null +++ b/src/build.rs @@ -0,0 +1,61 @@ +use crate::{UniError, Visitor, WalkStatus, Walker}; + +/// A type buildable from a walker. +pub trait Build<'value, 'ctx: 'value, WalkerErr>: Sized { + type Error; + + /// The builder that can be used to build a value. + type Builder: Builder<'value, 'ctx, WalkerErr, Value = Self, Error = Self::Error>; +} + +// /// Build a [`Buildable`] type from a walker. +// /// +// /// This calls [`Walker::walk`] on the walker. +// pub fn build< +// 'value, +// 'ctx: 'value, +// T: Build<'value, 'ctx, W::Error, Error = VisitorErr>, +// VisitorErr, +// W: ?Sized + Walker<'value, 'ctx, VisitorErr>, +// >( +// walker: &mut W, +// ) -> Result<(T, WalkStatus), UniError<W::Error, VisitorErr>> { +// T::Builder::build(walker) +// } +// +// pub trait BuilderExt<'value, 'ctx: 'value, WalkerErr>: Builder<'value, 'ctx, WalkerErr> { +// fn build<W: ?Sized + Walker<'value, 'ctx, Self::Error, Error = WalkerErr>>( +// walker: &mut W, +// ) -> Result<(Self::Value, WalkStatus), UniError<WalkerErr, Self::Error>>; +// } +// +// impl<'value, 'ctx: 'value, WalkerErr, T: Builder<'value, 'ctx, WalkerErr>> BuilderExt<'value, 'ctx, WalkerErr> for T { +// fn build<W: ?Sized + Walker<'value, 'ctx, Self::Error, Error = WalkerErr>>( +// walker: &mut W, +// ) -> Result<(Self::Value, WalkStatus), UniError<WalkerErr, Self::Error>> { +// let mut builder = Self::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, 'ctx: 'value, WalkerErr> { + type Error; + + /// 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, 'ctx, WalkerErr, Error = Self::Error>; + + /// Finish the value. + fn finish(self) -> Result<Self::Value, UniError<WalkerErr, Self::Error>> + where + Self: Sized; +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..d975312 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,93 @@ +use crate::protocol::{Protocol, ProtocolDescription}; + +pub trait WalkerError<'value, 'ctx: 'value> { + fn wrong_visit<P: Protocol<'value, 'ctx>>() -> Self; + fn needs_hint() -> Self; +} + +pub trait VisitorError<'value, 'ctx: 'value> { + fn wrong_hint<P: Protocol<'value, 'ctx>>() -> Self; +} + +#[derive(thiserror::Error, Debug)] +pub enum UniError<WalkerErr, VisitorErr> { + #[error(transparent)] + Walker(WalkerErr), + + #[error(transparent)] + Visitor(VisitorErr), +} + +#[derive(thiserror::Error, Debug)] +#[error("{message}")] +pub struct BasicError { + message: &'static str, +} + +impl BasicError { + pub fn new(message: &'static str) -> Self { + Self { message } + } + + pub fn message(&self) -> &'static str { + self.message + } +} + +impl<'value, 'ctx: 'value> WalkerError<'value, 'ctx> for BasicError { + fn wrong_visit<P: Protocol<'value, 'ctx>>() -> Self { + Self::new("wrong protocol for visit") + } + + fn needs_hint() -> Self { + Self::new("walker needs hint from visitor") + } +} + +impl<'value, 'ctx: 'value> VisitorError<'value, 'ctx> for BasicError { + fn wrong_hint<P: Protocol<'value, 'ctx>>() -> Self { + Self::new("wrong protocol for hint") + } +} + +/// Error wrapper that adds a missing variant. +#[derive(thiserror::Error, Debug)] +pub enum Missing<E> { + /// The value was never visted. + #[error("value is missing after walking")] + Missing, + + /// Another error. + #[error(transparent)] + Error(#[from] E), +} + +impl<'value, 'ctx: 'value, E: VisitorError<'value, 'ctx>> VisitorError<'value, 'ctx> + for Missing<E> +{ + fn wrong_hint<P: Protocol<'value, 'ctx>>() -> Self { + E::wrong_hint::<P>().into() + } +} + +#[derive(thiserror::Error, Debug)] +pub enum WrongProtocol<E> { + /// The wrong protocol was given in a query. + #[error("wrong protocol, expected: `{expected}`, got: `{got}`")] + WrongProtocol { + expected: ProtocolDescription, + got: ProtocolDescription, + }, + + /// Another error. + #[error(transparent)] + Error(#[from] E), +} + +impl<'value, 'ctx: 'value, E: VisitorError<'value, 'ctx>> VisitorError<'value, 'ctx> + for WrongProtocol<E> +{ + fn wrong_hint<P: Protocol<'value, 'ctx>>() -> Self { + E::wrong_hint::<P>().into() + } +} diff --git a/src/impls.rs b/src/impls.rs index 1c4d788..80670f0 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,141 +1,4 @@ -use crate::{ - lookup_hint, lookup_visit, - protocol::{AnyHint, AnyVisit, Protocol, ProtocolId}, - protocols::{self, str::Str}, - BasicError, Buildable, Error, Hint, HintGiven, Hints, Walkable, WalkableRef, -}; +#[cfg(feature = "alloc")] +pub mod alloc; -const _: () = { - pub struct Walker<'value>(&'value str); - - impl<'value> crate::Walker<'value> for Walker<'value> { - type Error = BasicError; - - fn hints(&mut self) -> &mut dyn Hints<'value, Self::Error> { - self - } - } - - impl<'value, Err: Error<'value>> Hints<'value, Err> for Walker<'value> { - fn protocol(&mut self, id: ProtocolId) -> Option<AnyHint<'_, 'value, Err>> { - match id { - id if id == ProtocolId::of::<Str>() => Some(AnyHint::new(self)), - _ => None, - } - } - } - - impl<'value, Err: Error<'value>> Hint<'value, Str, Err> for Walker<'value> { - fn hint( - &mut self, - visitor: &mut dyn crate::Visitor<'value, Err>, - _hint: <Str as crate::protocol::Protocol>::Hint, - ) -> Result<HintGiven, Err> { - lookup_visit::<Str, _, _>(visitor)? - .ok_or_else(Err::missing_visit::<Str>)? - .visit(protocols::str::Data::Value(self.0))?; - - Ok(HintGiven) - } - - fn known( - &mut self, - _hint: &<Str as crate::protocol::Protocol>::Hint, - ) -> Result<<Str as crate::protocol::Protocol>::Known, Err> { - Ok(protocols::str::Known { - len: Some(self.0.len()), - kind: Some(protocols::str::Kind::Value), - }) - } - } - - impl<'walking, 'value: 'walking> Walkable<'walking, 'value> for &'value str { - type Walker = Walker<'value>; - - fn walker(&'walking mut self) -> Self::Walker { - Walker(self) - } - } - - impl<'walking, 'value: 'walking> WalkableRef<'walking, 'value> for &'value str { - fn walker_ref(&'walking self) -> Self::Walker { - Walker(self) - } - } - - pub struct Visitor<'value>(Option<&'value str>); - - impl<'value, Err: Error<'value>> crate::Visitor<'value, Err> for Visitor<'value> { - fn request_hint( - &mut self, - hints: &mut dyn Hints<'value, Err>, - ) -> Result<Option<HintGiven>, Err> { - if let Some(hint) = lookup_hint::<Str, _, _>(hints)? { - Ok(Some(hint.hint( - self, - protocols::str::Hint { - kind: Some(protocols::str::Kind::Value), - min_len: None, - max_len: None, - }, - )?)) - } else { - Ok(None) - } - } - - fn protocol(&mut self, id: ProtocolId) -> Option<AnyVisit<'_, 'value, Err>> { - match id { - id if id == ProtocolId::of::<Str>() => Some(AnyVisit::new(self)), - _ => None, - } - } - } - - impl<'value, Err: Error<'value>> crate::Visit<'value, Str, Err> for Visitor<'value> { - fn visit<'walking>( - &'walking mut self, - accessor: <Str as Protocol<'value>>::Accessor<'walking, Err>, - ) -> Result<(), Err> { - match accessor { - protocols::str::Data::Value(str) | protocols::str::Data::Static(str) => { - self.0 = Some(str) - } - protocols::str::Data::Walking(_) => { - return Err(Err::custom("str value does not live long enough")) - } - } - Ok(()) - } - } - - impl<'value, Err: Error<'value>> crate::Builder<'value, Err> for Visitor<'value> { - type Value = &'value str; - - fn init() -> Self - where - Self: Sized, - { - Visitor(None) - } - - fn as_visitor(&mut self) -> &mut dyn crate::Visitor<'value, Err> { - self - } - - fn finish(self) -> Result<Self::Value, Err> - where - Self: Sized, - { - if let Some(str) = self.0 { - Ok(str) - } else { - Err(Err::custom_static_str("missing str")) - } - } - } - - impl<'value, Err: Error<'value>> Buildable<'value, Err> for &'value str { - type Builder = Visitor<'value>; - } -}; +pub mod core; diff --git a/src/impls/alloc.rs b/src/impls/alloc.rs new file mode 100644 index 0000000..67c81e2 --- /dev/null +++ b/src/impls/alloc.rs @@ -0,0 +1 @@ +// mod string; diff --git a/src/impls/alloc/string.rs b/src/impls/alloc/string.rs new file mode 100644 index 0000000..118eec0 --- /dev/null +++ b/src/impls/alloc/string.rs @@ -0,0 +1,213 @@ +use core::marker::PhantomData; + +use ::alloc::string::String; + +use crate::{error::{BasicError, UniError}, WalkerHints, protocol::{ProtocolId, AnyHint, Hint, lookup_visit, lookup_hint, AnyVisit, Protocol}, protocols::{str::Str, self}, HintGiven, walk::{WalkOnce, WalkMut, Walk}, build::{Builder, Build}}; + +macro_rules! impl_walker { + ($walker:ident) => { + impl<'value, 'ctx: 'value, VisitorErr: 'ctx> crate::Walker<'value, 'ctx, VisitorErr> + for $walker<'value, 'ctx> + { + type Error = BasicError; + + fn hints(&mut self) -> &mut dyn WalkerHints<'value, 'ctx, VisitorErr, Error = Self::Error> { + self + } + } + } +} + +macro_rules! impl_hints { + ($walker:ident, [$($protocol:ty),* $(,)?]) => { + impl<'value, 'ctx: 'value, VisitorErr: 'ctx> WalkerHints<'value, 'ctx, VisitorErr> + for $walker<'value, 'ctx> + { + type Error = BasicError; + + fn protocol( + &mut self, + id: ProtocolId, + ) -> Option<AnyHint<'_, 'value, 'ctx, Self::Error, VisitorErr>> { + match id { + $(id if id == ProtocolId::of::<$protocol>() => Some(AnyHint::new(self)),)? + _ => None, + } + } + } + } +} + +pub struct WalkerOnce<'value, 'ctx: 'value>(String, PhantomData<&'value ()>); + +impl_walker!(WalkerOnce); + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> WalkerHints<'value, 'ctx, VisitorErr> + for WalkerOnce<'value, 'ctx> +{ + type Error = BasicError; + + fn protocol( + &mut self, + id: ProtocolId, + ) -> Option<AnyHint<'_, 'value, 'ctx, Self::Error, VisitorErr>> { + match id { + id if id == ProtocolId::of::<Str>() => Some(AnyHint::new(self)), + _ => None, + } + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> Hint<'value, 'ctx, Str, VisitorErr> + for Walker<'value, 'ctx> +{ + type Error = BasicError; + + fn hint( + &mut self, + visitor: &mut dyn crate::Visitor<'value, 'ctx, Self::Error, Error = VisitorErr>, + _hint: <Str as crate::protocol::Protocol<'value, 'ctx>>::Hint, + ) -> Result<HintGiven, UniError<Self::Error, VisitorErr>> { + lookup_visit::<Str, _, _>(visitor) + .map_err(UniError::Walker)? + .ok_or_else(|| UniError::Walker(BasicError("visitor is missing the str protocol")))? + .visit(protocols::str::Data::Value(self.0))?; + + Ok(HintGiven) + } + + fn known( + &mut self, + _hint: &<Str as crate::protocol::Protocol<'value, 'ctx>>::Hint, + ) -> Result<<Str as crate::protocol::Protocol<'value, 'ctx>>::Known, BasicError> { + Ok(protocols::str::Known { + len: Some(self.0.len()), + kind: Some(protocols::str::Kind::Value), + }) + } +} + +pub struct WalkerMut<'value, 'ctx: 'value>(&'value mut String, PhantomData<&'ctx ()>); +pub struct WalkerRef<'value, 'ctx: 'value>(&'value String, PhantomData<&'ctx ()>); + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> WalkOnce<'value, 'ctx, VisitorErr> for &'ctx str { + type Error = BasicError; + type Walker = Walker<'value, 'ctx>; + + fn into_walker(self) -> Self::Walker { + Walker(self, PhantomData) + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> WalkMut<'value, 'ctx, VisitorErr> for &'ctx str { + type ErrorMut = BasicError; + type WalkerMut = Walker<'value, 'ctx>; + + fn walker_mut(&'value mut self) -> Self::Walker { + Walker(self, PhantomData) + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> Walk<'value, 'ctx, VisitorErr> for &'ctx str { + type ErrorRef = BasicError; + type WalkerRef = Walker<'value, 'ctx>; + + fn walker_ref(&'value self) -> Self::Walker { + Walker(self, PhantomData) + } +} + +pub struct Visitor<'value>(Option<&'value str>); + +impl<'value, 'ctx: 'value, WalkerErr: 'value> crate::Visitor<'value, 'ctx, WalkerErr> + for Visitor<'value> +{ + type Error = BasicError; + + fn request_hint( + &mut self, + hints: &mut dyn WalkerHints<'value, 'ctx, BasicError, Error = WalkerErr>, + ) -> Result<Option<HintGiven>, UniError<WalkerErr, BasicError>> { + if let Some(hint) = lookup_hint::<Str, _, _>(hints).map_err(UniError::Visitor)? { + Ok(Some(hint.hint( + self, + protocols::str::Hint { + kind: Some(protocols::str::Kind::Value), + min_len: None, + max_len: None, + }, + )?)) + } else { + Ok(None) + } + } + + fn protocol( + &mut self, + id: ProtocolId, + ) -> Option<AnyVisit<'_, 'value, 'ctx, WalkerErr, BasicError>> { + match id { + id if id == ProtocolId::of::<Str>() => Some(AnyVisit::new(self)), + _ => None, + } + } +} + +impl<'value, 'ctx: 'value, WalkerErr: 'value> crate::Visit<'value, 'ctx, Str, WalkerErr> + for Visitor<'value> +{ + type Error = BasicError; + + fn visit<'walking>( + &'walking mut self, + accessor: <Str as Protocol<'value, 'ctx>>::Accessor<'walking, WalkerErr, BasicError>, + ) -> Result<(), UniError<WalkerErr, BasicError>> { + match accessor { + protocols::str::Data::Value(str) + | protocols::str::Data::Context(str) + | protocols::str::Data::Static(str) => self.0 = Some(str), + protocols::str::Data::Walking(_) => { + return Err(UniError::Visitor(BasicError( + "str doesn't live long enough", + ))) + } + } + Ok(()) + } +} + +impl<'value, 'ctx: 'value, WalkerErr: 'value> Builder<'value, 'ctx, WalkerErr> for Visitor<'value> { + type Error = BasicError; + + type Value = &'value str; + + fn init() -> Self + where + Self: Sized, + { + Visitor(None) + } + + fn as_visitor( + &mut self, + ) -> &mut dyn crate::Visitor<'value, 'ctx, WalkerErr, Error = Self::Error> { + self + } + + fn finish(self) -> Result<Self::Value, UniError<WalkerErr, Self::Error>> + where + Self: Sized, + { + if let Some(str) = self.0 { + Ok(str) + } else { + Err(UniError::Visitor(BasicError("missing str"))) + } + } +} + +impl<'value, 'ctx: 'value, WalkerErr: 'value> Build<'value, 'ctx, WalkerErr> for &'value str { + type Error = BasicError; + + type Builder = Visitor<'value>; +} diff --git a/src/impls/core.rs b/src/impls/core.rs new file mode 100644 index 0000000..f9135be --- /dev/null +++ b/src/impls/core.rs @@ -0,0 +1,2 @@ +pub mod reference; +pub mod str; diff --git a/src/impls/core/reference.rs b/src/impls/core/reference.rs new file mode 100644 index 0000000..457af7a --- /dev/null +++ b/src/impls/core/reference.rs @@ -0,0 +1,199 @@ +//! Impls for references `&T` and `&mut T`. +//! +//! ## Walk +//! +//! `&T` implements [`WalkOnce`], [`WalkMut`], and [`Walk`] if `T` implements [`Walk`]. +//! +//! `&mut T` implements [`WalkOnce`], and [`WalkMut`] if `T` implements [`WalkMut`]. +//! +//! ## Build + +// === &T === + +use core::{any::Any, marker::PhantomData}; + +use crate::{ + build::{Build, Builder}, + error::{Missing, UniError, WalkerError, WrongProtocol}, + protocol::{AnyVisit, ProtocolId, Visit}, + protocol_list, + protocols::reference::{self, Reference}, + walk::{Walk, WalkMut, WalkOnce}, +}; + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx, T: ?Sized> WalkOnce<'value, 'ctx, VisitorErr> + for &'ctx T +where + T: Walk<'value, 'ctx, VisitorErr>, +{ + type ErrorOnce = T::Error; + type WalkerOnce = T::Walker; + + fn into_walker(self) -> T::Walker { + T::walker(self) + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx, T: ?Sized> WalkMut<'value, 'ctx, VisitorErr> + for &'ctx T +where + T: Walk<'value, 'ctx, VisitorErr>, +{ + type ErrorMut = T::Error; + type WalkerMut = T::Walker; + + fn walker_mut(&'value mut self) -> T::Walker { + T::walker(*self) + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx, T: ?Sized> Walk<'value, 'ctx, VisitorErr> for &'ctx T +where + T: Walk<'value, 'ctx, VisitorErr>, +{ + type Error = T::Error; + type Walker = T::Walker; + + fn walker(&'value self) -> Self::Walker { + T::walker(self) + } +} + +/// Error when building a reference. +/// +/// References can only be built if the walker gives a +/// long enough lifetime reference. See the [`Reference`] +/// protocol. +#[derive(thiserror::Error, Debug)] +pub enum ShortLifetime { + /// The walker gave a `'walking` lifetime reference + /// but a longer lifetime was needed. + #[error("got `'walking` lifetime")] + Walking, + + /// The walker gave a `'value` lifetime reference + /// but a longer lifetime was needed. + #[error("got `'value` lifetime")] + Value, + + /// The walker gave a `'context` lifetime reference + /// but a longer lifetime was needed. + #[error("got `'ctx` lifetime")] + Context, +} + +#[derive(Debug)] +pub struct BuilderRefValue<'value, T: ?Sized>(Option<&'value T>); + +impl<'value, 'ctx: 'value, WalkerErr: 'value, T: ?Sized + Any> Build<'value, 'ctx, WalkerErr> + for &'value T +{ + type Error = Missing<WrongProtocol<ShortLifetime>>; + + type Builder = BuilderRefValue<'value, T>; +} + +impl<'value, 'ctx: 'value, WalkerErr: 'value, T: ?Sized + Any> Builder<'value, 'ctx, WalkerErr> + for BuilderRefValue<'value, T> +{ + type Error = Missing<WrongProtocol<ShortLifetime>>; + + type Value = &'value T; + + fn init() -> Self + where + Self: Sized, + { + Self(None) + } + + fn as_visitor( + &mut self, + ) -> &mut dyn crate::Visitor<'value, 'ctx, WalkerErr, Error = Self::Error> { + self + } + + fn finish(self) -> Result<Self::Value, crate::error::UniError<WalkerErr, Self::Error>> + where + Self: Sized, + { + self.0.ok_or(UniError::Visitor(Missing::Missing)) + } +} + +impl<'value, 'ctx: 'value, WalkerErr: 'value, T: ?Sized + Any> + crate::Visitor<'value, 'ctx, WalkerErr> for BuilderRefValue<'value, T> +{ + type Error = Missing<WrongProtocol<ShortLifetime>>; + + fn protocol( + &mut self, + id: crate::protocol::ProtocolId, + ) -> Option<crate::protocol::AnyVisit<'_, 'value, 'ctx, WalkerErr, Self::Error>> { + match id { + id if id == ProtocolId::of::<Reference<T>>() => Some(AnyVisit::new(self)), + _ => None, + } + } + + fn all_protocols() -> impl Iterator<Item = crate::protocol::ProtocolDescription> + where + Self: Sized, + { + protocol_list![Reference<T>] + } +} + +impl<'value, 'ctx: 'value, WalkerErr: 'value, T: ?Sized + Any> + Visit<'value, 'ctx, Reference<T>, WalkerErr> for BuilderRefValue<'value, T> +{ + type Error = Missing<WrongProtocol<ShortLifetime>>; + + fn visit<'walking>( + &'walking mut self, + accessor: <Reference<T> as crate::protocol::Protocol<'value, 'ctx>>::Accessor< + 'walking, + WalkerErr, + Self::Error, + >, + ) -> Result<(), UniError<WalkerErr, Self::Error>> { + use reference::Ref::*; + + match accessor { + Walking(_) => Err(UniError::Visitor(Missing::Error(WrongProtocol::Error( + ShortLifetime::Walking, + )))), + Value(value) | Context(value) | Static(value) => { + self.0 = Some(value); + + Ok(()) + } + } + } +} + +// === &mut T === + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx, T> WalkOnce<'value, 'ctx, VisitorErr> for &'ctx mut T +where + T: WalkMut<'value, 'ctx, VisitorErr>, +{ + type ErrorOnce = T::ErrorMut; + type WalkerOnce = T::WalkerMut; + + fn into_walker(self) -> T::WalkerMut { + T::walker_mut(self) + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx, T> WalkMut<'value, 'ctx, VisitorErr> for &'ctx mut T +where + T: WalkMut<'value, 'ctx, VisitorErr>, +{ + type ErrorMut = T::ErrorMut; + type WalkerMut = T::WalkerMut; + + fn walker_mut(&'value mut self) -> T::WalkerMut { + T::walker_mut(self) + } +} diff --git a/src/impls/core/str.rs b/src/impls/core/str.rs new file mode 100644 index 0000000..a3fcc01 --- /dev/null +++ b/src/impls/core/str.rs @@ -0,0 +1,112 @@ +use core::marker::PhantomData; + +use crate::{ + build::{Build, Builder}, + error::{BasicError, UniError}, + protocol::{ + lookup_hint, lookup_visit, AnyHint, AnyVisit, Hint, Protocol, ProtocolDescription, + ProtocolId, + }, + protocol_list, + protocols::reference::{self, Reference}, + walk::{Walk, WalkMut, WalkOnce}, + walk_with_hints, HintGiven, WalkerHints, +}; + +use super::reference::BuilderRefValue; + +pub struct Walker<'value, 'ctx: 'value>(&'value str, PhantomData<&'ctx ()>); + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> crate::Walker<'value, 'ctx, VisitorErr> + for Walker<'value, 'ctx> +{ + type Error = BasicError; + + fn walk( + &mut self, + visitor: &mut dyn crate::Visitor<'value, 'ctx, Self::Error, Error = VisitorErr>, + ) -> Result<crate::WalkStatus, UniError<Self::Error, VisitorErr>> { + match lookup_visit::<Reference<str>, _, _>(visitor).map_err(UniError::Walker)? { + Some(visitor) => { + visitor.visit(reference::Ref::Value(self.0))?; + Ok(crate::WalkStatus::Continue) + } + None => Err(UniError::Walker(BasicError::new( + "visitor needs the reference protocol for `str`", + ))), + } + } + + fn all_protocols() -> impl Iterator<Item = ProtocolDescription> + where + Self: Sized, + { + protocol_list![Reference<str>] + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> WalkerHints<'value, 'ctx, VisitorErr> + for Walker<'value, 'ctx> +{ + type Error = BasicError; + + fn protocol( + &mut self, + id: ProtocolId, + ) -> Option<AnyHint<'_, 'value, 'ctx, Self::Error, VisitorErr>> { + match id { + id if id == ProtocolId::of::<Reference<str>>() => Some(AnyHint::new(self)), + _ => None, + } + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> Hint<'value, 'ctx, Reference<str>, VisitorErr> + for Walker<'value, 'ctx> +{ + type Error = BasicError; + + fn hint( + &mut self, + visitor: &mut dyn crate::Visitor<'value, 'ctx, Self::Error, Error = VisitorErr>, + _hint: <Reference<str> as crate::protocol::Protocol<'value, 'ctx>>::Hint, + ) -> Result<HintGiven, UniError<Self::Error, VisitorErr>> { + lookup_visit::<Reference<str>, _, _>(visitor) + .map_err(UniError::Walker)? + .ok_or_else(|| { + UniError::Walker(BasicError::new("visitor is missing the str protocol")) + })? + .visit(reference::Ref::Value(self.0))?; + + Ok(HintGiven) + } + + fn known( + &mut self, + _hint: &<Reference<str> as crate::protocol::Protocol<'value, 'ctx>>::Hint, + ) -> Result<<Reference<str> as crate::protocol::Protocol<'value, 'ctx>>::Known, BasicError> + { + Ok(reference::Known { + len: Some(self.0.len()), + kind: Some(reference::Kind::Context), + }) + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> WalkMut<'value, 'ctx, VisitorErr> for str { + type ErrorMut = BasicError; + type WalkerMut = Walker<'value, 'ctx>; + + fn walker_mut(&'value mut self) -> Self::WalkerMut { + Walker(self, PhantomData) + } +} + +impl<'value, 'ctx: 'value, VisitorErr: 'ctx> Walk<'value, 'ctx, VisitorErr> for str { + type Error = BasicError; + type Walker = Walker<'value, 'ctx>; + + fn walker(&'value self) -> Self::Walker { + Walker(self, PhantomData) + } +} @@ -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) } diff --git a/src/protocol.rs b/src/protocol.rs index 081e569..01336db 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; +use crate::{error::VisitorError, HintGiven, UniError, Visitor, WalkerError, WalkerHints}; /// A protocol between a walker and visitor. /// @@ -16,7 +16,7 @@ use crate::Error; /// /// 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>: Any { +pub trait Protocol<'value, 'ctx: 'value>: Any { /// Arbitrary hint metadata for the protocol. /// /// This allows a visitor to give extra information to a walker when hinting to @@ -35,9 +35,159 @@ pub trait Protocol<'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, Err: Error<'value>> + type Accessor<'walking, WalkerErr: 'value, VisitorErr: 'value> where 'value: 'walking; + + fn description() -> Option<&'static str> { + None + } +} + +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 { + Self { + id: || ProtocolId::of::<P>(), + name: || core::any::type_name::<P>(), + description: P::description, + } + } + + pub fn id(&self) -> ProtocolId { + (self.id)() + } + + pub fn name(&self) -> &'static str { + (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()), + } + } +} + +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()) + .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 VisitOps<'walking, 'value, 'ctx, P, WalkerErr, VisitorErr> = + &'walking mut dyn Visit<'value, 'ctx, P, WalkerErr, Error = VisitorErr>; + +/// Protocol specific hint for a walker. +pub trait Hint<'value, 'ctx: 'value, P: Protocol<'value, 'ctx>, VisitorErr> { + type Error; + + /// 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>>; + + /// 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, Self::Error>; +} + +/// Protocol specific visit for a visitor. +pub trait Visit<'value, 'ctx: 'value, P: Protocol<'value, 'ctx>, WalkerErr> { + type Error; + + /// Visit a value from the walker. + fn visit<'walking>( + &'walking mut self, + accessor: P::Accessor<'walking, WalkerErr, Self::Error>, + ) -> Result<(), UniError<WalkerErr, Self::Error>>; +} + +/// 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>, +>( + hints: &mut H, +) -> Result<Option<HintOps<'_, 'value, 'ctx, P, H::Error, VisitorErr>>, VisitorErr> { + match hints.protocol(ProtocolId::of::<P>()) { + Some(hint) => match hint.downcast::<P>() { + Ok(hint) => Ok(Some(hint)), + Err(_) => Err(VisitorErr::wrong_hint::<P>()), + }, + 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, + 'ctx, + P: Protocol<'value, 'ctx>, + WalkerErr: WalkerError<'value, 'ctx>, + V: ?Sized + Visitor<'value, 'ctx, WalkerErr>, +>( + visitor: &mut V, +) -> Result<Option<VisitOps<'_, 'value, 'ctx, P, WalkerErr, V::Error>>, WalkerErr> { + match visitor.protocol(ProtocolId::of::<P>()) { + Some(visit) => match visit.downcast::<P>() { + Ok(visit) => Ok(Some(visit)), + Err(_) => Err(WalkerError::wrong_visit::<P>()), + }, + None => Ok(None), + } } mod id { @@ -54,7 +204,7 @@ mod id { /// Get the ID of a protocol. /// /// The ID is unique per protocol. - pub fn of<'value, P: Protocol<'value>>() -> Self { + pub fn of<'value, 'ctx: 'value, P: Protocol<'value, 'ctx>>() -> Self { Self(TypeId::of::<P>()) } } @@ -68,13 +218,14 @@ mod any_hint { use super::{Protocol, ProtocolId}; /// Form of `Hint` without `P`. - trait ErasedHint<'value, Err>: Any {} + trait ErasedHint<'value, 'ctx: 'value, WalkerErr, VisitorErr>: 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, 'static, (), ()>>(); /// Type erased form of `&'walking mut dyn Hint<'value, P, Err>` where `P` is erased. - pub struct AnyHint<'walking, 'value, Err> { + pub struct AnyHint<'walking, 'value: 'walking, 'ctx: 'value, WalkerErr, VisitorErr> { /// ID of `P`. id: ProtocolId, @@ -82,14 +233,18 @@ mod any_hint { fat_ptr: MaybeUninit<[u8; DYN_PTR_SIZE]>, /// Mimick what we actually store with a trait without `P`. - _marker: PhantomData<&'walking mut dyn ErasedHint<'value, Err>>, + _marker: PhantomData<&'walking mut dyn ErasedHint<'value, 'ctx, VisitorErr, WalkerErr>>, } - impl<'walking, 'value, Err> AnyHint<'walking, 'value, Err> { + impl<'walking, 'value, 'ctx: 'value, WalkerErr, VisitorErr> + AnyHint<'walking, 'value, 'ctx, WalkerErr, VisitorErr> + { /// Erase the `P` in a hint. /// /// This allows returning a hint from a object safe method. - pub fn new<P: Protocol<'value>>(visit: &'walking mut dyn Hint<'value, P, Err>) -> Self { + pub fn new<P: Protocol<'value, 'ctx>>( + visit: &'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, + ) -> Self { Self { id: ProtocolId::of::<P>(), // SAFETY: A maybe uninit array of bytes can hold any pointer. @@ -102,9 +257,10 @@ 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>>( + pub fn downcast<P: Protocol<'value, 'ctx>>( self, - ) -> Result<&'walking mut dyn Hint<'value, P, Err>, Self> { + ) -> Result<&'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, 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 @@ -128,13 +284,14 @@ mod any_visit { use super::{Protocol, ProtocolId}; /// Form of `Visit` without `P`. - trait ErasedVisit<'value, Err>: Any {} + trait ErasedVisit<'value, 'ctx: 'value, WalkerErr, VisitorErr>: 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, 'static, (), ()>>(); /// Type erased form of `&'walking mut dyn Visit<'value, P, Err>` where `P` is erased. - pub struct AnyVisit<'walking, 'value, Err> { + pub struct AnyVisit<'walking, 'value: 'walking, 'ctx: 'value, WalkerErr, VisitorErr> { /// ID of `P`. id: ProtocolId, @@ -142,14 +299,18 @@ mod any_visit { fat_ptr: MaybeUninit<[u8; DYN_PTR_SIZE]>, /// Mimick what we actually store with a trait without `P`. - _marker: PhantomData<&'walking mut dyn ErasedVisit<'value, Err>>, + _marker: PhantomData<&'walking mut dyn ErasedVisit<'value, 'ctx, WalkerErr, VisitorErr>>, } - impl<'walking, 'value, Err> AnyVisit<'walking, 'value, Err> { + impl<'walking, 'value: 'walking, 'ctx: 'value, WalkerErr, VisitorErr> + AnyVisit<'walking, 'value, 'ctx, WalkerErr, VisitorErr> + { /// Erase the `P` in a visit. /// /// This allows returning a visit from a object safe method. - pub fn new<P: Protocol<'value>>(visit: &'walking mut dyn Visit<'value, P, Err>) -> Self { + pub fn new<P: Protocol<'value, 'ctx>>( + visit: &'walking mut dyn Visit<'value, 'ctx, P, WalkerErr, Error = VisitorErr>, + ) -> Self { Self { id: ProtocolId::of::<P>(), // SAFETY: A maybe uninit array of bytes can hold any pointer. @@ -162,9 +323,10 @@ 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<'value>>( + pub fn downcast<P: Protocol<'value, 'ctx>>( self, - ) -> Result<&'walking mut dyn Visit<'value, P, Err>, Self> { + ) -> Result<&'walking mut dyn Visit<'value, 'ctx, P, WalkerErr, Error = VisitorErr>, 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 @@ -189,20 +351,33 @@ mod generic_example { use super::{Protocol, ProtocolId}; - pub struct Generic<'walking, 'value, P, Err> { + pub struct Generic<'walking, 'value: 'walking, 'ctx: 'value, P, WalkerErr, VisitorErr> { id: ProtocolId, - fat_ptr: &'walking mut dyn Hint<'value, P, Err>, + fat_ptr: &'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, } - impl<'walking, 'value, P: Protocol<'value>, Err> Generic<'walking, 'value, P, Err> { - pub fn new(visit: &'walking mut dyn Hint<'value, P, Err>) -> Self { + impl< + 'walking, + 'value: 'walking, + 'ctx: 'value, + P: Protocol<'value, 'ctx>, + WalkerErr, + VisitorErr, + > Generic<'walking, 'value, 'ctx, P, WalkerErr, VisitorErr> + { + pub fn new( + visit: &'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, + ) -> Self { Self { id: ProtocolId::of::<P>(), fat_ptr: visit, } } - pub fn downcast(self) -> Result<&'walking mut dyn Hint<'value, P, Err>, Self> { + pub fn downcast( + self, + ) -> Result<&'walking mut dyn Hint<'value, 'ctx, P, VisitorErr, Error = WalkerErr>, Self> + { if self.id == ProtocolId::of::<P>() { // Notice how this is valid. Ok(self.fat_ptr) diff --git a/src/protocols.rs b/src/protocols.rs index 0da527f..ba1e6ff 100644 --- a/src/protocols.rs +++ b/src/protocols.rs @@ -1,58 +1,77 @@ pub mod recoverable { - use crate::*; + use crate::{error::UniError, protocol::Protocol, Visitor}; - pub trait Accessor<'value, Err> { + pub trait Accessor<'value, 'ctx: 'value, WalkerErr, VisitorErr> { /// Each time this is called the walker resets. - fn walk_new(&mut self, visitor: &mut dyn Visitor<'value, Err>) -> Result<(), Err>; + fn walk_new( + &mut self, + visitor: &mut dyn Visitor<'value, 'ctx, WalkerErr, Error = VisitorErr>, + ) -> Result<(), UniError<WalkerErr, VisitorErr>>; } pub enum Recoverable {} - impl<'value> Protocol<'value> for Recoverable { + impl<'value, 'ctx: 'value> Protocol<'value, 'ctx> for Recoverable { type Hint = (); type Known = (); - type Accessor<'walking, Err: Error<'value>> = &'walking dyn Accessor<'value, Err> + type Accessor<'walking, WalkerErr: 'value, VisitorErr: 'value> = &'walking dyn Accessor<'value, 'ctx, WalkerErr, VisitorErr> where 'value: 'walking; } } -pub mod str { - use crate::*; +pub mod reference { + use core::{any::Any, marker::PhantomData}; + + use crate::protocol::Protocol; pub enum Kind { Walking, Value, + Context, Static, } pub struct Hint { pub kind: Option<Kind>, + pub min_len: Option<usize>, pub max_len: Option<usize>, } pub struct Known { pub kind: Option<Kind>, + + /// For dynamically sized types. pub len: Option<usize>, } - pub enum Data<'walking, 'value> { - Walking(&'walking str), - Value(&'value str), - Static(&'static str), + pub enum Ref<'walking, 'value: 'walking, 'ctx: 'value, T: ?Sized + 'static> { + Walking(&'walking T), + Value(&'value T), + Context(&'ctx T), + Static(&'static T), } - pub enum Str {} + pub enum Mut<'walking, 'value: 'walking, 'ctx: 'value, T: ?Sized + 'static> { + Walking(&'walking mut T), + Value(&'value mut T), + Context(&'ctx mut T), + Static(&'static mut T), + } + + pub struct Reference<T: ?Sized + Any>(PhantomData<fn() -> T>); + + pub struct ReferenceMut<T: ?Sized + Any>(PhantomData<fn() -> T>); - impl<'value> Protocol<'value> for Str { + impl<'value, 'ctx: 'value, T: ?Sized + Any> Protocol<'value, 'ctx> for Reference<T> { type Hint = Hint; type Known = Known; - type Accessor<'walking, Err: Error<'value>> = Data<'walking, 'value> + type Accessor<'walking, WalkerErr: 'value, VisitorErr: 'value> = Ref<'walking, 'value, 'ctx, T> where 'value: 'walking; } diff --git a/src/transform.rs b/src/transform.rs new file mode 100644 index 0000000..12f7576 --- /dev/null +++ b/src/transform.rs @@ -0,0 +1,86 @@ +use crate::{ + build::{Build, Builder}, + walk::{Walk, WalkMut, WalkOnce}, + UniError, Walker, +}; + +pub fn from<'value, 'ctx: 'value, U, T, VisitorErr>( + value: T, +) -> Result<U, UniError<<T as WalkOnce<'value, 'ctx, VisitorErr>>::ErrorOnce, VisitorErr>> +where + U: Build< + 'value, + 'ctx, + <T as WalkOnce<'value, 'ctx, VisitorErr>>::ErrorOnce, + Error = VisitorErr, + >, + T: WalkOnce<'value, 'ctx, VisitorErr>, +{ + build_from::<U::Builder, _, _>(value) +} + +pub fn from_mut<'value, 'ctx, U, T, VisitorErr>( + value: &'value mut T, +) -> Result<U, UniError<<T as WalkMut<'value, 'ctx, VisitorErr>>::ErrorMut, VisitorErr>> +where + U: Build<'value, 'ctx, <T as WalkMut<'value, 'ctx, VisitorErr>>::ErrorMut, Error = VisitorErr>, + T: WalkMut<'value, 'ctx, VisitorErr>, +{ + build_from_mut::<U::Builder, _, _>(value) +} + +pub fn from_ref<'value, 'ctx: 'value, U, T, VisitorErr>( + value: &'value T, +) -> Result<U, UniError<<T as Walk<'value, 'ctx, VisitorErr>>::Error, VisitorErr>> +where + U: Build<'value, 'ctx, <T as Walk<'value, 'ctx, VisitorErr>>::Error, Error = VisitorErr>, + T: Walk<'value, 'ctx, VisitorErr>, +{ + build_from_ref::<U::Builder, _, _>(value) +} + +pub fn build_from<'value, 'ctx: 'value, B, T, VisitorErr>( + value: T, +) -> Result<B::Value, UniError<<T as WalkOnce<'value, 'ctx, VisitorErr>>::ErrorOnce, VisitorErr>> +where + B: Builder< + 'value, + 'ctx, + <T as WalkOnce<'value, 'ctx, VisitorErr>>::ErrorOnce, + Error = VisitorErr, + >, + T: WalkOnce<'value, 'ctx, VisitorErr>, +{ + let mut builder = B::init(); + value.into_walker().walk(builder.as_visitor())?; + builder.finish() +} + +pub fn build_from_mut<'value, 'ctx: 'value, B, T, VisitorErr>( + value: &'value mut T, +) -> Result<B::Value, UniError<<T as WalkMut<'value, 'ctx, VisitorErr>>::ErrorMut, VisitorErr>> +where + B: Builder< + 'value, + 'ctx, + <T as WalkMut<'value, 'ctx, VisitorErr>>::ErrorMut, + Error = VisitorErr, + >, + T: WalkMut<'value, 'ctx, VisitorErr>, +{ + let mut builder = B::init(); + value.walker_mut().walk(builder.as_visitor())?; + builder.finish() +} + +pub fn build_from_ref<'value, 'ctx: 'value, B, T, VisitorErr>( + value: &'value T, +) -> Result<B::Value, UniError<<T as Walk<'value, 'ctx, VisitorErr>>::Error, VisitorErr>> +where + B: Builder<'value, 'ctx, <T as Walk<'value, 'ctx, VisitorErr>>::Error, Error = VisitorErr>, + T: Walk<'value, 'ctx, VisitorErr>, +{ + let mut builder = B::init(); + value.walker().walk(builder.as_visitor())?; + builder.finish() +} diff --git a/src/walk.rs b/src/walk.rs new file mode 100644 index 0000000..e73df28 --- /dev/null +++ b/src/walk.rs @@ -0,0 +1,67 @@ +use crate::{error::WalkerError, Walker}; + +/// A type that can be walked once. +/// +/// A walkable type has one canonical walker type, but other walkers +/// can operate on the same walkable type. This trait gives the canonical +/// walker for an owned instance of the value. The `'value` lifetime +/// is the lifetime of the value itself. +/// +/// [`WalkableOnce`], [`WalkableMut`], and [`Walkable`] form a family of traits +/// similar to the [`FnOnce`], [`FnMut`], [`Fn`] family of traits. +pub trait WalkOnce<'value, 'ctx: 'value, VisitorErr> { + /// Error the walker can return during the walk. + type ErrorOnce: WalkerError<'value, 'ctx>; + + /// Walker over an instance of the type. + type WalkerOnce: Walker<'value, 'ctx, VisitorErr, Error = Self::ErrorOnce>; + + /// Create a walker over a value of the type. + /// + /// Walking over the value is able to transfer ownership to the visitor. + fn into_walker(self) -> Self::WalkerOnce; +} + +/// A type that can be walked using a mutable borrow. +/// +/// This trait gives the canonical walker for a mutably borrowed +/// instance of the value. The `'value` lifetime is the lifetime of +/// the value itself. The `'walking` lifetime can be used by +/// [`Self::ErrorMut`] to reference the value using the passed mutable borrow. +/// +/// [`WalkableOnce`], [`WalkableMut`], and [`Walkable`] form a family of traits +/// similar to the [`FnOnce`], [`FnMut`], [`Fn`] family of traits. +pub trait WalkMut<'value, 'ctx: 'value, VisitorErr> { + /// Error the walker can return during the walk. + type ErrorMut: WalkerError<'value, 'ctx>; + + /// Walker over an instance of the type. + type WalkerMut: Walker<'value, 'ctx, VisitorErr, Error = Self::ErrorMut>; + + fn walker_mut(&'value mut self) -> Self::WalkerMut; +} + +pub trait Walk<'value, 'ctx: 'value, VisitorErr>: WalkMut<'value, 'ctx, VisitorErr> { + type Error: WalkerError<'value, 'ctx>; + type Walker: Walker<'value, 'ctx, VisitorErr, Error = Self::Error>; + + fn walker(&'value self) -> Self::Walker; +} + +pub fn walker_once<'value, 'ctx: 'value, VisitorErr, W: WalkOnce<'value, 'ctx, VisitorErr>>( + value: W, +) -> W::WalkerOnce { + value.into_walker() +} + +pub fn walker_mut<'value, 'ctx: 'value, VisitorErr, W: WalkMut<'value, 'ctx, VisitorErr>>( + value: &'value mut W, +) -> W::WalkerMut { + value.walker_mut() +} + +pub fn walker<'value, 'ctx: 'value, VisitorErr, W: Walk<'value, 'ctx, VisitorErr>>( + value: &'value W, +) -> W::Walker { + value.walker() +} diff --git a/tests/demo.rs b/tests/demo.rs index b954052..86c022d 100644 --- a/tests/demo.rs +++ b/tests/demo.rs @@ -1,14 +1,18 @@ -use uniserde::walking_clone; +use uniserde::{impls::core::reference::BuilderRefValue, transform::build_from}; #[test] fn demo() { - let x = "test"; - let y = clone_str(&x); + let x = String::from("a"); + let y: &str = build_from::<BuilderRefValue<str>, _, _>(&*x).unwrap(); dbg!(y); + + let y: &String = uniserde::transform::from(&*x).unwrap(); + dbg!(y); + todo!(); } -#[inline(never)] -pub fn clone_str(x: &str) -> &str { - walking_clone(&x).unwrap() +#[no_mangle] +pub fn example(a: &str) -> &String { + uniserde::transform::from(a).unwrap() } |