#[macro_export]
macro_rules! lib_enum {
(
$(#[$enum_meta:meta])+
$enum_ty:ident: $base_ty:ident {
default: $default_variant:ident,
error: $err_ty:ident,
$(
$(#[$var_doc:meta])*
$variant:ident = $value:literal$(,)?
)+
}
) => {
paste::paste! {
$(#[$enum_meta])+
#[repr($base_ty)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum $enum_ty {
$(
$(#[$var_doc])*
$variant = $value,
)+
}
impl $enum_ty {
#[doc = "Creates a new [" $enum_ty "]."]
pub const fn new() -> Self {
Self::$default_variant
}
#[doc = "Converts a [`" $base_ty "`] into a [" $enum_ty "]."]
#[doc = ""]
#[doc = "# Panics"]
#[doc = ""]
#[doc = "Panics if the passed value is an invalid variant"]
pub const fn from_raw_unchecked(val: $base_ty) -> Self {
match Self::from_raw(val) {
Ok(v) => v,
Err(err) => panic!("invalid variant"),
}
}
#[doc = "Attempts to convert a [`" $base_ty "`] into a [" $enum_ty "]."]
pub const fn from_raw(val: $base_ty) -> ::core::result::Result<Self, $err_ty> {
match val {
$(
$value => Ok(Self::$variant),
)+
_ => Err($err_ty::InvalidVariant(val as usize)),
}
}
#[doc = "Converts a [" $enum_ty "] into a [`" $base_ty "`]."]
pub const fn into_raw(self) -> $base_ty {
self as $base_ty
}
}
impl Default for $enum_ty {
fn default() -> Self {
Self::new()
}
}
impl TryFrom<$base_ty> for $enum_ty {
type Error = $err_ty;
fn try_from(val: $base_ty) -> ::core::result::Result<Self, Self::Error> {
Self::from_raw(val)
}
}
impl From<$enum_ty> for $base_ty {
fn from(val: $enum_ty) -> Self {
val.into_raw()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_variants() {
let raw = [
$($value,)+
];
let exp = [
$($enum_ty::$variant,)+
];
raw.into_iter().zip(exp).for_each(|(r, e)| {
assert_eq!($enum_ty::from_raw(r), Ok(e));
assert_eq!($enum_ty::try_from(r), Ok(e));
assert_eq!(e.into_raw(), r);
assert_eq!($base_ty::from(e), r);
});
}
#[test]
fn test_invalid_variants() {
let raw = [
$($value,)+
];
let upper = match $base_ty::BITS {
128 | 64 | 32 | 16 => $base_ty::MAX >> ($base_ty::BITS - 10),
_ => $base_ty::MAX,
};
(0..=upper).chain([$base_ty::MAX]).filter(|r| !raw.iter().any(|v| v == r)).for_each(|r| {
assert_eq!($enum_ty::from_raw(r), Err($err_ty::InvalidVariant(r as usize)));
assert_eq!($enum_ty::try_from(r), Err($err_ty::InvalidVariant(r as usize)));
});
}
}
}
};
}
#[macro_export]
macro_rules! lib_bool_enum {
(
$(#[$enum_meta:meta])+
$enum_ty:ident {
$(#[$false_var_doc:meta])*
$false_variant:ident = false,
$(#[$true_var_doc:meta])*
$true_variant:ident = true,
}
) => {
paste::paste! {
$(#[$enum_meta])+
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum $enum_ty {
$(#[$false_var_doc])+
$false_variant = 0,
$(#[$true_var_doc])+
$true_variant = 1,
}
impl $enum_ty {
#[doc = "Creates a new [" $enum_ty "]."]
pub const fn new() -> Self {
Self::$false_variant
}
#[doc = "Attempts to convert a [`bool`] into a [" $enum_ty "]."]
pub const fn from_bool(val: bool) -> Self {
match val {
false => Self::$false_variant,
true => Self::$true_variant,
}
}
#[doc = "Converts a [" $enum_ty "] into a [`bool`]."]
pub const fn into_bool(self) -> bool {
match self {
Self::$false_variant => false,
Self::$true_variant => true,
}
}
#[doc = " Attempts to convert a [`u8`] into a [" $enum_ty "]."]
pub const fn try_from_u8(val: u8) -> $crate::result::Result<Self> {
match val {
0 => Ok(Self::$false_variant),
1 => Ok(Self::$true_variant),
_ => Err($crate::result::Error::invalid_field_variant(stringify!($enum_ty), val as usize)),
}
}
#[doc = "Converts a [" $enum_ty "] into a [`u8`]."]
pub const fn into_u8(self) -> u8 {
self as u8
}
}
impl Default for $enum_ty {
fn default() -> Self {
Self::new()
}
}
impl From<bool> for $enum_ty {
fn from(val: bool) -> Self {
Self::from_bool(val)
}
}
impl From<$enum_ty> for bool {
fn from(val: $enum_ty) -> Self {
val.into_bool()
}
}
impl TryFrom<u8> for $enum_ty {
type Error = $crate::result::Error;
fn try_from(val: u8) -> ::core::result::Result<Self, Self::Error> {
Self::try_from_u8(val)
}
}
impl From<$enum_ty> for u8 {
fn from(val: $enum_ty) -> Self {
val.into_u8()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_variants() {
let raw = [0, 1];
let exp = [$enum_ty::$false_variant, $enum_ty::$true_variant];
raw.into_iter().zip(exp).for_each(|(r, e)| {
let raw_bool = r != 0;
assert_eq!($enum_ty::from_bool(raw_bool), e);
assert_eq!($enum_ty::from(raw_bool), e);
assert_eq!($enum_ty::try_from_u8(r), Ok(e));
assert_eq!($enum_ty::try_from(r), Ok(e));
assert_eq!(e.into_bool(), raw_bool);
assert_eq!(bool::from(e), raw_bool);
assert_eq!(e.into_u8(), r);
assert_eq!(u8::from(e), r);
});
}
#[test]
fn test_invalid_variants() {
(2..=u8::MAX).for_each(|r| {
let exp_err = $crate::result::Error::invalid_field_variant(stringify!($enum_ty), r as usize);
assert_eq!($enum_ty::try_from_u8(r), Err(exp_err));
assert_eq!($enum_ty::try_from(r), Err(exp_err));
});
}
}
}
};
}