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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use anyhow::Context;
use config::{any_err, get_or_create_module, get_or_create_sub_module, serialize_options};
use mlua::{Lua, LuaSerdeExt, Value as LuaValue};
use serde_json::Value as JValue;

pub fn register(lua: &Lua) -> anyhow::Result<()> {
    let serde_mod = get_or_create_sub_module(lua, "serde")?;
    let kumo_mod = get_or_create_module(lua, "kumo")?;

    serde_mod.set("json_load", lua.create_async_function(json_load)?)?;
    serde_mod.set("json_parse", lua.create_function(json_parse)?)?;
    serde_mod.set("json_encode", lua.create_function(json_encode)?)?;
    serde_mod.set(
        "json_encode_pretty",
        lua.create_function(json_encode_pretty)?,
    )?;

    serde_mod.set("toml_load", lua.create_async_function(toml_load)?)?;
    serde_mod.set("toml_parse", lua.create_function(toml_parse)?)?;
    serde_mod.set("toml_encode", lua.create_function(toml_encode)?)?;
    serde_mod.set(
        "toml_encode_pretty",
        lua.create_function(toml_encode_pretty)?,
    )?;

    serde_mod.set("yaml_load", lua.create_async_function(yaml_load)?)?;
    serde_mod.set("yaml_parse", lua.create_function(yaml_parse)?)?;
    serde_mod.set("yaml_encode", lua.create_function(yaml_encode)?)?;
    // Note there is no pretty encoder for yaml, because the default one is pretty already.
    // See https://github.com/dtolnay/serde-yaml/issues/226

    // Backwards compatibility
    kumo_mod.set("json_load", lua.create_async_function(json_load)?)?;
    kumo_mod.set("json_parse", lua.create_function(json_parse)?)?;
    kumo_mod.set("json_encode", lua.create_function(json_encode)?)?;
    kumo_mod.set(
        "json_encode_pretty",
        lua.create_function(json_encode_pretty)?,
    )?;

    kumo_mod.set("toml_load", lua.create_async_function(toml_load)?)?;
    kumo_mod.set("toml_parse", lua.create_function(toml_parse)?)?;
    kumo_mod.set("toml_encode", lua.create_function(toml_encode)?)?;
    kumo_mod.set(
        "toml_encode_pretty",
        lua.create_function(toml_encode_pretty)?,
    )?;

    Ok(())
}

async fn json_load(lua: &Lua, file_name: String) -> mlua::Result<LuaValue> {
    let data = tokio::fs::read(&file_name)
        .await
        .with_context(|| format!("reading file {file_name}"))
        .map_err(any_err)?;

    let stripped = json_comments::StripComments::new(&*data);

    let obj: serde_json::Value = serde_json::from_reader(stripped)
        .with_context(|| format!("parsing {file_name} as json"))
        .map_err(any_err)?;
    lua.to_value_with(&obj, serialize_options())
}

fn json_parse(lua: &Lua, text: String) -> mlua::Result<LuaValue> {
    let stripped = json_comments::StripComments::new(text.as_bytes());
    let obj: serde_json::Value = serde_json::from_reader(stripped)
        .with_context(|| format!("parsing {text} as json"))
        .map_err(any_err)?;
    lua.to_value_with(&obj, serialize_options())
}

fn json_encode(_: &Lua, value: LuaValue) -> mlua::Result<String> {
    serde_json::to_string(&value).map_err(any_err)
}

fn json_encode_pretty(_: &Lua, value: LuaValue) -> mlua::Result<String> {
    serde_json::to_string_pretty(&value).map_err(any_err)
}

async fn toml_load(lua: &Lua, file_name: String) -> mlua::Result<LuaValue> {
    let data = tokio::fs::read_to_string(&file_name)
        .await
        .with_context(|| format!("reading file {file_name}"))
        .map_err(any_err)?;

    let obj: toml::Value = toml::from_str(&data)
        .with_context(|| format!("parsing {file_name} as toml"))
        .map_err(any_err)?;
    lua.to_value_with(&obj, serialize_options())
}

fn toml_parse(lua: &Lua, toml: String) -> mlua::Result<LuaValue> {
    let obj: toml::Value = toml::from_str(&toml)
        .with_context(|| format!("parsing {toml} as toml"))
        .map_err(any_err)?;
    lua.to_value_with(&obj, serialize_options())
}

fn toml_encode(_: &Lua, value: LuaValue) -> mlua::Result<String> {
    toml::to_string(&value).map_err(any_err)
}

fn toml_encode_pretty(_: &Lua, value: LuaValue) -> mlua::Result<String> {
    toml::to_string_pretty(&value).map_err(any_err)
}

async fn yaml_load(lua: &Lua, file_name: String) -> mlua::Result<LuaValue> {
    let data = tokio::fs::read(&file_name)
        .await
        .with_context(|| format!("reading file {file_name}"))
        .map_err(any_err)?;

    let value: JValue = serde_yaml::from_slice(&data)
        .with_context(|| format!("parsing {file_name} as yaml"))
        .map_err(any_err)?;
    lua.to_value_with(&value, serialize_options())
}

fn yaml_parse(lua: &Lua, text: String) -> mlua::Result<LuaValue> {
    let value: JValue = serde_yaml::from_str(&text)
        .with_context(|| format!("parsing {text} as yaml"))
        .map_err(any_err)?;
    lua.to_value_with(&value, serialize_options())
}

fn yaml_encode(_: &Lua, value: LuaValue) -> mlua::Result<String> {
    serde_yaml::to_string(&value).map_err(any_err)
}