-rw-r--r--protocols.md52
-rw-r--r--src/build/builders/debug.rs7
-rw-r--r--src/lib.rs81
-rw-r--r--src/macros.rs120
-rw-r--r--src/protocol/visitor/tag.rs131
-rw-r--r--src/walk/walkers/core/tag.rs3
6 files changed, 224 insertions, 170 deletions
diff --git a/protocols.md b/protocols.md
index f79e1c9..8d80900 100644
--- a/protocols.md
+++ b/protocols.md
@@ -1,7 +1,45 @@
-struct
-- name
-- type_id
-- fields
- - name
- - index
- - type_id
+# Walker
+
+## General Layout
+All the standard flows use the following structure.
+
+- Request Hint: Allow the builder to get exactly what it wants instead of using the full flow.
+- Header Tags: Tags for meta information about the value. These are what provide the rich structure of data.
+- Sequence / Value: The two basic ways to pass data are by a sequence of walkers or a single value.
+
+Nothing stops a walker from emitting multiple values to form a sequence. However, most builders
+will not expect this behavior. Instead, the sequence protocol allows a builder to construct a sequence
+of values *it* wants instead of what the walker is generating.
+
+## Map
+A sequence of key tagged values
+
+A map emits everything a plain sequence would.
+This allows using maps with builders that only undertand sequences.
+Note, the keys of the values will be lost if the builder doesn't understand key tags.
+
+- Tag - Map: A empty value that signals the value is a map instead of a normal sequence.
+- Sequence: A sequence of the key tagged values.
+ - Tag - Key: A (hopefully unique) key for the value.
+ - <flatten>: The value.
+
+## Struct
+A struct in Rust and more generally as a map.
+
+A struct emits everything a map would, and also it emits everything a plain sequence would.
+This allows using structs with builders that only understand maps and/or sequences.
+
+- Request Hint: Allow the builder to directly ask for what it wants.
+- Value: Attempt to give the struct directly to the builder.
+- Tag - Type ID: Useful for looking up things in a type map. Only visited if the struct is a Rust type.
+- Tag - Struct: A empty value that signals the value is a struct instead of just a map.
+ - Tag - Map: This only happens if the visitor didn't recognize the struct tag.
+- Tag - Type Name: The name of the type.
+- Tag - Field Names: All the field names of the struct.
+ - Sequence: A sequence of strings.
+ - Value: Field name as a `&'static str`.
+- Sequence: A sequence of the fields.
+ - Tag - Field: A empty value that signals the value is a struct field instead of just a map key.
+ - Tag - Key: The field name.
+ - Value: Field name as a `&'static str`.
+ - <flatten>: The value.
diff --git a/src/build/builders/debug.rs b/src/build/builders/debug.rs
index c8f9324..4a51281 100644
--- a/src/build/builders/debug.rs
+++ b/src/build/builders/debug.rs
@@ -10,7 +10,7 @@ use crate::{
visitor::{
request_hint::{DynRequestHint, RequestHint},
sequence::{DynSequence, Sequence},
- tag::{Dyn, DynTag, Tag},
+ tag::{Dyn, DynTag, Tag, DynFlow},
value::{DynValue, Value},
},
},
@@ -64,7 +64,7 @@ impl<'ctx, E: Effect<'ctx>> Tag<'ctx, Dyn, E> for Visitor<E> {
&'a mut self,
kind: Dyn,
walker: DynWalker<'a, 'ctx, E>,
- ) -> Future<'a, 'ctx, Flow, E> {
+ ) -> Future<'a, 'ctx, DynFlow, E> {
self.tab();
println!("Visit tag: {}", kind.0);
@@ -72,7 +72,8 @@ impl<'ctx, E: Effect<'ctx>> Tag<'ctx, Dyn, E> for Visitor<E> {
self.0 += 1;
let result = walker.walk(self).await;
self.0 -= 1;
- result
+ // DynFlow::Flow(result)
+ DynFlow::Skipped
})
}
}
diff --git a/src/lib.rs b/src/lib.rs
index c1709cc..7733233 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -28,8 +28,7 @@ use core::ops::ControlFlow;
pub use build::*;
use effect::{Effect, Future};
-use macros::TagError;
-use protocol::Visitor;
+use protocol::{Visitor, visitor::tag::TagError};
use symbol::Symbol;
pub use transform::*;
pub use walk::*;
@@ -60,6 +59,8 @@ pub const TAG_KEY: Symbol = Symbol::new("Key");
pub const TAG_SEQ_INDEX: Symbol = Symbol::new("Seq Index");
pub const TAG_SEQ_LEN: Symbol = Symbol::new("Seq Length");
pub const TAG_STRUCT: Symbol = Symbol::new("Struct");
+pub const TAG_MAP: Symbol = Symbol::new("Map");
+pub const TAG_SEQ: Symbol = Symbol::new("Sequence");
pub const TAG_FIELD: Symbol = Symbol::new("Field");
pub const TAG_ENUM: Symbol = Symbol::new("Enum");
@@ -68,6 +69,7 @@ pub enum DefaultMode {}
#[derive(Debug)]
pub enum StructWalkError {
Tag(TagError<never::Never>),
+ FieldTag(TagError<TagError<never::Never>>),
}
#[must_use]
@@ -146,12 +148,6 @@ macro_rules! Walk {
}
}
- // Follow the standard set of protocols for a struct.
- // - Tagged: struct name
- // - Tagged: struct type
- // - Tagged: struct field names
- // - Sequence: the fields
-
// Attempt to visit the value directly.
if !matches!($crate::protocol::visitor::value::visit_value::<_, E>(
visitor,
@@ -160,48 +156,67 @@ macro_rules! Walk {
return Ok(());
}
+ // Follow the standard set of protocols for a struct.
+ // - Tagged: struct name
+ // - Tagged: struct type
+ // - Tagged: struct field names
+ // - Sequence: the fields
+
// Describe the struct in a general way:
+ use $crate::protocol::visitor::Status as S;
+
// Give the type ID
- match $crate::macros::visit_tag::<{ $crate::TAG_TYPE_ID.to_int() }, E, _>(
+ match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_TYPE_ID.to_int() }, E, _>(
visitor,
$crate::walkers::core::value::ValueWalker::new(::core::any::TypeId::of::<$name>())
).await {
- Ok($crate::Flow::Continue) => {},
- Ok(_) => return Ok(()),
- Err(_) => unreachable!(),
+ Ok(S::Skipped) | Ok(S::Continue) => {},
+ Ok(S::Break) => return Ok(()),
+ Err(err) => return Err(StructWalkError::Tag(err)),
}
// Signal this is a struct.
- match $crate::macros::visit_tag::<{ $crate::TAG_STRUCT.to_int() }, E, _>(
+ match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_STRUCT.to_int() }, E, _>(
visitor,
$crate::walkers::core::noop::NoopWalker::new()
).await {
- Ok($crate::Flow::Continue) => {},
- _ => return Ok(()),
+ Ok(S::Skipped) => {
+ // If can't tag as a struct try as a map.
+ match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_MAP.to_int() }, E, _>(
+ visitor,
+ $crate::walkers::core::noop::NoopWalker::new()
+ ).await {
+ Ok(S::Skipped) | Ok(S::Continue) => {},
+ Ok(S::Break) => return Ok(()),
+ Err(err) => return Err(StructWalkError::Tag(err)),
+ }
+ },
+ Ok(S::Continue) => {},
+ Ok(S::Break) => return Ok(()),
+ Err(err) => return Err(StructWalkError::Tag(err)),
}
// Give the type name.
- match $crate::macros::visit_tag::<{ $crate::TAG_TYPE_NAME.to_int() }, E, _>(
+ match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_TYPE_NAME.to_int() }, E, _>(
visitor,
$crate::walkers::core::value::ValueWalker::new(stringify!($name))
).await {
- Ok($crate::Flow::Continue) => {},
- Ok(_) => return Ok(()),
- Err(_) => unreachable!(),
+ Ok(S::Skipped) | Ok(S::Continue) => {},
+ Ok(S::Break) => return Ok(()),
+ Err(err) => return Err(StructWalkError::Tag(err)),
}
// Give the field names before hand.
- match $crate::macros::visit_tag::<{ $crate::TAG_FIELD_NAMES.to_int() }, E, _>(
+ match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_FIELD_NAMES.to_int() }, E, _>(
visitor,
$crate::walkers::core::tag::StaticSliceWalker::<_, $crate::walkers::core::value::ValueWalker<&str>>::new(&[$(
stringify!($field)
),*])
).await {
- Ok($crate::Flow::Continue) => {},
- Ok(_) => return Ok(()),
- // Err(err) => return Err(StructWalkError::Tag(err)),
- Err(err) => todo!(),
+ Ok(S::Skipped) | Ok(S::Continue) => {},
+ Ok(S::Break) => return Ok(()),
+ Err(err) => return Err(StructWalkError::FieldTag(err)),
}
if !matches!($crate::protocol::visitor::sequence::visit_sequence::<E>(
@@ -219,26 +234,30 @@ macro_rules! Walk {
impl<'ctx, E: $crate::effect::Effect<'ctx>, M> $crate::protocol::visitor::sequence::SequenceScope<'ctx, E> for Walker<'ctx, M> {
fn next<'a>(&'a mut self, visitor: Visitor<'a, 'ctx>) -> Future<'a, 'ctx, Flow, E> {
E::wrap(async {
+ use $crate::protocol::visitor::Status as S;
+
let mut index = 0;
match self.field {$(
field if { index += 1; field == index - 1 } => {
- match $crate::macros::visit_tag::<{ $crate::TAG_FIELD.to_int() }, E, _>(
+ match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_FIELD.to_int() }, E, _>(
visitor,
$crate::walkers::core::noop::NoopWalker::new()
).await {
- Ok($crate::Flow::Continue) => {},
- _ => return Flow::Done,
+ Ok(S::Skipped) | Ok(S::Continue) => {},
+ Ok(S::Break) => return Flow::Break,
+ Err(err) => return Flow::Break,
}
- match $crate::macros::visit_tag::<{ $crate::TAG_KEY.to_int() }, E, _>(
+ match $crate::protocol::visitor::tag::visit_tag::<{ $crate::TAG_KEY.to_int() }, E, _>(
visitor,
$crate::walkers::core::value::ValueWalker::new(stringify!($field))
).await {
- Ok($crate::Flow::Continue) => {},
- Ok(_) => return Flow::Done,
- Err(_) => unreachable!(),
+ Ok(S::Skipped) | Ok(S::Continue) => {},
+ Ok(S::Break) => return Flow::Break,
+ Err(err) => return Flow::Break,
}
+ // Use the type's walker in the same mode as the parent struct.
let walker = $crate::Walk::<M, E>::into_walker(&self.value.$field);
$crate::Walker::<E>::walk(walker, visitor).await;
self.field += 1;
diff --git a/src/macros.rs b/src/macros.rs
index ffb52b1..e69de29 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -1,120 +0,0 @@
-use crate::{
- effect::{Effect, Future},
- protocol::{
- visitor::tag::{self, DynTag},
- Visitor,
- },
- symbol::Symbol,
- DynWalkerAdapter, DynWalkerError, Flow, Walker, WalkerTypes,
-};
-
-#[derive(Debug)]
-pub enum TagErrorKind<E> {
- NeverWalked,
-
- /// This can only happen if a panic happens furing the walk and is then caught before calling
- /// finish..
- WalkNeverFinished,
-
- Walker(E),
-}
-
-#[derive(Debug)]
-pub struct TagError<E> {
- symbol: Symbol,
- err: TagErrorKind<E>,
-}
-
-impl<E> TagError<E> {
- fn new<const SYMBOL: u64>(err: E) -> Self {
- Self {
- symbol: Symbol::from_int(SYMBOL),
- err: TagErrorKind::Walker(err),
- }
- }
-
- fn never_walked<const SYMBOL: u64>() -> Self {
- Self {
- symbol: Symbol::from_int(SYMBOL),
- err: TagErrorKind::NeverWalked,
- }
- }
-
- fn walk_never_finished<const SYMBOL: u64>() -> Self {
- Self {
- symbol: Symbol::from_int(SYMBOL),
- err: TagErrorKind::WalkNeverFinished,
- }
- }
-}
-
-pub fn visit_tag<'a, 'ctx: 'a, const SYMBOL: u64, E: Effect<'ctx>, W: Walker<'ctx, E> + 'a>(
- visitor: Visitor<'a, 'ctx>,
- walker: W,
-) -> Future<'a, 'ctx, Result<Flow, TagError<W::Error>>, E>
-where
- W: WalkerTypes<Output = ()>,
-{
- E::wrap(async {
- let result =
- if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, tag::Const<SYMBOL>, E>>() {
- // The visitor knows about this tag at compile time.
-
- // Wrap the walker to allow it to be passed to a dyn walker argument.
- let mut name_walker = DynWalkerAdapter::new(walker);
-
- // Visit the tag.
- let flow = object.visit(tag::Const, &mut name_walker).await;
-
- // Finish the dynamic walker to get any errors from it.
- let result = name_walker.finish();
-
- Some((flow, result))
- } else if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, tag::Dyn, E>>() {
- // The visitor can handle dynamic tags.
- // If the visitor can't handle the tag kind then it can call .skip on the walker
- // to disable the error for not walking it.
-
- // Wrap the walker to allow it to be passed to a dyn walker argument.
- let mut name_walker = DynWalkerAdapter::new(walker);
-
- // Visit the tag.
- let flow = object
- .visit(tag::Dyn(Symbol::from_int(SYMBOL)), &mut name_walker)
- .await;
-
- // Finish the dynamic walker to get any errors from it.
- let result = name_walker.finish();
-
- Some((flow, result))
- } else {
- None
- };
-
- let Some(result) = result else {
- // The visitor doesn't know about this tag protocol so just continue as normal.
- return Ok(Flow::Continue);
- };
-
- match result {
- // The happy path.
- (Flow::Continue, Ok(_)) => Ok(Flow::Continue),
-
- // The visitor wants to stop the control flow for some reason.
- (Flow::Break, Ok(_)) => Ok(Flow::Break),
- (Flow::Done, Ok(_)) => Ok(Flow::Done),
-
- // The walker had an error.
- (_, Err(DynWalkerError::Walker(err))) => Err(TagError::new::<SYMBOL>(err)),
-
- // The visitor never walked the dynamic walker. Aka it didn't call walk.
- (_, Err(DynWalkerError::NeverWalked(_))) => Err(TagError::never_walked::<SYMBOL>()),
-
- // This is very hard to cause. The visitor must panic inside of .walk but then
- // catch it in .visit.
- (_, Err(DynWalkerError::WalkNeverFinished)) => {
- Err(TagError::walk_never_finished::<SYMBOL>())
- }
- }
- })
-}
diff --git a/src/protocol/visitor/tag.rs b/src/protocol/visitor/tag.rs
index 1be53ae..2747c95 100644
--- a/src/protocol/visitor/tag.rs
+++ b/src/protocol/visitor/tag.rs
@@ -1,18 +1,18 @@
-use core::ops::ControlFlow;
-
use crate::{
- any::{TypeName, TypeNameable},
effect::{Effect, Future},
higher_ranked_type,
hkt::AnySend,
nameable,
- never::Never,
- protocol::{walker::hint::HintMeta, Visitor, Walker},
+ protocol::{walker::hint::HintMeta, Visitor},
symbol::Symbol,
- DynWalker, Flow,
+ DynWalker, Flow, WalkerTypes, DynWalkerAdapter, DynWalkerError,
};
+use super::Status;
+
pub trait Kind: 'static {
+ type Flow: Send;
+
fn symbol(&self) -> Symbol;
}
@@ -20,12 +20,21 @@ pub struct Const<const SYMBOL: u64>;
pub struct Dyn(pub Symbol);
impl<const SYMBOL: u64> Kind for Const<SYMBOL> {
+ type Flow = Flow;
+
fn symbol(&self) -> Symbol {
Symbol::from_int(SYMBOL)
}
}
+pub enum DynFlow {
+ Skipped,
+ Flow(Flow),
+}
+
impl Kind for Dyn {
+ type Flow = DynFlow;
+
fn symbol(&self) -> Symbol {
self.0
}
@@ -36,7 +45,7 @@ pub trait Tag<'ctx, K: Kind, E: Effect<'ctx>> {
&'a mut self,
kind: K,
walker: DynWalker<'a, 'ctx, E>,
- ) -> Future<'a, 'ctx, Flow, E>;
+ ) -> Future<'a, 'ctx, K::Flow, E>;
}
pub type DynTag<'a, 'ctx, K, E> = dyn Tag<'ctx, K, E> + Send + 'a;
@@ -74,3 +83,111 @@ impl<'a, 'ctx: 'a, K, E> HintMeta<'ctx> for DynTag<'a, 'ctx, K, E> {
type Hint = Hint<K>;
}
+
+#[derive(Debug)]
+pub enum TagErrorKind<E> {
+ NeverWalked,
+
+ /// This can only happen if a panic happens furing the walk and is then caught before calling
+ /// finish..
+ WalkNeverFinished,
+
+ Walker(E),
+}
+
+#[derive(Debug)]
+pub struct TagError<E> {
+ symbol: Symbol,
+ err: TagErrorKind<E>,
+}
+
+impl<E> TagError<E> {
+ fn new<const SYMBOL: u64>(err: E) -> Self {
+ Self {
+ symbol: Symbol::from_int(SYMBOL),
+ err: TagErrorKind::Walker(err),
+ }
+ }
+
+ fn never_walked<const SYMBOL: u64>() -> Self {
+ Self {
+ symbol: Symbol::from_int(SYMBOL),
+ err: TagErrorKind::NeverWalked,
+ }
+ }
+
+ fn walk_never_finished<const SYMBOL: u64>() -> Self {
+ Self {
+ symbol: Symbol::from_int(SYMBOL),
+ err: TagErrorKind::WalkNeverFinished,
+ }
+ }
+}
+
+
+pub fn visit_tag<'a, 'ctx: 'a, const SYMBOL: u64, E: Effect<'ctx>, W: crate::Walker<'ctx, E> + 'a>(
+ visitor: Visitor<'a, 'ctx>,
+ walker: W,
+) -> Future<'a, 'ctx, Result<Status, TagError<W::Error>>, E>
+where
+ W: WalkerTypes<Output = ()>,
+{
+ E::wrap(async {
+ let result =
+ if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, Const<SYMBOL>, E>>() {
+ // The visitor knows about this tag at compile time.
+
+ // Wrap the walker to allow it to be passed to a dyn walker argument.
+ let mut name_walker = DynWalkerAdapter::new(walker);
+
+ // Visit the tag.
+ let flow = object.visit(Const, &mut name_walker).await;
+
+ // Finish the dynamic walker to get any errors from it.
+ let result = name_walker.finish();
+
+ (DynFlow::Flow(flow), result)
+ } else if let Some(object) = visitor.upcast_mut::<DynTag<'_, 'ctx, Dyn, E>>() {
+ // The visitor can handle dynamic tags.
+ // If the visitor can't handle the tag kind then it can call .skip on the walker
+ // to disable the error for not walking it.
+
+ // Wrap the walker to allow it to be passed to a dyn walker argument.
+ let mut name_walker = DynWalkerAdapter::new(walker);
+
+ // Visit the tag.
+ let flow = object
+ .visit(Dyn(Symbol::from_int(SYMBOL)), &mut name_walker)
+ .await;
+
+ // Finish the dynamic walker to get any errors from it.
+ let result = name_walker.finish();
+
+ (flow, result)
+ } else {
+ return Ok(Status::Skipped);
+ };
+
+ match result {
+ // The happy path.
+ (DynFlow::Flow(Flow::Continue), Ok(_)) => Ok(Status::Continue),
+
+ // The visitor wants to stop the control flow for some reason.
+ (DynFlow::Flow(Flow::Break), Ok(_)) => Ok(Status::Break),
+ (DynFlow::Flow(Flow::Done), Ok(_)) => Ok(Status::Break),
+ (DynFlow::Skipped, Ok(_)) => Ok(Status::Skipped),
+
+ // The walker had an error.
+ (_, Err(DynWalkerError::Walker(err))) => Err(TagError::new::<SYMBOL>(err)),
+
+ // The visitor never walked the dynamic walker. Aka it didn't call walk.
+ (_, Err(DynWalkerError::NeverWalked(_))) => Err(TagError::never_walked::<SYMBOL>()),
+
+ // This is very hard to cause. The visitor must panic inside of .walk but then
+ // catch it in .visit.
+ (_, Err(DynWalkerError::WalkNeverFinished)) => {
+ Err(TagError::walk_never_finished::<SYMBOL>())
+ }
+ }
+ })
+}
diff --git a/src/walk/walkers/core/tag.rs b/src/walk/walkers/core/tag.rs
index 5c5d144..3bbe522 100644
--- a/src/walk/walkers/core/tag.rs
+++ b/src/walk/walkers/core/tag.rs
@@ -4,13 +4,12 @@ use crate::{
any::static_wrapper::OwnedStatic,
any_trait,
effect::{Effect, Future},
- macros::{visit_tag, TagError},
never::Never,
protocol::{
visitor::{
request_hint::{visit_request_hint, DynRequestHint, RequestHint},
sequence::{DynSequence, SequenceScope},
- tag::DynTag,
+ tag::{DynTag, TagError},
Status,
value::{visit_value, DynValue, Value},
},