1pub struct OptionsBuilder(Options);
3
4const DEFAULT: Options = (ByteOrder::NATIVE as Options) << BYTEORDER_BIT;
5
6pub const fn new() -> OptionsBuilder {
10    OptionsBuilder(DEFAULT)
11}
12
13pub type Options = u128;
21
22const BYTEORDER_BIT: Options = 0;
23const INTEGER_BIT: Options = 1;
24const LENGTH_BIT: Options = 2;
25const FLOAT_BIT: Options = 8;
26const LENGTH_WIDTH_BIT: Options = 16;
27
28impl OptionsBuilder {
29    pub const fn with_integer(self, integer: Integer) -> Self {
31        Self(self.0 | ((integer as Options) << INTEGER_BIT))
32    }
33
34    pub const fn with_float(self, float: Float) -> Self {
36        Self(self.0 | ((float as Options) << FLOAT_BIT))
37    }
38
39    pub const fn with_byte_order(self, byte_order: ByteOrder) -> Self {
41        Self(self.0 | ((byte_order as Options) << BYTEORDER_BIT))
42    }
43
44    pub const fn with_length(self, length: Integer) -> Self {
46        Self(self.0 | ((length as Options) << LENGTH_BIT))
47    }
48
49    pub const fn with_length_width(self, width: Width) -> Self {
51        let this = self.with_length(Integer::Fixed);
52        Self(this.0 | ((width as Options) << LENGTH_WIDTH_BIT))
53    }
54
55    pub const fn build(self) -> Options {
57        self.0
58    }
59}
60
61#[doc(hidden)]
62pub const fn integer<const OPT: Options>() -> Integer {
63    match (OPT >> INTEGER_BIT) & 0b1 {
64        0 => Integer::Variable,
65        _ => Integer::Fixed,
66    }
67}
68
69#[doc(hidden)]
70pub const fn float<const OPT: Options>() -> Float {
71    match (OPT >> FLOAT_BIT) & 0b11 {
72        0 => Float::Integer,
73        1 => Float::Variable,
74        _ => Float::Fixed,
75    }
76}
77
78#[doc(hidden)]
79pub const fn length<const OPT: Options>() -> Integer {
80    match (OPT >> LENGTH_BIT) & 0b1 {
81        0 => Integer::Variable,
82        _ => Integer::Fixed,
83    }
84}
85
86#[doc(hidden)]
87pub const fn length_width<const OPT: Options>() -> Width {
88    match (OPT >> LENGTH_WIDTH_BIT) & 0b11 {
89        0 => Width::U8,
90        1 => Width::U16,
91        2 => Width::U32,
92        _ => Width::U64,
93    }
94}
95
96#[doc(hidden)]
97pub const fn byteorder<const OPT: Options>() -> ByteOrder {
98    match (OPT >> BYTEORDER_BIT) & 0b1 {
99        0 => ByteOrder::LittleEndian,
100        _ => ByteOrder::BigEndian,
101    }
102}
103
104#[cfg_attr(test, derive(Debug, PartialEq))]
106#[repr(u8)]
107#[non_exhaustive]
108pub enum Integer {
109    Variable = 0,
111    Fixed = 1,
113}
114
115#[cfg_attr(test, derive(Debug, PartialEq))]
117#[repr(u8)]
118#[non_exhaustive]
119pub enum Float {
120    Integer = 0,
123    Variable = 1,
125    Fixed = 2,
127}
128
129#[derive(PartialEq, Eq)]
131#[cfg_attr(test, derive(Debug))]
132#[repr(u8)]
133#[non_exhaustive]
134pub enum ByteOrder {
135    LittleEndian = 0,
137    BigEndian = 1,
139}
140
141impl ByteOrder {
142    pub const NATIVE: Self = if cfg!(target_endian = "little") {
144        Self::LittleEndian
145    } else {
146        Self::BigEndian
147    };
148
149    pub const NETWORK: Self = Self::BigEndian;
151}
152
153#[doc(hidden)]
154#[macro_export]
155macro_rules! width_arm {
156    ($width:expr, $macro:path) => {
157        match $width {
158            $crate::exports::options::Width::U8 => {
159                $macro!(u8)
160            }
161            $crate::exports::options::Width::U16 => {
162                $macro!(u16)
163            }
164            $crate::exports::options::Width::U32 => {
165                $macro!(u32)
166            }
167            _ => {
168                $macro!(u64)
169            }
170        }
171    };
172}
173
174#[derive(Clone, Copy)]
176#[cfg_attr(test, derive(Debug, PartialEq))]
177#[repr(u8)]
178#[non_exhaustive]
179pub enum Width {
180    U8 = 0,
182    U16 = 1,
184    U32 = 2,
186    U64 = 3,
188}
189
190#[test]
191fn test_builds() {
192    macro_rules! assert_or_default {
193        ($expr:expr, $test:expr, $default:expr, ()) => {
194            assert_eq!(
195                $test,
196                $default,
197                "{}: Expected default value for {}",
198                stringify!($expr),
199                stringify!($test)
200            );
201        };
202
203        ($expr:expr, $test:expr, $_default:expr, ($expected:expr)) => {
204            assert_eq!(
205                $test,
206                $expected,
207                "{}: Expected custom value for {}",
208                stringify!($expr),
209                stringify!($test)
210            );
211        };
212    }
213
214    macro_rules! test_case {
215        ($expr:expr => {
216            $(byteorder = $byteorder:expr,)?
217            $(integer = $integer:expr,)?
218            $(float = $float:expr,)?
219            $(length = $length:expr,)?
220            $(length_width = $length_width:expr,)?
221        }) => {{
222            const O: Options = $expr.build();
223            assert_or_default!($expr, byteorder::<O>(), ByteOrder::NATIVE, ($($byteorder)?));
224            assert_or_default!($expr, integer::<O>(), Integer::Variable, ($($integer)?));
225            assert_or_default!($expr, float::<O>(), Float::Integer, ($($float)?));
226            assert_or_default!($expr, length::<O>(), Integer::Variable, ($($length)?));
227            assert_or_default!($expr, length_width::<O>(), Width::U8, ($($length_width)?));
228        }}
229    }
230
231    test_case! {
232        self::new() => {}
233    }
234
235    test_case! {
236        self::new().with_integer(Integer::Fixed) => {
237            integer = Integer::Fixed,
238        }
239    }
240
241    test_case! {
242        self::new().with_float(Float::Fixed) => {
243            float = Float::Fixed,
244        }
245    }
246
247    test_case! {
248        self::new().with_float(Float::Variable) => {
249            float = Float::Variable,
250        }
251    }
252
253    test_case! {
254        self::new().with_float(Float::Variable) => {
255            float = Float::Variable,
256        }
257    }
258
259    test_case! {
260        self::new().with_byte_order(ByteOrder::BigEndian) => {
261            byteorder = ByteOrder::BigEndian,
262        }
263    }
264
265    test_case! {
266        self::new().with_byte_order(ByteOrder::LittleEndian) => {
267            byteorder = ByteOrder::LittleEndian,
268        }
269    }
270
271    test_case! {
272        self::new().with_length_width(Width::U16) => {
273            length = Integer::Fixed,
274            length_width = Width::U16,
275        }
276    }
277
278    test_case! {
279        self::new().with_length_width(Width::U32) => {
280            length = Integer::Fixed,
281            length_width = Width::U32,
282        }
283    }
284
285    test_case! {
286        self::new().with_length_width(Width::U64) => {
287            length = Integer::Fixed,
288            length_width = Width::U64,
289        }
290    }
291}