added visitor error type and added blanked impls for references
Konnor Andrews 2024-01-06
parent 8cb44e8 · commit c98e4af
-rw-r--r--Cargo.lock58
-rw-r--r--Cargo.toml6
-rw-r--r--src/build.rs61
-rw-r--r--src/error.rs93
-rw-r--r--src/impls.rs143
-rw-r--r--src/impls/alloc.rs1
-rw-r--r--src/impls/alloc/string.rs213
-rw-r--r--src/impls/core.rs2
-rw-r--r--src/impls/core/reference.rs199
-rw-r--r--src/impls/core/str.rs112
-rw-r--r--src/lib.rs272
-rw-r--r--src/protocol.rs225
-rw-r--r--src/protocols.rs47
-rw-r--r--src/transform.rs86
-rw-r--r--src/walk.rs67
-rw-r--r--tests/demo.rs16
16 files changed, 1207 insertions, 394 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d11c57b..00f8264 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 1cd5a70..ca48ef2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)
+ }
+}
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)
}
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()
}