lbr_prelude/
json.rs

1//! Json serialization of Lambda Buffers types
2pub use crate::error::{Error, JsonType};
3use core::str::FromStr;
4use data_encoding::BASE64;
5#[cfg(feature = "derive")]
6pub use lbr_prelude_derive::Json;
7use num_bigint::BigInt;
8use serde_json;
9use serde_json::Value;
10use std::collections::{BTreeMap, BTreeSet};
11#[cfg(feature = "fs")]
12use std::future::Future;
13#[cfg(feature = "fs")]
14use std::{any::type_name, path::Path};
15#[cfg(feature = "fs")]
16use tokio::fs;
17
18pub mod lamval;
19
20/// Trait that lbf-prelude::json class maps to
21pub trait Json {
22    fn to_json(&self) -> Value;
23
24    fn from_json(value: &Value) -> Result<Self, Error>
25    where
26        Self: Sized;
27
28    fn to_json_string(&self) -> String {
29        self.to_json().to_string()
30    }
31
32    fn from_json_string(string: &str) -> Result<Self, Error>
33    where
34        Self: Sized,
35    {
36        Value::from_str(string)
37            .map_err(|err| Error::MalformedJson { source: err })
38            .and_then(|value| Self::from_json(&value))
39    }
40
41    /// Read a json object from a file, and decode it using lbf's Json trait.
42    /// The `type_name` is purely for error reporting purposes.
43    #[cfg(feature = "fs")]
44    fn read_lbf_json_file(path: impl AsRef<Path>) -> impl Future<Output = anyhow::Result<Self>>
45    where
46        Self: Sized,
47    {
48        async move {
49            let type_name = type_name::<Self>();
50            let json_str = fs::read_to_string(&path).await.map_err(|err| {
51                anyhow::anyhow!(
52                    "Unable to read file from {}: {}",
53                    path.as_ref().display(),
54                    err
55                )
56            })?;
57            let result = Self::from_json_string(&json_str).map_err(|err| {
58                anyhow::anyhow!(
59                    "Bad json of type {} in file {}: {}",
60                    type_name,
61                    path.as_ref().display(),
62                    err
63                )
64            })?;
65            Ok(result)
66        }
67    }
68
69    /// Encode a value using lbf's Json trait, and write the json string to a file.
70    /// The `type_name` is purely for error reporting purposes.
71    #[cfg(feature = "fs")]
72    fn write_lbf_json_file(
73        &self,
74        path: impl AsRef<Path>,
75    ) -> impl Future<Output = anyhow::Result<()>> {
76        async move {
77            let type_name = type_name::<Self>();
78            let json_str = self.to_json_string();
79            fs::write(&path, json_str).await.map_err(|err| {
80                anyhow::anyhow!(
81                    "Unable to write value of type {} to file {}: {}",
82                    type_name,
83                    path.as_ref().display(),
84                    err
85                )
86            })?;
87
88            Ok(())
89        }
90    }
91}
92
93//  lbf-prelude::json instance rule implementations
94
95impl Json for BigInt {
96    fn to_json(&self) -> Value {
97        let num = serde_json::Number::from_str(&self.to_string())
98            .expect("Failed to convert BigInt to json Number");
99
100        Value::Number(num)
101    }
102
103    fn from_json(value: &Value) -> Result<Self, Error> {
104        match value {
105            Value::Number(number) => {
106                BigInt::from_str(number.as_str()).map_err(|_| Error::UnexpectedJsonInvariant {
107                    wanted: "bigint".to_owned(),
108                    got: "unexpected string".to_owned(),
109                    parser: "BigInt".to_owned(),
110                })
111            }
112            _ => Err(Error::UnexpectedJsonType {
113                wanted: JsonType::Number,
114                got: JsonType::from(value),
115                parser: "Prelude.Integer".to_owned(),
116            }),
117        }
118    }
119}
120
121impl Json for bool {
122    fn to_json(&self) -> Value {
123        Value::Bool(*self)
124    }
125
126    fn from_json(value: &Value) -> Result<Self, Error> {
127        match value {
128            Value::Bool(bool) => Ok(*bool),
129            _ => Err(Error::UnexpectedJsonType {
130                wanted: JsonType::Bool,
131                got: JsonType::from(value),
132                parser: "Prelude.Bool".to_owned(),
133            }),
134        }
135    }
136}
137
138impl Json for char {
139    fn to_json(&self) -> Value {
140        String::from(*self).to_json()
141    }
142
143    fn from_json(value: &Value) -> Result<Self, Error> {
144        String::from_json(value).and_then(|str| {
145            let mut chars = str.chars();
146            let ch = chars.next();
147            let rest = chars.next();
148            match (ch, rest) {
149                (Some(ch), None) => Ok(ch),
150                _ => Err(Error::UnexpectedJsonInvariant {
151                    got: "string".to_owned(),
152                    wanted: "char".to_owned(),
153                    parser: "Prelude.Char".to_owned(),
154                }),
155            }
156        })
157    }
158}
159
160impl Json for Vec<u8> {
161    fn to_json(&self) -> Value {
162        BASE64.encode(self).to_json()
163    }
164
165    fn from_json(value: &Value) -> Result<Self, Error> {
166        String::from_json(value).and_then(|str| {
167            BASE64
168                .decode(&str.into_bytes())
169                .map_err(|_| Error::UnexpectedJsonInvariant {
170                    got: "string".to_owned(),
171                    wanted: "base64 string".to_owned(),
172                    parser: "Prelude.Bytes".to_owned(),
173                })
174        })
175    }
176}
177
178impl Json for String {
179    fn to_json(&self) -> Value {
180        Value::String(self.to_owned())
181    }
182
183    fn from_json(value: &Value) -> Result<Self, Error> {
184        match value {
185            Value::String(str) => Ok(str.clone()),
186            _ => Err(Error::UnexpectedJsonType {
187                wanted: JsonType::String,
188                got: JsonType::from(value),
189                parser: "Prelude.Text".to_owned(),
190            }),
191        }
192    }
193}
194
195impl<T> Json for Option<T>
196where
197    T: Json,
198{
199    fn to_json(&self) -> Value {
200        match self {
201            Some(val) => json_constructor("Just", vec![val.to_json()]),
202            None => json_constructor("Nothing", Vec::with_capacity(0)),
203        }
204    }
205
206    fn from_json(value: &Value) -> Result<Self, Error> {
207        case_json_constructor(
208            "Prelude.Maybe",
209            vec![
210                (
211                    "Nothing",
212                    Box::new(|ctor_fields| match &ctor_fields[..] {
213                        [] => Ok(None),
214                        _ => Err(Error::UnexpectedArrayLength {
215                            wanted: 0,
216                            got: ctor_fields.len(),
217                            parser: "Prelude.Maybe".to_owned(),
218                        }),
219                    }),
220                ),
221                (
222                    "Just",
223                    Box::new(|ctor_fields| match &ctor_fields[..] {
224                        [val] => Ok(Some(T::from_json(val)?)),
225                        _ => Err(Error::UnexpectedArrayLength {
226                            wanted: 1,
227                            got: ctor_fields.len(),
228                            parser: "Prelude.Maybe".to_owned(),
229                        }),
230                    }),
231                ),
232            ],
233            value,
234        )
235    }
236}
237
238impl<T, E> Json for Result<T, E>
239where
240    T: Json,
241    E: Json,
242{
243    fn to_json(&self) -> Value {
244        match self {
245            Ok(val) => json_constructor("Right", vec![val.to_json()]),
246            Err(val) => json_constructor("Left", vec![val.to_json()]),
247        }
248    }
249
250    fn from_json(value: &Value) -> Result<Self, Error> {
251        case_json_constructor(
252            "Prelude.Either",
253            vec![
254                (
255                    "Right",
256                    Box::new(|ctor_fields| match &ctor_fields[..] {
257                        [val] => Ok(Ok(T::from_json(val)?)),
258                        _ => Err(Error::UnexpectedArrayLength {
259                            wanted: 1,
260                            got: ctor_fields.len(),
261                            parser: "Prelude.Either".to_owned(),
262                        }),
263                    }),
264                ),
265                (
266                    "Left",
267                    Box::new(|ctor_fields| match &ctor_fields[..] {
268                        [val] => Ok(Err(E::from_json(val)?)),
269                        _ => Err(Error::UnexpectedArrayLength {
270                            wanted: 1,
271                            got: ctor_fields.len(),
272                            parser: "Prelude.Either".to_owned(),
273                        }),
274                    }),
275                ),
276            ],
277            value,
278        )
279    }
280}
281
282impl<T> Json for Vec<T>
283where
284    T: Json,
285{
286    fn to_json(&self) -> Value {
287        let values = self.iter().map(|val| val.to_json()).collect();
288
289        Value::Array(values)
290    }
291
292    fn from_json(value: &Value) -> Result<Self, Error> {
293        match value {
294            Value::Array(vec) => vec
295                .iter()
296                .map(|val| T::from_json(val))
297                .collect::<Result<Vec<T>, Error>>(),
298            _ => Err(Error::UnexpectedJsonType {
299                wanted: JsonType::Array,
300                got: JsonType::from(value),
301                parser: "Prelude.List".to_owned(),
302            }),
303        }
304    }
305}
306
307impl<T> Json for BTreeSet<T>
308where
309    T: Json + Eq + Ord,
310{
311    fn to_json(&self) -> Value {
312        let values = self.iter().map(|val| val.to_json()).collect();
313
314        Value::Array(values)
315    }
316
317    fn from_json(value: &Value) -> Result<Self, Error> {
318        Vec::from_json(value).and_then(|vec: Vec<Value>| {
319            let set = vec
320                .iter()
321                .map(|val| T::from_json(val))
322                .collect::<Result<BTreeSet<T>, Error>>()?;
323
324            if set.len() == vec.len() {
325                Ok(set)
326            } else {
327                Err(Error::UnexpectedJsonInvariant {
328                    wanted: "array with all unique elements".to_owned(),
329                    got: "invalid set".to_owned(),
330                    parser: "Prelude.Set".to_owned(),
331                })
332            }
333        })
334    }
335}
336
337impl<K, V> Json for BTreeMap<K, V>
338where
339    K: Json + Eq + Ord,
340    V: Json,
341{
342    fn to_json(&self) -> Value {
343        let values = self
344            .iter()
345            .map(|(key, val)| Value::Array(vec![key.to_json(), val.to_json()]))
346            .collect();
347
348        Value::Array(values)
349    }
350
351    fn from_json(value: &Value) -> Result<Self, Error> {
352        Vec::from_json(value).and_then(|vec: Vec<Value>| {
353            let set = vec
354                .iter()
355                .map(|kv_tuple| <(K, V)>::from_json(kv_tuple))
356                .collect::<Result<BTreeMap<K, V>, Error>>()?;
357
358            if set.len() == vec.len() {
359                Ok(set)
360            } else {
361                Err(Error::UnexpectedJsonInvariant {
362                    wanted: "array with all unique elements".to_owned(),
363                    got: "invalid set".to_owned(),
364                    parser: "Prelude.Map".to_owned(),
365                })
366            }
367        })
368    }
369}
370
371impl Json for () {
372    fn to_json(&self) -> Value {
373        Value::Null
374    }
375
376    fn from_json(value: &Value) -> Result<Self, Error> {
377        match value {
378            Value::Null => Ok(()),
379            _ => Err(Error::UnexpectedJsonType {
380                wanted: JsonType::Null,
381                got: JsonType::from(value),
382                parser: "Prelude.Unit".to_owned(),
383            }),
384        }
385    }
386}
387
388impl Json for Value {
389    fn to_json(&self) -> Value {
390        self.clone()
391    }
392
393    fn from_json(value: &Value) -> Result<Self, Error> {
394        Ok(value.clone())
395    }
396}
397
398impl<A, B> Json for (A, B)
399where
400    A: Json,
401    B: Json,
402{
403    fn to_json(&self) -> Value {
404        Value::Array(vec![self.0.to_json(), self.1.to_json()])
405    }
406
407    fn from_json(value: &Value) -> Result<Self, Error> {
408        Vec::from_json(value).and_then(|vec: Vec<Value>| match &vec[..] {
409            [a, b] => Ok((A::from_json(a)?, B::from_json(b)?)),
410            _ => Err(Error::UnexpectedArrayLength {
411                wanted: 2,
412                got: vec.len(),
413                parser: "Prelude.Tuple".to_owned(),
414            }),
415        })
416    }
417}
418
419/// Construct a JSON Array
420///
421/// LamVal Json builtin
422pub fn json_array(array: Vec<Value>) -> Value {
423    Value::Array(array)
424}
425
426/// Parse a JSON Array and its elements
427///
428/// LamVal Json builtin
429pub fn case_json_array<'a, T>(
430    parser_name: &'a str,
431    parse_arr: impl FnOnce(&'a Vec<Value>) -> Result<T, Error>,
432    value: &'a Value,
433) -> Result<T, Error> {
434    match value {
435        Value::Array(array) => parse_arr(array),
436        _ => Err(Error::UnexpectedJsonType {
437            wanted: JsonType::Array,
438            got: JsonType::from(value),
439            parser: parser_name.to_owned(),
440        }),
441    }
442}
443
444/// Construct a dictionary as a nested JSON Array
445/// k1: v1, k2: v2 -> `[[k1, v1], [k2, v2]]`
446///
447/// LamVal Json builtin
448pub fn json_map(map: Vec<(Value, Value)>) -> Value {
449    Value::Array(
450        map.into_iter()
451            .map(|(k, v)| Value::Array(vec![k, v]))
452            .collect(),
453    )
454}
455
456/// Parse a JSON Array as a dictionary
457///
458/// LamVal Json builtin
459pub fn case_json_map<'a, K, V>(
460    parser_name: &'a str,
461    parse_elem: impl Fn(&(Value, Value)) -> Result<(K, V), Error>,
462    value: &'a Value,
463) -> Result<BTreeMap<K, V>, Error>
464where
465    K: Ord,
466{
467    match value {
468        Value::Array(vec) => vec
469            .into_iter()
470            .map(|kv_tuple| parse_elem(&<(Value, Value)>::from_json(kv_tuple)?))
471            .collect::<Result<BTreeMap<K, V>, Error>>(),
472        _ => Err(Error::UnexpectedJsonType {
473            wanted: JsonType::Array,
474            got: JsonType::from(value),
475            parser: parser_name.to_owned(),
476        }),
477    }
478}
479
480/// Construct a JSON Object for a list of key-value pairs
481///
482/// LamVal Json builtin
483pub fn json_object(kvs: Vec<(String, Value)>) -> Value {
484    Value::Object(
485        kvs.iter()
486            .cloned()
487            .map(|(k, v)| (k, v))
488            .collect::<serde_json::Map<String, Value>>(),
489    )
490}
491
492/// Parse a JSON Object and its fields
493///
494/// LamVal Json builtin
495pub fn case_json_object<'a, T>(
496    parse_obj: impl FnOnce(&'a serde_json::Map<String, Value>) -> Result<T, Error>,
497    value: &'a Value,
498) -> Result<T, Error> {
499    match value {
500        Value::Object(obj) => parse_obj(obj),
501        _ => Err(Error::UnexpectedJsonType {
502            wanted: JsonType::Object,
503            got: JsonType::from(value),
504            parser: "Prelude.caseJsonObject".to_owned(),
505        }),
506    }
507}
508
509/// Extract a field from a JSON Object
510///
511/// LamVal Json builtin
512pub fn json_field<'a, T>(
513    name: &'a str,
514    obj: &'a serde_json::Map<String, Value>,
515    parse_field: impl Fn(&'a Value) -> Result<T, Error>,
516) -> Result<T, Error> {
517    obj.get(name)
518        .ok_or_else(|| Error::UnexpectedFieldName {
519            wanted: name.to_owned(),
520            got: obj.keys().cloned().collect(),
521            parser: name.to_owned(),
522        })
523        .and_then(parse_field)
524}
525
526/// Construct a JSON Value from a sum type.
527/// We always encode sum types into a `{"name": string, "fields": any[]}` format in JSON.
528///
529/// LamVal Json builtin
530pub fn json_constructor(ctor_name: &str, ctor_product: impl AsRef<Vec<Value>>) -> Value {
531    let mut obj = serde_json::Map::new();
532    obj.insert("name".to_owned(), Value::String(ctor_name.to_owned()));
533    obj.insert(
534        "fields".to_owned(),
535        Value::Array(ctor_product.as_ref().clone()),
536    );
537
538    Value::Object(obj)
539}
540
541/// Construct a closure that can parse a JSON object into a sum type.
542/// We always encode sum types into a `{"name": string, "fields": any[]}` format in JSON.
543///
544/// LamVal Json builtin
545pub fn case_json_constructor<'a, T: 'a>(
546    parser_name: &'a str,
547    ctor_parsers: Vec<(
548        &'a str,
549        Box<dyn 'a + Fn(&'a Vec<Value>) -> Result<T, Error>>,
550    )>,
551    value: &'a Value,
552) -> Result<T, Error> {
553    let ctor_parsers: BTreeMap<&'a str, Box<dyn 'a + Fn(&'a Vec<Value>) -> Result<T, Error>>> =
554        BTreeMap::from_iter(ctor_parsers);
555
556    match value {
557        Value::Object(ref obj) => {
558            let name = obj
559                .get("name")
560                .ok_or(Error::UnexpectedFieldName {
561                    wanted: "name".to_owned(),
562                    got: obj.keys().cloned().collect(),
563                    parser: parser_name.to_owned(),
564                })
565                .and_then(|name| match name {
566                    Value::String(str) => Ok(str),
567                    _ => Err(Error::UnexpectedJsonType {
568                        wanted: JsonType::String,
569                        got: JsonType::from(value),
570                        parser: parser_name.to_owned(),
571                    }),
572                })?;
573            let fields = obj
574                .get("fields")
575                .ok_or(Error::UnexpectedFieldName {
576                    wanted: "fields".to_owned(),
577                    got: obj.keys().cloned().collect(),
578                    parser: parser_name.to_owned(),
579                })
580                .and_then(|fields| match fields {
581                    Value::Array(str) => Ok(str),
582                    _ => Err(Error::UnexpectedJsonType {
583                        wanted: JsonType::Array,
584                        got: JsonType::from(value),
585                        parser: parser_name.to_owned(),
586                    }),
587                })?;
588
589            let names = ctor_parsers
590                .keys()
591                .map(std::ops::Deref::deref)
592                .collect::<Vec<_>>()
593                .join(", ");
594
595            let ctor_parser =
596                ctor_parsers
597                    .get(name.as_str())
598                    .ok_or(Error::UnexpectedJsonInvariant {
599                        wanted: format!("constructor names ({})", names),
600                        got: format!("unknown constructor name: {}", name),
601                        parser: parser_name.to_owned(),
602                    })?;
603
604            ctor_parser(fields)
605        }
606        _ => Err(Error::UnexpectedJsonType {
607            wanted: JsonType::Null,
608            got: JsonType::from(value),
609            parser: parser_name.to_owned(),
610        }),
611    }
612}