pub struct OptionsBuilder(Options);
const DEFAULT: Options = (ByteOrder::NATIVE as Options) << BYTEORDER_BIT;
pub const fn new() -> OptionsBuilder {
OptionsBuilder(DEFAULT)
}
pub type Options = u128;
const BYTEORDER_BIT: Options = 0;
const INTEGER_BIT: Options = 1;
const LENGTH_BIT: Options = 2;
const FLOAT_BIT: Options = 8;
const LENGTH_WIDTH_BIT: Options = 16;
impl OptionsBuilder {
pub const fn with_integer(self, integer: Integer) -> Self {
Self(self.0 | ((integer as Options) << INTEGER_BIT))
}
pub const fn with_float(self, float: Float) -> Self {
Self(self.0 | ((float as Options) << FLOAT_BIT))
}
pub const fn with_byte_order(self, byte_order: ByteOrder) -> Self {
Self(self.0 | ((byte_order as Options) << BYTEORDER_BIT))
}
pub const fn with_length(self, length: Integer) -> Self {
Self(self.0 | ((length as Options) << LENGTH_BIT))
}
pub const fn with_length_width(self, width: Width) -> Self {
let this = self.with_length(Integer::Fixed);
Self(this.0 | ((width as Options) << LENGTH_WIDTH_BIT))
}
pub const fn build(self) -> Options {
self.0
}
}
#[doc(hidden)]
pub const fn integer<const OPT: Options>() -> Integer {
match (OPT >> INTEGER_BIT) & 0b1 {
0 => Integer::Variable,
_ => Integer::Fixed,
}
}
#[doc(hidden)]
pub const fn float<const OPT: Options>() -> Float {
match (OPT >> FLOAT_BIT) & 0b11 {
0 => Float::Integer,
1 => Float::Variable,
_ => Float::Fixed,
}
}
#[doc(hidden)]
pub const fn length<const OPT: Options>() -> Integer {
match (OPT >> LENGTH_BIT) & 0b1 {
0 => Integer::Variable,
_ => Integer::Fixed,
}
}
#[doc(hidden)]
pub const fn length_width<const OPT: Options>() -> Width {
match (OPT >> LENGTH_WIDTH_BIT) & 0b11 {
0 => Width::U8,
1 => Width::U16,
2 => Width::U32,
_ => Width::U64,
}
}
#[doc(hidden)]
pub const fn byteorder<const OPT: Options>() -> ByteOrder {
match (OPT >> BYTEORDER_BIT) & 0b1 {
0 => ByteOrder::LittleEndian,
_ => ByteOrder::BigEndian,
}
}
#[cfg_attr(test, derive(Debug, PartialEq))]
#[repr(u8)]
#[non_exhaustive]
pub enum Integer {
Variable = 0,
Fixed = 1,
}
#[cfg_attr(test, derive(Debug, PartialEq))]
#[repr(u8)]
#[non_exhaustive]
pub enum Float {
Integer = 0,
Variable = 1,
Fixed = 2,
}
#[derive(PartialEq, Eq)]
#[cfg_attr(test, derive(Debug))]
#[repr(u8)]
#[non_exhaustive]
pub enum ByteOrder {
LittleEndian = 0,
BigEndian = 1,
}
impl ByteOrder {
pub const NATIVE: Self = if cfg!(target_endian = "little") {
Self::LittleEndian
} else {
Self::BigEndian
};
pub const NETWORK: Self = Self::BigEndian;
}
#[doc(hidden)]
#[macro_export]
macro_rules! width_arm {
($width:expr, $macro:path) => {
match $width {
$crate::exports::options::Width::U8 => {
$macro!(u8)
}
$crate::exports::options::Width::U16 => {
$macro!(u16)
}
$crate::exports::options::Width::U32 => {
$macro!(u32)
}
_ => {
$macro!(u64)
}
}
};
}
#[derive(Clone, Copy)]
#[cfg_attr(test, derive(Debug, PartialEq))]
#[repr(u8)]
#[non_exhaustive]
pub enum Width {
U8 = 0,
U16 = 1,
U32 = 2,
U64 = 3,
}
#[test]
fn test_builds() {
macro_rules! assert_or_default {
($expr:expr, $test:expr, $default:expr, ()) => {
assert_eq!(
$test,
$default,
"{}: Expected default value for {}",
stringify!($expr),
stringify!($test)
);
};
($expr:expr, $test:expr, $_default:expr, ($expected:expr)) => {
assert_eq!(
$test,
$expected,
"{}: Expected custom value for {}",
stringify!($expr),
stringify!($test)
);
};
}
macro_rules! test_case {
($expr:expr => {
$(byteorder = $byteorder:expr,)?
$(integer = $integer:expr,)?
$(float = $float:expr,)?
$(length = $length:expr,)?
$(length_width = $length_width:expr,)?
}) => {{
const O: Options = $expr.build();
assert_or_default!($expr, byteorder::<O>(), ByteOrder::NATIVE, ($($byteorder)?));
assert_or_default!($expr, integer::<O>(), Integer::Variable, ($($integer)?));
assert_or_default!($expr, float::<O>(), Float::Integer, ($($float)?));
assert_or_default!($expr, length::<O>(), Integer::Variable, ($($length)?));
assert_or_default!($expr, length_width::<O>(), Width::U8, ($($length_width)?));
}}
}
test_case! {
self::new() => {}
}
test_case! {
self::new().with_integer(Integer::Fixed) => {
integer = Integer::Fixed,
}
}
test_case! {
self::new().with_float(Float::Fixed) => {
float = Float::Fixed,
}
}
test_case! {
self::new().with_float(Float::Variable) => {
float = Float::Variable,
}
}
test_case! {
self::new().with_float(Float::Variable) => {
float = Float::Variable,
}
}
test_case! {
self::new().with_byte_order(ByteOrder::BigEndian) => {
byteorder = ByteOrder::BigEndian,
}
}
test_case! {
self::new().with_byte_order(ByteOrder::LittleEndian) => {
byteorder = ByteOrder::LittleEndian,
}
}
test_case! {
self::new().with_length_width(Width::U16) => {
length = Integer::Fixed,
length_width = Width::U16,
}
}
test_case! {
self::new().with_length_width(Width::U32) => {
length = Integer::Fixed,
length_width = Width::U32,
}
}
test_case! {
self::new().with_length_width(Width::U64) => {
length = Integer::Fixed,
length_width = Width::U64,
}
}
}