use std::hash::Hash;
use strum::IntoEnumIterator;
pub trait ValidRapidRecorderNamedUsize:
Into<usize>
+ From<usize>
+ Clone
+ Eq
+ PartialEq
+ Hash
+ Copy
+ std::fmt::Debug
+ IntoEnumIterator
{
}
#[macro_export]
macro_rules! impl_rapid_recorder_named_usize {
($enum_type:ty) => {
#[allow(clippy::from_over_into)]
impl Into<usize> for $enum_type {
fn into(self) -> usize {
self as usize
}
}
impl From<usize> for $enum_type {
fn from(value: usize) -> Self {
unsafe { std::mem::transmute(value as u32) }
}
}
#[allow(clippy::non_canonical_clone_impl)]
impl Clone for $enum_type {
fn clone(&self) -> Self {
unsafe {
let ptr = self as *const Self;
let value = *(ptr as *const u32);
std::mem::transmute(value)
}
}
}
impl Copy for $enum_type {}
impl PartialEq for $enum_type {
fn eq(&self, other: &Self) -> bool {
unsafe {
let self_ptr = self as *const Self;
let other_ptr = other as *const Self;
let self_value = *(self_ptr as *const u32);
let other_value = *(other_ptr as *const u32);
self_value == other_value
}
}
}
impl Eq for $enum_type {}
impl std::fmt::Debug for $enum_type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", *self)
}
}
impl std::hash::Hash for $enum_type {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe {
let ptr = self as *const Self;
let value = *(ptr as *const u32);
value.hash(state);
}
}
}
impl $crate::prelude::ValidRapidRecorderNamedUsize for $enum_type {}
};
}
pub fn validate_enum_for_recorder<T: ValidRapidRecorderNamedUsize>() -> Result<usize, String> {
let variant_count = T::iter().count();
for variant in T::iter() {
let as_usize: usize = variant.into();
let back_to_enum = T::from(as_usize);
if variant != back_to_enum {
return Err(format!(
"Conversion error: {:?} converted to {} and back to {:?}. These should be identical.",
variant, as_usize, back_to_enum
));
}
if as_usize >= variant_count {
return Err(format!(
"Variant {:?} has discriminant {} which is >= the enum's variant count {}. \
Ensure your variants have sequential values starting from 0.",
variant, as_usize, variant_count
));
}
}
Ok(variant_count)
}
#[cfg(test)]
mod tests {
use super::*;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
#[repr(u32)]
#[derive(EnumIter)]
enum TestEnum {
A,
B = 5,
C,
}
impl_rapid_recorder_named_usize!(TestEnum);
#[test]
fn test_trait_implementations() {
assert_eq!(<TestEnum as Into<usize>>::into(TestEnum::A), 0 as usize);
assert_eq!(<TestEnum as Into<usize>>::into(TestEnum::B), 5 as usize);
assert_eq!(<TestEnum as Into<usize>>::into(TestEnum::C), 6 as usize);
assert_eq!(TestEnum::from(0), TestEnum::A);
assert_eq!(TestEnum::from(5), TestEnum::B);
assert_eq!(TestEnum::from(6), TestEnum::C);
let a = TestEnum::A;
let cloned = a.clone();
assert_eq!(a, cloned);
assert_eq!(TestEnum::A, TestEnum::A);
assert_ne!(TestEnum::A, TestEnum::B);
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert(TestEnum::A, "A");
map.insert(TestEnum::B, "B");
assert_eq!(map.get(&TestEnum::A), Some(&"A"));
assert_eq!(map.get(&TestEnum::B), Some(&"B"));
}
}