//! # Design
//!
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
#![deny(elided_lifetimes_in_paths)]
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod any;
mod build;
pub mod effect;
pub mod hkt;
pub mod protocol;
pub mod symbol;
mod walk;
// pub mod impls;
mod transform;
// pub use build::Build;
// pub use build::Builder;
//
// pub use walk::Walk;
// pub use walk::Walker;
use core::ops::ControlFlow;
pub use build::*;
use effect::{Effect, Future};
use macros::TagError;
use protocol::Visitor;
use symbol::Symbol;
pub use transform::*;
pub use walk::*;
// #[doc(hidden)]
pub mod macros;
pub mod never {
mod sealed {
pub trait Extract {
type Never;
}
impl<T> Extract for fn() -> T {
type Never = T;
}
}
/// Test
pub type Never = <fn() -> ! as sealed::Extract>::Never;
}
pub const TAG_TYPE_NAME: Symbol = Symbol::new("Type Name");
pub const TAG_TYPE_ID: Symbol = Symbol::new("Type ID");
pub const TAG_TYPE_SIZE: Symbol = Symbol::new("Type Size");
pub const TAG_FIELD_NAMES: Symbol = Symbol::new("Field Names");
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_FIELD: Symbol = Symbol::new("Field");
pub const TAG_ENUM: Symbol = Symbol::new("Enum");
pub enum DefaultMode {}
#[derive(Debug)]
pub enum StructWalkError {
Tag(TagError<never::Never>),
}
#[must_use]
pub enum Flow {
/// Processing should continue as normal.
Continue,
/// Processing should stop.
///
/// This state signals some error happened.
Break,
/// Processing should stop.
Done,
}
#[macro_export]
macro_rules! Walk {
{
$(#[$($attr:tt)*])*
$vis:vis struct $name:ident {$(
$field:ident: $type:ty
),* $(,)?}
} => {
const _: () = {
impl<'ctx, M, E: $crate::effect::Effect<'ctx>> $crate::Walk<'ctx, M, E> for &'ctx $name {
type Walker = Walker<'ctx, M>;
fn into_walker(self) -> Self::Walker {
Walker {
value: self,
field: 0,
_marker: ::core::marker::PhantomData,
}
}
}
impl<'ctx> $crate::WalkerTypes for &'ctx $name {
type Error = $crate::StructWalkError;
type Output = ();
}
pub struct Walker<'ctx, M> {
value: &'ctx $name,
field: usize,
_marker: ::core::marker::PhantomData<fn() -> M>,
}
impl<'ctx, M> $crate::WalkerTypes for Walker<'ctx, M> {
type Error = $crate::StructWalkError;
type Output = ();
}
impl<'ctx, E: $crate::effect::Effect<'ctx>, M> $crate::Walker<'ctx, E> for Walker<'ctx, M> {
fn walk<'a>(
mut self,
visitor: $crate::protocol::Visitor<'a, 'ctx>,
) -> $crate::effect::Future<'a, 'ctx, Result<Self::Output, Self::Error>, E>
where
Self: 'a
{
E::wrap(async move {
// We should check if the visitor wants something specific.
if let Some(object) = visitor.upcast_mut::<$crate::protocol::visitor::request_hint::DynRequestHint<'_, 'ctx, E>>() {
// Allow the visitor to give a hint if it wants.
match object.request_hint(&mut self).await {
$crate::Flow::Continue => {
// The visitor wants the walker to continue to it's normal
// walking.
},
_ => {
// The visitor is done (either because of an error or because
// it already used a hint).
return Ok(());
},
}
}
// 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,
$crate::any::static_wrapper::BorrowedStatic(self.value)
).await, $crate::protocol::visitor::Status::Skipped) {
return Ok(());
}
// Describe the struct in a general way:
// Give the type ID
match $crate::macros::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!(),
}
// Signal this is a struct.
match $crate::macros::visit_tag::<{ $crate::TAG_STRUCT.to_int() }, E, _>(
visitor,
$crate::walkers::core::noop::NoopWalker::new()
).await {
Ok($crate::Flow::Continue) => {},
_ => return Ok(()),
}
// Give the type name.
match $crate::macros::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!(),
}
// Give the field names before hand.
match $crate::macros::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!(),
}
if !matches!($crate::protocol::visitor::sequence::visit_sequence::<E>(
visitor,
&mut self,
).await, $crate::protocol::visitor::Status::Skipped) {
return Ok(());
}
Ok(())
})
}
}
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 {
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, _>(
visitor,
$crate::walkers::core::noop::NoopWalker::new()
).await {
Ok($crate::Flow::Continue) => {},
_ => return Flow::Done,
}
match $crate::macros::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!(),
}
let walker = $crate::Walk::<M, E>::into_walker(&self.value.$field);
$crate::Walker::<E>::walk(walker, visitor).await;
self.field += 1;
Flow::Continue
}
)*
_ => Flow::Done
}
})
}
fn size_hint<'a>(&'a mut self) -> Future<'a, 'ctx, (usize, Option<usize>), E> {
const X: &[&str] = &[$(stringify!($field)),*];
E::ready((X.len(), Some(X.len())))
}
}
$crate::any::any_trait! {
impl['a, 'ctx, M] Walker<'ctx, M> = [
// dyn Hint<'a, 'ctx, OwnedStatic<bool>, E> + 'a,
]
}
};
};
}
#[cfg(test)]
mod test {
use crate::effect::{BlockOn, Blocking, Spin};
use super::*;
use macro_rules_attribute::derive;
#[derive(Walk!)]
struct Demo {
a: bool,
b: bool,
other: Other,
}
#[derive(Walk!)]
struct Other {
value: bool,
}
#[test]
fn demo() {
let value = Demo { a: true, b: false, other: Other { value: true } };
let walker = Walk::<DefaultMode, Blocking>::into_walker(&value);
let mut visitor = builders::debug::Visitor::<Blocking>::new();
dbg!(Spin::block_on(Walker::<Blocking>::walk(
walker,
&mut visitor
)));
todo!();
}
}