-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml8
-rw-r--r--src/impls.rs141
-rw-r--r--src/lib.rs283
-rw-r--r--src/protocol.rs214
-rw-r--r--src/protocols.rs59
-rw-r--r--tests/demo.rs14
8 files changed, 727 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..d11c57b
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "uniserde"
+version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..1cd5a70
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "uniserde"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/impls.rs b/src/impls.rs
new file mode 100644
index 0000000..1c4d788
--- /dev/null
+++ b/src/impls.rs
@@ -0,0 +1,141 @@
+use crate::{
+ lookup_hint, lookup_visit,
+ protocol::{AnyHint, AnyVisit, Protocol, ProtocolId},
+ protocols::{self, str::Str},
+ BasicError, Buildable, Error, Hint, HintGiven, Hints, Walkable, WalkableRef,
+};
+
+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>;
+ }
+};
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..0fdad32
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,283 @@
+#![no_std]
+
+pub mod impls;
+pub mod protocol;
+pub mod protocols;
+
+use core::fmt::Display;
+
+use protocol::{AnyHint, AnyVisit, Protocol, ProtocolId};
+
+/// Trait for all walker error types.
+///
+/// This gives an interface for visitors to generate their own errors.
+/// `'value` is the lifetime of the source value which the error may borrow from.
+pub trait Error<'value>: Sized + Display + 'value {
+ /// Create a custom error.
+ fn custom<T: Display>(message: T) -> Self;
+
+ /// Create a custom error from a static string message.
+ ///
+ /// This is useful for error types that can't store arbitrary strings.
+ /// The default impl forwards to `Self::custom`.
+ fn custom_static_str(message: &'static str) -> Self {
+ Self::custom(message)
+ }
+
+ /// Helper for making an error when a visitor is missing the
+ /// visit for a protocol it hinted.
+ fn missing_visit<P: Protocol<'value>>() -> Self {
+ Self::custom_static_str("visitor is missing expected protocol")
+ }
+}
+
+#[derive(Debug)]
+pub struct BasicError(pub &'static str);
+
+impl Display for BasicError {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.write_str(self.0)
+ }
+}
+
+impl<'value> Error<'value> for BasicError {
+ fn custom<T: Display>(_message: T) -> Self {
+ Self("basic error")
+ }
+
+ fn custom_static_str(message: &'static str) -> Self {
+ Self(message)
+ }
+}
+
+/// Token value showing a hint was given.
+pub struct HintGiven;
+
+/// Status of a walker after walking using a visitor.
+///
+/// Some walkers can be walked multiple times to extract multiple
+/// values.
+pub enum WalkStatus {
+ /// The walker is done.
+ ///
+ /// Attemping to call `walk` is likely to result in an error.
+ Done,
+
+ /// The walker can continue.
+ Continue,
+}
+
+/// Walker over a value with lifetime `'value`.
+pub trait Walker<'value> {
+ /// Error type the walker generates.
+ type Error: Error<'value>;
+
+ /// Walk the walker over the value.
+ ///
+ /// This is the main entrypoint for walking a value.
+ /// The walker should call [`Visit::visit`] on the provided visitor as it walks.
+ ///
+ /// The default impl calls [`Visitor::request_hint`] then returns an error if no hint
+ /// was given. Self describing formats can replace the default impl to fall back to
+ /// their own logic if no hint is given. It is recommended to keep the call to
+ /// [`Visitor::request_hint`] before using walker specific logic to pick a protocol.
+ fn walk(
+ &mut self,
+ visitor: &mut dyn Visitor<'value, Self::Error>,
+ ) -> Result<WalkStatus, Self::Error> {
+ // Request that the visitor give us a hint of what protocol to use.
+ match visitor.request_hint(self.hints())? {
+ Some(HintGiven) => Ok(WalkStatus::Done),
+ None => Err(<Self::Error as Error>::custom("walker needs a hint")),
+ }
+ }
+
+ /// Get the hints the walker supports.
+ ///
+ /// The hint lookup is seperate from [`Walker`] so that [`Visitor::request_hint`] can't
+ /// easily cause a infinite loop be calling [`Walker::walk`].
+ fn hints(&mut self) -> &mut dyn Hints<'value, Self::Error>;
+}
+
+/// Hint lookup for a walker.
+pub trait Hints<'value, Err> {
+ /// Query the walker for a given protocol.
+ ///
+ /// If the walker doesn't support the protocol then a `None` is returned.
+ /// Note, a walker can return `None` if it can't handle a hint of the protocol for the given
+ /// value.
+ fn protocol(&mut self, id: ProtocolId) -> Option<AnyHint<'_, 'value, Err>> {
+ let _ = id;
+ None
+ }
+}
+
+/// Visitor over a value to be built.
+pub trait Visitor<'value, Err> {
+ /// Request the visitor hint what protocol to use.
+ ///
+ /// It is not recommended to call this while in a protocol hint as a walker.
+ /// Calling this method when already processing a hint can cause a infinite loop.
+ ///
+ /// The visitor will hint the protocol by calling the [`Hint::hint`] method on the
+ /// the walker's returned hint instance for the protocol.
+ ///
+ /// A return value of `Ok(None)` means no hint was given to the walker.
+ fn request_hint(
+ &mut self,
+ hints: &mut dyn Hints<'value, Err>,
+ ) -> Result<Option<HintGiven>, Err> {
+ let _ = hints;
+ Ok(None)
+ }
+
+ /// Query the visitor for a given protocol.
+ ///
+ /// If the visitor doesn't support the protocol then a `None` is returned.
+ fn protocol(&mut self, id: ProtocolId) -> Option<AnyVisit<'_, 'value, Err>> {
+ let _ = id;
+ None
+ }
+}
+
+pub type HintOps<'walking, 'value, P, Err> = &'walking mut dyn Hint<'value, P, Err>;
+pub type VisitOps<'walking, 'value, P, Err> = &'walking mut dyn Visit<'value, P, Err>;
+
+/// Protocol specific hint for a walker.
+pub trait Hint<'value, P: Protocol<'value>, Err> {
+ /// Hint that protocol `P` should be used.
+ ///
+ /// After hinting a protocol, a walker should invoke only the same protocol on the visitor.
+ /// This is not forced though.
+ fn hint(
+ &mut self,
+ visitor: &mut dyn Visitor<'value, Err>,
+ hint: P::Hint,
+ ) -> Result<HintGiven, Err>;
+
+ /// Any information the walker has for the protocol.
+ ///
+ /// This information should be easy to get inside the walker, and should
+ /// only be used when making a decision of what protocol to hint as a visitor.
+ /// This can be helpful for doing things like preallocating space in the visitor.
+ ///
+ /// Most protocols will allow returning a value representing no knowledge is known by the
+ /// walker.
+ fn known(&mut self, hint: &P::Hint) -> Result<P::Known, Err>;
+}
+
+/// Protocol specific visit for a visitor.
+pub trait Visit<'value, P: Protocol<'value>, Err: Error<'value>> {
+ /// Visit a value from the walker.
+ fn visit<'walking>(&'walking mut self, accessor: P::Accessor<'walking, Err>)
+ -> Result<(), Err>;
+}
+
+/// A type buildable from a walker.
+pub trait Buildable<'value, Err>: Sized {
+ /// The builder that can be used to build a value.
+ type Builder: Builder<'value, Err, Value = Self>;
+}
+
+/// Build a [`Buildable`] type from a walker.
+///
+/// This calls [`Walker::walk`] on the walker.
+pub fn build<'value, T: Buildable<'value, Err>, Err, W: ?Sized + Walker<'value, Error = Err>>(
+ walker: &mut W,
+) -> Result<(T, WalkStatus), W::Error> {
+ let mut builder = T::Builder::init();
+ let status = walker.walk(builder.as_visitor())?;
+ Ok((builder.finish()?, status))
+}
+
+/// Extension to [`Visitor`] that allows constructing and finishing the visitor.
+pub trait Builder<'value, Err> {
+ /// Type to be built.
+ type Value: Sized;
+
+ /// Init a new builder.
+ fn init() -> Self
+ where
+ Self: Sized;
+
+ /// As a visitor.
+ fn as_visitor(&mut self) -> &mut dyn Visitor<'value, Err>;
+
+ /// Finish the value.
+ fn finish(self) -> Result<Self::Value, Err>
+ where
+ Self: Sized;
+}
+
+/// A walkable type.
+pub trait WalkableRef<'walking, 'value>: Walkable<'walking, 'value> {
+ /// Create a walker for a value.
+ fn walker_ref(&'walking self) -> Self::Walker;
+}
+
+/// A walkable type.
+pub trait Walkable<'walking, 'value> {
+ /// The walker for the type.
+ type Walker: Walker<'value>;
+
+ /// Create a walker for a value.
+ ///
+ /// In general, calling this multiple times will result in different walks.
+ fn walker(&'walking mut self) -> Self::Walker;
+}
+
+/// Helper to lookup a protocol's hint on a walker.
+///
+/// A `Ok(None)` is returned if the walker doesn't support the protocol.
+/// A `Err(...)` is returned if the walker returns a hint instance not for the protocol.
+pub fn lookup_hint<
+ 'value,
+ P: Protocol<'value>,
+ Err: Error<'value>,
+ H: ?Sized + Hints<'value, Err>,
+>(
+ hints: &mut H,
+) -> Result<Option<HintOps<'_, 'value, P, Err>>, Err> {
+ match hints.protocol(ProtocolId::of::<P>()) {
+ Some(hint) => match hint.downcast::<P>() {
+ Ok(hint) => Ok(Some(hint)),
+ Err(_) => Err(Err::custom_static_str(
+ "unexpected protocol hint instance from walker",
+ )),
+ },
+ None => Ok(None),
+ }
+}
+
+/// Helper to lookup a protocol's visit on a visitor.
+///
+/// A `Ok(None)` is returned if the visitor doesn't support the protocol.
+/// A `Err(...)` is returned if the visitor returns a visit instance not for the protocol.
+pub fn lookup_visit<
+ 'value,
+ P: Protocol<'value>,
+ Err: Error<'value>,
+ V: ?Sized + Visitor<'value, Err>,
+>(
+ visitor: &mut V,
+) -> Result<Option<VisitOps<'_, 'value, P, Err>>, Err> {
+ match visitor.protocol(ProtocolId::of::<P>()) {
+ Some(hint) => match hint.downcast::<P>() {
+ Ok(hint) => Ok(Some(hint)),
+ Err(_) => Err(Err::custom_static_str(
+ "unexpected protocol visit instance from visitor",
+ )),
+ },
+ None => Ok(None),
+ }
+}
+
+pub fn walking_clone<'walking, 'value, T>(
+ value: &'walking T,
+) -> Result<T, <T::Walker as Walker<'value>>::Error>
+where
+ T: Buildable<'value, <T::Walker as Walker<'value>>::Error> + WalkableRef<'walking, 'value>,
+{
+ let (value, _) = build(&mut value.walker_ref())?;
+ Ok(value)
+}
diff --git a/src/protocol.rs b/src/protocol.rs
new file mode 100644
index 0000000..081e569
--- /dev/null
+++ b/src/protocol.rs
@@ -0,0 +1,214 @@
+use core::any::Any;
+
+pub use any_hint::AnyHint;
+pub use any_visit::AnyVisit;
+pub use id::ProtocolId;
+
+use crate::Error;
+
+/// A protocol between a walker and visitor.
+///
+/// On the walker side this takes the form of hints a visitor can give.
+/// On the visitor side this takes the form of visits a walker can inject values into.
+///
+/// When a visitor hints a walker should use a particular protocol, its expected
+/// that the walker visits using that protocol.
+///
+/// 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 {
+ /// Arbitrary hint metadata for the protocol.
+ ///
+ /// This allows a visitor to give extra information to a walker when hinting to
+ /// use the protocol.
+ type Hint;
+
+ /// Data known about the protocol before hinting.
+ ///
+ /// This allows a walker to give extra information to a visitor to make a
+ /// better decision when selecting a hint.
+ type Known;
+
+ /// The visit data the walker provides to the visitor.
+ ///
+ /// This may be actual data or another walker for a part of the bigger value.
+ /// 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>>
+ where
+ 'value: 'walking;
+}
+
+mod id {
+ use super::Protocol;
+ use core::any::TypeId;
+
+ /// ID of a protocol.
+ ///
+ /// This can be used to query if a walker or visitor supports a protocol.
+ #[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Debug, Copy, Clone)]
+ pub struct ProtocolId(TypeId);
+
+ impl ProtocolId {
+ /// Get the ID of a protocol.
+ ///
+ /// The ID is unique per protocol.
+ pub fn of<'value, P: Protocol<'value>>() -> Self {
+ Self(TypeId::of::<P>())
+ }
+ }
+}
+
+mod any_hint {
+ use core::{any::Any, marker::PhantomData, mem::MaybeUninit};
+
+ use crate::Hint;
+
+ use super::{Protocol, ProtocolId};
+
+ /// Form of `Hint` without `P`.
+ trait ErasedHint<'value, Err>: 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, ()>>();
+
+ /// Type erased form of `&'walking mut dyn Hint<'value, P, Err>` where `P` is erased.
+ pub struct AnyHint<'walking, 'value, Err> {
+ /// ID of `P`.
+ id: ProtocolId,
+
+ /// This field stores a `&'walking mut dyn Hint<'value, P, Err>`.
+ 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>>,
+ }
+
+ impl<'walking, 'value, Err> AnyHint<'walking, 'value, Err> {
+ /// 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 {
+ Self {
+ id: ProtocolId::of::<P>(),
+ // SAFETY: A maybe uninit array of bytes can hold any pointer.
+ // Additionally, transmute makes sure the size is correct.
+ fat_ptr: unsafe { core::mem::transmute(visit) },
+ _marker: PhantomData,
+ }
+ }
+
+ /// 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>>(
+ self,
+ ) -> Result<&'walking mut dyn Hint<'value, P, Err>, 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
+ // type.
+ //
+ // An important note is this method takes ownership. Which allows it to return
+ // the borrow with the `'walking` lifetime instead of a sub-borrow.
+ Ok(unsafe { core::mem::transmute(self.fat_ptr) })
+ } else {
+ Err(self)
+ }
+ }
+ }
+}
+
+mod any_visit {
+ use core::{any::Any, marker::PhantomData, mem::MaybeUninit};
+
+ use crate::Visit;
+
+ use super::{Protocol, ProtocolId};
+
+ /// Form of `Visit` without `P`.
+ trait ErasedVisit<'value, Err>: 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, ()>>();
+
+ /// Type erased form of `&'walking mut dyn Visit<'value, P, Err>` where `P` is erased.
+ pub struct AnyVisit<'walking, 'value, Err> {
+ /// ID of `P`.
+ id: ProtocolId,
+
+ /// This field stores a `&'walking mut dyn Visit<'value, P, Err>`.
+ 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>>,
+ }
+
+ impl<'walking, 'value, Err> AnyVisit<'walking, 'value, Err> {
+ /// 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 {
+ Self {
+ id: ProtocolId::of::<P>(),
+ // SAFETY: A maybe uninit array of bytes can hold any pointer.
+ // Additionally, transmute makes sure the size is correct.
+ fat_ptr: unsafe { core::mem::transmute(visit) },
+ _marker: PhantomData,
+ }
+ }
+
+ /// 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>>(
+ self,
+ ) -> Result<&'walking mut dyn Visit<'value, P, Err>, 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
+ // type.
+ //
+ // An important note is this method takes ownership. Which allows it to return
+ // the borrow with the `'walking` lifetime instead of a sub-borrow.
+ Ok(unsafe { core::mem::transmute(self.fat_ptr) })
+ } else {
+ Err(self)
+ }
+ }
+ }
+}
+
+/// The following shows a safe form of the generic types in this module.
+/// This shows how the lifetimes are correct.
+#[cfg(test)]
+#[allow(unused)]
+mod generic_example {
+ use crate::Hint;
+
+ use super::{Protocol, ProtocolId};
+
+ pub struct Generic<'walking, 'value, P, Err> {
+ id: ProtocolId,
+ fat_ptr: &'walking mut dyn Hint<'value, P, Err>,
+ }
+
+ impl<'walking, 'value, P: Protocol<'value>, Err> Generic<'walking, 'value, P, Err> {
+ pub fn new(visit: &'walking mut dyn Hint<'value, P, Err>) -> Self {
+ Self {
+ id: ProtocolId::of::<P>(),
+ fat_ptr: visit,
+ }
+ }
+
+ pub fn downcast(self) -> Result<&'walking mut dyn Hint<'value, P, Err>, Self> {
+ if self.id == ProtocolId::of::<P>() {
+ // Notice how this is valid.
+ Ok(self.fat_ptr)
+ } else {
+ Err(self)
+ }
+ }
+ }
+}
diff --git a/src/protocols.rs b/src/protocols.rs
new file mode 100644
index 0000000..0da527f
--- /dev/null
+++ b/src/protocols.rs
@@ -0,0 +1,59 @@
+pub mod recoverable {
+ use crate::*;
+
+ pub trait Accessor<'value, Err> {
+ /// Each time this is called the walker resets.
+ fn walk_new(&mut self, visitor: &mut dyn Visitor<'value, Err>) -> Result<(), Err>;
+ }
+
+ pub enum Recoverable {}
+
+ impl<'value> Protocol<'value> for Recoverable {
+ type Hint = ();
+
+ type Known = ();
+
+ type Accessor<'walking, Err: Error<'value>> = &'walking dyn Accessor<'value, Err>
+ where
+ 'value: 'walking;
+ }
+}
+
+pub mod str {
+ use crate::*;
+
+ pub enum Kind {
+ Walking,
+ Value,
+ 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>,
+ pub len: Option<usize>,
+ }
+
+ pub enum Data<'walking, 'value> {
+ Walking(&'walking str),
+ Value(&'value str),
+ Static(&'static str),
+ }
+
+ pub enum Str {}
+
+ impl<'value> Protocol<'value> for Str {
+ type Hint = Hint;
+
+ type Known = Known;
+
+ type Accessor<'walking, Err: Error<'value>> = Data<'walking, 'value>
+ where
+ 'value: 'walking;
+ }
+}
diff --git a/tests/demo.rs b/tests/demo.rs
new file mode 100644
index 0000000..b954052
--- /dev/null
+++ b/tests/demo.rs
@@ -0,0 +1,14 @@
+use uniserde::walking_clone;
+
+#[test]
+fn demo() {
+ let x = "test";
+ let y = clone_str(&x);
+ dbg!(y);
+ todo!();
+}
+
+#[inline(never)]
+pub fn clone_str(x: &str) -> &str {
+ walking_clone(&x).unwrap()
+}