serde_utils/
quoted_int.rs1use alloy_primitives::U256;
8use serde::{Deserializer, Serializer};
9use serde_derive::{Deserialize, Serialize};
10use std::convert::TryFrom;
11use std::marker::PhantomData;
12
13macro_rules! define_mod {
14 ($int: ty) => {
15 pub struct QuotedIntVisitor<T> {
19 require_quotes: bool,
20 _phantom: PhantomData<T>,
21 }
22
23 impl<'a, T> serde::de::Visitor<'a> for QuotedIntVisitor<T>
24 where
25 T: From<$int> + Into<$int> + Copy + TryFrom<u64>,
26 {
27 type Value = T;
28
29 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
30 if self.require_quotes {
31 write!(formatter, "a quoted integer")
32 } else {
33 write!(formatter, "a quoted or unquoted integer")
34 }
35 }
36
37 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
38 where
39 E: serde::de::Error,
40 {
41 s.parse::<$int>()
42 .map(T::from)
43 .map_err(serde::de::Error::custom)
44 }
45
46 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
47 where
48 E: serde::de::Error,
49 {
50 if self.require_quotes {
51 Err(serde::de::Error::custom(
52 "received unquoted integer when quotes are required",
53 ))
54 } else {
55 T::try_from(v).map_err(|_| serde::de::Error::custom("invalid integer"))
56 }
57 }
58 }
59
60 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
62 #[serde(transparent)]
63 pub struct MaybeQuoted<T>
64 where
65 T: From<$int> + Into<$int> + Copy + TryFrom<u64>,
66 {
67 #[serde(with = "self")]
68 pub value: T,
69 }
70
71 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
76 #[serde(transparent)]
77 pub struct Quoted<T>
78 where
79 T: From<$int> + Into<$int> + Copy + TryFrom<u64>,
80 {
81 #[serde(with = "require_quotes")]
82 pub value: T,
83 }
84
85 pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
87 where
88 S: Serializer,
89 T: From<$int> + Into<$int> + Copy,
90 {
91 let v: $int = (*value).into();
92 serializer.serialize_str(&format!("{}", v))
93 }
94
95 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
97 where
98 D: Deserializer<'de>,
99 T: From<$int> + Into<$int> + Copy + TryFrom<u64>,
100 {
101 deserializer.deserialize_any(QuotedIntVisitor {
102 require_quotes: false,
103 _phantom: PhantomData,
104 })
105 }
106
107 pub mod require_quotes {
111 pub use super::serialize;
112 use super::*;
113
114 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
115 where
116 D: Deserializer<'de>,
117 T: From<$int> + Into<$int> + Copy + TryFrom<u64>,
118 {
119 deserializer.deserialize_any(QuotedIntVisitor {
120 require_quotes: true,
121 _phantom: PhantomData,
122 })
123 }
124 }
125
126 #[cfg(test)]
127 mod test {
128 use super::*;
129
130 #[test]
131 fn require_quotes() {
132 let x = serde_json::from_str::<Quoted<$int>>("\"8\"").unwrap();
133 assert_eq!(x.value, 8);
134 serde_json::from_str::<Quoted<$int>>("8").unwrap_err();
135 }
136 }
137 };
138}
139
140pub mod quoted_u8 {
141 use super::*;
142
143 define_mod!(u8);
144}
145
146pub mod quoted_u32 {
147 use super::*;
148
149 define_mod!(u32);
150}
151
152pub mod quoted_u64 {
153 use super::*;
154
155 define_mod!(u64);
156}
157
158pub mod quoted_i64 {
159 use super::*;
160
161 define_mod!(i64);
162}
163
164pub mod quoted_u256 {
165 use super::*;
166
167 struct U256Visitor;
168
169 impl<'de> serde::de::Visitor<'de> for U256Visitor {
170 type Value = U256;
171
172 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
173 formatter.write_str("a quoted U256 integer")
174 }
175
176 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
177 where
178 E: serde::de::Error,
179 {
180 U256::from_str_radix(v, 10).map_err(serde::de::Error::custom)
181 }
182 }
183
184 pub fn serialize<S>(value: &U256, serializer: S) -> Result<S::Ok, S::Error>
186 where
187 S: Serializer,
188 {
189 serializer.serialize_str(&format!("{}", value))
190 }
191
192 pub fn deserialize<'de, D>(deserializer: D) -> Result<U256, D::Error>
194 where
195 D: Deserializer<'de>,
196 {
197 deserializer.deserialize_str(U256Visitor)
198 }
199}
200
201#[cfg(test)]
202mod test {
203 use super::*;
204
205 #[derive(Debug, PartialEq, Serialize, Deserialize)]
206 #[serde(transparent)]
207 struct WrappedU256(#[serde(with = "quoted_u256")] U256);
208
209 #[test]
210 fn u256_with_quotes() {
211 assert_eq!(
212 &serde_json::to_string(&WrappedU256(U256::from(1))).unwrap(),
213 "\"1\""
214 );
215 assert_eq!(
216 serde_json::from_str::<WrappedU256>("\"1\"").unwrap(),
217 WrappedU256(U256::from(1))
218 );
219 }
220
221 #[test]
222 fn u256_without_quotes() {
223 serde_json::from_str::<WrappedU256>("1").unwrap_err();
224 }
225
226 #[derive(Debug, PartialEq, Serialize, Deserialize)]
227 #[serde(transparent)]
228 struct WrappedI64(#[serde(with = "quoted_i64")] i64);
229
230 #[test]
231 fn negative_i64_with_quotes() {
232 assert_eq!(
233 serde_json::from_str::<WrappedI64>("\"-200\"").unwrap().0,
234 -200
235 );
236 assert_eq!(
237 serde_json::to_string(&WrappedI64(-12_500)).unwrap(),
238 "\"-12500\""
239 );
240 }
241
242 #[test]
244 fn negative_i64_without_quotes() {
245 serde_json::from_str::<WrappedI64>("-200").unwrap_err();
246 }
247}