frunk_enum_core/
lib.rs

1#![deny(missing_docs)]
2
3//! This crate augments the `frunk` crate with extra functionallity to allow transmogrification
4//! between enums with similar structures.
5//!
6//! This crate specifically defines the data structures used in the generic representation of an
7//! enum, the `frunk-enum-derive` crate adds a proc-macro to auto-generate the per-type code needed
8//! to take advantage of this core function.
9
10use frunk::labelled::Transmogrifier;
11use frunk::{HCons, HNil};
12use std::marker::PhantomData;
13
14/// A building block for a generic enum.  The "additive-type" mirror for `frunk::HCons`.  This is
15/// normally used as:
16///
17/// ```ignore
18/// HEither<A,
19///         HEither<B,
20///                 HEither<C,
21///                         Void>>>
22/// ```
23pub enum HEither<H, T> {
24    /// The first variant.
25    Head(H),
26    /// The second/other variant.
27    Tail(T),
28}
29
30/// A generic representation of an enum variant.  This holds the ident of the variant at both type
31/// and value levels.
32pub struct Variant<K, T> {
33    /// A text representation of the variant ident
34    pub key: &'static str,
35    /// The value of the contents of the variant
36    pub value: T,
37    /// A type-level representation of the variant ident
38    pub name_type_holder: PhantomData<K>,
39}
40
41/// A macro to ease the creation of `Variant`s.  See `frunk::field!` for usage.
42#[macro_export]
43macro_rules! variant {
44    // No name provided and type is a tuple
45    (($($repeated: ty),*), $value: expr) => {
46        $crate::variant!( ($($repeated),*), $value, concat!( $(stringify!($repeated)),* ) )
47    };
48    // No name provided and type is a tuple, but with trailing commas
49    (($($repeated: ty,)*), $value: expr) => {
50        $crate::variant!( ($($repeated),*), $value )
51    };
52    // We are provided any type, with no stable name
53    ($name_type: ty, $value: expr) => {
54        $crate::variant!( $name_type, $value, stringify!($name_type) )
55    };
56    // We are provided any type, with a stable name
57    ($name_type: ty, $value: expr, $name: expr) => {
58        $crate::Variant::<$name_type,_> {
59            key: $name,
60            value: $value,
61            name_type_holder: std::marker::PhantomData,
62        }
63    }
64}
65
66impl<TargetHead, TargetTail, SourceHead, SourceTail, HeadIndices, TailIndices, Key>
67    Transmogrifier<HEither<Variant<Key, TargetHead>, TargetTail>, HCons<HeadIndices, TailIndices>>
68    for HEither<Variant<Key, SourceHead>, SourceTail>
69where
70    SourceHead: Transmogrifier<TargetHead, HeadIndices>,
71    SourceTail: Transmogrifier<TargetTail, TailIndices>,
72{
73    #[inline(always)]
74    fn transmogrify(self) -> HEither<Variant<Key, TargetHead>, TargetTail> {
75        match self {
76            HEither::Head(Variant {
77                value: h, key: k, ..
78            }) => HEither::Head(variant!(Key, h.transmogrify(), k)),
79            HEither::Tail(t) => HEither::Tail(t.transmogrify()),
80        }
81    }
82}
83
84/// An uninhabited type.
85pub enum Void {}
86
87impl Transmogrifier<Void, HNil> for Void {
88    fn transmogrify(self) -> Void {
89        match self {}
90    }
91}