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
128
129
130
131
132
133
134
135
136
137
138
139
//! wrapped_enum! - Wrap multiple types into an Enum
//!
//! This is useful when returning multiple types.
//! simply to just work on an enum of known types.
//!
//! Documentation is required when using public enums
//!
//! ```
//! # #[macro_use] extern crate wrapped_enum;
//! # use std::io;
//! #[derive(Debug)]
//! pub enum TestError {
//!     ErrorA,
//!     ErrorB(u8, u16),
//!     ErrorC(String),
//! }
//!
//! wrapped_enum!{
//!     #[derive(Debug)]
//!     /// Document your pub enums
//!     pub enum DocumentedEnum {
//!         /// Variants too
//!         Io(io::Error),
//!         /// Documentation is required for pub enums
//!         Test(TestError),
//!         /// Unknown error
//!         Unknown(()),
//!     }
//! }
//!
//! wrapped_enum!{
//!     #[derive(Debug, Eq, PartialEq)]
//!     /// You can't document private variants
//!     enum UndocumentedEnum {
//!         // However, there is nothing wrong with normal comments
//!         Byte(u8),        // 00-FF
//!         DoubleByte(u16), // 0000-FFFF
//!     }
//! }
//! 
//! # fn main() {
//! assert!(UndocumentedEnum::from(0u8) == UndocumentedEnum::Byte(0));
//! assert!(UndocumentedEnum::from(0u16) == UndocumentedEnum::DoubleByte(0));
//! # }
//! ```

#[macro_export]
macro_rules! wrapped_enum {
    ($(#[$attr:meta])*
     pub enum $enum_name:ident {
         $($(#[$variant_attr:meta])+
         $enum_variant_name:ident($ty:ty)),+
         $(,)*
     }
    ) => (
        $(#[$attr])*
        pub enum $enum_name { $($(#[$variant_attr])+ $enum_variant_name($ty)),+ }
        $(impl From<$ty> for $enum_name {
            fn from (ty: $ty) -> Self {
                $enum_name::$enum_variant_name(ty)
            }
        })+
    );
    ($(#[$attr:meta])*
     enum $enum_name:ident {
         $($enum_variant_name:ident($ty:ty)),+
         $(,)*
     }
    ) => (
        $(#[$attr])*
        enum $enum_name { $($enum_variant_name($ty)),+ }
        $(impl From<$ty> for $enum_name {
            fn from (ty: $ty) -> Self {
                $enum_name::$enum_variant_name(ty)
            }
        })+
    );
}

#[cfg(test)]
mod test {
    #[derive(Debug)]
    #[allow(dead_code)]
    pub enum CommunicationError {
        HumanError(Human, ErrorAction),
        UnknownError,
    }

    #[derive(Debug)]
    #[allow(dead_code)]
    pub enum ErrorAction {
        AttemptedToStealSecrets,
        StoleSecrets,
    }

    #[derive(Debug)]
    #[allow(dead_code)]
    pub enum Human {
        Alice, // Good
        Bob,   // Good
        Carol, // Good
        Dave,  // Good
        Eve,   // Bad
    }

    wrapped_enum!{
        #[derive(Debug)]
        /// Document your pub enums
        pub enum TestError {
//          /// Variants too
//          Io(io::Error),
            /// Documentation is required for pub enums
            Test(CommunicationError),
            /// Unknown error
            Unknown(()),
        }
    }

    wrapped_enum!{
        #[derive(Debug)]
        /// You can't document private variants
        enum UndocumentedEnum {
            // However, there is nothing wrong with normal comments
            Byte(u8),        // 00-FF
            DoubleByte(u16), // 0000-FFFF
        }
    }

    #[test]
    fn eve_intercept_alice_and_bobs_communication() {
        let error: Result<(), CommunicationError> =
            Err(CommunicationError::HumanError(Human::Eve,
                                               ErrorAction::AttemptedToStealSecrets));

        let result: Result<(), TestError> = error.map_err(From::from);

        assert!(result.is_err());
    }
}