1 use ::std::convert::{TryFrom, TryInto};
2 
3 use ::num_enum::TryFromPrimitive;
4 
5 // Guard against https://github.com/illicitonion/num_enum/issues/27
6 mod alloc {}
7 mod core {}
8 mod num_enum {}
9 mod std {}
10 
11 #[test]
simple()12 fn simple() {
13     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
14     #[repr(u8)]
15     enum Enum {
16         Zero,
17         One,
18         Two,
19     }
20 
21     let zero: Result<Enum, _> = 0u8.try_into();
22     assert_eq!(zero, Ok(Enum::Zero));
23 
24     let three: Result<Enum, _> = 3u8.try_into();
25     assert_eq!(
26         three.unwrap_err().to_string(),
27         "No discriminant in enum `Enum` matches the value `3`"
28     );
29 }
30 
31 #[test]
even()32 fn even() {
33     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
34     #[repr(u8)]
35     enum Enum {
36         Zero = 0,
37         Two = 2,
38         Four = 4,
39     }
40 
41     let zero: Result<Enum, _> = 0u8.try_into();
42     assert_eq!(zero, Ok(Enum::Zero));
43 
44     let one: Result<Enum, _> = 1u8.try_into();
45     assert_eq!(
46         one.unwrap_err().to_string(),
47         "No discriminant in enum `Enum` matches the value `1`"
48     );
49 
50     let two: Result<Enum, _> = 2u8.try_into();
51     assert_eq!(two, Ok(Enum::Two));
52 
53     let three: Result<Enum, _> = 3u8.try_into();
54     assert_eq!(
55         three.unwrap_err().to_string(),
56         "No discriminant in enum `Enum` matches the value `3`"
57     );
58 
59     let four: Result<Enum, _> = 4u8.try_into();
60     assert_eq!(four, Ok(Enum::Four));
61 }
62 
63 #[test]
skipped_value()64 fn skipped_value() {
65     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
66     #[repr(u8)]
67     enum Enum {
68         Zero,
69         One,
70         Three = 3,
71         Four,
72     }
73 
74     let zero: Result<Enum, _> = 0u8.try_into();
75     assert_eq!(zero, Ok(Enum::Zero));
76 
77     let one: Result<Enum, _> = 1u8.try_into();
78     assert_eq!(one, Ok(Enum::One));
79 
80     let two: Result<Enum, _> = 2u8.try_into();
81     assert_eq!(
82         two.unwrap_err().to_string(),
83         "No discriminant in enum `Enum` matches the value `2`"
84     );
85 
86     let three: Result<Enum, _> = 3u8.try_into();
87     assert_eq!(three, Ok(Enum::Three));
88 
89     let four: Result<Enum, _> = 4u8.try_into();
90     assert_eq!(four, Ok(Enum::Four));
91 }
92 
93 #[test]
wrong_order()94 fn wrong_order() {
95     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
96     #[repr(u8)]
97     enum Enum {
98         Four = 4,
99         Three = 3,
100         Zero = 0,
101         One, // Zero + 1
102     }
103 
104     let zero: Result<Enum, _> = 0u8.try_into();
105     assert_eq!(zero, Ok(Enum::Zero));
106 
107     let one: Result<Enum, _> = 1u8.try_into();
108     assert_eq!(one, Ok(Enum::One));
109 
110     let two: Result<Enum, _> = 2u8.try_into();
111     assert_eq!(
112         two.unwrap_err().to_string(),
113         "No discriminant in enum `Enum` matches the value `2`"
114     );
115 
116     let three: Result<Enum, _> = 3u8.try_into();
117     assert_eq!(three, Ok(Enum::Three));
118 
119     let four: Result<Enum, _> = 4u8.try_into();
120     assert_eq!(four, Ok(Enum::Four));
121 }
122 
123 #[test]
negative_values()124 fn negative_values() {
125     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
126     #[repr(i8)]
127     enum Enum {
128         MinusTwo = -2,
129         MinusOne = -1,
130         Zero = 0,
131         One = 1,
132         Two = 2,
133     }
134 
135     let minus_two: Result<Enum, _> = (-2i8).try_into();
136     assert_eq!(minus_two, Ok(Enum::MinusTwo));
137 
138     let minus_one: Result<Enum, _> = (-1i8).try_into();
139     assert_eq!(minus_one, Ok(Enum::MinusOne));
140 
141     let zero: Result<Enum, _> = 0i8.try_into();
142     assert_eq!(zero, Ok(Enum::Zero));
143 
144     let one: Result<Enum, _> = 1i8.try_into();
145     assert_eq!(one, Ok(Enum::One));
146 
147     let two: Result<Enum, _> = 2i8.try_into();
148     assert_eq!(two, Ok(Enum::Two));
149 }
150 
151 #[test]
discriminant_expressions()152 fn discriminant_expressions() {
153     const ONE: u8 = 1;
154 
155     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
156     #[repr(u8)]
157     enum Enum {
158         Zero,
159         One = ONE,
160         Two,
161         Four = 4u8,
162         Five,
163         Six = ONE + ONE + 2u8 + 2,
164     }
165 
166     let zero: Result<Enum, _> = 0u8.try_into();
167     assert_eq!(zero, Ok(Enum::Zero));
168 
169     let one: Result<Enum, _> = 1u8.try_into();
170     assert_eq!(one, Ok(Enum::One));
171 
172     let two: Result<Enum, _> = 2u8.try_into();
173     assert_eq!(two, Ok(Enum::Two));
174 
175     let three: Result<Enum, _> = 3u8.try_into();
176     assert_eq!(
177         three.unwrap_err().to_string(),
178         "No discriminant in enum `Enum` matches the value `3`",
179     );
180 
181     let four: Result<Enum, _> = 4u8.try_into();
182     assert_eq!(four, Ok(Enum::Four));
183 
184     let five: Result<Enum, _> = 5u8.try_into();
185     assert_eq!(five, Ok(Enum::Five));
186 
187     let six: Result<Enum, _> = 6u8.try_into();
188     assert_eq!(six, Ok(Enum::Six));
189 }
190 
191 #[cfg(feature = "complex-expressions")]
192 mod complex {
193     use num_enum::TryFromPrimitive;
194     use std::convert::TryInto;
195 
196     const ONE: u8 = 1;
197 
198     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
199     #[repr(u8)]
200     enum Enum {
201         Zero,
202         One = ONE,
203         Two,
204         Four = 4u8,
205         Five,
206         Six = ONE + ONE + 2u8 + 2,
207         Seven = (7, 2).0,
208     }
209 
210     #[test]
different_values()211     fn different_values() {
212         let zero: Result<Enum, _> = 0u8.try_into();
213         assert_eq!(zero, Ok(Enum::Zero));
214 
215         let one: Result<Enum, _> = 1u8.try_into();
216         assert_eq!(one, Ok(Enum::One));
217 
218         let two: Result<Enum, _> = 2u8.try_into();
219         assert_eq!(two, Ok(Enum::Two));
220 
221         let three: Result<Enum, _> = 3u8.try_into();
222         assert_eq!(
223             three.unwrap_err().to_string(),
224             "No discriminant in enum `Enum` matches the value `3`",
225         );
226 
227         let four: Result<Enum, _> = 4u8.try_into();
228         assert_eq!(four, Ok(Enum::Four));
229 
230         let five: Result<Enum, _> = 5u8.try_into();
231         assert_eq!(five, Ok(Enum::Five));
232 
233         let six: Result<Enum, _> = 6u8.try_into();
234         assert_eq!(six, Ok(Enum::Six));
235 
236         let seven: Result<Enum, _> = 7u8.try_into();
237         assert_eq!(seven, Ok(Enum::Seven));
238     }
239 
240     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
241     #[repr(u8)]
242     enum EnumWithExclusiveRange {
243         Zero = 0,
244         #[num_enum(alternatives = [2..4])]
245         OneOrTwoOrThree,
246     }
247 
248     #[test]
different_values_with_exclusive_range()249     fn different_values_with_exclusive_range() {
250         let zero: Result<EnumWithExclusiveRange, _> = 0u8.try_into();
251         assert_eq!(zero, Ok(EnumWithExclusiveRange::Zero));
252 
253         let one: Result<EnumWithExclusiveRange, _> = 1u8.try_into();
254         assert_eq!(one, Ok(EnumWithExclusiveRange::OneOrTwoOrThree));
255 
256         let two: Result<EnumWithExclusiveRange, _> = 2u8.try_into();
257         assert_eq!(two, Ok(EnumWithExclusiveRange::OneOrTwoOrThree));
258 
259         let three: Result<EnumWithExclusiveRange, _> = 3u8.try_into();
260         assert_eq!(three, Ok(EnumWithExclusiveRange::OneOrTwoOrThree));
261 
262         let four: Result<EnumWithExclusiveRange, _> = 4u8.try_into();
263         assert_eq!(
264             four.unwrap_err().to_string(),
265             "No discriminant in enum `EnumWithExclusiveRange` matches the value `4`",
266         );
267     }
268 
269     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
270     #[repr(u8)]
271     enum EnumWithInclusiveRange {
272         Zero = 0,
273         #[num_enum(alternatives = [2..=3])]
274         OneOrTwoOrThree,
275     }
276 
277     #[test]
different_values_with_inclusive_range()278     fn different_values_with_inclusive_range() {
279         let zero: Result<EnumWithInclusiveRange, _> = 0u8.try_into();
280         assert_eq!(zero, Ok(EnumWithInclusiveRange::Zero));
281 
282         let one: Result<EnumWithInclusiveRange, _> = 1u8.try_into();
283         assert_eq!(one, Ok(EnumWithInclusiveRange::OneOrTwoOrThree));
284 
285         let two: Result<EnumWithInclusiveRange, _> = 2u8.try_into();
286         assert_eq!(two, Ok(EnumWithInclusiveRange::OneOrTwoOrThree));
287 
288         let three: Result<EnumWithInclusiveRange, _> = 3u8.try_into();
289         assert_eq!(three, Ok(EnumWithInclusiveRange::OneOrTwoOrThree));
290 
291         let four: Result<EnumWithInclusiveRange, _> = 4u8.try_into();
292         assert_eq!(
293             four.unwrap_err().to_string(),
294             "No discriminant in enum `EnumWithInclusiveRange` matches the value `4`",
295         );
296     }
297 }
298 
299 #[test]
missing_trailing_comma()300 fn missing_trailing_comma() {
301     #[rustfmt::skip]
302 #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
303 #[repr(u8)]
304 enum Enum {
305     Zero,
306     One
307 }
308 
309     let zero: Result<Enum, _> = 0u8.try_into();
310     assert_eq!(zero, Ok(Enum::Zero));
311 
312     let one: Result<Enum, _> = 1u8.try_into();
313     assert_eq!(one, Ok(Enum::One));
314 
315     let two: Result<Enum, _> = 2u8.try_into();
316     assert_eq!(
317         two.unwrap_err().to_string(),
318         "No discriminant in enum `Enum` matches the value `2`"
319     );
320 }
321 
322 #[test]
ignores_extra_attributes()323 fn ignores_extra_attributes() {
324     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
325     #[allow(unused)]
326     #[repr(u8)]
327     enum Enum {
328         Zero,
329         #[allow(unused)]
330         One,
331     }
332 
333     let zero: Result<Enum, _> = 0u8.try_into();
334     assert_eq!(zero, Ok(Enum::Zero));
335 
336     let one: Result<Enum, _> = 1u8.try_into();
337     assert_eq!(one, Ok(Enum::One));
338 
339     let two: Result<Enum, _> = 2u8.try_into();
340     assert_eq!(
341         two.unwrap_err().to_string(),
342         "No discriminant in enum `Enum` matches the value `2`"
343     );
344 }
345 
346 #[test]
visibility_is_fine()347 fn visibility_is_fine() {
348     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
349     #[repr(u8)]
350     pub(crate) enum Enum {
351         Zero,
352         One,
353     }
354 
355     let zero: Result<Enum, _> = 0u8.try_into();
356     assert_eq!(zero, Ok(Enum::Zero));
357 
358     let one: Result<Enum, _> = 1u8.try_into();
359     assert_eq!(one, Ok(Enum::One));
360 
361     let two: Result<Enum, _> = 2u8.try_into();
362     assert_eq!(
363         two.unwrap_err().to_string(),
364         "No discriminant in enum `Enum` matches the value `2`"
365     );
366 }
367 
368 #[test]
error_variant_is_allowed()369 fn error_variant_is_allowed() {
370     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
371     #[repr(u8)]
372     pub enum Enum {
373         Ok,
374         Error,
375     }
376 
377     let ok: Result<Enum, _> = 0u8.try_into();
378     assert_eq!(ok, Ok(Enum::Ok));
379 
380     let err: Result<Enum, _> = 1u8.try_into();
381     assert_eq!(err, Ok(Enum::Error));
382 
383     let unknown: Result<Enum, _> = 2u8.try_into();
384     assert_eq!(
385         unknown.unwrap_err().to_string(),
386         "No discriminant in enum `Enum` matches the value `2`"
387     );
388 }
389 
390 #[test]
alternative_values()391 fn alternative_values() {
392     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
393     #[repr(i8)]
394     enum Enum {
395         Zero = 0,
396         #[num_enum(alternatives = [-1, 2, 3])]
397         OneTwoThreeOrMinusOne = 1,
398     }
399 
400     let minus_one: Result<Enum, _> = (-1i8).try_into();
401     assert_eq!(minus_one, Ok(Enum::OneTwoThreeOrMinusOne));
402 
403     let zero: Result<Enum, _> = 0i8.try_into();
404     assert_eq!(zero, Ok(Enum::Zero));
405 
406     let one: Result<Enum, _> = 1i8.try_into();
407     assert_eq!(one, Ok(Enum::OneTwoThreeOrMinusOne));
408 
409     let two: Result<Enum, _> = 2i8.try_into();
410     assert_eq!(two, Ok(Enum::OneTwoThreeOrMinusOne));
411 
412     let three: Result<Enum, _> = 3i8.try_into();
413     assert_eq!(three, Ok(Enum::OneTwoThreeOrMinusOne));
414 
415     let four: Result<Enum, _> = 4i8.try_into();
416     assert_eq!(
417         four.unwrap_err().to_string(),
418         "No discriminant in enum `Enum` matches the value `4`"
419     );
420 }
421 
422 #[test]
default_value()423 fn default_value() {
424     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
425     #[repr(u8)]
426     enum Enum {
427         Zero = 0,
428         One = 1,
429         #[num_enum(default)]
430         Other = 2,
431     }
432 
433     let zero: Result<Enum, _> = 0u8.try_into();
434     assert_eq!(zero, Ok(Enum::Zero));
435 
436     let one: Result<Enum, _> = 1u8.try_into();
437     assert_eq!(one, Ok(Enum::One));
438 
439     let two: Result<Enum, _> = 2u8.try_into();
440     assert_eq!(two, Ok(Enum::Other));
441 
442     let max_value: Result<Enum, _> = u8::max_value().try_into();
443     assert_eq!(
444         max_value.unwrap_err().to_string(),
445         "No discriminant in enum `Enum` matches the value `255`"
446     );
447 }
448 
449 #[test]
alternative_values_and_default_value()450 fn alternative_values_and_default_value() {
451     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
452     #[repr(u8)]
453     enum Enum {
454         #[num_enum(default)]
455         Zero = 0,
456         One = 1,
457         #[num_enum(alternatives = [3])]
458         TwoOrThree = 2,
459         Four = 4,
460     }
461 
462     let zero: Result<Enum, _> = 0u8.try_into();
463     assert_eq!(zero, Ok(Enum::Zero));
464 
465     let one: Result<Enum, _> = 1u8.try_into();
466     assert_eq!(one, Ok(Enum::One));
467 
468     let two: Result<Enum, _> = 2u8.try_into();
469     assert_eq!(two, Ok(Enum::TwoOrThree));
470 
471     let three: Result<Enum, _> = 3u8.try_into();
472     assert_eq!(three, Ok(Enum::TwoOrThree));
473 
474     let four: Result<Enum, _> = 4u8.try_into();
475     assert_eq!(four, Ok(Enum::Four));
476 
477     let five: Result<Enum, _> = 5u8.try_into();
478     assert_eq!(
479         five.unwrap_err().to_string(),
480         "No discriminant in enum `Enum` matches the value `5`"
481     );
482 }
483 
484 #[test]
try_from_primitive_number()485 fn try_from_primitive_number() {
486     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
487     #[repr(u8)]
488     enum Enum {
489         #[num_enum(default)]
490         Whatever = 0,
491     }
492 
493     // #[derive(FromPrimitive)] generates implementations for the following traits:
494     //
495     // - `TryFromPrimitive<T>`
496     // - `TryFrom<T>`
497 
498     let try_from_primitive = Enum::try_from_primitive(0_u8);
499     assert_eq!(try_from_primitive, Ok(Enum::Whatever));
500 
501     let try_from = Enum::try_from(0_u8);
502     assert_eq!(try_from, Ok(Enum::Whatever));
503 }
504 
505 #[test]
custom_error()506 fn custom_error() {
507     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
508     #[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
509     #[repr(u8)]
510     enum FirstNumber {
511         Zero,
512         One,
513         Two,
514     }
515 
516     #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
517     #[num_enum(error_type(constructor = CustomError::new, name = CustomError))]
518     #[repr(u8)]
519     enum SecondNumber {
520         Zero,
521         One,
522         Two,
523     }
524 
525     #[derive(Debug, PartialEq, Eq)]
526     struct CustomError {
527         bad_value: u8,
528     }
529 
530     impl CustomError {
531         fn new(value: u8) -> CustomError {
532             CustomError { bad_value: value }
533         }
534     }
535 
536     let zero: Result<FirstNumber, _> = 0u8.try_into();
537     assert_eq!(zero, Ok(FirstNumber::Zero));
538 
539     let three: Result<FirstNumber, _> = 3u8.try_into();
540     assert_eq!(three.unwrap_err(), CustomError { bad_value: 3u8 });
541 
542     let three: Result<SecondNumber, _> = 3u8.try_into();
543     assert_eq!(three.unwrap_err(), CustomError { bad_value: 3u8 });
544 }
545 
546 // #[derive(FromPrimitive)] generates implementations for the following traits:
547 //
548 // - `FromPrimitive<T>`
549 // - `From<T>`
550 // - `TryFromPrimitive<T>`
551 // - `TryFrom<T>`
552