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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
use crate::indexed_enum::Indexed;
use crate::valued_enum::Valued;

/// Produces an enum implementing the [Indexed] and [Valued] traits, meaning the enum's variants can
/// produce unique numbers of usize to identify each variant through [Indexed::discriminant], and
/// get back those variants through [Indexed::from_discriminant], and similar to it, each variant
/// has a value that can be taken from [Valued::value], where the variant can be taken back* from
/// that  value through [Valued::value_to_variant]
///
/// *Just if the value isn't repeated
/// <br><br>
/// To implement it write:
/// <br><br>
/// create_indexed_valued_enum!{ <br>
/// &nbsp;&nbsp;&nbsp;&nbsp;	#\[doc=**Documentation**] <br>
/// &nbsp;&nbsp;&nbsp;&nbsp;	#[derive(**Derive1**, **Derive2**, ...)] <br>
/// &nbsp;&nbsp;&nbsp;&nbsp;	#[features(**Feature1**, **Feature2**, ...)] <br>
/// &nbsp;&nbsp;&nbsp;&nbsp;	**Visibility** enum **Enum's name** values as **TypeOfValue**; <br>
/// &nbsp;&nbsp;&nbsp;&nbsp;	***Variant1***, ***Value1***,<br>
/// &nbsp;&nbsp;&nbsp;&nbsp;	***Variant2***, ***Value2***,<br>
/// &nbsp;&nbsp;&nbsp;&nbsp;	...<br>
/// &nbsp;&nbsp;&nbsp;&nbsp;	***VariantN***, ***ValueN***<br>
/// }
///
/// As example:
///
/// ```rust
/// use indexed_valued_enums::create_indexed_valued_enum;
///
/// create_indexed_valued_enum! {
///     #[doc="This is a custom enum that can get values of &'static str!"]
///     //This enum derives certain traits, although you don't need to write this
///     #[derive(Hash, Ord, PartialOrd, Eq, PartialEq, Debug)]
///     //Gives a list of features that are decomposed functions for specific behaviours, you have
///     //more details about them down below
///     #[features(Clone, DerefToValue, Delegators, ValueToVariantDelegators,
///                Serialize, Deserialize,
///                NanoDeBin, NanoSerBin, NanoDeJson, NanoSerJson)]
///     //Defines the enum and the value type it resolves to
///     pub enum MyOtherNumber valued as &'static str;
///     //Defines every variant and their value, note that values must constant and have 'static lifetime
///     Zero, "Zero position",
///     First, "First position",
///     Second, "Second position",
///     Third,  "Third position"
/// }
/// ```
///
/// On each of the fields you can indicate different parameters to change the implementation of the
/// enum:
///
/// * *Documentation*: Documentation of the enum
/// * *Derives*: List of derive macros you want the enum to execute (Optional)
/// * *Visibility*: Visibility of the enum (Optional)
/// * *EnumsName*: Name the enum will have
/// * *TypeOfValue*: type of the values the variant's resolve to
/// * Pairs of *Variant, Value*: Name of the variant's to create along to the name they resolve to,
///                              the values must be const and have 'static lifetime
/// * *Features*: List of specific implementations you want your enum to use, they are the following ones:
///     * DerefToValue: The enum implements Deref, making variants to resolve to their value
///                     directly, remember however these values won't mutate as they are constant
///                     references (&'static *TypeOfValue*), this is also the reason why these
///                     values require their life-time to be 'static
///     * Clone: The enum implements clone calling [Indexed::from_discriminant], this way it's not
///              required for the Derive Clone macro to expand to large enums
///     * Delegators: Implements delegator functions over this enum that call on the methods from
///                  [Indexed] and [Valued], this way it is not required to import or use the
///                  indexed_valued_enums crate directly, however, it doesn't delegate the methods
///                  [Valued::value_to_variant] and [Valued::value_to_variant_opt] as they
///                  require the type of value to implement [PartialEq], however, you can delegate
///                  these too with the feature **ValueToVariantDelegators**
///     * ValueToVariantDelegators: Implements delegator functions for [Valued::value_to_variant]
///                                 and [Valued::value_to_variant_opt]
///     * Serialize: Implements serde's Serialize trait where it serializes to an usize that
///                  represents this enum's discriminant
///     * Deserialize: Implements serde's Deserialize trait where it deserializes an enum variant's
///                    from it's enum's discriminant
///     * NanoSerBin: Implements nanoserde's SerBin trait where it serializes to an usize that
///                   represents this enum's discriminant
///     * NanoDeBin: Implements nanoserde's DeBin trait where it deserializes an enum variant's
///                  from it's enum's discriminant
///     * NanoSerJson: Implements nanoserde's SerJson trait where it serializes to an usize that
///                   represents this enum's discriminant
///     * NanoDeJson: Implements nanoserde's DeJson trait where it deserializes an enum variant's
///                  from it's enum's discriminant
#[macro_export]
macro_rules! create_indexed_valued_enum {
        (
        $(#[doc = $doc:expr])?
        $(#[derive($($derives:ident),*)])?
        $(#[features($($features:tt),*)])?
        $visibility:vis enum $enum_name:ident valued as $value_type:ty;
        $($variants:ident, $values:expr),+ $(,)?
    ) => {
        $(#[derive($($derives),*)])?
        $(#[doc = $doc])?
        #[repr(usize)]
        $visibility enum $enum_name{
            $($variants),+
        }

        impl indexed_valued_enums::indexed_enum::Indexed for $enum_name {
            #[doc = concat!("Array storing all the variants of the [",stringify!($enum_name),"]\
            enum where each variant is stored in ordered by their discriminant")]
            const VARIANTS: &'static [ Self ] = &[$($enum_name::$variants),+];
        }

        impl indexed_valued_enums::valued_enum::Valued for $enum_name {
            type Value = $value_type;

            #[doc = concat!("Array storing all the variants values of the \
             [",stringify!($enum_name),"] enum, each value is stored in the same order as the \
            discriminant of the variant they belong to")]
            const VALUES: &'static [ Self::Value] = & [$($values),+];
        }

        $(create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($features)*] })?
    };
    (process features
        [$enum_name:ident, $value_type:ty],
        [Delegators $($other_features:tt)*]
    )=>{
        impl $enum_name {
            #[doc = concat!("Gets the discriminant of this",stringify!($enum_name),", this \
            operation is O(1)")]
            pub fn discriminant(&self) -> usize {
                indexed_valued_enums::indexed_enum::Indexed::discriminant(self)
            }

            #[doc = concat!("Gets the",stringify!($enum_name),"'s variant corresponding to said \
            discriminant, this operation is O(1) as it just gets the discriminant as a copy from \
            [indexed_valued_enums::indexed_enum::Indexed::VARIANTS], meaning this enum does not \
            need to implement [Clone]")]
            pub fn from_discriminant_opt(discriminant: usize) -> Option<Self> {
                indexed_valued_enums::indexed_enum::Indexed::from_discriminant_opt(discriminant)
            }

            #[doc = concat!("Gets the",stringify!($enum_name),"'s variant corresponding to said \
            discriminant, this operation is O(1) as it just gets the discriminant as a copy from \
            [indexed_valued_enums::indexed_enum::Indexed::VARIANTS], meaning this enum does not \
            need to implement [Clone]")]
            pub fn from_discriminant(discriminant: usize) -> Self {
                indexed_valued_enums::indexed_enum::Indexed::from_discriminant(discriminant)
            }

            #[doc = concat!("Gives the value of type [",stringify!($value_type),"] corresponding \
            to this [", stringify!($enum_name),"] 's variant, this operation is O(1) as it just \
            gets the discriminant as a copy from \
            [indexed_valued_enums::valued_enum::Valued::VALUES] \
            <br><br>This always returns [Option::Some], so it's recommended to call\
            [",stringify!($enum_name),"::value] instead")]
            pub fn value_opt(&self) -> Option<$value_type> {
                indexed_valued_enums::valued_enum::Valued::value_opt(self)
            }

            #[doc = concat!("Gives the value of type [",stringify!($value_type),"] corresponding \
            to this [", stringify!($enum_name),"] 's variant, this operation is O(1) as it just \
            gets the discriminant as a copy from \
            [indexed_valued_enums::valued_enum::Valued::VALUES]")]
            pub fn value(&self) -> $value_type {
                indexed_valued_enums::valued_enum::Valued::value(self)
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
        (process features
        [$enum_name:ident, $value_type:ty],
        [ValueToVariantDelegators $($other_features:tt)*]
    )=>{
        impl $enum_name {
            #[doc = concat!("Gives [",stringify!($enum_name),"]'s variant corresponding to this \
            value <br><br> this is an O(n) operation as it does so by comparing every single value \
            contained in [indexed_valued_enums::valued_enum::Valued::VALUES]")]
            pub fn value_to_variant_opt(value: &$value_type) -> Option<Self> {
                indexed_valued_enums::valued_enum::Valued::value_to_variant_opt(value)
            }

            #[doc = concat!("Gives [",stringify!($enum_name),"]'s variant corresponding to this \
            value <br><br> this is an O(n) operation as it does so by comparing every single value \
            contained in [indexed_valued_enums::valued_enum::Valued::VALUES]")]
            pub fn value_to_variant(value: &$value_type) -> Self {
                indexed_valued_enums::valued_enum::Valued::value_to_variant(value)
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
    (process features
        [$enum_name:ident, $value_type:ty],
        [DerefToValue $($other_features:tt)*]
    )=>{
        impl core::ops::Deref for $enum_name{
            type Target = $value_type;

            #[doc = concat!("Gives the value of type [",stringify!($value_type),"] corresponding to \
            this [", stringify!($enum_name),"] 's variant <br><br>Since \
            [indexed_valued_enums::valued_enum::Valued::VALUES] is a constant array, the value will \
            be referenced for 'static")]
            fn deref(&self) -> &'static Self::Target {
                &<Self as indexed_valued_enums::valued_enum::Valued>::VALUES[self.discriminant()]
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
    (process features
        [$enum_name:ident, $value_type:ty],
        [Clone $($other_features:tt)*]
    )=>{
        impl core::clone::Clone for $enum_name {

            #[doc = concat!("Clones this [",stringify!($enum_name),"]'s variant<br><br>This clone \
            is taken from the constant array of\
            [indexed_valued_enums::indexed_enum::Indexed::VARIANTS], meaning this is a copy of that \
            array, and therefore not causing a long macro expansion")]
            fn clone(&self) -> Self {
                let discriminant = indexed_valued_enums::indexed_enum::Indexed::discriminant(self);
                indexed_valued_enums::indexed_enum::Indexed::from_discriminant(discriminant)
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
    (process features
        [$enum_name:ident, $value_type:ty],
        [Serialize $($other_features:tt)*]
    )=>{
        impl serde::Serialize for $enum_name {
            #[doc = concat!("Serializes this [",stringify!($enum_name),"]'s variant as it's \
            discriminant, reducing its serializing complexity")]
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
                serializer.serialize_u128(self.discriminant() as u128)
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
    (process features
        [$enum_name:ident, $value_type:ty],
        [Deserialize $($other_features:tt)*]
    )=>{
        impl<'de> serde::Deserialize<'de> for $enum_name {
            #[doc = concat!("Deserializes this [",stringify!($enum_name),"]'s variant from it's \
            discriminant, reducing its deserializing complexity")]
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
                match deserializer.deserialize_u128(indexed_valued_enums::serde_compatibility::discriminant_visitor::DISCRIMINANT_VISITOR) {
                    Ok(value) => {
                        match $enum_name::from_discriminant_opt(value) {
                            Some(value) => { Ok(value) }
                            None => { Err(serde::de::Error::custom("Deserialized an discriminant that is bigger than the amount of variants")) }
                        }
                    }
                    Err(error) => { Err(error) }
                }
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
    (process features
        [$enum_name:ident, $value_type:ty],
        [NanoSerBin $($other_features:tt)*]
    )=>{
        impl nanoserde::SerBin for $enum_name {
            #[doc = concat!("Serializes this [",stringify!($enum_name),"]'s variant as it's \
            discriminant, reducing its serializing complexity")]
            fn ser_bin(&self, output: &mut Vec<u8>) {
                self.discriminant().ser_bin(output)
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
    (process features
        [$enum_name:ident, $value_type:ty],
        [NanoDeBin $($other_features:tt)*]
    )=>{
        impl nanoserde::DeBin for $enum_name {
            #[doc = concat!("Deserializes this [",stringify!($enum_name),"]'s variant from it's \
            discriminant, reducing its deserializing complexity")]
            fn de_bin(offset: &mut usize, bytes: &[u8]) -> core::result::Result<Self, nanoserde::DeBinErr> {
                core::result::Result::Ok(
                    $enum_name::from_discriminant_opt(nanoserde::DeBin::de_bin(offset, bytes)?)
                        .ok_or_else(|| nanoserde::DeBinErr {
                            o: *offset,
                            l: core::mem::size_of::<usize>(),
                            s: bytes.len(),
                        })?)
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };

    (process features
        [$enum_name:ident, $value_type:ty],
        [NanoSerJson $($other_features:tt)*]
    )=>{
        impl nanoserde::SerJson for $enum_name {
            #[doc = concat!("Serializes this [",stringify!($enum_name),"]'s variant as it's \
            discriminant, reducing its serializing complexity")]
            fn ser_json(&self, _d: usize, state: &mut nanoserde::SerJsonState) {
                state.out.push_str(&self.discriminant().to_string());
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
    (process features
        [$enum_name:ident, $value_type:ty],
        [NanoDeJson $($other_features:tt)*]
    )=>{
        impl nanoserde::DeJson for $enum_name {
            #[doc = concat!("Deserializes this [",stringify!($enum_name),"]'s variant from it's \
            discriminant, reducing its deserializing complexity")]
            fn de_json(state: &mut nanoserde::DeJsonState, input: &mut core::str::Chars) -> Result<Self, nanoserde::DeJsonErr> {
                let val = state.u64_range(core::u64::MAX as u64)?;
                state.next_tok(input)?;
                let discriminant = val as usize;

                let variant = $enum_name::from_discriminant_opt(discriminant)
                    .ok_or_else(|| nanoserde::DeJsonErr{
                        msg: "Indicated discriminant doesn't not correspond to any variant of this enum".to_string(),
                        line: 0,
                        col: 0,
                    })?;
                return Ok(variant);
            }
        }

        create_indexed_valued_enum !{process features [$enum_name, $value_type], [$($other_features)*]}
    };
    (process features [$enum_name:ident, $value_type:ty], [])=>{};
}