Diffstat (limited to 'src/macros/build.rs')
| -rw-r--r-- | src/macros/build.rs | 171 |
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()),* + }), + }) + } + } + }; + }; } |