1#[macro_export]
3macro_rules! binrw_enum {
4 (
5 #[repr($ty:ty)]
6 $(#[$meta:meta])*
7 $vis:vis enum $name:ident {
8 $($variant:ident = $value:expr),* $(,)?
9 }
10 ) => {
11 $(#[$meta])*
12 #[repr($ty)]
13 $vis enum $name {
14 $($variant = $value),*
15 }
16
17 impl binrw::BinRead for $name {
18 type Args<'a> = ();
19
20 fn read_options<R: binrw::io::Read + binrw::io::Seek>(
21 reader: &mut R,
22 endian: binrw::Endian,
23 _: Self::Args<'_>
24 ) -> binrw::BinResult<Self> {
25 let val = <$ty>::read_options(reader, endian, ())?;
26 match val {
27 $(x if x == $value => Ok(Self::$variant),)*
28 other => Err(binrw::Error::AssertFail {
29 pos: reader.stream_position()?,
30 message: format!(
31 "Invalid value {} for enum {}",
32 other,
33 stringify!($name)
34 ),
35 }),
36 }
37 }
38 }
39
40 impl binrw::BinWrite for $name {
41 type Args<'a> = ();
42
43 fn write_options<W: binrw::io::Write + binrw::io::Seek>(
44 &self,
45 writer: &mut W,
46 endian: binrw::Endian,
47 _: Self::Args<'_>
48 ) -> binrw::BinResult<()> {
49 let val: $ty = match self {
50 $(Self::$variant => $value),*
51 };
52 val.write_options(writer, endian, ())
53 }
54 }
55 };
56}
57
58#[cfg(test)]
59mod tests {
60 use binrw::{BinRead, BinWrite};
61 use std::io::Cursor;
62
63 binrw_enum! {
64 #[repr(u8)]
65 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
66 pub enum TestSet {
67 Value1 = 1,
68 Value2 = 2,
69 Value3 = 3,
70 }
71 }
72 binrw_enum! {
73 #[repr(u16)]
74 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
75 pub enum TestSet16 {
76 Value1 = 1,
77 Value2 = 2,
78 Value3 = 3,
79 }
80 }
81
82 #[test]
83 fn test_enum_read_valid() {
84 let data = [2u8]; let mut cursor = Cursor::new(&data);
86 let value = TestSet::read_be(&mut cursor).unwrap();
87 assert_eq!(value, TestSet::Value2);
88 }
89
90 #[test]
91 fn test_enum_read_invalid() {
92 let data = [99u8]; let mut cursor = Cursor::new(&data);
94 let result = TestSet::read_be(&mut cursor);
95 assert!(result.is_err());
96 }
97
98 #[test]
99 fn test_enum_write() {
100 let mut buffer = Cursor::new(Vec::new());
101 TestSet::Value3.write_be(&mut buffer).unwrap();
102 assert_eq!(buffer.into_inner(), vec![3u8]);
103 }
104
105 #[test]
106 fn test_enum_read_valid_u16() {
107 let data = [0u8, 2u8]; let mut cursor = Cursor::new(&data);
109 let value = TestSet16::read_be(&mut cursor).unwrap();
110 assert_eq!(value, TestSet16::Value2);
111 }
112
113 #[test]
114 fn test_enum_read_invalid_u16() {
115 let data = [0u8, 99u8]; let mut cursor = Cursor::new(&data);
117 let result = TestSet16::read_be(&mut cursor);
118 assert!(result.is_err());
119 }
120
121 #[test]
122 fn test_enum_write_u16() {
123 let mut buffer = Cursor::new(Vec::new());
124 TestSet16::Value3.write_be(&mut buffer).unwrap();
125 assert_eq!(buffer.into_inner(), vec![0u8, 3u8]);
126 }
127}