1pub(crate) const JS_MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
21
22pub mod json_safe_u64 {
24 use serde::de::{self, Deserializer, Visitor};
25 use serde::ser::Serializer;
26
27 use super::JS_MAX_SAFE_INTEGER;
28
29 pub fn serialize<S: Serializer>(value: &u64, serializer: S) -> Result<S::Ok, S::Error> {
30 if serializer.is_human_readable() && *value > JS_MAX_SAFE_INTEGER {
31 serializer.serialize_str(&value.to_string())
32 } else {
33 serializer.serialize_u64(*value)
34 }
35 }
36
37 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
38 if deserializer.is_human_readable() {
39 deserializer.deserialize_any(U64OrStringVisitor)
40 } else {
41 serde::Deserialize::deserialize(deserializer)
42 }
43 }
44
45 struct U64OrStringVisitor;
46
47 impl<'de> Visitor<'de> for U64OrStringVisitor {
48 type Value = u64;
49
50 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
51 formatter.write_str("a u64 or a string containing a u64")
52 }
53
54 fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
55 Ok(v)
56 }
57
58 fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
59 u64::try_from(v)
60 .map_err(|_| de::Error::custom(format!("i64 value {v} out of u64 range")))
61 }
62
63 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
64 v.parse::<u64>()
65 .map_err(|_| de::Error::custom(format!("invalid u64 string: {v}")))
66 }
67 }
68}
69
70pub mod json_safe_i64 {
72 use serde::de::{self, Deserializer, Visitor};
73 use serde::ser::Serializer;
74
75 use super::JS_MAX_SAFE_INTEGER;
76
77 const JS_MIN_SAFE_INTEGER: i64 = -(JS_MAX_SAFE_INTEGER as i64);
78
79 pub fn serialize<S: Serializer>(value: &i64, serializer: S) -> Result<S::Ok, S::Error> {
80 if serializer.is_human_readable()
81 && (*value > JS_MAX_SAFE_INTEGER as i64 || *value < JS_MIN_SAFE_INTEGER)
82 {
83 serializer.serialize_str(&value.to_string())
84 } else {
85 serializer.serialize_i64(*value)
86 }
87 }
88
89 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<i64, D::Error> {
90 if deserializer.is_human_readable() {
91 deserializer.deserialize_any(I64OrStringVisitor)
92 } else {
93 serde::Deserialize::deserialize(deserializer)
94 }
95 }
96
97 struct I64OrStringVisitor;
98
99 impl<'de> Visitor<'de> for I64OrStringVisitor {
100 type Value = i64;
101
102 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
103 formatter.write_str("an i64 or a string containing an i64")
104 }
105
106 fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
107 Ok(v)
108 }
109
110 fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
111 i64::try_from(v)
112 .map_err(|_| de::Error::custom(format!("u64 value {v} out of i64 range")))
113 }
114
115 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
116 v.parse::<i64>()
117 .map_err(|_| de::Error::custom(format!("invalid i64 string: {v}")))
118 }
119 }
120}
121
122pub mod json_safe_option_u64 {
124 use serde::de::{self, Deserializer, Visitor};
125 use serde::ser::Serializer;
126
127 pub fn serialize<S: Serializer>(value: &Option<u64>, serializer: S) -> Result<S::Ok, S::Error> {
128 match value {
129 Some(v) => super::json_safe_u64::serialize(v, serializer),
130 None => serializer.serialize_none(),
131 }
132 }
133
134 pub fn deserialize<'de, D: Deserializer<'de>>(
135 deserializer: D,
136 ) -> Result<Option<u64>, D::Error> {
137 if deserializer.is_human_readable() {
138 deserializer.deserialize_option(OptionU64Visitor)
139 } else {
140 serde::Deserialize::deserialize(deserializer)
141 }
142 }
143
144 struct OptionU64Visitor;
145
146 impl<'de> Visitor<'de> for OptionU64Visitor {
147 type Value = Option<u64>;
148
149 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
150 formatter.write_str("null, a u64, or a string containing a u64")
151 }
152
153 fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
154 Ok(None)
155 }
156
157 fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
158 Ok(None)
159 }
160
161 fn visit_some<D: Deserializer<'de>>(
162 self,
163 deserializer: D,
164 ) -> Result<Self::Value, D::Error> {
165 super::json_safe_u64::deserialize(deserializer).map(Some)
166 }
167 }
168}
169
170pub mod json_safe_option_i64 {
172 use serde::de::{self, Deserializer, Visitor};
173 use serde::ser::Serializer;
174
175 pub fn serialize<S: Serializer>(value: &Option<i64>, serializer: S) -> Result<S::Ok, S::Error> {
176 match value {
177 Some(v) => super::json_safe_i64::serialize(v, serializer),
178 None => serializer.serialize_none(),
179 }
180 }
181
182 pub fn deserialize<'de, D: Deserializer<'de>>(
183 deserializer: D,
184 ) -> Result<Option<i64>, D::Error> {
185 if deserializer.is_human_readable() {
186 deserializer.deserialize_option(OptionI64Visitor)
187 } else {
188 serde::Deserialize::deserialize(deserializer)
189 }
190 }
191
192 struct OptionI64Visitor;
193
194 impl<'de> Visitor<'de> for OptionI64Visitor {
195 type Value = Option<i64>;
196
197 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
198 formatter.write_str("null, an i64, or a string containing an i64")
199 }
200
201 fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
202 Ok(None)
203 }
204
205 fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
206 Ok(None)
207 }
208
209 fn visit_some<D: Deserializer<'de>>(
210 self,
211 deserializer: D,
212 ) -> Result<Self::Value, D::Error> {
213 super::json_safe_i64::deserialize(deserializer).map(Some)
214 }
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use serde::{Deserialize, Serialize};
222
223 #[derive(Debug, PartialEq, Serialize, Deserialize)]
224 struct TestU64 {
225 #[serde(with = "json_safe_u64")]
226 value: u64,
227 }
228
229 #[derive(Debug, PartialEq, Serialize, Deserialize)]
230 struct TestI64 {
231 #[serde(with = "json_safe_i64")]
232 value: i64,
233 }
234
235 #[derive(Debug, PartialEq, Serialize, Deserialize)]
236 struct TestOptionU64 {
237 #[serde(default, with = "json_safe_option_u64")]
238 value: Option<u64>,
239 }
240
241 #[derive(Debug, PartialEq, Serialize, Deserialize)]
242 struct TestOptionI64 {
243 #[serde(default, with = "json_safe_option_i64")]
244 value: Option<i64>,
245 }
246
247 #[test]
248 fn u64_small_value_stays_number() {
249 let t = TestU64 { value: 42 };
250 let json = serde_json::to_value(&t).unwrap();
251 assert!(json["value"].is_number());
252 assert_eq!(json["value"].as_u64().unwrap(), 42);
253
254 let restored: TestU64 = serde_json::from_value(json).unwrap();
255 assert_eq!(t, restored);
256 }
257
258 #[test]
259 fn u64_large_value_becomes_string() {
260 let t = TestU64 { value: u64::MAX };
261 let json = serde_json::to_value(&t).unwrap();
262 assert!(json["value"].is_string());
263 assert_eq!(json["value"].as_str().unwrap(), "18446744073709551615");
264
265 let restored: TestU64 = serde_json::from_value(json).unwrap();
266 assert_eq!(t, restored);
267 }
268
269 #[test]
270 fn u64_at_max_safe_integer_stays_number() {
271 let t = TestU64 {
272 value: JS_MAX_SAFE_INTEGER,
273 };
274 let json = serde_json::to_value(&t).unwrap();
275 assert!(json["value"].is_number());
276 }
277
278 #[test]
279 fn u64_above_max_safe_integer_becomes_string() {
280 let t = TestU64 {
281 value: JS_MAX_SAFE_INTEGER + 1,
282 };
283 let json = serde_json::to_value(&t).unwrap();
284 assert!(json["value"].is_string());
285 }
286
287 #[test]
288 fn i64_small_value_stays_number() {
289 let t = TestI64 { value: -42 };
290 let json = serde_json::to_value(&t).unwrap();
291 assert!(json["value"].is_number());
292
293 let restored: TestI64 = serde_json::from_value(json).unwrap();
294 assert_eq!(t, restored);
295 }
296
297 #[test]
298 fn i64_large_value_becomes_string() {
299 let t = TestI64 { value: i64::MAX };
300 let json = serde_json::to_value(&t).unwrap();
301 assert!(json["value"].is_string());
302
303 let restored: TestI64 = serde_json::from_value(json).unwrap();
304 assert_eq!(t, restored);
305 }
306
307 #[test]
308 fn i64_large_negative_becomes_string() {
309 let t = TestI64 { value: i64::MIN };
310 let json = serde_json::to_value(&t).unwrap();
311 assert!(json["value"].is_string());
312
313 let restored: TestI64 = serde_json::from_value(json).unwrap();
314 assert_eq!(t, restored);
315 }
316
317 #[test]
318 fn option_u64_none_round_trip() {
319 let t = TestOptionU64 { value: None };
320 let json = serde_json::to_value(&t).unwrap();
321 assert!(json["value"].is_null());
322
323 let restored: TestOptionU64 = serde_json::from_value(json).unwrap();
324 assert_eq!(t, restored);
325 }
326
327 #[test]
328 fn option_u64_large_round_trip() {
329 let t = TestOptionU64 {
330 value: Some(u64::MAX),
331 };
332 let json = serde_json::to_value(&t).unwrap();
333 assert!(json["value"].is_string());
334
335 let restored: TestOptionU64 = serde_json::from_value(json).unwrap();
336 assert_eq!(t, restored);
337 }
338
339 #[test]
340 fn platform_value_u64_stays_native() {
341 let t = TestU64 { value: u64::MAX };
342 let pv = platform_value::to_value(&t).unwrap();
343
344 let restored: TestU64 = platform_value::from_value(pv).unwrap();
346 assert_eq!(t, restored);
347 }
348
349 #[test]
350 fn option_i64_none_round_trip() {
351 let t = TestOptionI64 { value: None };
352 let json = serde_json::to_value(&t).unwrap();
353 assert!(json["value"].is_null());
354
355 let restored: TestOptionI64 = serde_json::from_value(json).unwrap();
356 assert_eq!(t, restored);
357 }
358
359 #[test]
360 fn option_i64_large_round_trip() {
361 let t = TestOptionI64 {
362 value: Some(i64::MAX),
363 };
364 let json = serde_json::to_value(&t).unwrap();
365 assert!(json["value"].is_string());
366
367 let restored: TestOptionI64 = serde_json::from_value(json).unwrap();
368 assert_eq!(t, restored);
369 }
370
371 #[test]
372 fn option_i64_large_negative_round_trip() {
373 let t = TestOptionI64 {
374 value: Some(i64::MIN),
375 };
376 let json = serde_json::to_value(&t).unwrap();
377 assert!(json["value"].is_string());
378
379 let restored: TestOptionI64 = serde_json::from_value(json).unwrap();
380 assert_eq!(t, restored);
381 }
382
383 #[test]
384 fn option_i64_missing_field_deserializes_as_none() {
385 let json = serde_json::json!({});
386 let restored: TestOptionI64 = serde_json::from_value(json).unwrap();
387 assert_eq!(restored.value, None);
388 }
389
390 #[test]
391 fn option_u64_missing_field_deserializes_as_none() {
392 let json = serde_json::json!({});
393 let restored: TestOptionU64 = serde_json::from_value(json).unwrap();
394 assert_eq!(restored.value, None);
395 }
396
397 #[test]
398 fn u64_deserialize_from_i64_value() {
399 let json = serde_json::json!({"value": 42});
401 let restored: TestU64 = serde_json::from_value(json).unwrap();
402 assert_eq!(restored.value, 42);
403 }
404
405 #[test]
406 fn u64_deserialize_negative_i64_fails() {
407 let json = serde_json::json!({"value": -1});
409 let result = serde_json::from_value::<TestU64>(json);
410 assert!(result.is_err());
411 assert!(result.unwrap_err().to_string().contains("out of u64 range"));
412 }
413
414 #[test]
415 fn u64_deserialize_invalid_string_fails() {
416 let json = serde_json::json!({"value": "not_a_number"});
417 let result = serde_json::from_value::<TestU64>(json);
418 assert!(result.is_err());
419 assert!(result
420 .unwrap_err()
421 .to_string()
422 .contains("invalid u64 string"));
423 }
424
425 #[test]
426 fn i64_deserialize_u64_overflow_fails() {
427 let json = serde_json::json!({"value": u64::MAX.to_string()});
429 let result = serde_json::from_value::<TestI64>(json);
431 assert!(result.is_err());
432 }
433
434 #[test]
435 fn i64_deserialize_invalid_string_fails() {
436 let json = serde_json::json!({"value": "not_a_number"});
437 let result = serde_json::from_value::<TestI64>(json);
438 assert!(result.is_err());
439 assert!(result
440 .unwrap_err()
441 .to_string()
442 .contains("invalid i64 string"));
443 }
444
445 #[test]
446 fn platform_value_i64_stays_native() {
447 let t = TestI64 { value: i64::MAX };
448 let pv = platform_value::to_value(&t).unwrap();
449 let restored: TestI64 = platform_value::from_value(pv).unwrap();
450 assert_eq!(t, restored);
451 }
452
453 #[test]
454 fn platform_value_option_u64_round_trip() {
455 let t = TestOptionU64 {
456 value: Some(u64::MAX),
457 };
458 let pv = platform_value::to_value(&t).unwrap();
459 let restored: TestOptionU64 = platform_value::from_value(pv).unwrap();
460 assert_eq!(t, restored);
461 }
462
463 #[test]
464 fn platform_value_option_i64_round_trip() {
465 let t = TestOptionI64 {
466 value: Some(i64::MIN),
467 };
468 let pv = platform_value::to_value(&t).unwrap();
469 let restored: TestOptionI64 = platform_value::from_value(pv).unwrap();
470 assert_eq!(t, restored);
471 }
472
473 #[test]
474 fn option_u64_small_value_stays_number() {
475 let t = TestOptionU64 { value: Some(42) };
476 let json = serde_json::to_value(&t).unwrap();
477 assert!(json["value"].is_number());
478
479 let restored: TestOptionU64 = serde_json::from_value(json).unwrap();
480 assert_eq!(t, restored);
481 }
482
483 #[test]
484 fn option_i64_small_value_stays_number() {
485 let t = TestOptionI64 { value: Some(-42) };
486 let json = serde_json::to_value(&t).unwrap();
487 assert!(json["value"].is_number());
488
489 let restored: TestOptionI64 = serde_json::from_value(json).unwrap();
490 assert_eq!(t, restored);
491 }
492
493 #[test]
494 fn tagged_enum_with_u64_round_trip() {
495 #[derive(Debug, PartialEq, Serialize, Deserialize)]
496 #[serde(tag = "$formatVersion")]
497 enum Versioned {
498 #[serde(rename = "0")]
499 V0(TestU64),
500 }
501
502 let v = Versioned::V0(TestU64 { value: u64::MAX });
503 let json = serde_json::to_value(&v).unwrap();
504 assert_eq!(json["$formatVersion"], "0");
505 assert!(json["value"].is_string());
506
507 let restored: Versioned = serde_json::from_value(json).unwrap();
508 assert_eq!(v, restored);
509 }
510}