indexed_valued_enums/indexed_enum/mod.rs
1use crate::common_utils::split_usize_to_isizes;
2
3/// Allows getting an index from an enum's variant to a usize, and also get the same variant
4/// from said index, for example, having the following implementation:
5///
6/// ```rust
7/// #[repr(usize)]
8/// enum Number{ First, Second, Third }
9///
10/// impl indexed_valued_enums::indexed_enum::Indexed for Number{
11/// const VARIANTS_LEN: usize = 3;
12/// const VARIANTS_NAMES: &'static [&'static str] = &["First", "Second", "Third"];
13/// }
14///
15/// impl indexed_valued_enums::indexed_enum::IndexedWithVariant for Number{
16/// const VARIANTS: &'static [Self] = &[Number::First, Number::Second, Number::Third];
17/// }
18///
19/// ```
20/// Calling [variant_index] on every enum produces [First->0, Second->1, Third->2].
21///
22/// Calling on [from_variant_index] over the enums would produce [0->First, 1->Second, 2->Third].
23///
24/// Note this documentation it's solely informational, it is dis-recommended to implement this trait
25/// manually, but using the derive macro [crate::legacy::valued_enum::Valued] or the declarative macro
26/// [crate::create_indexed_valued_enum] instead.
27pub trait Indexed {
28 /// Number of variants.
29 ///
30 /// [IndexedWithVariant::VARIANTS] and [Self::VARIANTS_NAMES] should have the same length as this constant.
31 const VARIANTS_LEN: usize;
32
33 /// Array with the name of every variant
34 const VARIANTS_NAMES: &'static [&'static str];
35}
36
37/// Similar to [Indexed], but allows getting a constant of every variant for this enum.
38pub trait IndexedWithVariant: Sized where Self: 'static {
39
40 /// Array storing all the variants of the enum ordered by index.
41 const VARIANTS: &'static [Self];
42}
43
44/// Tells a target type this enum resolves to, where this target should be another enum just
45/// like the implementor, but where every variant has no field, being unit like.
46pub trait Fieldless : Indexed{
47 /// The target that should be an enum where every field is unit like.
48 type Target;
49}
50
51/// Gets the index for a variant of an enum marked with ``#[repr(usize)]``.
52///
53/// This operation is O(1).
54
55// DO NOT REMOVE INLINE, it will cause the variant_name function to crash.
56#[inline]
57pub const fn variant_index<T>(variant: &T) -> usize {
58 unsafe { *(variant as *const T).cast::<usize>() }
59}
60
61/// Gets the variant corresponding to said index, this operation is O(1) as it just gets
62/// the index as a read-copy from [IndexedWithVariant::VARIANTS].
63///
64/// The enum bust be marked with ``#[repr(usize)]`` and it doesn't need to implement the [Clone]
65/// trait as the array is treated as a raw pointer whose value is read without cloning through
66/// [core::ptr::read].
67///
68/// This operation is O(1).
69pub const fn from_variant_index<TIndexed: IndexedWithVariant>(index: usize) -> Option<TIndexed> {
70 if index >= TIndexed::VARIANTS.len() {
71 return None;
72 }
73 let (first_offset, second_offset, third_offset) = split_usize_to_isizes(index);
74 Some(unsafe {
75 TIndexed::VARIANTS
76 .as_ptr()
77 .offset(first_offset)
78 .offset(second_offset)
79 .offset(third_offset)
80 .read()
81 })
82}
83
84/// Gets the variant corresponding to said index as a static reference, this operation is O(1) as it
85/// just gets the index as a read-copy from [IndexedWithVariant::VARIANTS].
86///
87/// The enum bust be marked with ``#[repr(usize)]`` and it doesn't need to implement the [Clone]
88/// trait as the array is treated as a raw pointer whose value is read without cloning through
89/// [core::ptr::read].
90///
91/// This operation is O(1).
92pub const fn ref_from_variant_index<TIndexed: IndexedWithVariant>(index: usize) -> Option<&'static TIndexed> {
93 if index >= TIndexed::VARIANTS.len() {
94 return None;
95 }
96 let (first_offset, second_offset, third_offset) = split_usize_to_isizes(index);
97 unsafe {
98 TIndexed::VARIANTS
99 .as_ptr()
100 .offset(first_offset)
101 .offset(second_offset)
102 .offset(third_offset)
103 .as_ref()
104 }
105}
106
107/// Gets the name for a variant of an enum marked with ``#[repr(usize)]``.
108///
109/// This operation is O(1).
110pub const fn variant_name<TIndexed: Indexed>(variant: &TIndexed) -> &'static str {
111 <TIndexed as Indexed>::VARIANTS_NAMES[variant_index(variant)]
112}
113
114/// Gets the variant corresponding to said name.
115///
116/// The enum bust be marked with ``#[repr(usize)]`` and it doesn't need to implement the [Clone]
117/// trait as the array is treated as a raw pointer whose value is read without cloning through
118/// [core::ptr::read].
119///
120/// This operation is O(N), but since it is const, be inserted as const.
121pub const fn from_variant_name<TIndexed: Indexed + IndexedWithVariant>(search: &str) -> Option<TIndexed> {
122 let mut index = 0;
123 while index< <TIndexed as Indexed>::VARIANTS_LEN{
124 if compare_strings(search, <TIndexed as Indexed>::VARIANTS_NAMES[index]) {
125 return from_variant_index(index);
126 }
127 index+=1;
128 }
129 None
130}
131
132const fn compare_strings(string_1:&str, string_2:&str) -> bool {
133 if string_1.len()!=string_2.len(){
134 return false;
135 }
136 let bytes_1 = string_1.as_bytes();
137 let bytes_2 = string_2.as_bytes();
138 let mut index = 0;
139 while index<bytes_1.len(){
140 if bytes_1[index]!=bytes_2[index]{
141 return false;
142 }
143 index+=1;
144 }
145 true
146}
147
148/// Searches for a variant verifying ``VARIANT::value == search_value``.
149///
150/// This operation has a worst case scenario of O(N), where N is the length of the array, this
151/// happens when either the value cannot be found, or the value found is the last of the array.
152pub fn search_value<'value, ENUM, VALUE, VALUES>(search_value: &VALUE, values: VALUES) -> Option<ENUM>
153where
154 ENUM: IndexedWithVariant,
155 VALUE: PartialEq + 'value,
156 VALUES: Iterator<Item= &'value VALUE>
157{
158 values
159 .enumerate()
160 .filter(|(_, value)| search_value.eq(value))
161 .next()
162 .map(|(index, _)| from_variant_index(index))
163 .flatten()
164}