1use alloy_primitives::{U64, U256};
4use serde::{Deserialize, Deserializer, de};
5use std::str::FromStr;
6
7#[derive(Copy, Clone, Deserialize)]
9#[serde(untagged)]
10pub enum Numeric {
11 U256(U256),
13 Num(u64),
15}
16
17impl From<Numeric> for U256 {
18 fn from(n: Numeric) -> Self {
19 match n {
20 Numeric::U256(n) => n,
21 Numeric::Num(n) => Self::from(n),
22 }
23 }
24}
25
26impl FromStr for Numeric {
27 type Err = String;
28
29 fn from_str(s: &str) -> Result<Self, Self::Err> {
30 if let Ok(val) = s.parse::<u128>() {
31 Ok(Self::U256(U256::from(val)))
32 } else if s.starts_with("0x") {
33 U256::from_str_radix(s, 16).map(Numeric::U256).map_err(|err| err.to_string())
34 } else {
35 U256::from_str(s).map(Numeric::U256).map_err(|err| err.to_string())
36 }
37 }
38}
39
40#[derive(Copy, Clone, Deserialize)]
42#[serde(untagged)]
43pub enum Numeric64 {
44 Num(u64),
46 U64(U64),
48}
49
50impl From<Numeric64> for u64 {
51 fn from(n: Numeric64) -> Self {
52 match n {
53 Numeric64::Num(n) => n,
54 Numeric64::U64(n) => n.to::<Self>(),
55 }
56 }
57}
58
59#[derive(Deserialize)]
61#[serde(untagged)]
62pub enum NumericSeq {
63 Seq([Numeric; 1]),
65 U256(U256),
67 Num(u64),
69}
70
71#[derive(Deserialize)]
73#[serde(untagged)]
74pub enum Numeric64ValueOrSeq {
75 Seq([Numeric64; 1]),
77 Value(Numeric64),
79}
80
81pub fn deserialize_number<'de, D>(deserializer: D) -> Result<U256, D::Error>
83where
84 D: Deserializer<'de>,
85{
86 Numeric::deserialize(deserializer).map(Into::into)
87}
88
89pub fn deserialize_number_opt<'de, D>(deserializer: D) -> Result<Option<U256>, D::Error>
91where
92 D: Deserializer<'de>,
93{
94 let num = match Option::<Numeric>::deserialize(deserializer)? {
95 Some(Numeric::U256(n)) => Some(n),
96 Some(Numeric::Num(n)) => Some(U256::from(n)),
97 _ => None,
98 };
99
100 Ok(num)
101}
102
103pub fn deserialize_number_seq<'de, D>(deserializer: D) -> Result<U256, D::Error>
105where
106 D: Deserializer<'de>,
107{
108 let num = match NumericSeq::deserialize(deserializer)? {
109 NumericSeq::Seq(seq) => seq[0].into(),
110 NumericSeq::U256(n) => n,
111 NumericSeq::Num(n) => U256::from(n),
112 };
113
114 Ok(num)
115}
116
117pub fn deserialize_u64_seq<'de, D>(deserializer: D) -> Result<u64, D::Error>
119where
120 D: Deserializer<'de>,
121{
122 let num = match Numeric64ValueOrSeq::deserialize(deserializer)? {
123 Numeric64ValueOrSeq::Seq(seq) => seq[0].into(),
124 Numeric64ValueOrSeq::Value(num) => num.into(),
125 };
126
127 Ok(num)
128}
129
130pub fn deserialize_u64_seq_opt<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
133where
134 D: Deserializer<'de>,
135{
136 let seq = Vec::<Option<Numeric64>>::deserialize(deserializer)?;
137 if seq.len() > 1 {
138 return Err(de::Error::custom(format!(
139 "expected params sequence with length 0 or 1 but got {}",
140 seq.len()
141 )));
142 }
143 Ok(seq.into_iter().next().flatten().map(Into::into))
144}
145
146pub mod duration {
147 use serde::{Deserialize, Deserializer};
148 use std::time::Duration;
149
150 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
151 where
152 S: serde::Serializer,
153 {
154 let d = jiff::SignedDuration::try_from(*duration).map_err(serde::ser::Error::custom)?;
155 serializer.serialize_str(&format!("{d:#}"))
156 }
157
158 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
159 where
160 D: Deserializer<'de>,
161 {
162 let s = String::deserialize(deserializer)?;
163 let d = s.parse::<jiff::SignedDuration>().map_err(serde::de::Error::custom)?;
164 d.try_into().map_err(serde::de::Error::custom)
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use serde::de::IntoDeserializer;
172 use serde_json::json;
173
174 fn parse_u64_param(value: serde_json::Value) -> Result<u64, serde_json::Error> {
175 deserialize_u64_seq(value.into_deserializer())
176 }
177
178 fn parse_optional_u64_param(
179 value: serde_json::Value,
180 ) -> Result<Option<u64>, serde_json::Error> {
181 deserialize_u64_seq_opt(value.into_deserializer())
182 }
183
184 #[test]
185 fn deserialize_u64_seq_accepts_single_param_sequence_and_direct_value() {
186 let valid_values = [
187 json!([100]),
188 json!(100),
189 json!(["0x64"]),
190 json!("0x64"),
191 json!(["100"]),
192 json!("100"),
193 ];
194 for value in valid_values {
195 assert_eq!(parse_u64_param(value).unwrap(), 100);
196 }
197
198 for value in [json!([u64::MAX]), json!(u64::MAX)] {
199 assert_eq!(parse_u64_param(value).unwrap(), u64::MAX);
200 }
201 }
202
203 #[test]
204 fn deserialize_u64_seq_rejects_invalid_shape_and_overflow() {
205 for value in [
206 json!([]),
207 json!([1, 2]),
208 json!([null]),
209 json!(null),
210 json!(["0x10000000000000000"]),
211 json!("0x10000000000000000"),
212 json!(["18446744073709551616"]),
213 json!("18446744073709551616"),
214 ] {
215 assert!(parse_u64_param(value).is_err());
216 }
217 }
218
219 #[test]
220 fn deserialize_u64_seq_opt_accepts_empty_null_and_single_param_sequence() {
221 for value in [json!([]), json!([null])] {
222 assert_eq!(parse_optional_u64_param(value).unwrap(), None);
223 }
224
225 for value in [json!([100]), json!(["0x64"]), json!(["100"])] {
226 assert_eq!(parse_optional_u64_param(value).unwrap(), Some(100));
227 }
228
229 assert_eq!(parse_optional_u64_param(json!([u64::MAX])).unwrap(), Some(u64::MAX));
230 }
231
232 #[test]
233 fn deserialize_u64_seq_opt_rejects_invalid_shape_and_overflow() {
234 for value in [
235 json!([1, 2]),
236 json!(100),
237 json!("0x64"),
238 json!([["0x64"]]),
239 json!(["0x10000000000000000"]),
240 json!(["18446744073709551616"]),
241 ] {
242 assert!(parse_optional_u64_param(value).is_err());
243 }
244 }
245}