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)]
425mod tests {
426    use crate::converter::serde_json::BTreeValueJsonConverter;
427    use crate::{Error, Value};
428    use base64::prelude::BASE64_STANDARD;
429    use base64::Engine;
430    use serde_json::{json, Value as JsonValue};
431    use std::collections::BTreeMap;
432
433    #[test]
434    fn test_json_array() {
435        let json = json!({
436          "type": 5,
437          "protocolVersion": 1,
438          "revision": 0,
439          "signature": "HxtcTSpRdACokorvpx/f4ezM40e0WtgW2GUvjiwNkHPwKDppkIoS2cirhqpZURlhDuYdu+E0KllbHNlYghcK9Bg=",
440          "signaturePublicKeyId": 1,
441          "addPublicKeys": [
442            {
443              "id": 0,
444              "purpose": 0,
445              "securityLevel": 0,
446              "type": 0,
447              "data": "Aya0WP8EhKQ6Dq+51sAnqdPah664X9CUciVJYAfvfTnX",
448              "readOnly": false,
449              "signature": "HxtcTSpRdACokorvpx/f4ezM40e0WtgW2GUvjiwNkHPwKDppkIoS2cirhqpZURlhDuYdu+E0KllbHNlYghcK9Bg="
450            }
451          ],
452          "disablePublicKeys": [ 0 ],
453          "identityId": "62DHhTfZV3NvUbXUha1mavLqSEy2GaWYja2qeTYNUhk"
454        });
455
456        let value: Value = json.into();
457        let array = value
458            .get_optional_array_slice("addPublicKeys")
459            .expect("expected to get array slice")
460            .unwrap();
461        assert_eq!(array.len(), 1);
462        assert!(array.first().unwrap().is_map());
463        let array = value
464            .get_optional_array_slice("disablePublicKeys")
465            .expect("expected to get array slice")
466            .unwrap();
467        assert_eq!(array.len(), 1);
468    }
469
470    // -----------------------------------------------------------------------
471    // try_into_validating_json — all Value variants
472    // -----------------------------------------------------------------------
473
474    #[test]
475    fn validating_json_null() {
476        let result = Value::Null.try_into_validating_json().unwrap();
477        assert_eq!(result, JsonValue::Null);
478    }
479
480    #[test]
481    fn validating_json_bool() {
482        assert_eq!(
483            Value::Bool(true).try_into_validating_json().unwrap(),
484            JsonValue::Bool(true)
485        );
486        assert_eq!(
487            Value::Bool(false).try_into_validating_json().unwrap(),
488            JsonValue::Bool(false)
489        );
490    }
491
492    #[test]
493    fn validating_json_u8() {
494        let result = Value::U8(42).try_into_validating_json().unwrap();
495        assert_eq!(result, json!(42));
496    }
497
498    #[test]
499    fn validating_json_i8() {
500        let result = Value::I8(-5).try_into_validating_json().unwrap();
501        assert_eq!(result, json!(-5));
502    }
503
504    #[test]
505    fn validating_json_u16() {
506        let result = Value::U16(1000).try_into_validating_json().unwrap();
507        assert_eq!(result, json!(1000));
508    }
509
510    #[test]
511    fn validating_json_i16() {
512        let result = Value::I16(-1000).try_into_validating_json().unwrap();
513        assert_eq!(result, json!(-1000));
514    }
515
516    #[test]
517    fn validating_json_u32() {
518        let result = Value::U32(100_000).try_into_validating_json().unwrap();
519        assert_eq!(result, json!(100_000));
520    }
521
522    #[test]
523    fn validating_json_i32() {
524        let result = Value::I32(-100_000).try_into_validating_json().unwrap();
525        assert_eq!(result, json!(-100_000));
526    }
527
528    #[test]
529    fn validating_json_u64() {
530        let result = Value::U64(u64::MAX).try_into_validating_json().unwrap();
531        assert_eq!(result, json!(u64::MAX));
532    }
533
534    #[test]
535    fn validating_json_i64() {
536        let result = Value::I64(i64::MIN).try_into_validating_json().unwrap();
537        assert_eq!(result, json!(i64::MIN));
538    }
539
540    #[test]
541    fn validating_json_float() {
542        let result = Value::Float(3.14).try_into_validating_json().unwrap();
543        assert_eq!(result, json!(3.14));
544    }
545
546    #[test]
547    fn validating_json_text() {
548        let result = Value::Text("hello".into())
549            .try_into_validating_json()
550            .unwrap();
551        assert_eq!(result, json!("hello"));
552    }
553
554    #[test]
555    fn validating_json_u128_fits_u64() {
556        let val = u64::MAX as u128;
557        let result = Value::U128(val).try_into_validating_json().unwrap();
558        assert_eq!(result, json!(u64::MAX));
559    }
560
561    #[test]
562    fn validating_json_u128_too_large() {
563        let val = u64::MAX as u128 + 1;
564        let err = Value::U128(val).try_into_validating_json().unwrap_err();
565        assert_eq!(err, Error::IntegerSizeError);
566    }
567
568    #[test]
569    fn validating_json_i128_fits_i64_positive() {
570        let val = i64::MAX as i128;
571        let result = Value::I128(val).try_into_validating_json().unwrap();
572        assert_eq!(result, json!(i64::MAX));
573    }
574
575    #[test]
576    fn validating_json_i128_fits_i64_negative() {
577        let val = i64::MIN as i128;
578        let result = Value::I128(val).try_into_validating_json().unwrap();
579        assert_eq!(result, json!(i64::MIN));
580    }
581
582    #[test]
583    fn validating_json_i128_too_large_positive() {
584        let val = i64::MAX as i128 + 1;
585        let err = Value::I128(val).try_into_validating_json().unwrap_err();
586        assert_eq!(err, Error::IntegerSizeError);
587    }
588
589    #[test]
590    fn validating_json_i128_too_small_negative() {
591        let val = i64::MIN as i128 - 1;
592        let err = Value::I128(val).try_into_validating_json().unwrap_err();
593        assert_eq!(err, Error::IntegerSizeError);
594    }
595
596    #[test]
597    fn validating_json_bytes() {
598        let result = Value::Bytes(vec![1, 2, 3])
599            .try_into_validating_json()
600            .unwrap();
601        assert_eq!(result, json!([1, 2, 3]));
602    }
603
604    #[test]
605    fn validating_json_bytes20() {
606        let bytes = [7u8; 20];
607        let result = Value::Bytes20(bytes).try_into_validating_json().unwrap();
608        let arr: Vec<JsonValue> = bytes.iter().map(|b| json!(*b)).collect();
609        assert_eq!(result, JsonValue::Array(arr));
610    }
611
612    #[test]
613    fn validating_json_bytes32() {
614        let bytes = [9u8; 32];
615        let result = Value::Bytes32(bytes).try_into_validating_json().unwrap();
616        let arr: Vec<JsonValue> = bytes.iter().map(|b| json!(*b)).collect();
617        assert_eq!(result, JsonValue::Array(arr));
618    }
619
620    #[test]
621    fn validating_json_bytes36() {
622        let bytes = [11u8; 36];
623        let result = Value::Bytes36(bytes).try_into_validating_json().unwrap();
624        let arr: Vec<JsonValue> = bytes.iter().map(|b| json!(*b)).collect();
625        assert_eq!(result, JsonValue::Array(arr));
626    }
627
628    #[test]
629    fn validating_json_identifier() {
630        let bytes = [0xABu8; 32];
631        let result = Value::Identifier(bytes).try_into_validating_json().unwrap();
632        let arr: Vec<JsonValue> = bytes.iter().map(|b| json!(*b)).collect();
633        assert_eq!(result, JsonValue::Array(arr));
634    }
635
636    #[test]
637    fn validating_json_array_nested() {
638        let val = Value::Array(vec![Value::U64(1), Value::Text("two".into())]);
639        let result = val.try_into_validating_json().unwrap();
640        assert_eq!(result, json!([1, "two"]));
641    }
642
643    #[test]
644    fn validating_json_map() {
645        let map = vec![
646            (Value::Text("a".into()), Value::U64(1)),
647            (Value::Text("b".into()), Value::Bool(true)),
648        ];
649        let val = Value::Map(map);
650        let result = val.try_into_validating_json().unwrap();
651        assert_eq!(result, json!({"a": 1, "b": true}));
652    }
653
654    #[test]
655    fn validating_json_enum_u8_unsupported() {
656        let err = Value::EnumU8(vec![1, 2])
657            .try_into_validating_json()
658            .unwrap_err();
659        assert!(matches!(err, Error::Unsupported(_)));
660    }
661
662    #[test]
663    fn validating_json_enum_string_unsupported() {
664        let err = Value::EnumString(vec!["a".into()])
665            .try_into_validating_json()
666            .unwrap_err();
667        assert!(matches!(err, Error::Unsupported(_)));
668    }
669
670    // -----------------------------------------------------------------------
671    // From<JsonValue> for Value — all JSON variants
672    // -----------------------------------------------------------------------
673
674    #[test]
675    fn from_json_null() {
676        let val: Value = JsonValue::Null.into();
677        assert_eq!(val, Value::Null);
678    }
679
680    #[test]
681    fn from_json_bool_true() {
682        let val: Value = json!(true).into();
683        assert_eq!(val, Value::Bool(true));
684    }
685
686    #[test]
687    fn from_json_bool_false() {
688        let val: Value = json!(false).into();
689        assert_eq!(val, Value::Bool(false));
690    }
691
692    #[test]
693    fn from_json_positive_integer() {
694        let val: Value = json!(42).into();
695        assert_eq!(val, Value::U64(42));
696    }
697
698    #[test]
699    fn from_json_negative_integer() {
700        let val: Value = json!(-7).into();
701        assert_eq!(val, Value::I64(-7));
702    }
703
704    #[test]
705    fn from_json_float() {
706        let val: Value = json!(2.5).into();
707        assert_eq!(val, Value::Float(2.5));
708    }
709
710    #[test]
711    fn from_json_string() {
712        let val: Value = json!("hello").into();
713        assert_eq!(val, Value::Text("hello".into()));
714    }
715
716    #[test]
717    fn from_json_object() {
718        let val: Value = json!({"key": "value"}).into();
719        assert!(val.is_map());
720    }
721
722    // --- byte-array heuristic tests ---
723
724    #[test]
725    fn from_json_array_10_u8_range_becomes_bytes() {
726        // Exactly 10 elements, all in u8 range -> Bytes
727        let arr: Vec<JsonValue> = (0u64..10).map(|i| json!(i)).collect();
728        let val: Value = JsonValue::Array(arr).into();
729        assert_eq!(val, Value::Bytes(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
730    }
731
732    #[test]
733    fn from_json_array_9_u8_range_stays_array() {
734        // Only 9 elements -> stays as Array even though all are u8-range
735        let arr: Vec<JsonValue> = (0u64..9).map(|i| json!(i)).collect();
736        let val: Value = JsonValue::Array(arr).into();
737        assert!(matches!(val, Value::Array(_)));
738    }
739
740    #[test]
741    fn from_json_array_mixed_types_stays_array() {
742        // 10+ elements but mixed types -> stays as Array
743        let mut arr: Vec<JsonValue> = (0u64..10).map(|i| json!(i)).collect();
744        arr.push(json!("not_a_number"));
745        let val: Value = JsonValue::Array(arr).into();
746        assert!(matches!(val, Value::Array(_)));
747    }
748
749    #[test]
750    fn from_json_array_large_values_stays_array() {
751        // 10+ elements but values exceed u8 range -> stays as Array
752        let arr: Vec<JsonValue> = (0u64..12).map(|i| json!(i * 100)).collect();
753        let val: Value = JsonValue::Array(arr).into();
754        // Some values like 1100 exceed u8::MAX (255), so not all u8-range
755        assert!(matches!(val, Value::Array(_)));
756    }
757
758    #[test]
759    fn from_json_array_all_255_becomes_bytes() {
760        // 10 elements all at u8::MAX
761        let arr: Vec<JsonValue> = vec![json!(255); 10];
762        let val: Value = JsonValue::Array(arr).into();
763        assert_eq!(val, Value::Bytes(vec![255; 10]));
764    }
765
766    #[test]
767    fn from_json_array_with_negative_stays_array() {
768        // Negative numbers are not in u8 range
769        let mut arr: Vec<JsonValue> = (0u64..9).map(|i| json!(i)).collect();
770        arr.push(json!(-1));
771        let val: Value = JsonValue::Array(arr).into();
772        assert!(matches!(val, Value::Array(_)));
773    }
774
775    // -----------------------------------------------------------------------
776    // From<&JsonValue> for Value — reference variant
777    // -----------------------------------------------------------------------
778
779    #[test]
780    fn from_json_ref_null() {
781        let jv = JsonValue::Null;
782        let val: Value = (&jv).into();
783        assert_eq!(val, Value::Null);
784    }
785
786    #[test]
787    fn from_json_ref_array_becomes_bytes() {
788        let arr: Vec<JsonValue> = (0u64..15).map(|i| json!(i)).collect();
789        let jv = JsonValue::Array(arr);
790        let val: Value = (&jv).into();
791        assert!(matches!(val, Value::Bytes(_)));
792    }
793
794    #[test]
795    fn from_json_ref_array_short_stays_array() {
796        let arr: Vec<JsonValue> = (0u64..5).map(|i| json!(i)).collect();
797        let jv = JsonValue::Array(arr);
798        let val: Value = (&jv).into();
799        assert!(matches!(val, Value::Array(_)));
800    }
801
802    // -----------------------------------------------------------------------
803    // TryInto<JsonValue> for Value — bytes become base64, identifiers become bs58
804    // -----------------------------------------------------------------------
805
806    #[test]
807    fn try_into_json_bytes_become_base64() {
808        let bytes = vec![0xDE, 0xAD, 0xBE, 0xEF];
809        let expected = BASE64_STANDARD.encode(&bytes);
810        let result: JsonValue = Value::Bytes(bytes).try_into().unwrap();
811        assert_eq!(result, JsonValue::String(expected));
812    }
813
814    #[test]
815    fn try_into_json_bytes20_become_base64() {
816        let bytes = [0xAAu8; 20];
817        let expected = BASE64_STANDARD.encode(bytes);
818        let result: JsonValue = Value::Bytes20(bytes).try_into().unwrap();
819        assert_eq!(result, JsonValue::String(expected));
820    }
821
822    #[test]
823    fn try_into_json_bytes32_become_base64() {
824        let bytes = [0xBBu8; 32];
825        let expected = BASE64_STANDARD.encode(bytes);
826        let result: JsonValue = Value::Bytes32(bytes).try_into().unwrap();
827        assert_eq!(result, JsonValue::String(expected));
828    }
829
830    #[test]
831    fn try_into_json_bytes36_become_base64() {
832        let bytes = [0xCCu8; 36];
833        let expected = BASE64_STANDARD.encode(bytes);
834        let result: JsonValue = Value::Bytes36(bytes).try_into().unwrap();
835        assert_eq!(result, JsonValue::String(expected));
836    }
837
838    #[test]
839    fn try_into_json_identifier_becomes_bs58() {
840        let bytes = [0x01u8; 32];
841        let expected = bs58::encode(&bytes).into_string();
842        let result: JsonValue = Value::Identifier(bytes).try_into().unwrap();
843        assert_eq!(result, JsonValue::String(expected));
844    }
845
846    #[test]
847    fn try_into_json_u128_becomes_string() {
848        let result: JsonValue = Value::U128(u128::MAX).try_into().unwrap();
849        assert_eq!(result, JsonValue::String(u128::MAX.to_string()));
850    }
851
852    #[test]
853    fn try_into_json_i128_becomes_string() {
854        let result: JsonValue = Value::I128(i128::MIN).try_into().unwrap();
855        assert_eq!(result, JsonValue::String(i128::MIN.to_string()));
856    }
857
858    #[test]
859    fn try_into_json_null() {
860        let result: JsonValue = Value::Null.try_into().unwrap();
861        assert_eq!(result, JsonValue::Null);
862    }
863
864    #[test]
865    fn try_into_json_bool() {
866        let result: JsonValue = Value::Bool(true).try_into().unwrap();
867        assert_eq!(result, JsonValue::Bool(true));
868    }
869
870    #[test]
871    fn try_into_json_text() {
872        let result: JsonValue = Value::Text("abc".into()).try_into().unwrap();
873        assert_eq!(result, json!("abc"));
874    }
875
876    #[test]
877    fn try_into_json_integer_types() {
878        let r: JsonValue = Value::U8(1).try_into().unwrap();
879        assert_eq!(r, json!(1));
880        let r: JsonValue = Value::I8(-1).try_into().unwrap();
881        assert_eq!(r, json!(-1));
882        let r: JsonValue = Value::U16(500).try_into().unwrap();
883        assert_eq!(r, json!(500));
884        let r: JsonValue = Value::I16(-500).try_into().unwrap();
885        assert_eq!(r, json!(-500));
886        let r: JsonValue = Value::U32(70000).try_into().unwrap();
887        assert_eq!(r, json!(70000));
888        let r: JsonValue = Value::I32(-70000).try_into().unwrap();
889        assert_eq!(r, json!(-70000));
890        let r: JsonValue = Value::U64(123456789).try_into().unwrap();
891        assert_eq!(r, json!(123456789));
892        let r: JsonValue = Value::I64(-123456789).try_into().unwrap();
893        assert_eq!(r, json!(-123456789));
894    }
895
896    #[test]
897    fn try_into_json_array() {
898        let val = Value::Array(vec![Value::U64(1), Value::Bool(false)]);
899        let result: JsonValue = val.try_into().unwrap();
900        assert_eq!(result, json!([1, false]));
901    }
902
903    #[test]
904    fn try_into_json_map() {
905        let map = vec![(Value::Text("x".into()), Value::U64(99))];
906        let val = Value::Map(map);
907        let result: JsonValue = val.try_into().unwrap();
908        assert_eq!(result, json!({"x": 99}));
909    }
910
911    #[test]
912    fn try_into_json_enum_u8_error() {
913        let result: Result<JsonValue, Error> = Value::EnumU8(vec![1]).try_into();
914        assert!(matches!(result, Err(Error::Unsupported(_))));
915    }
916
917    #[test]
918    fn try_into_json_enum_string_error() {
919        let result: Result<JsonValue, Error> = Value::EnumString(vec!["a".into()]).try_into();
920        assert!(matches!(result, Err(Error::Unsupported(_))));
921    }
922
923    // -----------------------------------------------------------------------
924    // Round-trip: Value -> JsonValue -> Value for basic types
925    // -----------------------------------------------------------------------
926
927    #[test]
928    fn round_trip_null() {
929        let original = Value::Null;
930        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
931        let back: Value = json.into();
932        assert_eq!(back, original);
933    }
934
935    #[test]
936    fn round_trip_bool() {
937        let original = Value::Bool(true);
938        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
939        let back: Value = json.into();
940        assert_eq!(back, Value::Bool(true));
941    }
942
943    #[test]
944    fn round_trip_u64() {
945        let original = Value::U64(42);
946        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
947        let back: Value = json.into();
948        // JSON numbers parse back as U64
949        assert_eq!(back, Value::U64(42));
950    }
951
952    #[test]
953    fn round_trip_i64() {
954        let original = Value::I64(-42);
955        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
956        let back: Value = json.into();
957        assert_eq!(back, Value::I64(-42));
958    }
959
960    #[test]
961    fn round_trip_text() {
962        let original = Value::Text("hello world".into());
963        let json: JsonValue = original.clone().try_into_validating_json().unwrap();
964        let back: Value = json.into();
965        assert_eq!(back, original);
966    }
967
968    // -----------------------------------------------------------------------
969    // BTreeValueJsonConverter methods
970    // -----------------------------------------------------------------------
971
972    #[test]
973    fn btree_into_json_value() {
974        let mut map = BTreeMap::new();
975        map.insert("x".to_string(), Value::U64(10));
976        map.insert("y".to_string(), Value::Text("test".into()));
977        let json = map.into_json_value().unwrap();
978        assert!(json.is_object());
979        assert_eq!(json["x"], json!(10));
980        assert_eq!(json["y"], json!("test"));
981    }
982
983    #[test]
984    fn btree_into_validating_json_value() {
985        let mut map = BTreeMap::new();
986        map.insert("n".to_string(), Value::U64(5));
987        let json = map.into_validating_json_value().unwrap();
988        assert_eq!(json["n"], json!(5));
989    }
990
991    #[test]
992    fn btree_to_json_value() {
993        let mut map = BTreeMap::new();
994        map.insert("k".to_string(), Value::Bool(true));
995        let json = map.to_json_value().unwrap();
996        assert_eq!(json["k"], json!(true));
997        // Original map is still available (borrow, not move)
998        assert!(map.contains_key("k"));
999    }
1000
1001    #[test]
1002    fn btree_to_validating_json_value() {
1003        let mut map = BTreeMap::new();
1004        map.insert("v".to_string(), Value::I64(-1));
1005        let json = map.to_validating_json_value().unwrap();
1006        assert_eq!(json["v"], json!(-1));
1007    }
1008
1009    #[test]
1010    fn btree_from_json_value() {
1011        let json = json!({"a": 1, "b": "two"});
1012        let map = BTreeMap::<String, Value>::from_json_value(json).unwrap();
1013        assert_eq!(map.get("a"), Some(&Value::U64(1)));
1014        assert_eq!(map.get("b"), Some(&Value::Text("two".into())));
1015    }
1016
1017    #[test]
1018    fn btree_from_json_value_non_object_error() {
1019        let json = json!([1, 2, 3]);
1020        let result = BTreeMap::<String, Value>::from_json_value(json);
1021        assert!(result.is_err());
1022    }
1023
1024    // -----------------------------------------------------------------------
1025    // From<BTreeMap<String, JsonValue>> for Value
1026    // -----------------------------------------------------------------------
1027
1028    #[test]
1029    fn from_btree_json_map() {
1030        let mut btree = BTreeMap::new();
1031        btree.insert("key".to_string(), json!(42));
1032        let val: Value = btree.into();
1033        assert!(val.is_map());
1034    }
1035
1036    #[test]
1037    fn from_btree_json_map_ref() {
1038        let mut btree = BTreeMap::new();
1039        btree.insert("key".to_string(), json!(42));
1040        let val: Value = (&btree).into();
1041        assert!(val.is_map());
1042    }
1043
1044    // -----------------------------------------------------------------------
1045    // try_to_validating_json (borrow variant) mirrors try_into_validating_json
1046    // -----------------------------------------------------------------------
1047
1048    #[test]
1049    fn try_to_validating_json_basic() {
1050        let val = Value::U64(99);
1051        let json = val.try_to_validating_json().unwrap();
1052        assert_eq!(json, json!(99));
1053    }
1054
1055    #[test]
1056    fn try_to_validating_json_u128_too_large() {
1057        let val = Value::U128(u128::MAX);
1058        let err = val.try_to_validating_json().unwrap_err();
1059        assert_eq!(err, Error::IntegerSizeError);
1060    }
1061
1062    #[test]
1063    fn try_to_validating_json_i128_too_large() {
1064        let val = Value::I128(i128::MAX);
1065        let err = val.try_to_validating_json().unwrap_err();
1066        assert_eq!(err, Error::IntegerSizeError);
1067    }
1068
1069    #[test]
1070    fn try_to_validating_json_i128_too_small() {
1071        let val = Value::I128(i128::MIN);
1072        let err = val.try_to_validating_json().unwrap_err();
1073        assert_eq!(err, Error::IntegerSizeError);
1074    }
1075
1076    #[test]
1077    fn try_to_validating_json_enum_u8_error() {
1078        let val = Value::EnumU8(vec![1]);
1079        let err = val.try_to_validating_json().unwrap_err();
1080        assert!(matches!(err, Error::Unsupported(_)));
1081    }
1082
1083    #[test]
1084    fn try_to_validating_json_enum_string_error() {
1085        let val = Value::EnumString(vec!["a".into()]);
1086        let err = val.try_to_validating_json().unwrap_err();
1087        assert!(matches!(err, Error::Unsupported(_)));
1088    }
1089
1090    // -----------------------------------------------------------------------
1091    // try_into_validating_btree_map_json
1092    // -----------------------------------------------------------------------
1093
1094    #[test]
1095    fn try_into_validating_btree_map_json_success() {
1096        let map = vec![(Value::Text("k".into()), Value::U64(7))];
1097        let val = Value::Map(map);
1098        let result = val.try_into_validating_btree_map_json().unwrap();
1099        assert_eq!(result.get("k"), Some(&json!(7)));
1100    }
1101
1102    // -----------------------------------------------------------------------
1103    // convert_from_serde_json_map
1104    // -----------------------------------------------------------------------
1105
1106    #[test]
1107    fn convert_from_serde_json_map_basic() {
1108        let pairs = vec![
1109            ("a".to_string(), json!(1)),
1110            ("b".to_string(), json!("hello")),
1111        ];
1112        let result: BTreeMap<String, Value> = Value::convert_from_serde_json_map(pairs);
1113        assert_eq!(result.get("a"), Some(&Value::U64(1)));
1114        assert_eq!(result.get("b"), Some(&Value::Text("hello".into())));
1115    }
1116
1117    #[test]
1118    fn validating_json_float_nan_becomes_zero() {
1119        // NaN cannot be represented in JSON Number, falls back to 0
1120        let result = Value::Float(f64::NAN).try_into_validating_json().unwrap();
1121        assert_eq!(result, json!(0));
1122    }
1123}