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
#![doc = include_str!("../README.md")]

use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;

/// Macro that implements bunch of traits for enums that
/// simply are aliases for integer type
///
/// example:
/// ```
/// use integral_enum::IntegralEnum;
///
/// #[derive(IntegralEnum)]
/// #[repr(u8)]
/// // repr is not required, by default `IntegralEnum` assumes that repr is u8
/// // FIXME: repr should be infered to avoid specific errors
/// pub enum Yuu {
///     Hatred,
///     Pain,
/// }
///
/// assert_eq!(Yuu::try_from(0), Ok(Yuu::Hatred));
/// assert_eq!(Yuu::try_from(1), Ok(Yuu::Pain));
/// assert_eq!(Yuu::try_from(123), Err(()));
/// ```
///
/// Macro will automatically generate such trait
/// implementations: Clone, Copy, PartialEq, Eq, Debug,
/// Display, TryFrom.
///
/// To resolve conflicts with other derive-macro you should
/// use the #[enum_disable(...)] attribute, it has the
/// following options:
///
/// `debug`, `display`, `clone`, `copy`, `partial_eq`,
/// `total_eq`, `try_from`
///
/// `total_eq` depends on `partial_eq`, `copy` depends on
/// `clone`, so if you disable dependency, dependant
/// implementation will be disabled too, for example:
///
/// ```
/// use integral_enum::IntegralEnum;
///
/// #[derive(Debug, IntegralEnum)]
/// #[enum_disable(debug)]
/// enum Affection {
///     FellInLove,
///     HatesYou,
///     // From love to hate is one step, after all :D
/// }
///
/// assert_eq!(format!("{:?}", Affection::FellInLove), "FellInLove");
/// assert_eq!(format!("{}", Affection::HatesYou), "HatesYou");
/// // display is still available
/// ```
#[proc_macro_derive(IntegralEnum, attributes(enum_disable))]
#[proc_macro_error]
pub fn implied_integral_enum(stream: TokenStream) -> TokenStream {
    inner::enum_try_from(stream, true, "enum_disable")
}

/// Strict version of the `IntegralEnum`. It implements only
/// specified implementations, for example this compiles and
/// successfully exits:
///
/// ```
/// use integral_enum::StrictIntegralEnum;
///
/// #[derive(StrictIntegralEnum)]
/// #[enum_impl(display, partial_eq, try_from)]
/// enum MusicGenre {
///     SynthWave,
///     HeavyMetal,
/// }
///
/// assert!(MusicGenre::try_from(0) == Ok(MusicGenre::SynthWave));
/// assert!(format!("{}", MusicGenre::SynthWave) == "SynthWave");
/// ```
///
/// And this will fail:
///
/// ```compile_fail
/// use integral_enum::StrictIntegralEnum;
///
/// #[derive(StrictIntegralEnum)]
/// #[enum_impl(try_from)]
/// enum MusicGenre {
///     SynthWave,
///     HeavyMetal,
/// }
///
/// assert!(MusicGenre::try_from(0) == Ok(MusicGenre::SynthWave));
/// assert!(format!("{}", MusicGenre::SynthWave) == "SynthWave");
/// ```
#[proc_macro_derive(StrictIntegralEnum, attributes(enum_impl))]
#[proc_macro_error]
pub fn strict_integral_enum(stream: TokenStream) -> TokenStream {
    inner::enum_try_from(stream, false, "enum_impl")
}

mod attribute_parser;
mod discriminant_counter;
mod inner;