Skip to main content

topcoat_runtime/surrogate/
integer.rs

1use ref_cast::RefCast;
2use serde::{Deserialize, Serialize, de, ser::SerializeStruct};
3
4use crate::{BoolSurrogate, impl_surrogate, impl_surrogate_mut, impl_surrogate_ref};
5
6#[derive(Deserialize)]
7#[serde(deny_unknown_fields)]
8struct Integer {
9    t: String,
10    bits: u32,
11    v: String,
12}
13
14macro_rules! integer_op {
15    ($surrogate:ident, $trait:ident, $method:ident, $checked:ident) => {
16        impl core::ops::$trait for $surrogate {
17            type Output = Self;
18
19            #[inline]
20            #[track_caller]
21            fn $method(self, rhs: Self) -> Self {
22                Self(
23                    self.0
24                        .$checked(rhs.0)
25                        .expect(concat!("invalid integer ", stringify!($method),)),
26                )
27            }
28        }
29    };
30}
31
32macro_rules! integer_cmp {
33    ($surrogate:ident, $method:ident, $op:tt) => {
34        impl $surrogate {
35            #[inline]
36            pub fn $method(&self, rhs: &Self) -> BoolSurrogate {
37                BoolSurrogate::new(self.0 $op rhs.0)
38            }
39        }
40    };
41}
42
43macro_rules! integer_surrogate {
44    ($real:ident, $surrogate:ident) => {
45        #[doc = concat!("A `", stringify!($real), "` in a runtime expression.")]
46        ///
47        /// Arithmetic panics on overflow or division by zero in every build profile.
48        #[derive(Debug, Clone, Copy, RefCast)]
49        #[repr(transparent)]
50        pub struct $surrogate($real);
51
52        impl $surrogate {
53            #[inline]
54            pub(crate) const fn new(value: $real) -> Self {
55                Self(value)
56            }
57        }
58
59        impl_surrogate!($real, $surrogate);
60        impl_surrogate_ref!($real, $surrogate);
61        impl_surrogate_mut!($real, $surrogate);
62
63        impl std::fmt::Display for $surrogate {
64            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65                self.0.fmt(f)
66            }
67        }
68
69        impl Serialize for $surrogate {
70            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71            where
72                S: serde::Serializer,
73            {
74                let mut value = serializer.serialize_struct("Integer", 3)?;
75                value.serialize_field("t", stringify!($real))?;
76                value.serialize_field("bits", &$real::BITS)?;
77                value.serialize_field("v", &self.0.to_string())?;
78                value.end()
79            }
80        }
81
82        impl<'de> Deserialize<'de> for $surrogate {
83            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84            where
85                D: serde::Deserializer<'de>,
86            {
87                let value = Integer::deserialize(deserializer)?;
88                if value.t != stringify!($real) {
89                    return Err(de::Error::invalid_value(
90                        de::Unexpected::Str(&value.t),
91                        &stringify!($real),
92                    ));
93                }
94                if value.bits != $real::BITS {
95                    return Err(de::Error::custom("integer width does not match the server"));
96                }
97                let integer: $real = value.v.parse().map_err(de::Error::custom)?;
98                if value.v != integer.to_string() {
99                    return Err(de::Error::custom("expected a canonical decimal integer"));
100                }
101                Ok(Self(integer))
102            }
103        }
104
105        integer_op!($surrogate, Add, add, checked_add);
106        integer_op!($surrogate, Sub, sub, checked_sub);
107        integer_op!($surrogate, Mul, mul, checked_mul);
108        integer_op!($surrogate, Div, div, checked_div);
109        integer_op!($surrogate, Rem, rem, checked_rem);
110
111        integer_cmp!($surrogate, eq, ==);
112        integer_cmp!($surrogate, ne, !=);
113        integer_cmp!($surrogate, gt, >);
114        integer_cmp!($surrogate, lt, <);
115        integer_cmp!($surrogate, ge, >=);
116        integer_cmp!($surrogate, le, <=);
117    };
118}
119
120macro_rules! signed_integer_surrogate {
121    ($real:ident, $surrogate:ident) => {
122        integer_surrogate!($real, $surrogate);
123
124        impl core::ops::Neg for $surrogate {
125            type Output = Self;
126
127            #[inline]
128            #[track_caller]
129            fn neg(self) -> Self {
130                Self(self.0.checked_neg().expect("integer negation overflow"))
131            }
132        }
133    };
134}
135
136integer_surrogate!(u8, U8Surrogate);
137integer_surrogate!(u16, U16Surrogate);
138integer_surrogate!(u32, U32Surrogate);
139integer_surrogate!(u64, U64Surrogate);
140integer_surrogate!(u128, U128Surrogate);
141integer_surrogate!(usize, UsizeSurrogate);
142signed_integer_surrogate!(i8, I8Surrogate);
143signed_integer_surrogate!(i16, I16Surrogate);
144signed_integer_surrogate!(i32, I32Surrogate);
145signed_integer_surrogate!(i64, I64Surrogate);
146signed_integer_surrogate!(i128, I128Surrogate);
147signed_integer_surrogate!(isize, IsizeSurrogate);
148
149#[cfg(test)]
150mod tests {
151    use serde_json::json;
152
153    use super::*;
154
155    macro_rules! integer_tests {
156        ($real:ident, $surrogate:ident) => {
157            #[test]
158            fn $real() {
159                for value in [$real::MIN, 0, 1, $real::MAX] {
160                    let wire = serde_json::to_value($surrogate::new(value)).unwrap();
161                    assert_eq!(wire, json!({
162                        "t": stringify!($real), "bits": $real::BITS, "v": value.to_string(),
163                    }));
164                    let decoded: $surrogate = serde_json::from_value(wire).unwrap();
165                    assert_eq!(decoded.0, value);
166                    assert_eq!(decoded.to_string(), value.to_string());
167                }
168                for digits in ["", "-0", "+1", "01", "1.0", "1e3", " 1", "1 ", "0xff"] {
169                    assert!(serde_json::from_value::<$surrogate>(json!({
170                        "t": stringify!($real), "bits": $real::BITS, "v": digits,
171                    })).is_err());
172                }
173                for wire in [
174                    json!({ "t": "f64", "bits": $real::BITS, "v": "1" }),
175                    json!({ "t": stringify!($real), "bits": $real::BITS / 2, "v": "1" }),
176                    json!({ "t": stringify!($real), "bits": $real::BITS, "v": 1 }),
177                    json!({ "t": stringify!($real), "v": "1" }),
178                    json!({ "t": stringify!($real), "bits": $real::BITS, "v": "1", "extra": true }),
179                    json!({ "t": stringify!($real), "bits": $real::BITS, "v": format!("{}0", $real::MAX) }),
180                ] {
181                    assert!(serde_json::from_value::<$surrogate>(wire).is_err());
182                }
183            }
184        };
185    }
186
187    integer_tests!(u8, U8Surrogate);
188    integer_tests!(u16, U16Surrogate);
189    integer_tests!(u32, U32Surrogate);
190    integer_tests!(u64, U64Surrogate);
191    integer_tests!(u128, U128Surrogate);
192    integer_tests!(usize, UsizeSurrogate);
193    integer_tests!(i8, I8Surrogate);
194    integer_tests!(i16, I16Surrogate);
195    integer_tests!(i32, I32Surrogate);
196    integer_tests!(i64, I64Surrogate);
197    integer_tests!(i128, I128Surrogate);
198    integer_tests!(isize, IsizeSurrogate);
199}