Diffstat (limited to 'src/macros/build.rs')
-rw-r--r--src/macros/build.rs171
1 files changed, 169 insertions, 2 deletions
diff --git a/src/macros/build.rs b/src/macros/build.rs
index 7eb3fbe..b02c123 100644
--- a/src/macros/build.rs
+++ b/src/macros/build.rs
@@ -107,7 +107,7 @@ macro_rules! Build {
marker: Self::FieldMarker,
builders: &'a mut Self::Builders,
) -> $crate::protocol::DynVisitor<'a, 'ctx> {
- use $crate::Builder;
+ use $crate::protocol::AsVisitor;
match marker {
$(Field::$field => builders.$field.as_visitor()),*
@@ -133,5 +133,172 @@ macro_rules! Build {
}
}
};
- }
+ };
+ {
+ $(#[$($attr:tt)*])*
+ $vis:vis enum $name:ident {$(
+ $variant:ident($value:ty)
+ ),* $(,)?}
+ } => {
+ #[allow(non_upper_case_globals, non_snake_case, non_camel_case_types)]
+ const _: () = {
+ // add a module here to seal fields.
+ impl<'ctx, M: 'ctx, E: $crate::effect::Effect> $crate::Build<'ctx, M, E> for $name {
+ type Builder = $crate::builders::core::r#enum::EnumBuilder<'ctx, __Info, M, E>;
+ }
+
+ $vis struct __Info;
+
+ $vis enum __Builders<'ctx, M, E: Effect> {
+ $($variant(<$value as $crate::Build<'ctx, M, E>>::Builder)),*
+ }
+
+ #[derive(Copy, Clone)]
+ $vis enum __Marker {
+ $($variant),*
+ }
+
+ impl ::core::fmt::Display for __Marker {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ f.write_str(match self {
+ $(Self::$variant => stringify!($variant)),*
+ })
+ }
+ }
+
+ mod variant_index {
+ enum __Variants {
+ $($variant),*
+ }
+
+ $(pub const $variant: u32 = __Variants::$variant as u32;)*
+ }
+
+ $vis struct __ErrorBuilder<'ctx, M, E: Effect> {
+ $($variant: Option<<<$value as $crate::Build<'ctx, M, E>>::Builder as BuilderTypes>::Error>),*
+ }
+
+ $vis enum __Error<'ctx, M, E: Effect> {
+ __Guess {
+ $($variant: <<$value as $crate::Build<'ctx, M, E>>::Builder as BuilderTypes>::Error),*
+ },
+ $($variant(<<$value as $crate::Build<'ctx, M, E>>::Builder as BuilderTypes>::Error)),*
+ }
+
+ impl<'ctx, M, E: Effect> ::core::fmt::Display for __Error<'ctx, M, E> {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ match self {
+ Self::__Guess {
+ $($variant),*
+ } => {
+ $(writeln!(f, "{}: {}", stringify!($variant), $variant)?;)*
+
+ Ok(())
+ }
+ $(Self::$variant(value) => write!(f, "{}: {}", stringify!($variant), value)),*
+ }
+ }
+ }
+
+ impl<'ctx, M, E: Effect> ::core::fmt::Debug for __Error<'ctx, M, E> {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ match self {
+ Self::__Guess {
+ $($variant),*
+ } => {
+ $(writeln!(f, "{}: {:?}", stringify!($variant), $variant)?;)*
+
+ Ok(())
+ }
+ $(Self::$variant(value) => write!(f, "{}: {:?}", stringify!($variant), value)),*
+ }
+ }
+ }
+
+ impl<'ctx, M: 'ctx, E: $crate::effect::Effect> $crate::builders::core::r#enum::EnumBuildInfo<'ctx, M, E> for __Info {
+ type Builders = __Builders<'ctx, M, E>;
+
+ type Seed = ();
+
+ type Error = __Error<'ctx, M, E>;
+
+ type ValueT = $crate::any::OwnedStatic<$name>;
+
+ type T = $name;
+
+ type VariantMarker = __Marker;
+
+ fn new_builder<'a>(
+ seed: Self::Seed,
+ variant: Self::VariantMarker,
+ ) -> ErasedEffective<'a, Self::Builders, E> {
+ match variant {
+ $(__Marker::$variant => {
+ Builder::<E>::from_seed(Default::default()).map(|builder| __Builders::$variant(builder))
+ })*
+ }
+ }
+
+ fn finish_builder<'a>(
+ builder: Self::Builders,
+ ) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E> {
+ match builder {
+ $(__Builders::$variant(builder) => builder.build().map(|value| value.map($name::$variant).map_err(__Error::$variant))),*
+ }
+ }
+
+ fn from_value<'a>(value: TypeName::T<'a, 'ctx, Self::ValueT>) -> Self::T {
+ value.0
+ }
+
+ fn as_visitor<'a>(builder: &'a mut Self::Builders) -> DynVisitor<'a, 'ctx> {
+ match builder {
+ $(__Builders::$variant(builder) => builder.as_visitor()),*
+ }
+ }
+
+ fn marker_from_name(name: &str) -> Option<Self::VariantMarker> {
+ match name {
+ $(stringify!($variant) => Some(__Marker::$variant),)*
+ _ => None
+ }
+ }
+
+ fn marker_from_discriminant(discriminant: u32) -> Option<Self::VariantMarker> {
+ match discriminant {
+ $(variant_index::$variant => Some(__Marker::$variant),)*
+ _ => None
+ }
+ }
+
+ fn guess_variant<'a>(
+ seed: Self::Seed,
+ scope: DynRecoverableScope<'a, 'ctx, E>,
+ ) -> ErasedEffective<'a, Result<Self::T, Self::Error>, E> {
+ E::ready((scope, Err(__ErrorBuilder::<M, E> { A: None, B: None })))
+ $(.or_else_update(|scope, result| {
+ let mut error = result.into_error();
+
+ <<$value as Build<M, E>>::Builder as Builder<_>>::from_seed(Default::default())
+ .map(|builder| (scope, builder))
+ .as_ctx(|(scope, builder)| scope.new_walk(builder.as_visitor()).cast())
+ .then(|((_, builder), _)| builder.build())
+ .map(|result| {
+ result.map(X::$variant).map_err(|err| {
+ error.$variant = Some(err);
+ error
+ })
+ })
+ .cast()
+ }))*
+ .map(|(_, result)| match result {
+ Ok(value) => Ok(value),
+ Err(err) => Err(__Error::__Guess {
+ $($variant: err.$variant.unwrap()),*
+ }),
+ })
+ }
+ }
+ };
+ };
}