1#![doc = include_str!(concat!("../", core::env!("CARGO_PKG_README")))]
2
3#[cfg(feature = "derive")]
4pub use enum_table_derive::Enumable;
5
6pub mod builder;
7
8mod impls;
9mod intrinsics;
10mod macros;
11
12use dev_macros::*;
13use intrinsics::from_usize;
14
15pub trait Enumable: Sized + 'static {
20 const VARIANTS: &'static [Self];
21 const COUNT: usize = Self::VARIANTS.len();
22}
23
24pub struct EnumTable<K: Enumable, V, const N: usize> {
71 table: [(usize, V); N],
72 _phantom: core::marker::PhantomData<K>,
73}
74
75impl<K: Enumable, V, const N: usize> EnumTable<K, V, N> {
76 pub const fn new(table: [(usize, V); N]) -> Self {
85 Self {
86 table,
87 _phantom: core::marker::PhantomData,
88 }
89 }
90
91 pub fn new_with_fn(mut f: impl FnMut(&K) -> V) -> Self {
100 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
101
102 for variant in K::VARIANTS {
103 builder.push(variant, f(variant));
104 }
105
106 builder.build_to()
107 }
108
109 pub fn try_new_with_fn<E>(
125 mut f: impl FnMut(&K) -> Result<V, E>,
126 ) -> Result<Self, (&'static K, E)> {
127 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
128
129 for variant in K::VARIANTS {
130 match f(variant) {
131 Ok(value) => builder.push(variant, value),
132 Err(e) => return Err((variant, e)),
133 }
134 }
135
136 Ok(builder.build_to())
137 }
138
139 pub fn checked_new_with_fn(mut f: impl FnMut(&K) -> Option<V>) -> Result<Self, &'static K> {
155 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
156
157 for variant in K::VARIANTS {
158 if let Some(value) = f(variant) {
159 builder.push(variant, value);
160 } else {
161 return Err(variant);
162 }
163 }
164
165 Ok(builder.build_to())
166 }
167
168 pub const fn get(&self, variant: &K) -> &V {
174 use_variant_value!(self, variant, i, {
175 return &self.table[i].1;
176 });
177 }
178
179 pub const fn get_mut(&mut self, variant: &K) -> &mut V {
185 use_variant_value!(self, variant, i, {
186 return &mut self.table[i].1;
187 });
188 }
189
190 pub const fn set(&mut self, variant: &K, value: V) -> V {
199 use_variant_value!(self, variant, i, {
200 return core::mem::replace(&mut self.table[i].1, value);
201 });
202 }
203
204 pub const fn len(&self) -> usize {
206 N
207 }
208
209 pub const fn is_empty(&self) -> bool {
211 false
212 }
213
214 pub fn keys(&self) -> impl Iterator<Item = &K> {
216 self.table
217 .iter()
218 .map(|(discriminant, _)| from_usize(discriminant))
219 }
220
221 pub fn values(&self) -> impl Iterator<Item = &V> {
223 self.table.iter().map(|(_, value)| value)
224 }
225
226 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
228 self.table.iter_mut().map(|(_, value)| value)
229 }
230
231 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
233 self.table
234 .iter()
235 .map(|(discriminant, value)| (from_usize(discriminant), value))
236 }
237
238 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
240 self.table
241 .iter_mut()
242 .map(|(discriminant, value)| (from_usize(discriminant), value))
243 }
244}
245
246mod dev_macros {
247 macro_rules! use_variant_value {
248 ($self:ident, $variant:ident, $i:ident,{$($tt:tt)+}) => {
249 let discriminant = crate::intrinsics::to_usize($variant);
250
251 let mut $i = 0;
252 while $i < $self.table.len() {
253 if $self.table[$i].0 == discriminant {
254 $($tt)+
255 }
256 $i += 1;
257 }
258 unreachable!();
259 };
260 }
261
262 pub(super) use use_variant_value;
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
270 enum Color {
271 Red,
272 Green,
273 Blue,
274 }
275
276 impl Enumable for Color {
277 const VARIANTS: &'static [Self] = &[Color::Red, Color::Green, Color::Blue];
278 }
279
280 const TABLES: EnumTable<Color, &'static str, { Color::COUNT }> =
281 crate::et!(Color, &'static str, |color| match color {
282 Color::Red => "Red",
283 Color::Green => "Green",
284 Color::Blue => "Blue",
285 });
286
287 #[test]
288 fn new_with_fn() {
289 let table =
290 EnumTable::<Color, &'static str, { Color::COUNT }>::new_with_fn(|color| match color {
291 Color::Red => "Red",
292 Color::Green => "Green",
293 Color::Blue => "Blue",
294 });
295
296 assert_eq!(table.get(&Color::Red), &"Red");
297 assert_eq!(table.get(&Color::Green), &"Green");
298 assert_eq!(table.get(&Color::Blue), &"Blue");
299 }
300
301 #[test]
302 fn try_new_with_fn() {
303 let table =
304 EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
305 |color| match color {
306 Color::Red => Ok::<&'static str, core::convert::Infallible>("Red"),
307 Color::Green => Ok("Green"),
308 Color::Blue => Ok("Blue"),
309 },
310 );
311
312 assert!(table.is_ok());
313 let table = table.unwrap();
314
315 assert_eq!(table.get(&Color::Red), &"Red");
316 assert_eq!(table.get(&Color::Green), &"Green");
317 assert_eq!(table.get(&Color::Blue), &"Blue");
318
319 let error_table = EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
320 |color| match color {
321 Color::Red => Ok("Red"),
322 Color::Green => Err("Error on Green"),
323 Color::Blue => Ok("Blue"),
324 },
325 );
326
327 assert!(error_table.is_err());
328 let (variant, error) = error_table.unwrap_err();
329
330 assert_eq!(variant, &Color::Green);
331 assert_eq!(error, "Error on Green");
332 }
333
334 #[test]
335 fn checked_new_with_fn() {
336 let table =
337 EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
338 match color {
339 Color::Red => Some("Red"),
340 Color::Green => Some("Green"),
341 Color::Blue => Some("Blue"),
342 }
343 });
344
345 assert!(table.is_ok());
346 let table = table.unwrap();
347
348 assert_eq!(table.get(&Color::Red), &"Red");
349 assert_eq!(table.get(&Color::Green), &"Green");
350 assert_eq!(table.get(&Color::Blue), &"Blue");
351
352 let error_table =
353 EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
354 match color {
355 Color::Red => Some("Red"),
356 Color::Green => None,
357 Color::Blue => Some("Blue"),
358 }
359 });
360
361 assert!(error_table.is_err());
362 let variant = error_table.unwrap_err();
363
364 assert_eq!(variant, &Color::Green);
365 }
366
367 #[test]
368 fn get() {
369 assert_eq!(TABLES.get(&Color::Red), &"Red");
370 assert_eq!(TABLES.get(&Color::Green), &"Green");
371 assert_eq!(TABLES.get(&Color::Blue), &"Blue");
372 }
373
374 #[test]
375 fn get_mut() {
376 let mut table = TABLES;
377 assert_eq!(table.get_mut(&Color::Red), &mut "Red");
378 assert_eq!(table.get_mut(&Color::Green), &mut "Green");
379 assert_eq!(table.get_mut(&Color::Blue), &mut "Blue");
380
381 *table.get_mut(&Color::Red) = "Changed Red";
382 *table.get_mut(&Color::Green) = "Changed Green";
383 *table.get_mut(&Color::Blue) = "Changed Blue";
384
385 assert_eq!(table.get(&Color::Red), &"Changed Red");
386 assert_eq!(table.get(&Color::Green), &"Changed Green");
387 assert_eq!(table.get(&Color::Blue), &"Changed Blue");
388 }
389
390 #[test]
391 fn set() {
392 let mut table = TABLES;
393 assert_eq!(table.set(&Color::Red, "New Red"), "Red");
394 assert_eq!(table.set(&Color::Green, "New Green"), "Green");
395 assert_eq!(table.set(&Color::Blue, "New Blue"), "Blue");
396
397 assert_eq!(table.get(&Color::Red), &"New Red");
398 assert_eq!(table.get(&Color::Green), &"New Green");
399 assert_eq!(table.get(&Color::Blue), &"New Blue");
400 }
401
402 #[test]
403 fn keys() {
404 let keys: Vec<_> = TABLES.keys().collect();
405 assert_eq!(keys, vec![&Color::Red, &Color::Green, &Color::Blue]);
406 }
407
408 #[test]
409 fn values() {
410 let values: Vec<_> = TABLES.values().collect();
411 assert_eq!(values, vec![&"Red", &"Green", &"Blue"]);
412 }
413
414 #[test]
415 fn iter() {
416 let iter: Vec<_> = TABLES.iter().collect();
417 assert_eq!(
418 iter,
419 vec![
420 (&Color::Red, &"Red"),
421 (&Color::Green, &"Green"),
422 (&Color::Blue, &"Blue")
423 ]
424 );
425 }
426
427 #[test]
428 fn iter_mut() {
429 let mut table = TABLES;
430 for (key, value) in table.iter_mut() {
431 *value = match key {
432 Color::Red => "Changed Red",
433 Color::Green => "Changed Green",
434 Color::Blue => "Changed Blue",
435 };
436 }
437 let iter: Vec<_> = table.iter().collect();
438 assert_eq!(
439 iter,
440 vec![
441 (&Color::Red, &"Changed Red"),
442 (&Color::Green, &"Changed Green"),
443 (&Color::Blue, &"Changed Blue")
444 ]
445 );
446 }
447}