mod_serde/
lib.rs

1use anyhow::Context;
2use config::{
3    any_err, get_or_create_module, get_or_create_sub_module, materialize_to_lua_value,
4    serialize_options,
5};
6use mlua::{Lua, LuaSerdeExt, Value as LuaValue};
7use serde_json::Value as JValue;
8
9pub fn register(lua: &Lua) -> anyhow::Result<()> {
10    let serde_mod = get_or_create_sub_module(lua, "serde")?;
11    let kumo_mod = get_or_create_module(lua, "kumo")?;
12
13    serde_mod.set("json_load", lua.create_async_function(json_load)?)?;
14    serde_mod.set("json_parse", lua.create_function(json_parse)?)?;
15    serde_mod.set("json_encode", lua.create_function(json_encode)?)?;
16    serde_mod.set(
17        "json_encode_pretty",
18        lua.create_function(json_encode_pretty)?,
19    )?;
20
21    serde_mod.set("toml_load", lua.create_async_function(toml_load)?)?;
22    serde_mod.set("toml_parse", lua.create_function(toml_parse)?)?;
23    serde_mod.set("toml_encode", lua.create_function(toml_encode)?)?;
24    serde_mod.set(
25        "toml_encode_pretty",
26        lua.create_function(toml_encode_pretty)?,
27    )?;
28
29    serde_mod.set("yaml_load", lua.create_async_function(yaml_load)?)?;
30    serde_mod.set("yaml_parse", lua.create_function(yaml_parse)?)?;
31    serde_mod.set("yaml_encode", lua.create_function(yaml_encode)?)?;
32    // Note there is no pretty encoder for yaml, because the default one is pretty already.
33    // See https://github.com/dtolnay/serde-yaml/issues/226
34
35    // Backwards compatibility
36    kumo_mod.set("json_load", lua.create_async_function(json_load)?)?;
37    kumo_mod.set("json_parse", lua.create_function(json_parse)?)?;
38    kumo_mod.set("json_encode", lua.create_function(json_encode)?)?;
39    kumo_mod.set(
40        "json_encode_pretty",
41        lua.create_function(json_encode_pretty)?,
42    )?;
43
44    kumo_mod.set("toml_load", lua.create_async_function(toml_load)?)?;
45    kumo_mod.set("toml_parse", lua.create_function(toml_parse)?)?;
46    kumo_mod.set("toml_encode", lua.create_function(toml_encode)?)?;
47    kumo_mod.set(
48        "toml_encode_pretty",
49        lua.create_function(toml_encode_pretty)?,
50    )?;
51
52    Ok(())
53}
54
55async fn json_load(lua: Lua, file_name: String) -> mlua::Result<LuaValue> {
56    let data = tokio::fs::read(&file_name)
57        .await
58        .with_context(|| format!("reading file {file_name}"))
59        .map_err(any_err)?;
60
61    let stripped = json_comments::StripComments::new(&*data);
62
63    let obj: serde_json::Value = serde_json::from_reader(stripped)
64        .with_context(|| format!("parsing {file_name} as json"))
65        .map_err(any_err)?;
66    lua.to_value_with(&obj, serialize_options())
67}
68
69fn json_parse(lua: &Lua, text: String) -> mlua::Result<LuaValue> {
70    let stripped = json_comments::StripComments::new(text.as_bytes());
71    let obj: serde_json::Value = serde_json::from_reader(stripped)
72        .with_context(|| format!("parsing {text} as json"))
73        .map_err(any_err)?;
74    lua.to_value_with(&obj, serialize_options())
75}
76
77fn json_encode(lua: &Lua, value: LuaValue) -> mlua::Result<String> {
78    let value = materialize_to_lua_value(lua, value)?;
79    serde_json::to_string(&value).map_err(any_err)
80}
81
82fn json_encode_pretty(lua: &Lua, value: LuaValue) -> mlua::Result<String> {
83    let value = materialize_to_lua_value(lua, value)?;
84    serde_json::to_string_pretty(&value).map_err(any_err)
85}
86
87async fn toml_load(lua: Lua, file_name: String) -> mlua::Result<LuaValue> {
88    let data = tokio::fs::read_to_string(&file_name)
89        .await
90        .with_context(|| format!("reading file {file_name}"))
91        .map_err(any_err)?;
92
93    let obj: toml::Value = toml::from_str(&data)
94        .with_context(|| format!("parsing {file_name} as toml"))
95        .map_err(any_err)?;
96    lua.to_value_with(&obj, serialize_options())
97}
98
99fn toml_parse(lua: &Lua, toml: String) -> mlua::Result<LuaValue> {
100    let obj: toml::Value = toml::from_str(&toml)
101        .with_context(|| format!("parsing {toml} as toml"))
102        .map_err(any_err)?;
103    lua.to_value_with(&obj, serialize_options())
104}
105
106fn toml_encode(lua: &Lua, value: LuaValue) -> mlua::Result<String> {
107    let value = materialize_to_lua_value(lua, value)?;
108    toml::to_string(&value).map_err(any_err)
109}
110
111fn toml_encode_pretty(lua: &Lua, value: LuaValue) -> mlua::Result<String> {
112    let value = materialize_to_lua_value(lua, value)?;
113    toml::to_string_pretty(&value).map_err(any_err)
114}
115
116async fn yaml_load(lua: Lua, file_name: String) -> mlua::Result<LuaValue> {
117    let data = tokio::fs::read(&file_name)
118        .await
119        .with_context(|| format!("reading file {file_name}"))
120        .map_err(any_err)?;
121
122    let value: JValue = serde_yaml::from_slice(&data)
123        .with_context(|| format!("parsing {file_name} as yaml"))
124        .map_err(any_err)?;
125    lua.to_value_with(&value, serialize_options())
126}
127
128fn yaml_parse(lua: &Lua, text: String) -> mlua::Result<LuaValue> {
129    let value: JValue = serde_yaml::from_str(&text)
130        .with_context(|| format!("parsing {text} as yaml"))
131        .map_err(any_err)?;
132    lua.to_value_with(&value, serialize_options())
133}
134
135fn yaml_encode(lua: &Lua, value: LuaValue) -> mlua::Result<String> {
136    let value = materialize_to_lua_value(lua, value)?;
137    serde_yaml::to_string(&value).map_err(any_err)
138}