added effects and tagged protocol
| -rw-r--r-- | Cargo.lock | 297 | ||||
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/any.rs | 2 | ||||
| -rw-r--r-- | src/build.rs | 41 | ||||
| -rw-r--r-- | src/protocol.rs | 28 | ||||
| -rw-r--r-- | src/protocol/visitor.rs | 4 | ||||
| -rw-r--r-- | src/protocol/visitor/request_hint.rs | 18 | ||||
| -rw-r--r-- | src/protocol/visitor/sequence.rs | 30 | ||||
| -rw-r--r-- | src/protocol/visitor/tagged.rs | 38 | ||||
| -rw-r--r-- | src/protocol/visitor/value.rs | 49 | ||||
| -rw-r--r-- | src/protocol/walker/hint.rs | 62 | ||||
| -rw-r--r-- | src/walk.rs | 15 | ||||
| -rw-r--r-- | tests/async.rs | 148 | ||||
| -rw-r--r-- | tests/demo.rs | 40 |
14 files changed, 666 insertions, 109 deletions
@@ -3,12 +3,42 @@ version = 3 [[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -36,6 +66,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -48,7 +93,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -75,6 +120,18 @@ dependencies = [ ] [[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hermit-abi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -99,6 +156,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] name = "macro_rules_attribute" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -115,6 +182,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8dd856d451cc0da70e2ef2ce95a18e39a93b7558bedf10201ad28503f918568" [[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -125,12 +218,60 @@ dependencies = [ ] [[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -235,6 +376,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] name = "rustix" version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -244,7 +391,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -260,6 +407,12 @@ dependencies = [ ] [[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] name = "serde" version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -280,6 +433,31 @@ dependencies = [ ] [[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] name = "syn" version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -300,7 +478,37 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -310,6 +518,7 @@ dependencies = [ "macro_rules_attribute", "proptest", "serde", + "tokio", ] [[package]] @@ -341,11 +550,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -354,53 +587,95 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" @@ -7,7 +7,7 @@ edition = "2021" serde = { version = "1.0", default-features = false, optional = true } [features] -default = [] +default = ["alloc"] std = ["alloc", "serde?/std"] alloc = ["serde?/alloc"] serde = ["dep:serde"] @@ -15,6 +15,7 @@ serde = ["dep:serde"] [dev-dependencies] macro_rules_attribute = "0.2.0" proptest = "1.4.0" +tokio = { version = "1.36.0", features = ["full"] } [profile.test.package.proptest] opt-level = 3 @@ -110,7 +110,7 @@ macro_rules! nameable { ::core::marker::PhantomData<fn() -> ($( $(*const $generic,)* )?)> ); - impl<$a, $lt $(, $($generic)*)?> + impl<$a, $lt $(, $($generic),*)?> $crate::any::TypeNameable<$a, $lt> for $type where $($nameable_bound)* diff --git a/src/build.rs b/src/build.rs index efadc25..0d103ce 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,7 +1,10 @@ // pub mod builders; // pub mod protocols; -use crate::protocol::Visitor; +use crate::{ + protocol::{SyncEffect, Visitor}, + Walker, +}; /// A buildable type. pub trait Build<'ctx>: Sized { @@ -32,7 +35,7 @@ pub trait Builder<'ctx>: Default { /// Get the builder as a visitor that a walker can use. /// /// This is expected to just be `self`. - fn as_visitor(&mut self) -> Visitor<'_, 'ctx>; + fn as_visitor(&mut self) -> &mut Visitor<'ctx>; /// Finish the value. /// @@ -40,3 +43,37 @@ pub trait Builder<'ctx>: Default { /// it will be reported here. fn build(self) -> Result<Self::Value, Self::Error>; } + +#[derive(Debug)] +pub enum BuildError<B, W> { + Builder(B), + Walker(W), +} + +pub fn build_with<'ctx, B: Builder<'ctx>, W: Walker<'ctx, Effect = SyncEffect>>( + walker: W, +) -> Result<B::Value, BuildError<B::Error, W::Error>> { + let mut builder = B::default(); + + if let core::ops::ControlFlow::Break(err) = walker.walk(builder.as_visitor()) { + return Err(BuildError::Walker(err)); + } + + builder.build().map_err(BuildError::Builder) +} + +#[cfg(feature = "alloc")] +use crate::protocol::AsyncEffect; + +#[cfg(feature = "alloc")] +pub async fn async_build_with<'ctx, B: Builder<'ctx>, W: Walker<'ctx, Effect = AsyncEffect>>( + walker: W, +) -> Result<B::Value, BuildError<B::Error, W::Error>> { + let mut builder = B::default(); + + if let core::ops::ControlFlow::Break(err) = walker.walk(builder.as_visitor()).await { + return Err(BuildError::Walker(err)); + } + + builder.build().map_err(BuildError::Builder) +} diff --git a/src/protocol.rs b/src/protocol.rs index f06f095..26ec664 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -38,6 +38,28 @@ use crate::any::AnyTrait; pub mod visitor; pub mod walker; -pub type Visitor<'a, 'ctx> = &'a mut dyn AnyTrait<'ctx>; -pub type Walker<'a, 'ctx> = &'a mut dyn AnyTrait<'ctx>; -pub type ControlFlow<B = (), C = ()> = core::ops::ControlFlow<B, C>; +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; + +pub type Visitor<'ctx> = dyn AnyTrait<'ctx>; +pub type Walker<'ctx> = dyn AnyTrait<'ctx>; + +pub trait Effect { + type ControlFlow<'a, C, B>; +} +pub type ControlFlowFor<'a, E = SyncEffect, C = (), B = ()> = <E as Effect>::ControlFlow<'a, C, B>; + +pub enum SyncEffect {} + +impl Effect for SyncEffect { + type ControlFlow<'a, C, B> = core::ops::ControlFlow<B, C>; +} + +#[cfg(feature = "alloc")] +pub enum AsyncEffect {} + +#[cfg(feature = "alloc")] +impl Effect for AsyncEffect { + type ControlFlow<'a, C, B> = + core::pin::Pin<Box<dyn core::future::Future<Output = core::ops::ControlFlow<B, C>> + 'a>>; +} diff --git a/src/protocol/visitor.rs b/src/protocol/visitor.rs index cee7dd4..7b91c7a 100644 --- a/src/protocol/visitor.rs +++ b/src/protocol/visitor.rs @@ -1,9 +1,9 @@ mod request_hint; mod sequence; -// pub mod tagged; +mod tagged; mod value; -// pub mod borrow; pub use request_hint::*; pub use sequence::*; +pub use tagged::*; pub use value::*; diff --git a/src/protocol/visitor/request_hint.rs b/src/protocol/visitor/request_hint.rs index 179a68a..87c073d 100644 --- a/src/protocol/visitor/request_hint.rs +++ b/src/protocol/visitor/request_hint.rs @@ -1,17 +1,21 @@ -use core::ops::ControlFlow; - -use crate::{nameable, protocol::Walker}; +use crate::{ + nameable, + protocol::{ControlFlowFor, Effect, SyncEffect, Walker}, +}; /// Protocol for requesting a hint from a visitor. -pub trait RequestHint<'ctx> { +pub trait RequestHint<'ctx, E: Effect = SyncEffect> { /// Call this to request a hint. /// /// `walker` is what the visitor (`self`) will call to give a hint using the /// [`Hint`][crate::builtins::walker::Hint] protocol. - fn request_hint(&mut self, walker: Walker<'_, 'ctx>) -> ControlFlow<()>; + fn request_hint<'a>(&'a mut self, walker: &'a mut Walker<'ctx>) -> ControlFlowFor<'a, E>; } nameable! { - pub struct Name['a, 'ctx]; - impl for dyn RequestHint<'ctx> + 'a where {'ctx: 'a} + pub struct Name['a, 'ctx, E]; + impl [E] for dyn RequestHint<'ctx, E> + 'a where { + E: Effect + 'static, + 'ctx: 'a + } } diff --git a/src/protocol/visitor/sequence.rs b/src/protocol/visitor/sequence.rs index 3cb8897..9b24377 100644 --- a/src/protocol/visitor/sequence.rs +++ b/src/protocol/visitor/sequence.rs @@ -1,20 +1,28 @@ use crate::{ nameable, - protocol::{walker::HintMeta, ControlFlow, Visitor}, + protocol::{walker::HintMeta, ControlFlowFor, Effect, SyncEffect, Visitor}, }; -pub trait Sequence<'ctx> { - fn visit(&mut self, scope: &mut dyn SequenceScope<'ctx>) -> ControlFlow; +pub trait Sequence<'ctx, E: Effect = SyncEffect> { + fn visit<'a>(&'a mut self, scope: &'a mut dyn SequenceScope<'ctx, E>) -> ControlFlowFor<'a, E>; } nameable! { - pub struct Name['a, 'ctx]; - impl for dyn Sequence<'ctx> + 'a where { 'ctx: 'a } - impl where dyn Sequence<'ctx> + 'a { 'ctx: 'a } + pub struct Name['a, 'ctx, E]; + + impl [E] for dyn Sequence<'ctx, E> + 'a where { + E: Effect + 'static, + 'ctx: 'a + } + + impl [E] where dyn Sequence<'ctx, E> + 'a { + E: Effect + 'static, + 'ctx: 'a + } } -pub trait SequenceScope<'ctx> { - fn next(&mut self, visitor: Visitor<'_, 'ctx>) -> ControlFlow<(), Status>; +pub trait SequenceScope<'ctx, E: Effect = SyncEffect> { + fn next<'a>(&'a mut self, visitor: &'a mut Visitor<'ctx>) -> ControlFlowFor<'a, E, Status>; } #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)] @@ -32,10 +40,12 @@ pub struct Hint { pub len: (usize, Option<usize>), } -impl<'ctx> HintMeta<'ctx> for dyn Sequence<'ctx> + '_ { +impl<'ctx, E: Effect> HintMeta<'ctx> for dyn Sequence<'ctx, E> + '_ { type Known<'a> = Known where 'ctx: 'a; - type Hint = Hint; + type Hint<'a> = Hint + where + 'ctx: 'a; } diff --git a/src/protocol/visitor/tagged.rs b/src/protocol/visitor/tagged.rs index c447a5f..4cf3912 100644 --- a/src/protocol/visitor/tagged.rs +++ b/src/protocol/visitor/tagged.rs @@ -1,25 +1,33 @@ use crate::{ - builtins::walker::hint::Meta, - protocol::{Implementer, Protocol}, + nameable, + protocol::{walker::HintMeta, ControlFlowFor, Effect, SyncEffect, Visitor}, symbol::Symbol, }; -pub enum Tagged {} +pub trait Tagged<'ctx, E: Effect = SyncEffect> { + fn visit<'a>(&'a mut self, scope: &'a mut dyn TaggedScope<'ctx, E>) -> ControlFlowFor<'a, E>; +} -pub trait TaggedWalker<'ctx> { - fn kind(&mut self) -> Symbol; +nameable! { + pub struct Name['a, 'ctx, E]; - fn walk_tag(&mut self, visitor: &mut dyn Implementer<'ctx>) -> Result<(), ()>; + impl [E] for dyn Tagged<'ctx, E> + 'a where { + E: Effect + 'static, + 'ctx: 'a + } - fn walk_value(&mut self, visitor: &mut dyn Implementer<'ctx>) -> Result<(), ()>; + impl [E] where dyn Tagged<'ctx, E> + 'a { + E: Effect + 'static, + 'ctx: 'a + } } -pub trait Object<'ctx> { - fn visit(&mut self, walker: &mut dyn TaggedWalker<'ctx>) -> Result<(), ()>; -} +pub trait TaggedScope<'ctx, E: Effect = SyncEffect> { + fn kind(&mut self) -> Symbol; + + fn tag<'a>(&'a mut self, visitor: &'a mut Visitor<'ctx>) -> ControlFlowFor<'a, E>; -impl Protocol for Tagged { - type Object<'a, 'ctx: 'a> = &'a mut dyn Object<'ctx>; + fn value<'a>(&'a mut self, visitor: &'a mut Visitor<'ctx>) -> ControlFlowFor<'a, E>; } pub struct Known { @@ -30,8 +38,8 @@ pub struct Hint { pub kind: Option<Symbol>, } -impl Meta for Tagged { - type Known<'a, 'ctx: 'a> = Known; +impl<'ctx, Return> HintMeta<'ctx> for dyn Tagged<'ctx, Return> + '_ { + type Known<'a> = Known where 'ctx: 'a; - type Hint<'a, 'ctx: 'a> = Hint; + type Hint<'a> = Hint where 'ctx: 'a; } diff --git a/src/protocol/visitor/value.rs b/src/protocol/visitor/value.rs index 3cb8908..ed8a8ec 100644 --- a/src/protocol/visitor/value.rs +++ b/src/protocol/visitor/value.rs @@ -5,13 +5,13 @@ use crate::{ any::{TypeName, TypeNameable}, nameable, - protocol::{walker::HintMeta, ControlFlow}, + protocol::{walker::HintMeta, ControlFlowFor, Effect, SyncEffect}, }; /// Trait object for the [`Value`] protocol. /// /// Types implementing the [`Value`] protocol will implement this trait. -pub trait Value<T> { +pub trait Value<'a, T, E: Effect = SyncEffect> { /// Visit a value of type `T`. /// /// Use this to give a value to a visitor. Its expected that a walker @@ -21,30 +21,41 @@ pub trait Value<T> { /// If a [`ControlFlow::Break`] is returned then the walker /// should stop walking as soon as possible as there has likely been /// and error. - fn visit(&mut self, value: T) -> ControlFlow; + fn visit(&'a mut self, value: T) -> ControlFlowFor<'a, E>; } nameable! { - pub struct Name['a, 'ctx, T]; - impl [T::Name] for dyn Value<T> + 'a where { T: TypeNameable<'a, 'ctx> + ?Sized } - impl [T] where dyn Value<T::Nameable> + 'a { T: TypeName<'a, 'ctx> + ?Sized } + pub struct Name['a, 'ctx, T, E]; + + impl [T::Name, E] for dyn Value<'a, T, E> + 'a where { + T: TypeNameable<'a, 'ctx> + ?Sized, + E: Effect + 'static, + } + + impl [T, E] where dyn Value<'a, T::Nameable, E> + 'a { + T: TypeName<'a, 'ctx> + ?Sized, + E: Effect + 'static, + } } // This enrolls the Value protocol into the walker hint system. -impl<'ctx, T> HintMeta<'ctx> for dyn Value<T> + '_ { +impl<'ctx, T, E: Effect> HintMeta<'ctx> for dyn Value<'_, T, E> + '_ { type Known<'a> = () where 'ctx: 'a; - type Hint = (); + type Hint<'a> = () where 'ctx: 'a; } #[cfg(test)] mod test { + use core::ops::ControlFlow; + use crate::{ any::{ static_wrapper::{BorrowedMutStatic, BorrowedStatic, OwnedStatic}, AnyTrait, }, any_trait, + protocol::SyncControlFlow, }; use super::*; @@ -53,15 +64,21 @@ mod test { fn visit() { struct Visitor(Option<i32>); - impl Value<OwnedStatic<i32>> for Visitor { - fn visit(&mut self, OwnedStatic(value): OwnedStatic<i32>) -> ControlFlow<()> { + impl Value<'_, OwnedStatic<i32>, SyncControlFlow> for Visitor { + fn visit( + &mut self, + OwnedStatic(value): OwnedStatic<i32>, + ) -> core::ops::ControlFlow<()> { self.0 = Some(value); ControlFlow::Continue(()) } } - impl Value<BorrowedStatic<'_, i32>> for Visitor { - fn visit(&mut self, BorrowedStatic(value): BorrowedStatic<'_, i32>) -> ControlFlow<()> { + impl Value<'_, BorrowedStatic<'_, i32>, SyncControlFlow> for Visitor { + fn visit( + &mut self, + BorrowedStatic(value): BorrowedStatic<'_, i32>, + ) -> core::ops::ControlFlow<()> { self.0 = Some(*value); ControlFlow::Continue(()) } @@ -69,15 +86,15 @@ mod test { any_trait! { impl['a, 'ctx] Visitor = [ - dyn Value<OwnedStatic<i32>>, - dyn Value<BorrowedStatic<'ctx, i32>>, + dyn Value<'a, OwnedStatic<i32>, SyncControlFlow> + 'a, + dyn Value<'a, BorrowedStatic<'ctx, i32, SyncControlFlow>> + 'a, ]; } let mut v = Visitor(None); let object: &mut dyn AnyTrait<'_> = &mut v; object - .upcast_mut::<dyn Value<OwnedStatic<i32>>>() + .upcast_mut::<dyn Value<'_, OwnedStatic<i32, SyncControlFlow>>>() .unwrap() .visit(OwnedStatic(42)); @@ -85,7 +102,7 @@ mod test { let object: &mut dyn AnyTrait<'_> = &mut v; object - .upcast_mut::<dyn Value<BorrowedStatic<'_, i32>>>() + .upcast_mut::<dyn Value<'_, BorrowedStatic<'_, i32, SyncControlFlow>>>() .unwrap() .visit(BorrowedStatic(&101)); diff --git a/src/protocol/walker/hint.rs b/src/protocol/walker/hint.rs index c7d85d1..abdb136 100644 --- a/src/protocol/walker/hint.rs +++ b/src/protocol/walker/hint.rs @@ -7,7 +7,7 @@ use crate::{ any::{TypeName, TypeNameable}, nameable, - protocol::{ControlFlow, Visitor}, + protocol::{ControlFlowFor, Effect, SyncEffect, Visitor}, }; /// Meta information for the hint. @@ -17,38 +17,57 @@ pub trait HintMeta<'ctx> { /// Information known by the walker. /// /// This should be information easy to get without changing the state of the walker - /// in an irreversable way. + /// in an irreversible way. type Known<'a> where 'ctx: 'a; /// Extra information the visitor can give to the walker about what it is expecting. - type Hint; + type Hint<'a> + where + 'ctx: 'a; } /// Object implementing the [`Hint`] protocol. -pub trait Hint<'ctx, Protocol: HintMeta<'ctx>> { +pub trait Hint<'ctx, Protocol: HintMeta<'ctx>, E: Effect = SyncEffect> { /// Hint to the walker to use the `P` protocol. /// /// This should only be called once per [`RequestHint`]. - fn hint(&mut self, visitor: Visitor<'_, 'ctx>, hint: Protocol::Hint) -> ControlFlow; + fn hint<'a>( + &'a mut self, + visitor: &'a mut Visitor<'ctx>, + hint: Protocol::Hint<'a>, + ) -> ControlFlowFor<'a, E>; /// Ask the walker for information about it's support of the protocol. - fn known(&mut self, hint: &Protocol::Hint) -> ControlFlow<(), Protocol::Known<'_>>; + fn known<'a>( + &'a mut self, + hint: &'a Protocol::Hint<'a>, + ) -> ControlFlowFor<'a, E, Protocol::Known<'_>> + where + 'ctx: 'a; } nameable! { - pub struct Name['a, 'ctx, Protocol]; - impl [Protocol::Name] for dyn Hint<'ctx, Protocol> + 'a where { - Protocol: TypeNameable<'a, 'ctx> + ?Sized, 'ctx: 'a + pub struct Name['a, 'ctx, Protocol, E]; + + impl [Protocol::Name, E] for dyn Hint<'ctx, Protocol, E> + 'a where { + Protocol: TypeNameable<'a, 'ctx> + ?Sized, + E: Effect + 'static, + 'ctx: 'a, } - impl [Protocol] where dyn Hint<'ctx, Protocol::Nameable> + 'a { - Protocol: TypeName<'a, 'ctx> + ?Sized, 'ctx: 'a + + impl [Protocol, E] where dyn Hint<'ctx, Protocol::Nameable, E> + 'a { + Protocol: TypeName<'a, 'ctx> + ?Sized, + E: Effect + 'static, + 'ctx: 'a, } } #[cfg(test)] mod test { + use core::ops::ControlFlow; + use crate::any::{LtTypeId, TypeNameable}; use super::*; @@ -64,18 +83,21 @@ mod test { } impl<'ctx, X> Hint<'ctx, Y> for X { - fn hint( - &mut self, - visitor: Visitor<'_, 'ctx>, - hint: <Y as HintMeta<'_>>::Hint, + fn hint<'a>( + &'a mut self, + visitor: &'a mut Visitor<'ctx>, + hint: <Y as HintMeta<'ctx>>::Hint<'a>, ) -> ControlFlow<()> { todo!() } - fn known( - &mut self, - hint: &<Y as HintMeta<'_>>::Hint, - ) -> ControlFlow<(), <Y as HintMeta<'_>>::Known<'_>> { + fn known<'a>( + &'a mut self, + hint: &'a <Y as HintMeta<'ctx>>::Hint<'a>, + ) -> ControlFlow<(), <Y as HintMeta<'ctx>>::Known<'a>> + where + 'ctx: 'a, + { todo!() } } @@ -83,7 +105,7 @@ mod test { impl<'ctx> HintMeta<'ctx> for Y { type Known<'a> = () where 'ctx: 'a; - type Hint = (); + type Hint<'a> = () where 'ctx: 'a; } let x = X; diff --git a/src/walk.rs b/src/walk.rs index 9eda9d8..88f3287 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -1,7 +1,7 @@ // pub mod protocols; // pub mod walkers; -use crate::protocol::Visitor; +use crate::protocol::{ControlFlowFor, Effect, Visitor}; /// A type that can be walked. pub trait Walk<'ctx>: Sized { @@ -9,6 +9,10 @@ pub trait Walk<'ctx>: Sized { type Walker: Walker<'ctx> + From<Self>; } +pub fn into_walker<'ctx, T: Walk<'ctx>>(value: T) -> T::Walker { + <T as Walk<'ctx>>::Walker::from(value) +} + /// Walker for a type. /// /// The `'ctx` lifetime is some lifetime that is longer than `Self`. @@ -19,7 +23,8 @@ pub trait Walk<'ctx>: Sized { /// - Call [Self::walk()] to walk the value. Data will be sent to the provided /// visitor. pub trait Walker<'ctx> { - /// Error that can happen while walking the value. + type Effect: Effect; + type Error; /// An arbitrary type the walker is left with after walking. @@ -30,5 +35,9 @@ pub trait Walker<'ctx> { /// Walk the value. /// /// The walker should send data to the `visitor` as it walks the value. - fn walk(self, visitor: Visitor<'_, 'ctx>) -> Result<Self::Output, Self::Error>; + #[must_use] + fn walk<'a>( + self, + visitor: &'a mut Visitor<'ctx>, + ) -> ControlFlowFor<'a, Self::Effect, Self::Output, Self::Error>; } diff --git a/tests/async.rs b/tests/async.rs new file mode 100644 index 0000000..2c446a1 --- /dev/null +++ b/tests/async.rs @@ -0,0 +1,148 @@ +use std::{collections::VecDeque, future::Future, ops::ControlFlow, pin::Pin}; +use treaty::{ + any::{any_trait, static_wrapper::OwnedStatic}, + async_build_with, build_with, into_walker, + protocol::{ + visitor::{Sequence, SequenceScope, Status, Value}, + AsyncEffect, ControlFlowFor, Visitor, + }, + Builder, Walk, Walker, +}; + +#[tokio::test] +async fn demo() { + let a = Data::Sequence(vec![ + Data::Bool(true), + Data::Sequence(vec![Data::Bool(false), Data::Bool(true)]), + Data::Bool(false), + ]); + + let s = async_build_with::<JsonLike, _>(into_walker(a)) + .await + .unwrap(); + + assert_eq!(s, "[true,[false,true,],false,]"); +} + +#[derive(Debug)] +enum Data { + Bool(bool), + Sequence(Vec<Data>), +} + +const _: () = { + struct Impl(Data); + + impl From<Data> for Impl { + fn from(value: Data) -> Self { + Self(value) + } + } + + impl<'ctx> Walk<'ctx> for Data { + type Walker = Impl; + } + + impl<'ctx> Walker<'ctx> for Impl { + type Effect = AsyncEffect; + + type Error = (); + + type Output = (); + + fn walk<'a>(self, visitor: &'a mut Visitor<'ctx>) -> ControlFlowFor<'a, AsyncEffect> { + Box::pin(async { + match self.0 { + Data::Bool(value) => walk_bool(value, visitor), + Data::Sequence(value) => walk_vec(value, visitor).await, + } + core::ops::ControlFlow::Continue(()) + }) + } + } +}; + +fn walk_bool(value: bool, visitor: &mut Visitor<'_>) { + visitor + .upcast_mut::<dyn Value<OwnedStatic<bool>>>() + .unwrap() + .visit(OwnedStatic(value)); +} + +async fn walk_vec(value: Vec<Data>, visitor: &mut Visitor<'_>) { + struct Scope(VecDeque<Data>); + + impl<'ctx> SequenceScope<'ctx, AsyncEffect> for Scope { + fn next<'a>( + &'a mut self, + visitor: &'a mut Visitor<'ctx>, + ) -> ControlFlowFor<'a, AsyncEffect, Status> { + Box::pin(async { + if let Some(value) = self.0.pop_front() { + into_walker(value).walk(visitor).await; + + ControlFlow::Continue(Status::Continue) + } else { + ControlFlow::Continue(Status::Done) + } + }) + } + } + + let mut scope = Scope(value.into()); + + visitor + .upcast_mut::<dyn Sequence<'_, AsyncEffect>>() + .unwrap() + .visit(&mut scope) + .await; +} + +#[derive(Default)] +struct JsonLike(String); + +impl<'ctx> Builder<'ctx> for JsonLike { + type Error = (); + + type Value = String; + + fn as_visitor(&mut self) -> &mut Visitor<'ctx> { + self + } + + fn build(self) -> Result<Self::Value, Self::Error> { + Ok(self.0) + } +} + +any_trait! { + impl['a, 'ctx] JsonLike = [ + dyn Value<'a, OwnedStatic<bool>> + 'a, + dyn Sequence<'ctx, AsyncEffect> + 'a, + ]; +} + +impl Value<'_, OwnedStatic<bool>> for JsonLike { + fn visit(&mut self, value: OwnedStatic<bool>) -> core::ops::ControlFlow<()> { + self.0.push_str(&format!("{}", value.0)); + std::ops::ControlFlow::Continue(()) + } +} + +impl<'ctx> Sequence<'ctx, AsyncEffect> for JsonLike { + fn visit<'a>( + &'a mut self, + scope: &'a mut dyn SequenceScope<'ctx, AsyncEffect>, + ) -> ControlFlowFor<'a, AsyncEffect> { + Box::pin(async { + self.0.push_str("["); + while let std::ops::ControlFlow::Continue(treaty::protocol::visitor::Status::Continue) = + scope.next(self).await + { + self.0.push_str(","); + } + self.0.push_str("]"); + std::ops::ControlFlow::Continue(()) + }) + } +} diff --git a/tests/demo.rs b/tests/demo.rs index 6f3565b..9baf602 100644 --- a/tests/demo.rs +++ b/tests/demo.rs @@ -1,9 +1,10 @@ -use std::{any::TypeId, collections::VecDeque, ops::ControlFlow}; +use std::{collections::VecDeque, ops::ControlFlow}; use treaty::{ any::{any_trait, static_wrapper::OwnedStatic}, + build_with, into_walker, protocol::{ visitor::{Sequence, SequenceScope, Value}, - Visitor, + SyncEffect, Visitor, }, Builder, Walk, Walker, }; @@ -16,11 +17,9 @@ fn demo() { Data::Bool(false), ]); - let mut builder = JsonLike::default(); - <Data as Walk>::Walker::from(a).walk(builder.as_visitor()).unwrap(); - dbg!(builder.build().unwrap()); + let s = build_with::<JsonLike, _>(into_walker(a)).unwrap(); - todo!() + assert_eq!(s, "[true,[false,true,],false,]"); } #[derive(Debug)] @@ -43,37 +42,42 @@ const _: () = { } impl<'ctx> Walker<'ctx> for Impl { + type Effect = SyncEffect; + type Error = (); type Output = (); - fn walk(self, visitor: Visitor<'_, 'ctx>) -> Result<Self::Output, Self::Error> { + fn walk( + self, + visitor: &mut Visitor<'ctx>, + ) -> core::ops::ControlFlow<Self::Error, Self::Output> { match self.0 { Data::Bool(value) => walk_bool(value, visitor), Data::Sequence(value) => walk_vec(value, visitor), } - Ok(()) + core::ops::ControlFlow::Continue(()) } } }; -fn walk_bool(value: bool, visitor: Visitor<'_, '_>) { +fn walk_bool(value: bool, visitor: &mut Visitor<'_>) { visitor .upcast_mut::<dyn Value<OwnedStatic<bool>>>() .unwrap() .visit(OwnedStatic(value)); } -fn walk_vec(value: Vec<Data>, visitor: Visitor<'_, '_>) { +fn walk_vec(value: Vec<Data>, visitor: &mut Visitor<'_>) { struct Scope(VecDeque<Data>); impl<'ctx> SequenceScope<'ctx> for Scope { fn next( &mut self, - visitor: Visitor<'_, 'ctx>, - ) -> treaty::protocol::ControlFlow<(), treaty::protocol::visitor::Status> { + visitor: &mut Visitor<'ctx>, + ) -> core::ops::ControlFlow<(), treaty::protocol::visitor::Status> { if let Some(value) = self.0.pop_front() { - <<Data as Walk>::Walker>::from(value).walk(visitor).unwrap(); + into_walker(value).walk(visitor); ControlFlow::Continue(treaty::protocol::visitor::Status::Continue) } else { @@ -98,7 +102,7 @@ impl<'ctx> Builder<'ctx> for JsonLike { type Value = String; - fn as_visitor(&mut self) -> Visitor<'_, 'ctx> { + fn as_visitor(&mut self) -> &mut Visitor<'ctx> { self } @@ -109,20 +113,20 @@ impl<'ctx> Builder<'ctx> for JsonLike { any_trait! { impl['a, 'ctx] JsonLike = [ - dyn Value<OwnedStatic<bool>> + 'a, + dyn Value<'a, OwnedStatic<bool>> + 'a, dyn Sequence<'ctx> + 'a, ]; } -impl Value<OwnedStatic<bool>> for JsonLike { - fn visit(&mut self, value: OwnedStatic<bool>) -> treaty::protocol::ControlFlow { +impl Value<'_, OwnedStatic<bool>> for JsonLike { + fn visit(&mut self, value: OwnedStatic<bool>) -> core::ops::ControlFlow<()> { self.0.push_str(&format!("{}", value.0)); ControlFlow::Continue(()) } } impl<'ctx> Sequence<'ctx> for JsonLike { - fn visit(&mut self, scope: &mut dyn SequenceScope<'ctx>) -> treaty::protocol::ControlFlow { + fn visit(&mut self, scope: &mut dyn SequenceScope<'ctx>) -> core::ops::ControlFlow<()> { self.0.push_str("["); while let ControlFlow::Continue(treaty::protocol::visitor::Status::Continue) = scope.next(self) |