Diffstat (limited to 'tests/async.rs')
-rw-r--r--tests/async.rs148
1 files changed, 148 insertions, 0 deletions
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(())
+ })
+ }
+}