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::Value;
427    use serde_json::json;
428
429    #[test]
430    fn test_json_array() {
431        let json = json!({
432          "type": 5,
433          "protocolVersion": 1,
434          "revision": 0,
435          "signature": "HxtcTSpRdACokorvpx/f4ezM40e0WtgW2GUvjiwNkHPwKDppkIoS2cirhqpZURlhDuYdu+E0KllbHNlYghcK9Bg=",
436          "signaturePublicKeyId": 1,
437          "addPublicKeys": [
438            {
439              "id": 0,
440              "purpose": 0,
441              "securityLevel": 0,
442              "type": 0,
443              "data": "Aya0WP8EhKQ6Dq+51sAnqdPah664X9CUciVJYAfvfTnX",
444              "readOnly": false,
445              "signature": "HxtcTSpRdACokorvpx/f4ezM40e0WtgW2GUvjiwNkHPwKDppkIoS2cirhqpZURlhDuYdu+E0KllbHNlYghcK9Bg="
446            }
447          ],
448          "disablePublicKeys": [ 0 ],
449          "identityId": "62DHhTfZV3NvUbXUha1mavLqSEy2GaWYja2qeTYNUhk"
450        });
451
452        let value: Value = json.into();
453        let array = value
454            .get_optional_array_slice("addPublicKeys")
455            .expect("expected to get array slice")
456            .unwrap();
457        assert_eq!(array.len(), 1);
458        assert!(array.first().unwrap().is_map());
459        let array = value
460            .get_optional_array_slice("disablePublicKeys")
461            .expect("expected to get array slice")
462            .unwrap();
463        assert_eq!(array.len(), 1);
464    }
465}