1use std::cmp::Ordering;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4
5pub trait CheckedEnum: Sized + 'static {
6 type Storage: Copy + Clone + 'static;
7
8 fn try_from_storage(val: Self::Storage) -> Option<Self>;
9 fn to_storage(self) -> Self::Storage;
10}
11
12pub struct UncheckedEnum<T>
13where
14 T: CheckedEnum,
15{
16 pub value: T::Storage,
17}
18
19impl<T> Copy for UncheckedEnum<T> where T: CheckedEnum {}
20impl<T> Clone for UncheckedEnum<T>
21where
22 T: CheckedEnum,
23{
24 fn clone(&self) -> Self {
25 *self
26 }
27}
28
29impl<T> fmt::Debug for UncheckedEnum<T>
30where
31 T: CheckedEnum,
32 T: fmt::Debug,
33 T::Storage: fmt::Debug,
34{
35 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
36 fmt.debug_struct("UncheckedEnum")
37 .field("enum", &self.as_enum())
38 .field("value", &self.value)
39 .finish()
40 }
41}
42
43impl<T> PartialEq for UncheckedEnum<T>
44where
45 T: CheckedEnum,
46 T::Storage: PartialEq,
47{
48 fn eq(&self, other: &Self) -> bool {
49 self.value == other.value
50 }
51
52 fn ne(&self, other: &Self) -> bool {
53 self.value != other.value
54 }
55}
56
57impl<T> PartialEq<T> for UncheckedEnum<T>
58where
59 T: CheckedEnum,
60 T: PartialEq,
61{
62 fn eq(&self, other: &T) -> bool {
63 T::try_from_storage(self.value).as_ref() == Some(other)
64 }
65}
66
67impl<T> PartialEq<Option<T>> for UncheckedEnum<T>
68where
69 T: CheckedEnum,
70 T: PartialEq,
71{
72 fn eq(&self, other: &Option<T>) -> bool {
73 T::try_from_storage(self.value) == *other
74 }
75}
76
77impl<T> Eq for UncheckedEnum<T>
78where
79 T: CheckedEnum,
80 T::Storage: Eq,
81{
82}
83
84impl<T> PartialOrd for UncheckedEnum<T>
85where
86 T: CheckedEnum,
87 T::Storage: PartialOrd,
88{
89 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
90 self.value.partial_cmp(&other.value)
91 }
92}
93
94impl<T> Ord for UncheckedEnum<T>
95where
96 T: CheckedEnum,
97 T::Storage: Ord,
98{
99 fn cmp(&self, other: &Self) -> Ordering {
100 self.value.cmp(&other.value)
101 }
102}
103
104impl<T> Hash for UncheckedEnum<T>
105where
106 T: CheckedEnum,
107 T::Storage: Hash,
108{
109 fn hash<H>(&self, state: &mut H)
110 where
111 H: Hasher,
112 {
113 self.value.hash(state)
114 }
115}
116
117impl<T> UncheckedEnum<T>
118where
119 T: CheckedEnum,
120{
121 #[inline]
122 pub fn new(value: T::Storage) -> Self {
123 UncheckedEnum { value }
124 }
125
126 #[inline]
127 pub fn as_enum(self) -> Option<T> {
128 T::try_from_storage(self.value)
129 }
130
131 #[inline]
132 pub unsafe fn as_enum_unchecked(&self) -> T {
133 ::std::mem::transmute_copy(self)
134 }
135}
136
137impl<T> From<T> for UncheckedEnum<T>
138where
139 T: CheckedEnum,
140{
141 fn from(val: T) -> UncheckedEnum<T> {
142 UncheckedEnum::new(T::to_storage(val))
143 }
144}
145
146macro_rules! imp_from_prim {
147 ($prim:ident) => {
148 impl<T> From< $prim > for UncheckedEnum<T>
149 where
150 T: CheckedEnum<Storage = $prim>,
151 {
152 #[inline]
153 fn from(value: T::Storage) -> Self {
154 UncheckedEnum::new(value)
155 }
156 }
157 };
158 ($($prim:ident)*) => {
159 $(imp_from_prim!($prim);)*
160 }
161}
162
163imp_from_prim!(i8 u8 i16 u16 i32 u32 i64 u64 isize usize);
164
165#[macro_export]
166macro_rules! checked_enum {
167 ($ety:ident($repr:ty) => { $from:ident, $to:ident }) => {
168 impl $crate::CheckedEnum for $ety {
169 type Storage = $repr;
170
171 #[inline]
172 fn try_from_storage(val: Self::Storage) -> Option<Self> {
173 $ety::$from(val)
174 }
175
176 #[inline]
177 fn to_storage(self) -> Self::Storage {
178 $ety::$to(self)
179 }
180 }
181 };
182}