jdwp_client/
utils.rs

1/// Macro to implement BinRead + BinWrite for repr(xx) enums
2#[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]; // binary data for Value2
85        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]; // not mapped to any variant
93        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]; // binary data for Value2
108        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]; // not mapped to any variant
116        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}