sentry_types/
macros.rs

1#![cfg_attr(not(feature = "protocol"), allow(unused))]
2
3/// Helper macro to implement string based serialization.
4///
5/// If a type implements `Display` then this automatically
6/// implements a serializer for that type that dispatches
7/// appropriately.
8macro_rules! impl_str_ser {
9    ($type:ty) => {
10        impl ::serde::ser::Serialize for $type {
11            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
12            where
13                S: ::serde::ser::Serializer,
14            {
15                serializer.serialize_str(&self.to_string())
16            }
17        }
18    };
19}
20
21/// Helper macro to implement string based deserialization.
22///
23/// If a type implements `FromStr` then this automatically
24/// implements a deserializer for that type that dispatches
25/// appropriately.
26#[allow(unused_macros)]
27macro_rules! impl_str_de {
28    ($type:ty) => {
29        impl<'de> ::serde::de::Deserialize<'de> for $type {
30            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
31            where
32                D: ::serde::de::Deserializer<'de>,
33            {
34                <::std::borrow::Cow<str>>::deserialize(deserializer)?
35                    .parse()
36                    .map_err(::serde::de::Error::custom)
37            }
38        }
39    };
40}
41
42/// Helper macro to implement string based serialization and deserialization.
43///
44/// If a type implements `FromStr` and `Display` then this automatically
45/// implements a serializer/deserializer for that type that dispatches
46/// appropriately.
47#[allow(unused_macros)]
48macro_rules! impl_str_serde {
49    ($type:ty) => {
50        impl_str_ser!($type);
51        impl_str_de!($type);
52    };
53}
54
55#[cfg(test)]
56mod str_tests {
57    use std::fmt;
58    use std::io::Cursor;
59    use std::str::FromStr;
60
61    struct Test;
62
63    impl FromStr for Test {
64        type Err = &'static str;
65
66        fn from_str(string: &str) -> Result<Self, Self::Err> {
67            match string {
68                "test" => Ok(Test),
69                _ => Err("failed"),
70            }
71        }
72    }
73
74    impl fmt::Display for Test {
75        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76            write!(f, "test")
77        }
78    }
79
80    impl_str_serde!(Test);
81
82    #[test]
83    fn test_serialize_string() {
84        assert_eq!("\"test\"", serde_json::to_string(&Test).unwrap());
85    }
86
87    #[test]
88    fn test_deserialize() {
89        assert!(serde_json::from_str::<Test>("\"test\"").is_ok());
90    }
91
92    #[test]
93    fn test_deserialize_owned() {
94        assert!(serde_json::from_reader::<_, Test>(Cursor::new("\"test\"")).is_ok());
95    }
96}
97
98/// Helper macro to implement serialization into base 16 (hex) string
99/// representation. Implements `Display` and `Serialize`.
100macro_rules! impl_hex_ser {
101    ($type:ident) => {
102        impl ::std::fmt::Display for $type {
103            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
104                write!(f, "{:#x}", self.0)
105            }
106        }
107
108        impl ::serde::ser::Serialize for $type {
109            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
110            where
111                S: ::serde::ser::Serializer,
112            {
113                serializer.serialize_str(&self.to_string())
114            }
115        }
116    };
117}
118
119/// Helper macro to implement deserialization from both numeric values or their
120/// base16 (hex) / base10 representations as string. Implements `FromStr` and
121/// `Deserialize`.
122macro_rules! impl_hex_de {
123    ($type:ident, $num:ident) => {
124        impl ::std::str::FromStr for $type {
125            type Err = ::std::num::ParseIntError;
126
127            fn from_str(s: &str) -> Result<$type, ::std::num::ParseIntError> {
128                if s.starts_with("0x") || s.starts_with("0X") {
129                    $num::from_str_radix(&s[2..], 16).map($type)
130                } else {
131                    s.parse::<$num>().map($type)
132                }
133            }
134        }
135
136        impl<'de> ::serde::de::Deserialize<'de> for $type {
137            fn deserialize<D>(deserializer: D) -> Result<$type, D::Error>
138            where
139                D: ::serde::de::Deserializer<'de>,
140            {
141                struct HexVisitor;
142
143                impl<'de> ::serde::de::Visitor<'de> for HexVisitor {
144                    type Value = $type;
145
146                    fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
147                        write!(f, "a number or hex string")
148                    }
149
150                    fn visit_i64<E: ::serde::de::Error>(self, v: i64) -> Result<Self::Value, E> {
151                        Ok($type(v as $num))
152                    }
153
154                    fn visit_u64<E: ::serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
155                        Ok($type(v as $num))
156                    }
157
158                    fn visit_str<E: ::serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
159                        v.parse().map_err(::serde::de::Error::custom)
160                    }
161                }
162
163                deserializer.deserialize_any(HexVisitor)
164            }
165        }
166    };
167}
168
169/// Helper macro to implement serialization from both numeric values or their
170/// hex representation as string.
171///
172/// This implements `Serialize`, `Deserialize`, `Display` and `FromStr`.
173/// Serialization will always use a `"0xbeef"` representation of the value.
174/// Deserialization supports raw numbers as well as string representations
175/// in hex and base10.
176macro_rules! impl_hex_serde {
177    ($type:ident, $num:ident) => {
178        impl_hex_ser!($type);
179        impl_hex_de!($type, $num);
180    };
181}
182
183#[cfg(test)]
184mod hex_tests {
185    use std::io::Cursor;
186
187    #[derive(Debug, PartialEq)]
188    struct Hex(u32);
189
190    impl_hex_serde!(Hex, u32);
191
192    #[test]
193    fn test_hex_to_string() {
194        assert_eq!("0x0", &Hex(0).to_string());
195        assert_eq!("0x2a", &Hex(42).to_string());
196    }
197
198    #[test]
199    fn test_hex_serialize() {
200        assert_eq!("\"0x0\"", serde_json::to_string(&Hex(0)).unwrap());
201        assert_eq!("\"0x2a\"", serde_json::to_string(&Hex(42)).unwrap());
202    }
203
204    #[test]
205    fn test_hex_from_string() {
206        assert_eq!(Hex(0), "0".parse().unwrap());
207        assert_eq!(Hex(42), "42".parse().unwrap());
208        assert_eq!(Hex(42), "0x2a".parse().unwrap());
209        assert_eq!(Hex(42), "0X2A".parse().unwrap());
210    }
211
212    #[test]
213    fn test_hex_deserialize() {
214        assert_eq!(Hex(0), serde_json::from_str("\"0\"").unwrap());
215        assert_eq!(Hex(42), serde_json::from_str("\"42\"").unwrap());
216        assert_eq!(Hex(42), serde_json::from_str("\"0x2a\"").unwrap());
217        assert_eq!(Hex(42), serde_json::from_str("\"0X2A\"").unwrap());
218    }
219
220    #[test]
221    fn test_hex_deserialize_owned() {
222        assert_eq!(
223            Hex(0),
224            serde_json::from_reader(Cursor::new("\"0\"")).unwrap()
225        );
226        assert_eq!(
227            Hex(42),
228            serde_json::from_reader(Cursor::new("\"42\"")).unwrap()
229        );
230        assert_eq!(
231            Hex(42),
232            serde_json::from_reader(Cursor::new("\"0x2a\"")).unwrap()
233        );
234        assert_eq!(
235            Hex(42),
236            serde_json::from_reader(Cursor::new("\"0X2A\"")).unwrap()
237        );
238    }
239
240    #[test]
241    fn test_invalid() {
242        let result = serde_json::from_str::<Hex>("true").unwrap_err();
243        assert_eq!(
244            "invalid type: boolean `true`, expected a number or hex string at line 1 column 4",
245            result.to_string()
246        );
247    }
248}