binary_layout/fields/
char.rs

1use crate::LayoutAs;
2use core::convert::Infallible;
3
4/// This error is thrown when trying to read a char that isn't a valid unicode codepoint, see [char].
5#[derive(Debug)]
6pub struct InvalidCharError(pub(crate) ());
7
8impl core::fmt::Display for InvalidCharError {
9    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
10        write!(fmt, "InvalidCharError")
11    }
12}
13
14#[cfg(feature = "std")]
15impl std::error::Error for InvalidCharError {}
16
17impl LayoutAs<u32> for char {
18    type ReadError = InvalidCharError;
19    type WriteError = Infallible;
20
21    fn try_read(v: u32) -> Result<Self, Self::ReadError> {
22        char::from_u32(v).ok_or(InvalidCharError(()))
23    }
24
25    fn try_write(v: Self) -> Result<u32, Self::WriteError> {
26        Ok(u32::from(v))
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33    use crate::{binary_layout, InfallibleResultExt, WrappedFieldError};
34
35    const INVALID_UNICODE: u32 = 0xD83Du32;
36
37    macro_rules! test_char {
38        ($endian:ident, $endian_type:ty, $from_endian_fn:ident, $to_endian_fn:ident) => {
39            paste::paste! {
40                #[allow(non_snake_case)]
41                #[test]
42                fn [<test_char_ $endian endian_viewapi_tryread_write>]() {
43                    binary_layout!(layout, $endian_type, {
44                        field1: char as u32,
45                        field2: char as u32,
46                        field3: char as u32,
47                        field4: char as u32,
48                    });
49                    let mut storage = [0; 1024];
50                    storage[12..16].copy_from_slice(&INVALID_UNICODE.$to_endian_fn()); // Invalid unicode code point into field4
51
52                    let mut view = layout::View::new(&mut storage);
53
54                    view.field1_mut().write('a');
55                    view.field2_mut().write('我');
56                    view.field3_mut().write('\0');
57
58                    assert_eq!('a', view.field1().try_read().unwrap());
59                    assert_eq!('我', view.field2().try_read().unwrap());
60                    assert_eq!('\0', view.field3().try_read().unwrap());
61                    assert!(matches!(view.field4().try_read(), Err(WrappedFieldError::LayoutAsError(InvalidCharError(_)))));
62
63                    assert_eq!('a', char::try_from(u32::$from_endian_fn((&storage[0..4]).try_into().unwrap())).unwrap());
64                    assert_eq!('我', char::try_from(u32::$from_endian_fn((&storage[4..8]).try_into().unwrap())).unwrap());
65                    assert_eq!('\0', char::try_from(u32::$from_endian_fn((&storage[8..12]).try_into().unwrap())).unwrap());
66                    assert_eq!(INVALID_UNICODE, u32::$from_endian_fn((&storage[12..16]).try_into().unwrap()));
67                }
68
69                #[allow(non_snake_case)]
70                #[test]
71                fn [<test_char_ $endian endian_viewapi_tryread_trywrite>]() {
72                    binary_layout!(layout, $endian_type, {
73                        field1: char as u32,
74                        field2: char as u32,
75                        field3: char as u32,
76                        field4: char as u32,
77                    });
78                    let mut storage = [0; 1024];
79                    storage[12..16].copy_from_slice(&INVALID_UNICODE.$to_endian_fn()); // Invalid unicode code point into field4
80
81                    let mut view = layout::View::new(&mut storage);
82
83                    view.field1_mut().try_write('a').infallible_unwrap();
84                    view.field2_mut().try_write('我').infallible_unwrap();
85                    view.field3_mut().try_write('\0').infallible_unwrap();
86
87                    assert_eq!('a', view.field1().try_read().unwrap());
88                    assert_eq!('我', view.field2().try_read().unwrap());
89                    assert_eq!('\0', view.field3().try_read().unwrap());
90                    assert!(matches!(view.field4().try_read(), Err(WrappedFieldError::LayoutAsError(InvalidCharError(_)))));
91
92                    assert_eq!('a', char::try_from(u32::$from_endian_fn((&storage[0..4]).try_into().unwrap())).unwrap());
93                    assert_eq!('我', char::try_from(u32::$from_endian_fn((&storage[4..8]).try_into().unwrap())).unwrap());
94                    assert_eq!('\0', char::try_from(u32::$from_endian_fn((&storage[8..12]).try_into().unwrap())).unwrap());
95                    assert_eq!(INVALID_UNICODE, u32::$from_endian_fn((&storage[12..16]).try_into().unwrap()));
96                }
97            }
98        }
99    }
100
101    test_char!(little, LittleEndian, from_le_bytes, to_le_bytes);
102    test_char!(big, BigEndian, from_be_bytes, to_be_bytes);
103    test_char!(native, NativeEndian, from_ne_bytes, to_ne_bytes);
104}