Skip to main content

alux_shape_rust/
written.rs

1//! What a generated layout is compiled against.
2//!
3//! A term states how a value is written; a layout keeps the value and names that writing with a
4//! wrapper. These are those wrappers — the runtime counterpart of [`crate::RustShape`]'s `hex` and
5//! `decimal`, so a generated declaration compiles without anything else being written by hand.
6
7use serde::de::{Error as DeError, Unexpected};
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9use std::fmt::Display;
10use std::str::FromStr;
11
12/// Bytes written as `0x`-prefixed hexadecimal.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
14pub struct Hex<T>(pub T);
15
16/// Writes bytes as the hexadecimal text a shape states.
17fn write_hex<S>(bytes: &[u8], writer: S) -> Result<S::Ok, S::Error>
18where
19    S: Serializer,
20{
21    let mut text = String::with_capacity(2 + bytes.len() * 2);
22    text.push_str("0x");
23
24    for byte in bytes {
25        text.push_str(&format!("{byte:02x}"));
26    }
27
28    writer.serialize_str(&text)
29}
30
31/// Reads the bytes hexadecimal text states.
32fn read_hex<E>(text: &str) -> Result<Vec<u8>, E>
33where
34    E: DeError,
35{
36    let digits = text
37        .strip_prefix("0x")
38        .ok_or_else(|| E::invalid_value(Unexpected::Str(text), &"hexadecimal text, `0x`-prefixed"))?;
39
40    if digits.len() % 2 != 0 {
41        return Err(E::invalid_length(digits.len(), &"an even number of hexadecimal digits"));
42    }
43
44    (0..digits.len() / 2)
45        .map(|index| {
46            u8::from_str_radix(&digits[index * 2..index * 2 + 2], 16)
47                .map_err(|_| E::invalid_value(Unexpected::Str(text), &"hexadecimal digits"))
48        })
49        .collect()
50}
51
52impl<const N: usize> Serialize for Hex<[u8; N]> {
53    fn serialize<S>(&self, writer: S) -> Result<S::Ok, S::Error>
54    where
55        S: Serializer,
56    {
57        write_hex(&self.0, writer)
58    }
59}
60
61impl<'de, const N: usize> Deserialize<'de> for Hex<[u8; N]> {
62    fn deserialize<D>(reader: D) -> Result<Self, D::Error>
63    where
64        D: Deserializer<'de>,
65    {
66        let text = String::deserialize(reader)?;
67        let bytes = read_hex::<D::Error>(&text)?;
68
69        bytes.try_into().map(Hex).map_err(|bytes: Vec<u8>| D::Error::invalid_length(bytes.len(), &"the stated length"))
70    }
71}
72
73impl Serialize for Hex<Vec<u8>> {
74    fn serialize<S>(&self, writer: S) -> Result<S::Ok, S::Error>
75    where
76        S: Serializer,
77    {
78        write_hex(&self.0, writer)
79    }
80}
81
82impl<'de> Deserialize<'de> for Hex<Vec<u8>> {
83    fn deserialize<D>(reader: D) -> Result<Self, D::Error>
84    where
85        D: Deserializer<'de>,
86    {
87        let text = String::deserialize(reader)?;
88
89        read_hex::<D::Error>(&text).map(Hex)
90    }
91}
92
93/// A number written as decimal text, for a width JSON carries no integer of.
94#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
95pub struct Decimal<T>(pub T);
96
97impl<T> Serialize for Decimal<T>
98where
99    T: Display,
100{
101    fn serialize<S>(&self, writer: S) -> Result<S::Ok, S::Error>
102    where
103        S: Serializer,
104    {
105        writer.collect_str(&self.0)
106    }
107}
108
109impl<'de, T> Deserialize<'de> for Decimal<T>
110where
111    T: FromStr,
112    T::Err: Display,
113{
114    fn deserialize<D>(reader: D) -> Result<Self, D::Error>
115    where
116        D: Deserializer<'de>,
117    {
118        let text = String::deserialize(reader)?;
119
120        text.parse().map(Decimal).map_err(D::Error::custom)
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn bytes_are_written_and_read_as_hexadecimal() {
130        let value = Hex([0xbe_u8, 0xef]);
131        let json = serde_json::to_string(&value).expect("writes");
132
133        assert_eq!(json, "\"0xbeef\"");
134        assert_eq!(serde_json::from_str::<Hex<[u8; 2]>>(&json).expect("reads"), value);
135    }
136
137    #[test]
138    fn a_wide_number_is_written_and_read_as_decimal_text() {
139        let value = Decimal(u128::from(u64::MAX) + 1);
140        let json = serde_json::to_string(&value).expect("writes");
141
142        assert_eq!(json, "\"18446744073709551616\"");
143        assert_eq!(serde_json::from_str::<Decimal<u128>>(&json).expect("reads"), value);
144    }
145
146    #[test]
147    fn text_that_is_not_hexadecimal_is_not_read() {
148        assert!(serde_json::from_str::<Hex<[u8; 2]>>("\"beef\"").is_err());
149        assert!(serde_json::from_str::<Hex<[u8; 2]>>("\"0xbe\"").is_err());
150    }
151}