1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! A Koto language module for working with JSON data

use {
    koto_runtime::{
        runtime_error, unexpected_type_error_with_slice, Value, ValueList, ValueMap, ValueVec,
    },
    koto_serialize::SerializableValue,
    serde_json::Value as JsonValue,
};

pub fn json_value_to_koto_value(value: &serde_json::Value) -> Result<Value, String> {
    let result = match value {
        JsonValue::Null => Value::Null,
        JsonValue::Bool(b) => Value::Bool(*b),
        JsonValue::Number(n) => match n.as_i64() {
            Some(n64) => Value::Number(n64.into()),
            None => match n.as_f64() {
                Some(n64) => Value::Number(n64.into()),
                None => return Err(format!("Number is out of range: {}", n)),
            },
        },
        JsonValue::String(s) => Value::Str(s.as_str().into()),
        JsonValue::Array(a) => {
            match a
                .iter()
                .map(json_value_to_koto_value)
                .collect::<Result<ValueVec, String>>()
            {
                Ok(result) => Value::List(ValueList::with_data(result)),
                Err(e) => return Err(e),
            }
        }
        JsonValue::Object(o) => {
            let map = ValueMap::with_capacity(o.len());
            for (key, value) in o.iter() {
                map.add_value(key, json_value_to_koto_value(value)?);
            }
            Value::Map(map)
        }
    };

    Ok(result)
}

pub fn make_module() -> ValueMap {
    use Value::*;

    let result = ValueMap::new();

    result.add_fn("from_string", |vm, args| match vm.get_args(args) {
        [Str(s)] => match serde_json::from_str(s) {
            Ok(value) => match json_value_to_koto_value(&value) {
                Ok(result) => Ok(result),
                Err(e) => runtime_error!("json.from_string: Error while parsing input: {}", e),
            },
            Err(e) => runtime_error!(
                "json.from_string: Error while parsing input: {}",
                e.to_string()
            ),
        },
        unexpected => {
            unexpected_type_error_with_slice("json.from_string", "a String as argument", unexpected)
        }
    });

    result.add_fn("to_string", |vm, args| match vm.get_args(args) {
        [value] => match serde_json::to_string_pretty(&SerializableValue(value)) {
            Ok(result) => Ok(Str(result.into())),
            Err(e) => runtime_error!("json.to_string: {}", e),
        },
        unexpected => {
            unexpected_type_error_with_slice("json.to_string", "a Value as argument", unexpected)
        }
    });

    result
}