Skip to main content

platform_value/converter/
serde_json.rs

1use crate::value_map::ValueMap;
2use crate::{Error, Value};
3use base64::prelude::BASE64_STANDARD;
4use base64::Engine;
5use serde_json::{Map, Number, Value as JsonValue};
6use std::collections::BTreeMap;
7
8impl Value {
9    pub fn convert_from_serde_json_map<I, R>(map: I) -> R
10    where
11        I: IntoIterator<Item = (String, JsonValue)>,
12        R: FromIterator<(String, Value)>,
13    {
14        map.into_iter()
15            .map(|(key, serde_json_value)| (key, serde_json_value.into()))
16            .collect()
17    }
18
19    pub fn try_into_validating_json(self) -> Result<JsonValue, Error> {
20        Ok(match self {
21            Value::U128(i) => {
22                if i > u64::MAX as u128 {
23                    return Err(Error::IntegerSizeError);
24                }
25                JsonValue::Number((i as u64).into())
26            }
27            Value::I128(i) => {
28                if i > i64::MAX as i128 {
29                    return Err(Error::IntegerSizeError);
30                }
31                if i < i64::MIN as i128 {
32                    return Err(Error::IntegerSizeError);
33                }
34                JsonValue::Number((i as i64).into())
35            }
36            Value::U64(i) => JsonValue::Number(i.into()),
37            Value::I64(i) => JsonValue::Number(i.into()),
38            Value::U32(i) => JsonValue::Number(i.into()),
39            Value::I32(i) => JsonValue::Number(i.into()),
40            Value::U16(i) => JsonValue::Number(i.into()),
41            Value::I16(i) => JsonValue::Number(i.into()),
42            Value::U8(i) => JsonValue::Number(i.into()),
43            Value::I8(i) => JsonValue::Number(i.into()),
44            // JSON cannot represent NaN/±∞ (`from_f64` returns None) — fail
45            // loudly instead of silently substituting 0, which would be
46            // indistinguishable from a real zero to JSON consumers.
47            Value::Float(float) => JsonValue::Number(Number::from_f64(float).ok_or_else(|| {
48                Error::Unsupported("non-finite float (NaN/±∞) is not representable in JSON".into())
49            })?),
50            Value::Text(string) => JsonValue::String(string),
51            Value::Bool(value) => JsonValue::Bool(value),
52            Value::Null => JsonValue::Null,
53            Value::Array(array) => JsonValue::Array(
54                array
55                    .into_iter()
56                    .map(|value| value.try_into_validating_json())
57                    .collect::<Result<Vec<JsonValue>, Error>>()?,
58            ),
59            Value::Map(map) => JsonValue::Object(
60                map.into_iter()
61                    .map(|(k, v)| {
62                        let string = k.into_text()?;
63                        Ok((string, v.try_into_validating_json()?))
64                    })
65                    .collect::<Result<Map<String, JsonValue>, Error>>()?,
66            ),
67            Value::Identifier(bytes) => {
68                // In order to be able to validate using JSON schema it needs to be in byte form
69                JsonValue::Array(
70                    bytes
71                        .into_iter()
72                        .map(|a| JsonValue::Number(a.into()))
73                        .collect(),
74                )
75            }
76            Value::Bytes(bytes) => JsonValue::Array(
77                bytes
78                    .into_iter()
79                    .map(|byte| JsonValue::Number(byte.into()))
80                    .collect(),
81            ),
82            Value::Bytes20(bytes) => JsonValue::Array(
83                bytes
84                    .into_iter()
85                    .map(|byte| JsonValue::Number(byte.into()))
86                    .collect(),
87            ),
88            Value::Bytes32(bytes) => JsonValue::Array(
89                bytes
90                    .into_iter()
91                    .map(|byte| JsonValue::Number(byte.into()))
92                    .collect(),
93            ),
94            Value::Bytes36(bytes) => JsonValue::Array(
95                bytes
96                    .into_iter()
97                    .map(|byte| JsonValue::Number(byte.into()))
98                    .collect(),
99            ),
100            Value::EnumU8(_) => {
101                return Err(Error::Unsupported(
102                    "No support for conversion of EnumU8 to JSONValue".to_string(),
103                ))
104            }
105            Value::EnumString(_) => {
106                return Err(Error::Unsupported(
107                    "No support for conversion of EnumString to JSONValue".to_string(),
108                ))
109            }
110        })
111    }
112
113    pub fn try_into_validating_btree_map_json(self) -> Result<BTreeMap<String, JsonValue>, Error> {
114        self.into_btree_string_map()?
115            .into_iter()
116            .map(|(key, value)| Ok((key, value.try_into_validating_json()?)))
117            .collect()
118    }
119
120    pub fn try_to_validating_json(&self) -> Result<JsonValue, Error> {
121        Ok(match self {
122            Value::U128(i) => {
123                if *i > u64::MAX as u128 {
124                    return Err(Error::IntegerSizeError);
125                }
126                JsonValue::Number((*i as u64).into())
127            }
128            Value::I128(i) => {
129                if *i > i64::MAX as i128 {
130                    return Err(Error::IntegerSizeError);
131                }
132                if *i < i64::MIN as i128 {
133                    return Err(Error::IntegerSizeError);
134                }
135                JsonValue::Number((*i as i64).into())
136            }
137            Value::U64(i) => JsonValue::Number((*i).into()),
138            Value::I64(i) => JsonValue::Number((*i).into()),
139            Value::U32(i) => JsonValue::Number((*i).into()),
140            Value::I32(i) => JsonValue::Number((*i).into()),
141            Value::U16(i) => JsonValue::Number((*i).into()),
142            Value::I16(i) => JsonValue::Number((*i).into()),
143            Value::U8(i) => JsonValue::Number((*i).into()),
144            Value::I8(i) => JsonValue::Number((*i).into()),
145            // JSON cannot represent NaN/±∞ — fail loudly (see owned path above).
146            Value::Float(float) => {
147                JsonValue::Number(Number::from_f64(*float).ok_or_else(|| {
148                    Error::Unsupported(
149                        "non-finite float (NaN/±∞) is not representable in JSON".into(),
150                    )
151                })?)
152            }
153            Value::Text(string) => JsonValue::String(string.clone()),
154            Value::Bool(value) => JsonValue::Bool(*value),
155            Value::Null => JsonValue::Null,
156            Value::Array(array) => JsonValue::Array(
157                array
158                    .iter()
159                    .map(|value| value.try_to_validating_json())
160                    .collect::<Result<Vec<JsonValue>, Error>>()?,
161            ),
162            Value::Map(map) => JsonValue::Object(
163                map.iter()
164                    .map(|(k, v)| {
165                        let string = k.to_text()?;
166                        Ok((string, v.try_to_validating_json()?))
167                    })
168                    .collect::<Result<Map<String, JsonValue>, Error>>()?,
169            ),
170            Value::Identifier(bytes) => {
171                // In order to be able to validate using JSON schema it needs to be in byte form
172                JsonValue::Array(
173                    bytes
174                        .iter()
175                        .map(|a| JsonValue::Number((*a).into()))
176                        .collect(),
177                )
178            }
179            Value::Bytes(bytes) => JsonValue::Array(
180                bytes
181                    .iter()
182                    .map(|byte| JsonValue::Number((*byte).into()))
183                    .collect(),
184            ),
185            Value::Bytes20(bytes) => JsonValue::Array(
186                bytes
187                    .iter()
188                    .map(|byte| JsonValue::Number((*byte).into()))
189                    .collect(),
190            ),
191            Value::Bytes32(bytes) => JsonValue::Array(
192                bytes
193                    .iter()
194                    .map(|byte| JsonValue::Number((*byte).into()))
195                    .collect(),
196            ),
197            Value::Bytes36(bytes) => JsonValue::Array(
198                bytes
199                    .iter()
200                    .map(|byte| JsonValue::Number((*byte).into()))
201                    .collect(),
202            ),
203            Value::EnumU8(_) => {
204                return Err(Error::Unsupported(
205                    "No support for conversion of EnumU8 to JSONValue".to_string(),
206                ))
207            }
208            Value::EnumString(_) => {
209                return Err(Error::Unsupported(
210                    "No support for conversion of EnumString to JSONValue".to_string(),
211                ))
212            }
213        })
214    }
215}
216
217impl From<JsonValue> for Value {
218    fn from(value: JsonValue) -> Self {
219        match value {
220            JsonValue::Null => Self::Null,
221            JsonValue::Bool(value) => Self::Bool(value),
222            JsonValue::Number(number) => {
223                if let Some(value) = number.as_u64() {
224                    return Self::U64(value);
225                } else if let Some(value) = number.as_i64() {
226                    return Self::I64(value);
227                } else if let Some(value) = number.as_f64() {
228                    return Self::Float(value);
229                }
230                unreachable!("this shouldn't be reachable")
231            }
232            JsonValue::String(string) => Self::Text(string),
233            JsonValue::Array(array) => {
234                // Critical-2 fix: faithful array → array conversion. The previous
235                // heuristic ("if len >= 10 and all elements ≤ 255 then call it
236                // bytes") was a JS-DPP-era workaround for clients that sent
237                // binary as JSON arrays of u8. With the canonical-trait
238                // unification (HR = base64 strings, non-HR = Value::Bytes;
239                // BinaryData/Identifier/Bytes* deserializers handle both),
240                // the heuristic is no longer needed and was actively corrupting
241                // genuine arrays of small integers (e.g., a document property
242                // typed as "list of small ints" of length 10+).
243                Self::Array(array.into_iter().map(|v| v.into()).collect())
244            }
245            JsonValue::Object(map) => {
246                Self::Map(map.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
247            }
248        }
249    }
250}
251
252impl From<&JsonValue> for Value {
253    fn from(value: &JsonValue) -> Self {
254        match value {
255            JsonValue::Null => Self::Null,
256            JsonValue::Bool(value) => Self::Bool(*value),
257            JsonValue::Number(number) => {
258                if let Some(value) = number.as_u64() {
259                    return Self::U64(value);
260                } else if let Some(value) = number.as_i64() {
261                    return Self::I64(value);
262                } else if let Some(value) = number.as_f64() {
263                    return Self::Float(value);
264                }
265                unreachable!("this shouldn't be reachable")
266            }
267            JsonValue::String(string) => Self::Text(string.clone()),
268            JsonValue::Array(array) => {
269                // Critical-2 fix: see owned-form comment above.
270                Self::Array(array.iter().map(|v| v.into()).collect())
271            }
272            JsonValue::Object(map) => Self::Map(
273                map.into_iter()
274                    .map(|(k, v)| (k.clone().into(), v.into()))
275                    .collect(),
276            ),
277        }
278    }
279}
280
281impl TryInto<JsonValue> for Value {
282    type Error = Error;
283
284    fn try_into(self) -> Result<JsonValue, Self::Error> {
285        Ok(match self {
286            // U128/I128 can't fit in serde_json::Number (backed by u64/i64/f64) — must be strings
287            Value::U128(i) => JsonValue::String(i.to_string()),
288            Value::I128(i) => JsonValue::String(i.to_string()),
289            Value::U64(i) => JsonValue::Number(i.into()),
290            Value::I64(i) => JsonValue::Number(i.into()),
291            Value::U32(i) => JsonValue::Number(i.into()),
292            Value::I32(i) => JsonValue::Number(i.into()),
293            Value::U16(i) => JsonValue::Number(i.into()),
294            Value::I16(i) => JsonValue::Number(i.into()),
295            Value::U8(i) => JsonValue::Number(i.into()),
296            Value::I8(i) => JsonValue::Number(i.into()),
297            Value::Bytes(bytes) => JsonValue::String(BASE64_STANDARD.encode(bytes.as_slice())),
298            Value::Bytes20(bytes) => JsonValue::String(BASE64_STANDARD.encode(bytes.as_slice())),
299            Value::Bytes32(bytes) => JsonValue::String(BASE64_STANDARD.encode(bytes.as_slice())),
300            Value::Bytes36(bytes) => JsonValue::String(BASE64_STANDARD.encode(bytes.as_slice())),
301            // JSON cannot represent NaN/±∞ — fail loudly (see validating path above).
302            Value::Float(float) => JsonValue::Number(Number::from_f64(float).ok_or_else(|| {
303                Error::Unsupported("non-finite float (NaN/±∞) is not representable in JSON".into())
304            })?),
305            Value::Text(string) => JsonValue::String(string),
306            Value::Bool(value) => JsonValue::Bool(value),
307            Value::Null => JsonValue::Null,
308            Value::Array(array) => JsonValue::Array(
309                array
310                    .into_iter()
311                    .map(|value| value.try_into())
312                    .collect::<Result<Vec<JsonValue>, Error>>()?,
313            ),
314            Value::Map(map) => JsonValue::Object(
315                map.into_iter()
316                    .map(|(k, v)| {
317                        let string = k.into_text()?;
318                        Ok((string, v.try_into()?))
319                    })
320                    .collect::<Result<Map<String, JsonValue>, Error>>()?,
321            ),
322            Value::Identifier(bytes) => {
323                JsonValue::String(bs58::encode(bytes.as_slice()).into_string())
324            }
325            Value::EnumU8(_) => {
326                return Err(Error::Unsupported(
327                    "No support for conversion of EnumU8 to JSONValue".to_string(),
328                ))
329            }
330            Value::EnumString(_) => {
331                return Err(Error::Unsupported(
332                    "No support for conversion of EnumString to JSONValue".to_string(),
333                ))
334            }
335        })
336    }
337}
338
339pub trait BTreeValueJsonConverter {
340    fn into_json_value(self) -> Result<JsonValue, Error>;
341    fn into_validating_json_value(self) -> Result<JsonValue, Error>;
342    fn to_json_value(&self) -> Result<JsonValue, Error>;
343    fn to_validating_json_value(&self) -> Result<JsonValue, Error>;
344    fn from_json_value(value: JsonValue) -> Result<Self, Error>
345    where
346        Self: Sized;
347}
348
349impl BTreeValueJsonConverter for BTreeMap<String, Value> {
350    fn into_json_value(self) -> Result<JsonValue, Error> {
351        Ok(JsonValue::Object(
352            self.into_iter()
353                .map(|(key, value)| Ok((key, value.try_into()?)))
354                .collect::<Result<Map<String, JsonValue>, Error>>()?,
355        ))
356    }
357
358    fn into_validating_json_value(self) -> Result<JsonValue, Error> {
359        Ok(JsonValue::Object(
360            self.into_iter()
361                .map(|(key, value)| Ok((key, value.try_into_validating_json()?)))
362                .collect::<Result<Map<String, JsonValue>, Error>>()?,
363        ))
364    }
365
366    fn to_json_value(&self) -> Result<JsonValue, Error> {
367        Ok(JsonValue::Object(
368            self.iter()
369                .map(|(key, value)| Ok((key.clone(), value.clone().try_into()?)))
370                .collect::<Result<Map<String, JsonValue>, Error>>()?,
371        ))
372    }
373
374    fn to_validating_json_value(&self) -> Result<JsonValue, Error> {
375        Ok(JsonValue::Object(
376            self.iter()
377                .map(|(key, value)| Ok((key.to_owned(), value.try_to_validating_json()?)))
378                .collect::<Result<Map<String, JsonValue>, Error>>()?,
379        ))
380    }
381
382    fn from_json_value(value: JsonValue) -> Result<Self, Error> {
383        let platform_value: Value = value.into();
384        platform_value.into_btree_string_map()
385    }
386}
387
388impl From<BTreeMap<String, JsonValue>> for Value {
389    fn from(value: BTreeMap<String, JsonValue>) -> Self {
390        let map: ValueMap = value
391            .into_iter()
392            .map(|(key, json_value)| {
393                let value: Value = json_value.into();
394                (Value::Text(key), value)
395            })
396            .collect();
397        Value::Map(map)
398    }
399}
400
401impl From<&BTreeMap<String, JsonValue>> for Value {
402    fn from(value: &BTreeMap<String, JsonValue>) -> Self {
403        let map: ValueMap = value
404            .iter()
405            .map(|(key, json_value)| {
406                let value: Value = json_value.into();
407                (Value::Text(key.clone()), value)
408            })
409            .collect();
410        Value::Map(map)
411    }
412}
413
414#[cfg(test)]
415#[allow(clippy::approx_constant)]
416mod tests {
417    use crate::converter::serde_json::BTreeValueJsonConverter;
418    use crate::{Error, Value};
419    use base64::prelude::BASE64_STANDARD;
420    use base64::Engine;
421    use serde_json::{json, Value as JsonValue};
422    use std::collections::BTreeMap;
423
424    #[test]
425    fn test_json_array() {
426        let json = json!({
427          "type": 5,
428          "protocolVersion": 1,
429          "revision": 0,
430          "signature": "HxtcTSpRdACokorvpx/f4ezM40e0WtgW2GUvjiwNkHPwKDppkIoS2cirhqpZURlhDuYdu+E0KllbHNlYghcK9Bg=",
431          "signaturePublicKeyId": 1,
432          "addPublicKeys": [
433            {
434              "id": 0,
435              "purpose": 0,
436              "securityLevel": 0,
437              "type": 0,
438              "data": "Aya0WP8EhKQ6Dq+51sAnqdPah664X9CUciVJYAfvfTnX",
439              "readOnly": false,
440              "signature": "HxtcTSpRdACokorvpx/f4ezM40e0WtgW2GUvjiwNkHPwKDppkIoS2cirhqpZURlhDuYdu+E0KllbHNlYghcK9Bg="
441            }
442          ],
443          "disablePublicKeys": [ 0 ],
444          "identityId": "62DHhTfZV3NvUbXUha1mavLqSEy2GaWYja2qeTYNUhk"
445        });
446
447        let value: Value = json.into();
448        let array = value
449            .get_optional_array_slice("addPublicKeys")
450            .expect("expected to get array slice")
451            .unwrap();
452        assert_eq!(array.len(), 1);
453        assert!(array.first().unwrap().is_map());
454        let array = value
455            .get_optional_array_slice("disablePublicKeys")
456            .expect("expected to get array slice")
457            .unwrap();
458        assert_eq!(array.len(), 1);
459    }
460
461    // -----------------------------------------------------------------------
462    // try_into_validating_json — all Value variants
463    // -----------------------------------------------------------------------
464
465    #[test]
466    fn validating_json_null() {
467        let result = Value::Null.try_into_validating_json().unwrap();
468        assert_eq!(result, JsonValue::Null);
469    }
470
471    #[test]
472    fn validating_json_bool() {
473        assert_eq!(
474            Value::Bool(true).try_into_validating_json().unwrap(),
475            JsonValue::Bool(true)
476        );
477        assert_eq!(
478            Value::Bool(false).try_into_validating_json().unwrap(),
479            JsonValue::Bool(false)
480        );
481    }
482
483    #[test]
484    fn validating_json_u8() {
485        let result = Value::U8(42).try_into_validating_json().unwrap();
486        assert_eq!(result, json!(42));
487    }
488
489    #[test]
490    fn validating_json_i8() {
491        let result = Value::I8(-5).try_into_validating_json().unwrap();
492        assert_eq!(result, json!(-5));
493    }
494
495    #[test]
496    fn validating_json_u16() {
497        let result = Value::U16(1000).try_into_validating_json().unwrap();
498        assert_eq!(result, json!(1000));
499    }
500
501    #[test]
502    fn validating_json_i16() {
503        let result = Value::I16(-1000).try_into_validating_json().unwrap();
504        assert_eq!(result, json!(-1000));
505    }
506
507    #[test]
508    fn validating_json_u32() {
509        let result = Value::U32(100_000).try_into_validating_json().unwrap();
510        assert_eq!(result, json!(100_000));
511    }
512
513    #[test]
514    fn validating_json_i32() {
515        let result = Value::I32(-100_000).try_into_validating_json().unwrap();
516        assert_eq!(result, json!(-100_000));
517    }
518
519    #[test]
520    fn validating_json_u64() {
521        let result = Value::U64(u64::MAX).try_into_validating_json().unwrap();
522        assert_eq!(result, json!(u64::MAX));
523    }
524
525    #[test]
526    fn validating_json_i64() {
527        let result = Value::I64(i64::MIN).try_into_validating_json().unwrap();
528        assert_eq!(result, json!(i64::MIN));
529    }
530
531    #[test]
532    fn validating_json_float() {
533        let result = Value::Float(3.14).try_into_validating_json().unwrap();
534        assert_eq!(result, json!(3.14));
535    }
536
537    #[test]
538    fn validating_json_text() {
539        let result = Value::Text("hello".into())
540            .try_into_validating_json()
541            .unwrap();
542        assert_eq!(result, json!("hello"));
543    }
544
545    #[test]
546    fn validating_json_u128_fits_u64() {
547        let val = u64::MAX as u128;
548        let result = Value::U128(val).try_into_validating_json().unwrap();
549        assert_eq!(result, json!(u64::MAX));
550    }
551
552    #[test]
553    fn validating_json_u128_too_large() {
554        let val = u64::MAX as u128 + 1;
555        let err = Value::U128(val).try_into_validating_json().unwrap_err();
556        assert_eq!(err, Error::IntegerSizeError);
557    }
558
559    #[test]
560    fn validating_json_i128_fits_i64_positive() {
561        let val = i64::MAX as i128;
562        let result = Value::I128(val).try_into_validating_json().unwrap();
563        assert_eq!(result, json!(i64::MAX));
564    }
565
566    #[test]
567    fn validating_json_i128_fits_i64_negative() {
568        let val = i64::MIN as i128;
569        let result = Value::I128(val).try_into_validating_json().unwrap();
570        assert_eq!(result, json!(i64::MIN));
571    }
572
573    #[test]
574    fn validating_json_i128_too_large_positive() {
575        let val = i64::MAX as i128 + 1;
576        let err = Value::I128(val).try_into_validating_json().unwrap_err();
577        assert_eq!(err, Error::IntegerSizeError);
578    }
579
580    #[test]
581    fn validating_json_i128_too_small_negative() {
582        let val = i64::MIN as i128 - 1;
583        let err = Value::I128(val).try_into_validating_json().unwrap_err();
584        assert_eq!(err, Error::IntegerSizeError);
585    }
586
587    #[test]
588    fn validating_json_bytes() {
589        let result = Value::Bytes(vec![1, 2, 3])
590            .try_into_validating_json()
591            .unwrap();
592        assert_eq!(result, json!([1, 2, 3]));
593    }
594
595    #[test]
596    fn validating_json_bytes20() {
597        let bytes = [7u8; 20];
598        let result = Value::Bytes20(bytes).try_into_validating_json().unwrap();
599        let arr: Vec<JsonValue> = bytes.iter().map(|b| json!(*b)).collect();
600        assert_eq!(result, JsonValue::Array(arr));
601    }
602
603    #[test]
604    fn validating_json_bytes32() {
605        let bytes = [9u8; 32];
606        let result = Value::Bytes32(bytes).try_into_validating_json().unwrap();
607        let arr: Vec<JsonValue> = bytes.iter().map(|b| json!(*b)).collect();
608        assert_eq!(result, JsonValue::Array(arr));
609    }
610
611    #[test]
612    fn validating_json_bytes36() {
613        let bytes = [11u8; 36];
614        let result = Value::Bytes36(bytes).try_into_validating_json().unwrap();
615        let arr: Vec<JsonValue> = bytes.iter().map(|b| json!(*b)).collect();
616        assert_eq!(result, JsonValue::Array(arr));
617    }
618
619    #[test]
620    fn validating_json_identifier() {
621        let bytes = [0xABu8; 32];
622        let result = Value::Identifier(bytes).try_into_validating_json().unwrap();
623        let arr: Vec<JsonValue> = bytes.iter().map(|b| json!(*b)).collect();
624        assert_eq!(result, JsonValue::Array(arr));
625    }
626
627    #[test]
628    fn validating_json_array_nested() {
629        let val = Value::Array(vec![Value::U64(1), Value::Text("two".into())]);
630        let result = val.try_into_validating_json().unwrap();
631        assert_eq!(result, json!([1, "two"]));
632    }
633
634    #[test]
635    fn validating_json_map() {
636        let map = vec![
637            (Value::Text("a".into()), Value::U64(1)),
638            (Value::Text("b".into()), Value::Bool(true)),
639        ];
640        let val = Value::Map(map);
641        let result = val.try_into_validating_json().unwrap();
642        assert_eq!(result, json!({"a": 1, "b": true}));
643    }
644
645    #[test]
646    fn validating_json_enum_u8_unsupported() {
647        let err = Value::EnumU8(vec![1, 2])
648            .try_into_validating_json()
649            .unwrap_err();
650        assert!(matches!(err, Error::Unsupported(_)));
651    }
652
653    #[test]
654    fn validating_json_enum_string_unsupported() {
655        let err = Value::EnumString(vec!["a".into()])
656            .try_into_validating_json()
657            .unwrap_err();
658        assert!(matches!(err, Error::Unsupported(_)));
659    }
660
661    // -----------------------------------------------------------------------
662    // From<JsonValue> for Value — all JSON variants
663    // -----------------------------------------------------------------------
664
665    #[test]
666    fn from_json_null() {
667        let val: Value = JsonValue::Null.into();
668        assert_eq!(val, Value::Null);
669    }
670
671    #[test]
672    fn from_json_bool_true() {
673        let val: Value = json!(true).into();
674        assert_eq!(val, Value::Bool(true));
675    }
676
677    #[test]
678    fn from_json_bool_false() {
679        let val: Value = json!(false).into();
680        assert_eq!(val, Value::Bool(false));
681    }
682
683    #[test]
684    fn from_json_positive_integer() {
685        let val: Value = json!(42).into();
686        assert_eq!(val, Value::U64(42));
687    }
688
689    #[test]
690    fn from_json_negative_integer() {
691        let val: Value = json!(-7).into();
692        assert_eq!(val, Value::I64(-7));
693    }
694
695    #[test]
696    fn from_json_float() {
697        let val: Value = json!(2.5).into();
698        assert_eq!(val, Value::Float(2.5));
699    }
700
701    #[test]
702    fn from_json_string() {
703        let val: Value = json!("hello").into();
704        assert_eq!(val, Value::Text("hello".into()));
705    }
706
707    #[test]
708    fn from_json_object() {
709        let val: Value = json!({"key": "value"}).into();
710        assert!(val.is_map());
711    }
712
713    // -----------------------------------------------------------------------
714    // From<JsonValue> for Value — array conversion is faithful
715    //
716    // The previous `len >= 10 && all u8-range` heuristic that silently
717    // reclassified JSON arrays as `Value::Bytes` was removed. JSON arrays now
718    // always become `Value::Array(...)` regardless of length / content
719    // shape. Binary fields should flow through canonical encodings
720    // (base64 strings in JSON, decoded by the receiver's Deserialize impl).
721    // -----------------------------------------------------------------------
722
723    #[test]
724    fn from_json_array_10_u8_range_stays_array_not_bytes() {
725        // Previously: heuristic silently coerced this to Value::Bytes.
726        // Now: faithful array → array conversion.
727        let arr: Vec<JsonValue> = (0u64..10).map(|i| json!(i)).collect();
728        let val: Value = JsonValue::Array(arr).into();
729        assert_eq!(
730            val,
731            Value::Array(vec![
732                Value::U64(0),
733                Value::U64(1),
734                Value::U64(2),
735                Value::U64(3),
736                Value::U64(4),
737                Value::U64(5),
738                Value::U64(6),
739                Value::U64(7),
740                Value::U64(8),
741                Value::U64(9),
742            ])
743        );
744    }
745
746    #[test]
747    fn from_json_array_9_u8_range_stays_array() {
748        let arr: Vec<JsonValue> = (0u64..9).map(|i| json!(i)).collect();
749        let val: Value = JsonValue::Array(arr).into();
750        assert!(matches!(val, Value::Array(_)));
751    }
752
753    #[test]
754    fn from_json_array_mixed_types_stays_array() {
755        let mut arr: Vec<JsonValue> = (0u64..10).map(|i| json!(i)).collect();
756        arr.push(json!("not_a_number"));
757        let val: Value = JsonValue::Array(arr).into();
758        assert!(matches!(val, Value::Array(_)));
759    }
760
761    #[test]
762    fn from_json_array_large_values_stays_array() {
763        let arr: Vec<JsonValue> = (0u64..12).map(|i| json!(i * 100)).collect();
764        let val: Value = JsonValue::Array(arr).into();
765        assert!(matches!(val, Value::Array(_)));
766    }
767
768    #[test]
769    fn from_json_array_all_255_stays_array_not_bytes() {
770        // Previously: heuristic coerced this to Value::Bytes.
771        // Now: faithful array → array.
772        let arr: Vec<JsonValue> = vec![json!(255); 10];
773        let val: Value = JsonValue::Array(arr).into();
774        assert_eq!(val, Value::Array(vec![Value::U64(255); 10]));
775    }
776
777    #[test]
778    fn from_json_array_with_negative_stays_array() {
779        let mut arr: Vec<JsonValue> = (0u64..9).map(|i| json!(i)).collect();
780        arr.push(json!(-1));
781        let val: Value = JsonValue::Array(arr).into();
782        assert!(matches!(val, Value::Array(_)));
783    }
784
785    #[test]
786    fn from_json_long_byte_like_array_stays_array_not_bytes() {
787        // Round-trip pin: a 1000-element JSON array of small ints (e.g., a
788        // document property typed as `array of integers`) previously got
789        // silently corrupted into Value::Bytes(1000 bytes). The roundtrip
790        // back via `try_into::<JsonValue>` would then emit a base64 string
791        // instead of the original array. After the Critical-2 fix, the
792        // conversion is faithful in both directions.
793        let original: JsonValue = JsonValue::Array(vec![json!(7u64); 1000]);
794        let value: Value = original.clone().into();
795        assert!(
796            matches!(value, Value::Array(_)),
797            "1000-element u8-range JSON array must stay an array, not silently \
798             become bytes"
799        );
800        let recovered: JsonValue = value.try_into().unwrap();
801        assert_eq!(original, recovered, "round-trip JSON array → Value → JSON");
802    }
803
804    // -----------------------------------------------------------------------
805    // From<&JsonValue> for Value — reference variant
806    // -----------------------------------------------------------------------
807
808    #[test]
809    fn from_json_ref_null() {
810        let jv = JsonValue::Null;
811        let val: Value = (&jv).into();
812        assert_eq!(val, Value::Null);
813    }
814
815    #[test]
816    fn from_json_ref_array_stays_array_not_bytes() {
817        // Mirrors `from_json_array_10_u8_range_stays_array_not_bytes`
818        // for the borrowed `From<&JsonValue>` variant — same Critical-2
819        // fix applies to both impls.
820        let arr: Vec<JsonValue> = (0u64..15).map(|i| json!(i)).collect();
821        let jv = JsonValue::Array(arr);
822        let val: Value = (&jv).into();
823        assert!(matches!(val, Value::Array(_)));
824    }
825
826    #[test]
827    fn from_json_ref_array_short_stays_array() {
828        let arr: Vec<JsonValue> = (0u64..5).map(|i| json!(i)).collect();
829        let jv = JsonValue::Array(arr);
830        let val: Value = (&jv).into();
831        assert!(matches!(val, Value::Array(_)));
832    }
833
834    // -----------------------------------------------------------------------
835    // TryInto<JsonValue> for Value — bytes become base64, identifiers become bs58
836    // -----------------------------------------------------------------------
837
838    #[test]
839    fn try_into_json_bytes_become_base64() {
840        let bytes = vec![0xDE, 0xAD, 0xBE, 0xEF];
841        let expected = BASE64_STANDARD.encode(&bytes);
842        let result: JsonValue = Value::Bytes(bytes).try_into().unwrap();
843        assert_eq!(result, JsonValue::String(expected));
844    }
845
846    #[test]
847    fn try_into_json_bytes20_become_base64() {
848        let bytes = [0xAAu8; 20];
849        let expected = BASE64_STANDARD.encode(bytes);
850        let result: JsonValue = Value::Bytes20(bytes).try_into().unwrap();
851        assert_eq!(result, JsonValue::String(expected));
852    }
853
854    #[test]
855    fn try_into_json_bytes32_become_base64() {
856        let bytes = [0xBBu8; 32];
857        let expected = BASE64_STANDARD.encode(bytes);
858        let result: JsonValue = Value::Bytes32(bytes).try_into().unwrap();
859        assert_eq!(result, JsonValue::String(expected));
860    }
861
862    #[test]
863    fn try_into_json_bytes36_become_base64() {
864        let bytes = [0xCCu8; 36];
865        let expected = BASE64_STANDARD.encode(bytes);
866        let result: JsonValue = Value::Bytes36(bytes).try_into().unwrap();
867        assert_eq!(result, JsonValue::String(expected));
868    }
869
870    #[test]
871    fn try_into_json_identifier_becomes_bs58() {
872        let bytes = [0x01u8; 32];
873        let expected = bs58::encode(&bytes).into_string();
874        let result: JsonValue = Value::Identifier(bytes).try_into().unwrap();
875        assert_eq!(result, JsonValue::String(expected));
876    }
877
878    #[test]
879    fn try_into_json_u128_becomes_string() {
880        let result: JsonValue = Value::U128(u128::MAX).try_into().unwrap();
881        assert_eq!(result, JsonValue::String(u128::MAX.to_string()));
882    }
883
884    #[test]
885    fn try_into_json_i128_becomes_string() {
886        let result: JsonValue = Value::I128(i128::MIN).try_into().unwrap();
887        assert_eq!(result, JsonValue::String(i128::MIN.to_string()));
888    }
889
890    #[test]
891    fn try_into_json_null() {
892        let result: JsonValue = Value::Null.try_into().unwrap();
893        assert_eq!(result, JsonValue::Null);
894    }
895
896    #[test]
897    fn try_into_json_bool() {
898        let result: JsonValue = Value::Bool(true).try_into().unwrap();
899        assert_eq!(result, JsonValue::Bool(true));
900    }
901
902    #[test]
903    fn try_into_json_text() {
904        let result: JsonValue = Value::Text("abc".into()).try_into().unwrap();
905        assert_eq!(result, json!("abc"));
906    }
907
908    #[test]
909    fn try_into_json_integer_types() {
910        let r: JsonValue = Value::U8(1).try_into().unwrap();
911        assert_eq!(r, json!(1));
912        let r: JsonValue = Value::I8(-1).try_into().unwrap();
913        assert_eq!(r, json!(-1));
914        let r: JsonValue = Value::U16(500).try_into().unwrap();
915        assert_eq!(r, json!(500));
916        let r: JsonValue = Value::I16(-500).try_into().unwrap();
917        assert_eq!(r, json!(-500));
918        let r: JsonValue = Value::U32(70000).try_into().unwrap();
919        assert_eq!(r, json!(70000));
920        let r: JsonValue = Value::I32(-70000).try_into().unwrap();
921        assert_eq!(r, json!(-70000));
922        let r: JsonValue = Value::U64(123456789).try_into().unwrap();
923        assert_eq!(r, json!(123456789));
924        let r: JsonValue = Value::I64(-123456789).try_into().unwrap();
925        assert_eq!(r, json!(-123456789));
926    }
927
928    #[test]
929    fn try_into_json_array() {
930        let val = Value::Array(vec![Value::U64(1), Value::Bool(false)]);
931        let result: JsonValue = val.try_into().unwrap();
932        assert_eq!(result, json!([1, false]));
933    }
934
935    #[test]
936    fn try_into_json_map() {
937        let map = vec![(Value::Text("x".into()), Value::U64(99))];
938        let val = Value::Map(map);
939        let result: JsonValue = val.try_into().unwrap();
940        assert_eq!(result, json!({"x": 99}));
941    }
942
943    #[test]
944    fn try_into_json_enum_u8_error() {
945        let result: Result<JsonValue, Error> = Value::EnumU8(vec![1]).try_into();
946        assert!(matches!(result, Err(Error::Unsupported(_))));
947    }
948
949    #[test]
950    fn try_into_json_enum_string_error() {
951        let result: Result<JsonValue, Error> = Value::EnumString(vec!["a".into()]).try_into();
952        assert!(matches!(result, Err(Error::Unsupported(_))));
953    }
954
955    // -----------------------------------------------------------------------
956    // Round-trip: Value -> JsonValue -> Value for basic types
957    // -----------------------------------------------------------------------
958
959    #[test]
960    fn round_trip_null() {
961        let original = Value::Null;
962        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
963        let back: Value = json.into();
964        assert_eq!(back, original);
965    }
966
967    #[test]
968    fn round_trip_bool() {
969        let original = Value::Bool(true);
970        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
971        let back: Value = json.into();
972        assert_eq!(back, Value::Bool(true));
973    }
974
975    #[test]
976    fn round_trip_u64() {
977        let original = Value::U64(42);
978        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
979        let back: Value = json.into();
980        // JSON numbers parse back as U64
981        assert_eq!(back, Value::U64(42));
982    }
983
984    #[test]
985    fn round_trip_i64() {
986        let original = Value::I64(-42);
987        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
988        let back: Value = json.into();
989        assert_eq!(back, Value::I64(-42));
990    }
991
992    #[test]
993    fn round_trip_text() {
994        let original = Value::Text("hello world".into());
995        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
996        let back: Value = json.into();
997        assert_eq!(back, original);
998    }
999
1000    // -----------------------------------------------------------------------
1001    // BTreeValueJsonConverter methods
1002    // -----------------------------------------------------------------------
1003
1004    #[test]
1005    fn btree_into_json_value() {
1006        let mut map = BTreeMap::new();
1007        map.insert("x".to_string(), Value::U64(10));
1008        map.insert("y".to_string(), Value::Text("test".into()));
1009        let json = map.into_json_value().unwrap();
1010        assert!(json.is_object());
1011        assert_eq!(json["x"], json!(10));
1012        assert_eq!(json["y"], json!("test"));
1013    }
1014
1015    #[test]
1016    fn btree_into_validating_json_value() {
1017        let mut map = BTreeMap::new();
1018        map.insert("n".to_string(), Value::U64(5));
1019        let json = map.into_validating_json_value().unwrap();
1020        assert_eq!(json["n"], json!(5));
1021    }
1022
1023    #[test]
1024    fn btree_to_json_value() {
1025        let mut map = BTreeMap::new();
1026        map.insert("k".to_string(), Value::Bool(true));
1027        let json = map.to_json_value().unwrap();
1028        assert_eq!(json["k"], json!(true));
1029        // Original map is still available (borrow, not move)
1030        assert!(map.contains_key("k"));
1031    }
1032
1033    #[test]
1034    fn btree_to_validating_json_value() {
1035        let mut map = BTreeMap::new();
1036        map.insert("v".to_string(), Value::I64(-1));
1037        let json = map.to_validating_json_value().unwrap();
1038        assert_eq!(json["v"], json!(-1));
1039    }
1040
1041    #[test]
1042    fn btree_from_json_value() {
1043        let json = json!({"a": 1, "b": "two"});
1044        let map = BTreeMap::<String, Value>::from_json_value(json).unwrap();
1045        assert_eq!(map.get("a"), Some(&Value::U64(1)));
1046        assert_eq!(map.get("b"), Some(&Value::Text("two".into())));
1047    }
1048
1049    #[test]
1050    fn btree_from_json_value_non_object_error() {
1051        let json = json!([1, 2, 3]);
1052        let result = BTreeMap::<String, Value>::from_json_value(json);
1053        assert!(result.is_err());
1054    }
1055
1056    // -----------------------------------------------------------------------
1057    // From<BTreeMap<String, JsonValue>> for Value
1058    // -----------------------------------------------------------------------
1059
1060    #[test]
1061    fn from_btree_json_map() {
1062        let mut btree = BTreeMap::new();
1063        btree.insert("key".to_string(), json!(42));
1064        let val: Value = btree.into();
1065        assert!(val.is_map());
1066    }
1067
1068    #[test]
1069    fn from_btree_json_map_ref() {
1070        let mut btree = BTreeMap::new();
1071        btree.insert("key".to_string(), json!(42));
1072        let val: Value = (&btree).into();
1073        assert!(val.is_map());
1074    }
1075
1076    // -----------------------------------------------------------------------
1077    // try_to_validating_json (borrow variant) mirrors try_into_validating_json
1078    // -----------------------------------------------------------------------
1079
1080    #[test]
1081    fn try_to_validating_json_basic() {
1082        let val = Value::U64(99);
1083        let json = val.try_to_validating_json().unwrap();
1084        assert_eq!(json, json!(99));
1085    }
1086
1087    #[test]
1088    fn try_to_validating_json_u128_too_large() {
1089        let val = Value::U128(u128::MAX);
1090        let err = val.try_to_validating_json().unwrap_err();
1091        assert_eq!(err, Error::IntegerSizeError);
1092    }
1093
1094    #[test]
1095    fn try_to_validating_json_i128_too_large() {
1096        let val = Value::I128(i128::MAX);
1097        let err = val.try_to_validating_json().unwrap_err();
1098        assert_eq!(err, Error::IntegerSizeError);
1099    }
1100
1101    #[test]
1102    fn try_to_validating_json_i128_too_small() {
1103        let val = Value::I128(i128::MIN);
1104        let err = val.try_to_validating_json().unwrap_err();
1105        assert_eq!(err, Error::IntegerSizeError);
1106    }
1107
1108    #[test]
1109    fn try_to_validating_json_enum_u8_error() {
1110        let val = Value::EnumU8(vec![1]);
1111        let err = val.try_to_validating_json().unwrap_err();
1112        assert!(matches!(err, Error::Unsupported(_)));
1113    }
1114
1115    #[test]
1116    fn try_to_validating_json_enum_string_error() {
1117        let val = Value::EnumString(vec!["a".into()]);
1118        let err = val.try_to_validating_json().unwrap_err();
1119        assert!(matches!(err, Error::Unsupported(_)));
1120    }
1121
1122    // -----------------------------------------------------------------------
1123    // try_into_validating_btree_map_json
1124    // -----------------------------------------------------------------------
1125
1126    #[test]
1127    fn try_into_validating_btree_map_json_success() {
1128        let map = vec![(Value::Text("k".into()), Value::U64(7))];
1129        let val = Value::Map(map);
1130        let result = val.try_into_validating_btree_map_json().unwrap();
1131        assert_eq!(result.get("k"), Some(&json!(7)));
1132    }
1133
1134    // -----------------------------------------------------------------------
1135    // convert_from_serde_json_map
1136    // -----------------------------------------------------------------------
1137
1138    #[test]
1139    fn convert_from_serde_json_map_basic() {
1140        let pairs = vec![
1141            ("a".to_string(), json!(1)),
1142            ("b".to_string(), json!("hello")),
1143        ];
1144        let result: BTreeMap<String, Value> = Value::convert_from_serde_json_map(pairs);
1145        assert_eq!(result.get("a"), Some(&Value::U64(1)));
1146        assert_eq!(result.get("b"), Some(&Value::Text("hello".into())));
1147    }
1148
1149    #[test]
1150    fn non_finite_floats_error_in_all_three_json_converters() {
1151        // JSON (RFC 8259) cannot represent NaN/±∞. The converters used to
1152        // silently substitute 0 (`Number::from_f64(..).unwrap_or(0)`), which
1153        // made `Value::Float(f64::NAN)` indistinguishable from a real zero to
1154        // every JSON consumer. All three Value→JSON paths now fail loudly
1155        // instead — consistent with the loud IntegerSizeError the same
1156        // functions return for out-of-range integers.
1157        for bad in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
1158            // owned validating path
1159            assert!(matches!(
1160                Value::Float(bad).try_into_validating_json(),
1161                Err(Error::Unsupported(_))
1162            ));
1163            // by-ref validating path
1164            assert!(matches!(
1165                Value::Float(bad).try_to_validating_json(),
1166                Err(Error::Unsupported(_))
1167            ));
1168            // plain TryInto path
1169            let plain: Result<JsonValue, Error> = Value::Float(bad).try_into();
1170            assert!(matches!(plain, Err(Error::Unsupported(_))));
1171        }
1172        // Finite floats still convert.
1173        assert_eq!(
1174            Value::Float(1.5).try_into_validating_json().unwrap(),
1175            json!(1.5)
1176        );
1177    }
1178}