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