1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::{
    effect::{Blocking, ReadyValue},
    transform, DefaultMode,
};

#[macro_export]
macro_rules! Build {
    {
        $(#[$($attr:tt)*])*
        $vis:vis struct $name:ident {$(
            $fvis:vis $field:ident: $type:ty
        ),* $(,)?}
    } => {
        const _: () = {
            impl<'ctx, M, E: $crate::effect::Effect> $crate::Build<'ctx, M, E> for $name {
                type Builder = $crate::builders::core::r#struct::StructBuilder<'ctx, Info, M, E>;
            }

            $vis struct Builders<'ctx, M, E: $crate::effect::Effect> {
                $($field: <$type as $crate::Build<'ctx, M, E>>::Builder),*
            }

            #[derive(Copy, Clone, Debug)]
            $vis enum Field {
                $($field),*
            }

            mod field_index {
                enum __Fields {
                    $($field),*
                }

                $(pub const $field: usize = __Fields::$field as usize;)*
            }

            #[derive(Debug)]
            $vis enum Error {
                $($field(<$type as $crate::BuilderTypes>::Error)),*
            }

            $vis struct Info;

            impl<'ctx, M, E: $crate::effect::Effect> $crate::builders::core::r#struct::StructTypeInfo<'ctx, M, E> for Info {
                type Builders = Builders<'ctx, M, E>;
                type FieldMarker = Field;
                type T = $name;
                type Error = Error;
                type Seed = ($(<$type as $crate::BuilderTypes>::Seed),*);

                fn new_builders<'a>(seed: Self::Seed) -> $crate::effect::Future<'a, Self::Builders, E> {
                    let ($($field),*) = seed;

                    E::wrap(async move {
                        Builders {
                            $($field: $crate::Builder::<E>::from_seed($field).await),*
                        }
                    })
                }

                fn from_builders<'a>(builders: Self::Builders) -> $crate::effect::Future<'a, Result<Self::T, Self::Error>, E> {
                    use $crate::Builder;

                    E::wrap(async {
                        Ok($name {
                            $($field: builders.$field.build().await.map_err(Error::$field)?),*
                        })
                    })
                }

                fn as_visitor<'a>(
                    marker: Self::FieldMarker,
                    builders: &'a mut Self::Builders,
                ) -> $crate::protocol::DynVisitor<'a, 'ctx> {
                    use $crate::Builder;

                    match marker {
                        $(Field::$field => builders.$field.as_visitor()),*
                    }
                }

                fn marker_from_index(index: usize) -> Option<Self::FieldMarker> {
                    match index {
                        $(field_index::$field => Some(Field::$field),)*
                        _ => None
                    }
                }

                fn marker_from_name(name: &str) -> Option<Self::FieldMarker> {
                    match name {
                        $(stringify!($field) => Some(Field::$field),)*
                        _ => None
                    }
                }
            }
        };
    }
}

#[test]
fn demo() {
    use crate::Walk;
    use macro_rules_attribute::derive;

    #[derive(Build!, Walk!, Debug)]
    struct X {
        a: bool,
        b: bool,
    }

    #[derive(Build!, Walk!, Debug)]
    struct Y {
        b: bool,
        a: bool,
    }

    let value = X { a: true, b: false };

    let other = transform::<<Y as crate::Build<'_, DefaultMode, _>>::Builder, _, Blocking>(
        ((), ()),
        Walk::<DefaultMode, _>::into_walker(&value),
    )
    .value();

    dbg!(other);

    // todo!();
}