enum_map/
internal.rs

1// SPDX-FileCopyrightText: 2017 - 2023 Kamila Borowska <kamila@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    /// Length of the enum.
19    const LENGTH: usize;
20
21    /// Takes an usize, and returns an element matching `into_usize` function.
22    fn from_usize(value: usize) -> Self;
23    /// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
24    fn into_usize(self) -> usize;
25}
26
27/// Trait associating enum with an array.
28///
29/// This exists due to limitations of Rust compiler that prevent arrays from using
30/// associated constants in structures. The array length must match `LENGTH` of an
31/// `Enum`.
32pub trait EnumArray<V>: Enum {
33    /// Representation of an enum map for type `V`.
34    type Array: Array<V>;
35}
36
37/// Array for enum-map storage.
38///
39/// This trait is inteded for primitive array types (with fixed length).
40///
41/// # Safety
42///
43/// The array length needs to match actual storage.
44pub unsafe trait Array<V> {
45    // This is necessary duplication because the length in Enum trait can be
46    // provided by user and may not be trustworthy for unsafe code.
47    const LENGTH: usize;
48}
49
50unsafe impl<V, const N: usize> Array<V> for [V; N] {
51    const LENGTH: usize = N;
52}
53
54#[doc(hidden)]
55#[inline]
56pub fn out_of_bounds() -> ! {
57    panic!("index out of range for Enum::from_usize");
58}
59
60impl Enum for bool {
61    const LENGTH: usize = 2;
62
63    #[inline]
64    fn from_usize(value: usize) -> Self {
65        match value {
66            0 => false,
67            1 => true,
68            _ => out_of_bounds(),
69        }
70    }
71    #[inline]
72    fn into_usize(self) -> usize {
73        usize::from(self)
74    }
75}
76
77impl<T> EnumArray<T> for bool {
78    type Array = [T; Self::LENGTH];
79}
80
81impl Enum for () {
82    const LENGTH: usize = 1;
83
84    #[inline]
85    fn from_usize(value: usize) -> Self {
86        match value {
87            0 => (),
88            _ => out_of_bounds(),
89        }
90    }
91    #[inline]
92    fn into_usize(self) -> usize {
93        0
94    }
95}
96
97impl<T> EnumArray<T> for () {
98    type Array = [T; Self::LENGTH];
99}
100
101impl Enum for u8 {
102    const LENGTH: usize = 256;
103
104    #[inline]
105    fn from_usize(value: usize) -> Self {
106        value.try_into().unwrap_or_else(|_| out_of_bounds())
107    }
108    #[inline]
109    fn into_usize(self) -> usize {
110        usize::from(self)
111    }
112}
113
114impl<T> EnumArray<T> for u8 {
115    type Array = [T; Self::LENGTH];
116}
117
118impl Enum for Infallible {
119    const LENGTH: usize = 0;
120
121    #[inline]
122    fn from_usize(_: usize) -> Self {
123        out_of_bounds();
124    }
125    #[inline]
126    fn into_usize(self) -> usize {
127        match self {}
128    }
129}
130
131impl<T> EnumArray<T> for Infallible {
132    type Array = [T; Self::LENGTH];
133}
134
135impl Enum for Ordering {
136    const LENGTH: usize = 3;
137
138    #[inline]
139    fn from_usize(value: usize) -> Self {
140        match value {
141            0 => Ordering::Less,
142            1 => Ordering::Equal,
143            2 => Ordering::Greater,
144            _ => out_of_bounds(),
145        }
146    }
147    #[inline]
148    fn into_usize(self) -> usize {
149        match self {
150            Ordering::Less => 0,
151            Ordering::Equal => 1,
152            Ordering::Greater => 2,
153        }
154    }
155}
156
157impl<T> EnumArray<T> for Ordering {
158    type Array = [T; Self::LENGTH];
159}