dpp/serialization/json/
safe_integer_map.rs

1//! Serde `with` modules for `BTreeMap` fields containing u64 keys and/or values.
2//!
3//! Unlike bare u64 fields (handled automatically by `#[json_safe_fields]`), map
4//! fields need explicit `#[serde(with = "...")]` annotations because the macro
5//! cannot inject `serde(with)` into generic container internals.
6//!
7//! ## When to use
8//!
9//! If a `#[json_safe_fields]`-annotated struct has a `BTreeMap<K, u64>` field,
10//! the compile-time `JsonSafeFields` check will fail (u64 doesn't implement the
11//! trait). Add one of these modules as a `serde(with)` annotation to fix it.
12//!
13//! ## Available modules
14//!
15//! - [`json_safe_u64_u64_map`] — `BTreeMap<u64, u64>`
16//! - [`json_safe_identifier_u64_map`] — `BTreeMap<Identifier, u64>`
17//! - [`json_safe_generic_u64_value_map`] — `BTreeMap<K, u64>` for any serializable key
18//! - [`json_safe_u64_nested_identifier_u64_map`] — `BTreeMap<u64, BTreeMap<Identifier, u64>>`
19//!
20//! ## Behavior
21//!
22//! Same as `safe_integer.rs`: uses `is_human_readable()` to only stringify in
23//! JSON mode. platform_value and bincode stay native.
24
25use super::safe_integer::JS_MAX_SAFE_INTEGER;
26
27/// Serde `with` module for `BTreeMap<u64, u64>` fields.
28///
29/// - Keys: JSON map keys are always strings, so keys are inherently safe.
30///   Deserialization accepts both string and numeric keys.
31/// - Values: Large u64 values are serialized as strings in JSON.
32/// - Non-HR (platform_value): native u64 for both keys and values.
33pub mod json_safe_u64_u64_map {
34    use serde::de::{self, Deserializer, MapAccess, Visitor};
35    use serde::ser::{SerializeMap, Serializer};
36    use std::collections::BTreeMap;
37
38    use super::JS_MAX_SAFE_INTEGER;
39
40    pub fn serialize<S: Serializer>(
41        map: &BTreeMap<u64, u64>,
42        serializer: S,
43    ) -> Result<S::Ok, S::Error> {
44        if serializer.is_human_readable() {
45            let mut s = serializer.serialize_map(Some(map.len()))?;
46            for (k, v) in map {
47                // JSON keys are always strings
48                s.serialize_entry(
49                    &k.to_string(),
50                    &if *v > JS_MAX_SAFE_INTEGER {
51                        serde_json::Value::String(v.to_string())
52                    } else {
53                        serde_json::Value::Number((*v).into())
54                    },
55                )?;
56            }
57            s.end()
58        } else {
59            serde::Serialize::serialize(map, serializer)
60        }
61    }
62
63    pub fn deserialize<'de, D: Deserializer<'de>>(
64        deserializer: D,
65    ) -> Result<BTreeMap<u64, u64>, D::Error> {
66        if deserializer.is_human_readable() {
67            deserializer.deserialize_map(U64U64MapVisitor)
68        } else {
69            serde::Deserialize::deserialize(deserializer)
70        }
71    }
72
73    struct U64U64MapVisitor;
74
75    impl<'de> Visitor<'de> for U64U64MapVisitor {
76        type Value = BTreeMap<u64, u64>;
77
78        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
79            f.write_str("a map with u64 keys and u64 values (numbers or strings)")
80        }
81
82        fn visit_map<M: MapAccess<'de>>(self, mut access: M) -> Result<Self::Value, M::Error> {
83            let mut map = BTreeMap::new();
84            while let Some((key, value)) =
85                access.next_entry::<serde_json::Value, serde_json::Value>()?
86            {
87                let k = json_value_to_u64(&key).map_err(de::Error::custom)?;
88                let v = json_value_to_u64(&value).map_err(de::Error::custom)?;
89                map.insert(k, v);
90            }
91            Ok(map)
92        }
93    }
94
95    fn json_value_to_u64(v: &serde_json::Value) -> Result<u64, String> {
96        match v {
97            serde_json::Value::Number(n) => n
98                .as_u64()
99                .ok_or_else(|| format!("expected u64 number, got: {n}")),
100            serde_json::Value::String(s) => s
101                .parse::<u64>()
102                .map_err(|_| format!("invalid u64 string: {s}")),
103            other => Err(format!("expected u64 or string, got: {other}")),
104        }
105    }
106}
107
108/// Serde `with` module for `BTreeMap<Identifier, u64>` fields.
109///
110/// - Keys: Identifier (not u64, uses its own serde impl).
111/// - Values: Large u64 values are serialized as strings in JSON.
112/// - Non-HR (platform_value): native serialization.
113pub mod json_safe_identifier_u64_map {
114    use platform_value::Identifier;
115    use serde::de::{self, Deserializer, MapAccess, Visitor};
116    use serde::ser::{SerializeMap, Serializer};
117    use std::collections::BTreeMap;
118
119    use super::JS_MAX_SAFE_INTEGER;
120
121    pub fn serialize<S: Serializer>(
122        map: &BTreeMap<Identifier, u64>,
123        serializer: S,
124    ) -> Result<S::Ok, S::Error> {
125        if serializer.is_human_readable() {
126            let mut s = serializer.serialize_map(Some(map.len()))?;
127            for (k, v) in map {
128                s.serialize_entry(
129                    k,
130                    &if *v > JS_MAX_SAFE_INTEGER {
131                        serde_json::Value::String(v.to_string())
132                    } else {
133                        serde_json::Value::Number((*v).into())
134                    },
135                )?;
136            }
137            s.end()
138        } else {
139            serde::Serialize::serialize(map, serializer)
140        }
141    }
142
143    pub fn deserialize<'de, D: Deserializer<'de>>(
144        deserializer: D,
145    ) -> Result<BTreeMap<Identifier, u64>, D::Error> {
146        if deserializer.is_human_readable() {
147            deserializer.deserialize_map(IdentifierU64MapVisitor)
148        } else {
149            serde::Deserialize::deserialize(deserializer)
150        }
151    }
152
153    struct IdentifierU64MapVisitor;
154
155    impl<'de> Visitor<'de> for IdentifierU64MapVisitor {
156        type Value = BTreeMap<Identifier, u64>;
157
158        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
159            f.write_str("a map with Identifier keys and u64 values (numbers or strings)")
160        }
161
162        fn visit_map<M: MapAccess<'de>>(self, mut access: M) -> Result<Self::Value, M::Error> {
163            let mut map = BTreeMap::new();
164            while let Some((key, value)) = access.next_entry::<Identifier, serde_json::Value>()? {
165                let v = match &value {
166                    serde_json::Value::Number(n) => n
167                        .as_u64()
168                        .ok_or_else(|| de::Error::custom(format!("expected u64 number, got: {n}"))),
169                    serde_json::Value::String(s) => s
170                        .parse::<u64>()
171                        .map_err(|_| de::Error::custom(format!("invalid u64 string: {s}"))),
172                    other => Err(de::Error::custom(format!(
173                        "expected u64 or string, got: {other}"
174                    ))),
175                }?;
176                map.insert(key, v);
177            }
178            Ok(map)
179        }
180    }
181}
182
183/// Serde `with` module for `BTreeMap<u64, BTreeMap<Identifier, u64>>` fields.
184///
185/// - Outer keys: u64 (JSON keys always strings, accept both).
186/// - Inner keys: Identifier.
187/// - Inner values: u64, stringified when large.
188/// - Non-HR (platform_value): native serialization.
189pub mod json_safe_u64_nested_identifier_u64_map {
190    use platform_value::Identifier;
191    use serde::de::{self, Deserializer, MapAccess, Visitor};
192    use serde::ser::{SerializeMap, Serializer};
193    use std::collections::BTreeMap;
194
195    use super::JS_MAX_SAFE_INTEGER;
196
197    pub fn serialize<S: Serializer>(
198        map: &BTreeMap<u64, BTreeMap<Identifier, u64>>,
199        serializer: S,
200    ) -> Result<S::Ok, S::Error> {
201        if serializer.is_human_readable() {
202            let mut outer = serializer.serialize_map(Some(map.len()))?;
203            for (k, inner) in map {
204                let safe_inner: BTreeMap<&Identifier, serde_json::Value> = inner
205                    .iter()
206                    .map(|(ik, iv)| {
207                        let v = if *iv > JS_MAX_SAFE_INTEGER {
208                            serde_json::Value::String(iv.to_string())
209                        } else {
210                            serde_json::Value::Number((*iv).into())
211                        };
212                        (ik, v)
213                    })
214                    .collect();
215                outer.serialize_entry(&k.to_string(), &safe_inner)?;
216            }
217            outer.end()
218        } else {
219            serde::Serialize::serialize(map, serializer)
220        }
221    }
222
223    pub fn deserialize<'de, D: Deserializer<'de>>(
224        deserializer: D,
225    ) -> Result<BTreeMap<u64, BTreeMap<Identifier, u64>>, D::Error> {
226        if deserializer.is_human_readable() {
227            deserializer.deserialize_map(OuterMapVisitor)
228        } else {
229            serde::Deserialize::deserialize(deserializer)
230        }
231    }
232
233    struct OuterMapVisitor;
234
235    impl<'de> Visitor<'de> for OuterMapVisitor {
236        type Value = BTreeMap<u64, BTreeMap<Identifier, u64>>;
237
238        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
239            f.write_str("a nested map: u64 -> (Identifier -> u64)")
240        }
241
242        fn visit_map<M: MapAccess<'de>>(self, mut access: M) -> Result<Self::Value, M::Error> {
243            let mut map = BTreeMap::new();
244            while let Some((key_str, inner_json)) =
245                access.next_entry::<serde_json::Value, BTreeMap<Identifier, serde_json::Value>>()?
246            {
247                let k = match &key_str {
248                    serde_json::Value::Number(n) => n
249                        .as_u64()
250                        .ok_or_else(|| de::Error::custom(format!("expected u64 key, got: {n}"))),
251                    serde_json::Value::String(s) => s
252                        .parse::<u64>()
253                        .map_err(|_| de::Error::custom(format!("invalid u64 key: {s}"))),
254                    other => Err(de::Error::custom(format!("expected u64 key, got: {other}"))),
255                }?;
256
257                let inner: BTreeMap<Identifier, u64> = inner_json
258                    .into_iter()
259                    .map(|(ik, iv)| {
260                        let v = match &iv {
261                            serde_json::Value::Number(n) => n.as_u64().ok_or_else(|| {
262                                de::Error::custom(format!("expected u64 value, got: {n}"))
263                            }),
264                            serde_json::Value::String(s) => s
265                                .parse::<u64>()
266                                .map_err(|_| de::Error::custom(format!("invalid u64 string: {s}"))),
267                            other => Err(de::Error::custom(format!(
268                                "expected u64 or string, got: {other}"
269                            ))),
270                        }?;
271                        Ok((ik, v))
272                    })
273                    .collect::<Result<_, M::Error>>()?;
274
275                map.insert(k, inner);
276            }
277            Ok(map)
278        }
279    }
280}
281
282/// Generic serde `with` module for `BTreeMap<K, u64>` fields where K is any
283/// serializable/deserializable key type.
284///
285/// - Keys: Use their own serde impl (unchanged).
286/// - Values: Large u64 values are serialized as strings in JSON.
287/// - Non-HR (platform_value): native serialization.
288pub mod json_safe_generic_u64_value_map {
289    use serde::de::{self, Deserializer, MapAccess, Visitor};
290    use serde::ser::{SerializeMap, Serializer};
291    use std::collections::BTreeMap;
292    use std::marker::PhantomData;
293
294    use super::JS_MAX_SAFE_INTEGER;
295
296    pub fn serialize<K, S>(map: &BTreeMap<K, u64>, serializer: S) -> Result<S::Ok, S::Error>
297    where
298        K: serde::Serialize + Ord,
299        S: Serializer,
300    {
301        if serializer.is_human_readable() {
302            let mut s = serializer.serialize_map(Some(map.len()))?;
303            for (k, v) in map {
304                s.serialize_entry(
305                    k,
306                    &if *v > JS_MAX_SAFE_INTEGER {
307                        serde_json::Value::String(v.to_string())
308                    } else {
309                        serde_json::Value::Number((*v).into())
310                    },
311                )?;
312            }
313            s.end()
314        } else {
315            serde::Serialize::serialize(map, serializer)
316        }
317    }
318
319    pub fn deserialize<'de, K, D>(deserializer: D) -> Result<BTreeMap<K, u64>, D::Error>
320    where
321        K: serde::Deserialize<'de> + Ord,
322        D: Deserializer<'de>,
323    {
324        if deserializer.is_human_readable() {
325            deserializer.deserialize_map(GenericU64ValueMapVisitor(PhantomData))
326        } else {
327            serde::Deserialize::deserialize(deserializer)
328        }
329    }
330
331    struct GenericU64ValueMapVisitor<K>(PhantomData<K>);
332
333    impl<'de, K> Visitor<'de> for GenericU64ValueMapVisitor<K>
334    where
335        K: serde::Deserialize<'de> + Ord,
336    {
337        type Value = BTreeMap<K, u64>;
338
339        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
340            f.write_str("a map with u64 values (numbers or strings)")
341        }
342
343        fn visit_map<M: MapAccess<'de>>(self, mut access: M) -> Result<Self::Value, M::Error> {
344            let mut map = BTreeMap::new();
345            while let Some((key, value)) = access.next_entry::<K, serde_json::Value>()? {
346                let v = match &value {
347                    serde_json::Value::Number(n) => n
348                        .as_u64()
349                        .ok_or_else(|| de::Error::custom(format!("expected u64 number, got: {n}"))),
350                    serde_json::Value::String(s) => s
351                        .parse::<u64>()
352                        .map_err(|_| de::Error::custom(format!("invalid u64 string: {s}"))),
353                    other => Err(de::Error::custom(format!(
354                        "expected u64 or string, got: {other}"
355                    ))),
356                }?;
357                map.insert(key, v);
358            }
359            Ok(map)
360        }
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use super::*;
367    use platform_value::string_encoding::Encoding;
368    use platform_value::Identifier;
369    use serde::{Deserialize, Serialize};
370    use std::collections::BTreeMap;
371
372    #[derive(Debug, PartialEq, Serialize, Deserialize)]
373    struct TestIdentifierU64Map {
374        #[serde(with = "json_safe_identifier_u64_map")]
375        data: BTreeMap<Identifier, u64>,
376    }
377
378    #[derive(Debug, PartialEq, Serialize, Deserialize)]
379    struct TestU64U64Map {
380        #[serde(with = "json_safe_u64_u64_map")]
381        data: BTreeMap<u64, u64>,
382    }
383
384    #[derive(Debug, PartialEq, Serialize, Deserialize)]
385    struct TestNestedMap {
386        #[serde(with = "json_safe_u64_nested_identifier_u64_map")]
387        data: BTreeMap<u64, BTreeMap<Identifier, u64>>,
388    }
389
390    #[test]
391    fn identifier_u64_map_small_values_stay_numbers() {
392        let id = Identifier::random();
393        let mut data = BTreeMap::new();
394        data.insert(id, 42u64);
395        let t = TestIdentifierU64Map { data };
396        let json = serde_json::to_value(&t).unwrap();
397        let restored: TestIdentifierU64Map = serde_json::from_value(json).unwrap();
398        assert_eq!(t, restored);
399    }
400
401    #[test]
402    fn identifier_u64_map_large_values_become_strings() {
403        let id = Identifier::random();
404        let mut data = BTreeMap::new();
405        data.insert(id, u64::MAX);
406        let t = TestIdentifierU64Map { data };
407        let json = serde_json::to_value(&t).unwrap();
408
409        // The value should be a string
410        let map_obj = json["data"].as_object().unwrap();
411        let val = map_obj.values().next().unwrap();
412        assert!(val.is_string());
413        assert_eq!(val.as_str().unwrap(), "18446744073709551615");
414
415        let restored: TestIdentifierU64Map = serde_json::from_value(json).unwrap();
416        assert_eq!(t, restored);
417    }
418
419    #[test]
420    fn u64_u64_map_round_trip_with_large_values() {
421        let mut data = BTreeMap::new();
422        data.insert(100u64, 42u64);
423        data.insert(u64::MAX, u64::MAX);
424        let t = TestU64U64Map { data };
425        let json = serde_json::to_value(&t).unwrap();
426
427        // Large value should be stringified
428        let map_obj = json["data"].as_object().unwrap();
429        let large_val = &map_obj["18446744073709551615"];
430        assert!(large_val.is_string());
431
432        let restored: TestU64U64Map = serde_json::from_value(json).unwrap();
433        assert_eq!(t, restored);
434    }
435
436    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
437    struct CustomKey(String);
438
439    #[derive(Debug, PartialEq, Serialize, Deserialize)]
440    struct TestGenericMap {
441        #[serde(with = "json_safe_generic_u64_value_map")]
442        data: BTreeMap<CustomKey, u64>,
443    }
444
445    #[test]
446    fn generic_map_small_values_stay_numbers() {
447        let mut data = BTreeMap::new();
448        data.insert(CustomKey("alice".into()), 42u64);
449        let t = TestGenericMap { data };
450        let json = serde_json::to_value(&t).unwrap();
451
452        let val = &json["data"]["alice"];
453        assert!(val.is_number());
454        assert_eq!(val.as_u64().unwrap(), 42);
455
456        let restored: TestGenericMap = serde_json::from_value(json).unwrap();
457        assert_eq!(t, restored);
458    }
459
460    #[test]
461    fn generic_map_large_values_become_strings() {
462        let mut data = BTreeMap::new();
463        data.insert(CustomKey("bob".into()), u64::MAX);
464        let t = TestGenericMap { data };
465        let json = serde_json::to_value(&t).unwrap();
466
467        let val = &json["data"]["bob"];
468        assert!(val.is_string());
469        assert_eq!(val.as_str().unwrap(), "18446744073709551615");
470
471        let restored: TestGenericMap = serde_json::from_value(json).unwrap();
472        assert_eq!(t, restored);
473    }
474
475    #[test]
476    fn generic_map_mixed_values_round_trip() {
477        let mut data = BTreeMap::new();
478        data.insert(CustomKey("small".into()), 100u64);
479        data.insert(CustomKey("large".into()), u64::MAX);
480        let t = TestGenericMap { data };
481        let json = serde_json::to_value(&t).unwrap();
482
483        // Small stays number, large becomes string
484        assert!(json["data"]["small"].is_number());
485        assert!(json["data"]["large"].is_string());
486
487        let restored: TestGenericMap = serde_json::from_value(json).unwrap();
488        assert_eq!(t, restored);
489    }
490
491    #[test]
492    fn u64_u64_map_small_values_stay_numbers() {
493        let mut data = BTreeMap::new();
494        data.insert(5u64, 10u64);
495        let t = TestU64U64Map { data };
496        let json = serde_json::to_value(&t).unwrap();
497        let map_obj = json["data"].as_object().unwrap();
498        let val = &map_obj["5"];
499        assert!(val.is_number());
500
501        let restored: TestU64U64Map = serde_json::from_value(json).unwrap();
502        assert_eq!(t, restored);
503    }
504
505    #[test]
506    fn u64_u64_map_empty_round_trip() {
507        let t = TestU64U64Map {
508            data: BTreeMap::new(),
509        };
510        let json = serde_json::to_value(&t).unwrap();
511        let restored: TestU64U64Map = serde_json::from_value(json).unwrap();
512        assert_eq!(t, restored);
513    }
514
515    #[test]
516    fn identifier_u64_map_empty_round_trip() {
517        let t = TestIdentifierU64Map {
518            data: BTreeMap::new(),
519        };
520        let json = serde_json::to_value(&t).unwrap();
521        let restored: TestIdentifierU64Map = serde_json::from_value(json).unwrap();
522        assert_eq!(t, restored);
523    }
524
525    #[test]
526    fn platform_value_generic_map_round_trip() {
527        let mut data = BTreeMap::new();
528        data.insert(CustomKey("x".into()), u64::MAX);
529        let t = TestGenericMap { data };
530        let pv = platform_value::to_value(&t).unwrap();
531        let restored: TestGenericMap = platform_value::from_value(pv).unwrap();
532        assert_eq!(t, restored);
533    }
534
535    #[test]
536    fn u64_u64_map_invalid_value_type_fails() {
537        let json = serde_json::json!({"data": {"1": true}});
538        let result = serde_json::from_value::<TestU64U64Map>(json);
539        assert!(result.is_err());
540    }
541
542    #[test]
543    fn u64_u64_map_invalid_value_string_fails() {
544        let json = serde_json::json!({"data": {"1": "not_a_number"}});
545        let result = serde_json::from_value::<TestU64U64Map>(json);
546        assert!(result.is_err());
547    }
548
549    #[test]
550    fn identifier_u64_map_invalid_value_type_fails() {
551        let id = Identifier::random();
552        let json = serde_json::json!({"data": {id.to_string(Encoding::Base58): [1, 2, 3]}});
553        let result = serde_json::from_value::<TestIdentifierU64Map>(json);
554        assert!(result.is_err());
555    }
556
557    #[test]
558    fn nested_map_empty_inner_round_trip() {
559        let mut data = BTreeMap::new();
560        data.insert(1u64, BTreeMap::new());
561        let t = TestNestedMap { data };
562        let json = serde_json::to_value(&t).unwrap();
563        let restored: TestNestedMap = serde_json::from_value(json).unwrap();
564        assert_eq!(t, restored);
565    }
566
567    #[test]
568    fn nested_map_round_trip() {
569        let id = Identifier::random();
570        let mut inner = BTreeMap::new();
571        inner.insert(id, u64::MAX);
572        let mut data = BTreeMap::new();
573        data.insert(1735689600000u64, inner);
574        let t = TestNestedMap { data };
575        let json = serde_json::to_value(&t).unwrap();
576        let restored: TestNestedMap = serde_json::from_value(json).unwrap();
577        assert_eq!(t, restored);
578    }
579
580    // --- Additional tests for json_safe_u64_nested_identifier_u64_map ---
581
582    #[test]
583    fn nested_map_multiple_inner_keys_round_trip() {
584        let id1 = Identifier::random();
585        let id2 = Identifier::random();
586        let mut inner = BTreeMap::new();
587        inner.insert(id1, 42u64);
588        inner.insert(id2, u64::MAX);
589        let mut data = BTreeMap::new();
590        data.insert(1u64, inner);
591        let t = TestNestedMap { data };
592        let json = serde_json::to_value(&t).unwrap();
593        let restored: TestNestedMap = serde_json::from_value(json).unwrap();
594        assert_eq!(t, restored);
595    }
596
597    #[test]
598    fn nested_map_multiple_outer_keys_round_trip() {
599        let id1 = Identifier::random();
600        let id2 = Identifier::random();
601
602        let mut inner1 = BTreeMap::new();
603        inner1.insert(id1, 100u64);
604        let mut inner2 = BTreeMap::new();
605        inner2.insert(id2, u64::MAX);
606
607        let mut data = BTreeMap::new();
608        data.insert(0u64, inner1);
609        data.insert(u64::MAX, inner2);
610
611        let t = TestNestedMap { data };
612        let json = serde_json::to_value(&t).unwrap();
613
614        // Verify outer key u64::MAX is a string key
615        let map_obj = json["data"].as_object().unwrap();
616        assert!(map_obj.contains_key("18446744073709551615"));
617
618        let restored: TestNestedMap = serde_json::from_value(json).unwrap();
619        assert_eq!(t, restored);
620    }
621
622    #[test]
623    fn nested_map_empty_outer_round_trip() {
624        let t = TestNestedMap {
625            data: BTreeMap::new(),
626        };
627        let json = serde_json::to_value(&t).unwrap();
628        let restored: TestNestedMap = serde_json::from_value(json).unwrap();
629        assert_eq!(t, restored);
630    }
631
632    #[test]
633    fn nested_map_large_outer_key_stringified_in_json() {
634        let id = Identifier::random();
635        let mut inner = BTreeMap::new();
636        inner.insert(id, 42u64);
637        let mut data = BTreeMap::new();
638        data.insert(u64::MAX, inner);
639        let t = TestNestedMap { data };
640        let json = serde_json::to_value(&t).unwrap();
641        // Outer key should be string since JSON map keys are always strings
642        let map_obj = json["data"].as_object().unwrap();
643        assert!(map_obj.contains_key("18446744073709551615"));
644        let restored: TestNestedMap = serde_json::from_value(json).unwrap();
645        assert_eq!(t, restored);
646    }
647
648    #[test]
649    fn nested_map_inner_small_values_stay_numbers() {
650        let id = Identifier::random();
651        let mut inner = BTreeMap::new();
652        inner.insert(id, 42u64);
653        let mut data = BTreeMap::new();
654        data.insert(1u64, inner);
655        let t = TestNestedMap { data };
656        let json = serde_json::to_value(&t).unwrap();
657
658        // Navigate to the inner map value and verify it's a number
659        let outer = json["data"].as_object().unwrap();
660        let inner_map = outer["1"].as_object().unwrap();
661        let val = inner_map.values().next().unwrap();
662        assert!(val.is_number());
663    }
664
665    // --- Additional tests for json_safe_generic_u64_value_map ---
666
667    #[test]
668    fn generic_map_empty_round_trip() {
669        let t = TestGenericMap {
670            data: BTreeMap::new(),
671        };
672        let json = serde_json::to_value(&t).unwrap();
673        let restored: TestGenericMap = serde_json::from_value(json).unwrap();
674        assert_eq!(t, restored);
675    }
676
677    #[test]
678    fn generic_map_platform_value_empty_round_trip() {
679        let t = TestGenericMap {
680            data: BTreeMap::new(),
681        };
682        let pv = platform_value::to_value(&t).unwrap();
683        let restored: TestGenericMap = platform_value::from_value(pv).unwrap();
684        assert_eq!(t, restored);
685    }
686
687    #[test]
688    fn generic_map_at_safe_boundary_stays_number() {
689        let mut data = BTreeMap::new();
690        data.insert(CustomKey("boundary".into()), JS_MAX_SAFE_INTEGER);
691        let t = TestGenericMap { data };
692        let json = serde_json::to_value(&t).unwrap();
693        assert!(json["data"]["boundary"].is_number());
694
695        let restored: TestGenericMap = serde_json::from_value(json).unwrap();
696        assert_eq!(t, restored);
697    }
698
699    #[test]
700    fn generic_map_above_safe_boundary_becomes_string() {
701        let mut data = BTreeMap::new();
702        data.insert(CustomKey("above".into()), JS_MAX_SAFE_INTEGER + 1);
703        let t = TestGenericMap { data };
704        let json = serde_json::to_value(&t).unwrap();
705        assert!(json["data"]["above"].is_string());
706
707        let restored: TestGenericMap = serde_json::from_value(json).unwrap();
708        assert_eq!(t, restored);
709    }
710
711    #[test]
712    fn generic_map_zero_value_round_trip() {
713        let mut data = BTreeMap::new();
714        data.insert(CustomKey("zero".into()), 0u64);
715        let t = TestGenericMap { data };
716        let json = serde_json::to_value(&t).unwrap();
717        assert!(json["data"]["zero"].is_number());
718        assert_eq!(json["data"]["zero"].as_u64().unwrap(), 0);
719
720        let restored: TestGenericMap = serde_json::from_value(json).unwrap();
721        assert_eq!(t, restored);
722    }
723
724    // --- Error path tests for generic_map ---
725
726    #[test]
727    fn generic_map_invalid_value_type_fails() {
728        let json = serde_json::json!({"data": {"key": true}});
729        let result = serde_json::from_value::<TestGenericMap>(json);
730        assert!(result.is_err());
731    }
732
733    #[test]
734    fn generic_map_invalid_value_string_fails() {
735        let json = serde_json::json!({"data": {"key": "not_a_number"}});
736        let result = serde_json::from_value::<TestGenericMap>(json);
737        assert!(result.is_err());
738    }
739
740    #[test]
741    fn generic_map_null_value_fails() {
742        let json = serde_json::json!({"data": {"key": null}});
743        let result = serde_json::from_value::<TestGenericMap>(json);
744        assert!(result.is_err());
745    }
746
747    #[test]
748    fn generic_map_array_value_fails() {
749        let json = serde_json::json!({"data": {"key": [1, 2, 3]}});
750        let result = serde_json::from_value::<TestGenericMap>(json);
751        assert!(result.is_err());
752    }
753
754    // --- Error path tests for identifier_u64_map ---
755
756    #[test]
757    fn identifier_u64_map_invalid_value_string_fails() {
758        let id = Identifier::random();
759        let json = serde_json::json!({"data": {id.to_string(Encoding::Base58): "not_a_number"}});
760        let result = serde_json::from_value::<TestIdentifierU64Map>(json);
761        assert!(result.is_err());
762    }
763
764    #[test]
765    fn identifier_u64_map_null_value_fails() {
766        let id = Identifier::random();
767        let json = serde_json::json!({"data": {id.to_string(Encoding::Base58): null}});
768        let result = serde_json::from_value::<TestIdentifierU64Map>(json);
769        assert!(result.is_err());
770    }
771
772    // --- Error path tests for nested_map ---
773
774    #[test]
775    fn nested_map_invalid_inner_value_type_fails() {
776        let id = Identifier::random();
777        let json =
778            serde_json::json!({"data": {"1": {id.to_string(Encoding::Base58): "not_a_number"}}});
779        let result = serde_json::from_value::<TestNestedMap>(json);
780        assert!(result.is_err());
781    }
782
783    #[test]
784    fn nested_map_invalid_outer_key_fails() {
785        let id = Identifier::random();
786        let json =
787            serde_json::json!({"data": {"not_a_number": {id.to_string(Encoding::Base58): 42}}});
788        let result = serde_json::from_value::<TestNestedMap>(json);
789        assert!(result.is_err());
790    }
791
792    // --- u64_u64_map additional tests ---
793
794    #[test]
795    fn u64_u64_map_at_safe_boundary_stays_number() {
796        let mut data = BTreeMap::new();
797        data.insert(1u64, JS_MAX_SAFE_INTEGER);
798        let t = TestU64U64Map { data };
799        let json = serde_json::to_value(&t).unwrap();
800        let map_obj = json["data"].as_object().unwrap();
801        let val = &map_obj["1"];
802        assert!(val.is_number());
803
804        let restored: TestU64U64Map = serde_json::from_value(json).unwrap();
805        assert_eq!(t, restored);
806    }
807
808    #[test]
809    fn u64_u64_map_above_safe_boundary_becomes_string() {
810        let mut data = BTreeMap::new();
811        data.insert(1u64, JS_MAX_SAFE_INTEGER + 1);
812        let t = TestU64U64Map { data };
813        let json = serde_json::to_value(&t).unwrap();
814        let map_obj = json["data"].as_object().unwrap();
815        let val = &map_obj["1"];
816        assert!(val.is_string());
817
818        let restored: TestU64U64Map = serde_json::from_value(json).unwrap();
819        assert_eq!(t, restored);
820    }
821
822    #[test]
823    fn u64_u64_map_multiple_entries_round_trip() {
824        let mut data = BTreeMap::new();
825        data.insert(0u64, 0u64);
826        data.insert(42u64, JS_MAX_SAFE_INTEGER);
827        data.insert(u64::MAX, u64::MAX);
828        let t = TestU64U64Map { data };
829        let json = serde_json::to_value(&t).unwrap();
830
831        // Value 0 should be a number
832        let map_obj = json["data"].as_object().unwrap();
833        assert!(map_obj["0"].is_number());
834        // JS_MAX_SAFE_INTEGER should be a number
835        assert!(map_obj["42"].is_number());
836        // u64::MAX should be a string
837        assert!(map_obj["18446744073709551615"].is_string());
838
839        let restored: TestU64U64Map = serde_json::from_value(json).unwrap();
840        assert_eq!(t, restored);
841    }
842
843    #[test]
844    fn identifier_u64_map_multiple_entries_round_trip() {
845        let id1 = Identifier::random();
846        let id2 = Identifier::random();
847        let mut data = BTreeMap::new();
848        data.insert(id1, 0u64);
849        data.insert(id2, u64::MAX);
850        let t = TestIdentifierU64Map { data };
851        let json = serde_json::to_value(&t).unwrap();
852        let restored: TestIdentifierU64Map = serde_json::from_value(json).unwrap();
853        assert_eq!(t, restored);
854    }
855}