lune_std_serde/
encode_decode.rs

1use mlua::prelude::*;
2
3use serde_json::Value as JsonValue;
4use serde_yaml2::wrapper::YamlNodeWrapper as YamlValue;
5use toml::Value as TomlValue;
6
7// NOTE: These are options for going from other format -> lua ("serializing" lua values)
8const LUA_SERIALIZE_OPTIONS: LuaSerializeOptions = LuaSerializeOptions::new()
9    .set_array_metatable(false)
10    .serialize_none_to_null(false)
11    .serialize_unit_to_null(false);
12
13// NOTE: These are options for going from lua -> other format ("deserializing" lua values)
14const LUA_DESERIALIZE_OPTIONS: LuaDeserializeOptions = LuaDeserializeOptions::new()
15    .sort_keys(true)
16    .deny_recursive_tables(false)
17    .deny_unsupported_types(true);
18
19/**
20    An encoding and decoding format supported by Lune.
21
22    Encode / decode in this case is synonymous with serialize / deserialize.
23*/
24#[derive(Debug, Clone, Copy)]
25pub enum EncodeDecodeFormat {
26    Json,
27    JsonC,
28    Yaml,
29    Toml,
30}
31
32impl FromLua for EncodeDecodeFormat {
33    fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
34        if let LuaValue::String(s) = &value {
35            match s.to_string_lossy().to_ascii_lowercase().trim() {
36                "json" => Ok(Self::Json),
37                "jsonc" => Ok(Self::JsonC),
38                "yaml" => Ok(Self::Yaml),
39                "toml" => Ok(Self::Toml),
40                kind => Err(LuaError::FromLuaConversionError {
41                    from: value.type_name(),
42                    to: "EncodeDecodeFormat".to_string(),
43                    message: Some(format!(
44                        "Invalid format '{kind}', valid formats are:  json, yaml, toml"
45                    )),
46                }),
47            }
48        } else {
49            Err(LuaError::FromLuaConversionError {
50                from: value.type_name(),
51                to: "EncodeDecodeFormat".to_string(),
52                message: None,
53            })
54        }
55    }
56}
57
58/**
59    Configuration for encoding and decoding values.
60
61    Encoding / decoding in this case is synonymous with serialize / deserialize.
62*/
63#[derive(Debug, Clone, Copy)]
64pub struct EncodeDecodeConfig {
65    pub format: EncodeDecodeFormat,
66    pub pretty: bool,
67}
68
69impl From<EncodeDecodeFormat> for EncodeDecodeConfig {
70    fn from(format: EncodeDecodeFormat) -> Self {
71        Self {
72            format,
73            pretty: false,
74        }
75    }
76}
77
78impl From<(EncodeDecodeFormat, bool)> for EncodeDecodeConfig {
79    fn from(value: (EncodeDecodeFormat, bool)) -> Self {
80        Self {
81            format: value.0,
82            pretty: value.1,
83        }
84    }
85}
86
87/**
88    Encodes / serializes the given value into a string, using the specified configuration.
89
90    # Errors
91
92    Errors when the encoding fails.
93*/
94pub fn encode(value: LuaValue, lua: &Lua, config: EncodeDecodeConfig) -> LuaResult<LuaString> {
95    let bytes = match config.format {
96        EncodeDecodeFormat::Json | EncodeDecodeFormat::JsonC => {
97            let serialized: JsonValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?;
98            if config.pretty {
99                serde_json::to_vec_pretty(&serialized).into_lua_err()?
100            } else {
101                serde_json::to_vec(&serialized).into_lua_err()?
102            }
103        }
104        EncodeDecodeFormat::Yaml => {
105            let serialized: YamlValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?;
106            serde_yaml2::to_string(serialized)
107                .into_lua_err()?
108                .into_bytes()
109        }
110        EncodeDecodeFormat::Toml => {
111            let serialized: TomlValue = lua.from_value_with(value, LUA_DESERIALIZE_OPTIONS)?;
112            let s = if config.pretty {
113                toml::to_string_pretty(&serialized).into_lua_err()?
114            } else {
115                toml::to_string(&serialized).into_lua_err()?
116            };
117            s.as_bytes().to_vec()
118        }
119    };
120    lua.create_string(bytes)
121}
122
123/**
124    Decodes / deserializes the given string into a value, using the specified configuration.
125
126    # Errors
127
128    Errors when the decoding fails.
129*/
130pub fn decode(
131    bytes: impl AsRef<[u8]>,
132    lua: &Lua,
133    config: EncodeDecodeConfig,
134) -> LuaResult<LuaValue> {
135    let bytes = bytes.as_ref();
136    match config.format {
137        EncodeDecodeFormat::Json => {
138            let value: JsonValue = serde_json::from_slice(bytes).into_lua_err()?;
139            lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS)
140        }
141        EncodeDecodeFormat::JsonC => {
142            let string: String = String::from_utf8(bytes.to_vec()).into_lua_err()?;
143            let value: JsonValue =
144                jsonc_parser::parse_to_serde_value(&string, &jsonc_parser::ParseOptions::default())
145                    .map(|v| v.unwrap_or(JsonValue::Null))
146                    .into_lua_err()?;
147            lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS)
148        }
149        EncodeDecodeFormat::Yaml => {
150            let string: String = String::from_utf8(bytes.to_vec()).into_lua_err()?;
151            let value: YamlValue = serde_yaml2::from_str(&string).into_lua_err()?;
152            lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS)
153        }
154        EncodeDecodeFormat::Toml => {
155            if let Ok(s) = String::from_utf8(bytes.to_vec()) {
156                let value: TomlValue = toml::from_str(&s).into_lua_err()?;
157                lua.to_value_with(&value, LUA_SERIALIZE_OPTIONS)
158            } else {
159                Err(LuaError::RuntimeError(
160                    "TOML must be valid utf-8".to_string(),
161                ))
162            }
163        }
164    }
165}