#[macro_export]
macro_rules! decodable_enum {
($(#[$meta:meta])* $visibility:vis enum $name:ident<
$raw_type:ty,
$error_type:ident,
$error_path:ident
> {
$($(#[$variant_meta:meta])* $variant:ident = $val:expr),*,
}) => {
$(#[$meta])*
#[derive(
::core::clone::Clone,
::core::marker::Copy,
::core::fmt::Debug,
::core::cmp::Eq,
::core::hash::Hash,
::core::cmp::PartialEq)]
$visibility enum $name {
$($(#[$variant_meta])* $variant = $val),*
}
impl $name {
pub const VALUES : &'static [$raw_type] = &[$($val),*,];
pub const VARIANTS : &'static [$name] = &[$($name::$variant),*,];
pub fn name(&self) -> &'static ::core::primitive::str {
match self {
$($name::$variant => ::core::stringify!($variant)),*
}
}
}
impl ::core::convert::From<&$name> for $raw_type {
fn from(v: &$name) -> $raw_type {
match v {
$($name::$variant => $val),*,
}
}
}
impl ::core::convert::TryFrom<$raw_type> for $name {
type Error = $error_type;
fn try_from(value: $raw_type) -> ::core::result::Result<Self, $error_type> {
match value {
$($val => ::core::result::Result::Ok($name::$variant)),*,
_ => ::core::result::Result::Err($error_type::$error_path),
}
}
}
}
}
pub trait Decodable: ::core::marker::Sized {
type Error;
fn decode(buf: &[u8]) -> ::core::result::Result<Self, Self::Error>;
}
pub trait Encodable: ::core::marker::Sized {
type Error;
fn encoded_len(&self) -> ::core::primitive::usize;
fn encode(&self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error>;
}
#[macro_export]
macro_rules! codable_as_bitmask {
($type:ty, $raw_type:ty, $error_type:ident, $error_path:ident) => {
impl $type {
pub fn from_bits(
v: $raw_type,
) -> impl ::std::iter::Iterator<Item = ::core::result::Result<$type, $error_type>> {
(0..<$raw_type>::BITS).map(|bit| 1 << bit).filter(move |val| (v & val) != 0).map(
|val| {
::std::convert::TryInto::<$type>::try_into(val)
.map_err(|_| $error_type::$error_path)
},
)
}
pub fn to_bits<'a>(
mut it: impl ::std::iter::Iterator<Item = &'a $type>,
) -> ::core::result::Result<$raw_type, $error_type> {
it.try_fold(0, |acc, item| {
let v = ::std::convert::Into::<$raw_type>::into(item);
if v == 0 {
return ::core::result::Result::Err($error_type::$error_path);
}
if (v as f64).log2().ceil() != (v as f64).log2().floor() {
return ::core::result::Result::Err($error_type::$error_path);
}
::core::result::Result::Ok(acc | v)
})
}
}
};
}
#[cfg(test)]
#[no_implicit_prelude]
mod test {
use ::assert_matches::assert_matches;
use ::core::convert::{From, TryFrom};
use ::core::option::Option::Some;
use ::core::result::Result;
use ::core::{assert, assert_eq, panic};
use ::std::collections::HashSet;
use ::std::iter::{IntoIterator, Iterator};
use ::std::vec;
#[derive(Debug, PartialEq)]
pub(crate) enum TestError {
OutOfRange,
}
decodable_enum! {
pub(crate) enum TestEnum<u16, TestError, OutOfRange> {
One = 0x0001,
Two = 0x0002,
Max = 0xFFFF,
}
}
codable_as_bitmask!(TestEnum, u16, TestError, OutOfRange);
decodable_enum! {
pub(crate) enum TestEnum2<u16, TestError, OutOfRange> {
One = 0x0001, Two = 0x0002, Big = 0x4000, }
}
codable_as_bitmask!(TestEnum2, u16, TestError, OutOfRange);
#[test]
fn try_from_success() {
let one = TestEnum::try_from(1);
assert!(one.is_ok());
assert_eq!(TestEnum::One, one.unwrap());
let two = TestEnum::try_from(2);
assert!(two.is_ok());
assert_eq!(TestEnum::Two, two.unwrap());
let max = TestEnum::try_from(65535);
assert!(max.is_ok());
assert_eq!(TestEnum::Max, max.unwrap());
}
#[test]
fn try_from_error() {
let err = TestEnum::try_from(5);
assert_matches!(err.err(), Some(TestError::OutOfRange));
}
#[test]
fn into_rawtype() {
let raw = u16::from(&TestEnum::One);
assert_eq!(1, raw);
let raw = u16::from(&TestEnum::Two);
assert_eq!(2, raw);
let raw = u16::from(&TestEnum::Max);
assert_eq!(65535, raw);
}
#[test]
fn test_values() {
let v = TestEnum::VALUES.to_vec();
assert_eq!(3, v.len());
assert_eq!(1, v[0]);
assert_eq!(2, v[1]);
assert_eq!(65535, v[2]);
let v = TestEnum2::VALUES.to_vec();
assert_eq!(v, vec![1, 2, 16384]);
}
#[test]
fn test_variants() {
let v = TestEnum::VARIANTS.to_vec();
assert_eq!(3, v.len());
assert_eq!(TestEnum::One, v[0]);
assert_eq!(TestEnum::Two, v[1]);
assert_eq!(TestEnum::Max, v[2]);
}
#[test]
fn test_name() {
assert_eq!("One", TestEnum::One.name());
assert_eq!("Two", TestEnum::Two.name());
assert_eq!("Max", TestEnum::Max.name());
assert_eq!("Big", TestEnum2::Big.name());
}
#[test]
fn as_bitmask() {
let one_and_big = 0x4001;
let enums: HashSet<TestEnum2> = TestEnum2::from_bits(one_and_big)
.collect::<Result<HashSet<_>, _>>()
.expect("should not fail");
assert_eq!(2, enums.len());
let expected_enums = [TestEnum2::One, TestEnum2::Big].into_iter().collect();
assert_eq!(enums, expected_enums);
let all = TestEnum2::VARIANTS;
let value = TestEnum2::to_bits(all.iter()).expect("should work");
assert_eq!(0x4003, value);
}
#[test]
fn bitmask_errors() {
let max = 65535;
let res: Result<HashSet<TestEnum>, _> = TestEnum::from_bits(max).collect();
let _ = res.expect_err("should have failed");
let res: vec::Vec<Result<TestEnum, TestError>> = TestEnum::from_bits(max).collect();
let valid: vec::Vec<TestEnum> = res.into_iter().filter_map(|v| v.ok()).collect();
assert_eq!(valid.len(), 2);
let all = TestEnum::VARIANTS;
let _ = TestEnum::to_bits(all.iter()).expect_err("should fail");
}
}