Skip to main content

enum_map/
internal.rs

1// SPDX-FileCopyrightText: 2017 - 2023 Luna Borowska <luna@borowska.pw>
2// SPDX-FileCopyrightText: 2021 Bruno CorrĂȘa Zimmermann <brunoczim@gmail.com>
3// SPDX-FileCopyrightText: 2022 philipp <descpl@yahoo.de>
4//
5// SPDX-License-Identifier: MIT OR Apache-2.0
6
7use core::cmp::Ordering;
8use core::convert::Infallible;
9
10/// Enum mapping type.
11///
12/// This trait is implemented by `#[derive(Enum)]`.
13///
14/// This trait is also implemented by `bool` and `u8`. While `u8` is
15/// strictly speaking not an actual enum, there are good reasons to consider
16/// it like one, as array of `u8` keys is a relatively common pattern.
17pub trait Enum: Sized {
18    /// Representation of an enum.
19    ///
20    /// For an enum with four elements it looks like this.
21    ///
22    /// ```
23    /// type Array<V> = [V; 4];
24    /// ```
25    type Array<V>: Array;
26
27    /// Takes an usize, and returns an element matching `into_usize` function.
28    fn from_usize(value: usize) -> Self;
29    /// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
30    fn into_usize(self) -> usize;
31}
32
33/// Array for enum-map storage.
34///
35/// This trait is inteded for primitive array types (with fixed length).
36///
37/// # Safety
38///
39/// The array length needs to match actual storage.
40pub unsafe trait Array {
41    // This is necessary duplication because the length in Enum trait can be
42    // provided by user and may not be trustworthy for unsafe code.
43    const LENGTH: usize;
44}
45
46unsafe impl<V, const N: usize> Array for [V; N] {
47    const LENGTH: usize = N;
48}
49
50#[doc(hidden)]
51#[inline]
52pub fn out_of_bounds() -> ! {
53    panic!("index out of range for Enum::from_usize");
54}
55
56impl Enum for bool {
57    type Array<V> = [V; 2];
58
59    #[inline]
60    fn from_usize(value: usize) -> Self {
61        match value {
62            0 => false,
63            1 => true,
64            _ => out_of_bounds(),
65        }
66    }
67    #[inline]
68    fn into_usize(self) -> usize {
69        usize::from(self)
70    }
71}
72
73impl Enum for () {
74    type Array<V> = [V; 1];
75
76    #[inline]
77    fn from_usize(value: usize) -> Self {
78        match value {
79            0 => (),
80            _ => out_of_bounds(),
81        }
82    }
83    #[inline]
84    fn into_usize(self) -> usize {
85        0
86    }
87}
88
89impl Enum for u8 {
90    type Array<V> = [V; 256];
91
92    #[inline]
93    fn from_usize(value: usize) -> Self {
94        value.try_into().unwrap_or_else(|_| out_of_bounds())
95    }
96    #[inline]
97    fn into_usize(self) -> usize {
98        usize::from(self)
99    }
100}
101
102impl Enum for Infallible {
103    type Array<V> = [V; 0];
104
105    #[inline]
106    fn from_usize(_: usize) -> Self {
107        out_of_bounds();
108    }
109    #[inline]
110    fn into_usize(self) -> usize {
111        match self {}
112    }
113}
114
115impl Enum for Ordering {
116    type Array<V> = [V; 3];
117
118    #[inline]
119    fn from_usize(value: usize) -> Self {
120        match value {
121            0 => Ordering::Less,
122            1 => Ordering::Equal,
123            2 => Ordering::Greater,
124            _ => out_of_bounds(),
125        }
126    }
127    #[inline]
128    fn into_usize(self) -> usize {
129        match self {
130            Ordering::Less => 0,
131            Ordering::Equal => 1,
132            Ordering::Greater => 2,
133        }
134    }
135}