lux_lib/lua_rockspec/
serde_util.rs1use std::{collections::HashMap, fmt::Display};
2
3use itertools::Itertools;
4use serde::{de, Deserialize, Deserializer};
5use thiserror::Error;
6
7#[derive(Hash, Debug, Eq, PartialEq, Clone)]
8pub enum LuaTableKey {
9 IntKey(u64),
10 StringKey(String),
11}
12
13impl<'de> Deserialize<'de> for LuaTableKey {
14 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
15 where
16 D: serde::Deserializer<'de>,
17 {
18 let value = serde_json::Value::deserialize(deserializer)?;
19 if value.is_u64() {
20 Ok(LuaTableKey::IntKey(value.as_u64().unwrap()))
21 } else if value.is_string() {
22 Ok(LuaTableKey::StringKey(value.as_str().unwrap().into()))
23 } else {
24 Err(de::Error::custom(format!(
25 "Could not parse Lua table key. Expected an integer or string, but got {}",
26 value
27 )))
28 }
29 }
30}
31
32pub fn deserialize_vec_from_lua_array_or_string<'de, D, T>(
37 deserializer: D,
38) -> std::result::Result<Vec<T>, D::Error>
39where
40 D: Deserializer<'de>,
41 T: From<String>,
42{
43 let values = serde_json::Value::deserialize(deserializer)?;
44 if values.is_string() {
45 let value = T::from(values.as_str().unwrap().into());
46 Ok(vec![value])
47 } else {
48 mlua_json_value_to_vec(values).map_err(de::Error::custom)
49 }
50}
51
52#[derive(Error, Debug)]
53#[error("expected list of strings")]
54pub struct ExpectedListOfStrings;
55
56#[derive(Error, Debug)]
57#[error("expected table with strings as keys")]
58pub struct ExpectedTableOfStrings;
59
60pub fn mlua_json_value_to_map<T>(
63 values: serde_json::Value,
64) -> Result<HashMap<String, T>, ExpectedTableOfStrings>
65where
66 T: From<String>,
67{
68 values
69 .as_object()
70 .ok_or(ExpectedTableOfStrings)?
71 .clone()
72 .into_iter()
73 .map(|(key, value)| {
74 let value: String = value
75 .as_str()
76 .map(|s| s.into())
77 .ok_or(ExpectedTableOfStrings)?;
78
79 Ok((key, value.into()))
80 })
81 .try_collect()
82}
83
84pub fn mlua_json_value_to_vec<T>(values: serde_json::Value) -> Result<Vec<T>, ExpectedListOfStrings>
87where
88 T: From<String>,
89{
90 if let Some(values_as_obj) = values.as_object() {
93 if values_as_obj.is_empty() {
94 return Ok(Vec::default());
95 }
96 }
97 values
98 .as_array()
99 .ok_or(ExpectedListOfStrings)?
100 .iter()
101 .map(|val| {
102 let str: String = val
103 .as_str()
104 .map(|s| s.into())
105 .ok_or(ExpectedListOfStrings)?;
106 Ok(str.into())
107 })
108 .try_collect()
109}
110
111pub(crate) enum DisplayLuaValue {
112 Boolean(bool),
116 String(String),
117 List(Vec<Self>),
118 Table(Vec<DisplayLuaKV>),
119}
120
121pub(crate) struct DisplayLuaKV {
122 pub(crate) key: String,
123 pub(crate) value: DisplayLuaValue,
124}
125
126impl Display for DisplayLuaValue {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 match self {
129 DisplayLuaValue::Boolean(b) => write!(f, "{}", b),
132 DisplayLuaValue::String(s) => write!(f, "\"{}\"", s),
133 DisplayLuaValue::List(l) => {
134 writeln!(f, "{{")?;
135
136 for item in l {
137 writeln!(f, "{},", item)?;
138 }
139
140 write!(f, "}}")?;
141
142 Ok(())
143 }
144 DisplayLuaValue::Table(t) => {
145 writeln!(f, "{{")?;
146
147 for item in t {
148 writeln!(f, "{},", item)?;
149 }
150
151 write!(f, "}}")?;
152
153 Ok(())
154 }
155 }
156 }
157}
158
159impl Display for DisplayLuaKV {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 if !self
162 .key
163 .chars()
164 .all(|c| c == '_' || c.is_ascii_alphanumeric())
165 {
166 write!(f, "['{}'] = {}", self.key, self.value)
167 } else {
168 write!(f, "{} = {}", self.key, self.value)
169 }
170 }
171}
172
173pub(crate) trait DisplayAsLuaKV {
175 fn display_lua(&self) -> DisplayLuaKV;
176}
177
178pub(crate) trait DisplayAsLuaValue {
179 fn display_lua_value(&self) -> DisplayLuaValue;
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn display_lua_value() {
188 let value = DisplayLuaValue::String("hello".to_string());
189 assert_eq!(format!("{}", value), "\"hello\"");
190
191 let value = DisplayLuaValue::Boolean(true);
192 assert_eq!(format!("{}", value), "true");
193
194 let value = DisplayLuaValue::List(vec![
195 DisplayLuaValue::String("hello".to_string()),
196 DisplayLuaValue::Boolean(true),
197 ]);
198 assert_eq!(format!("{}", value), "{\n\"hello\",\ntrue,\n}");
199
200 let value = DisplayLuaValue::Table(vec![
201 DisplayLuaKV {
202 key: "key".to_string(),
203 value: DisplayLuaValue::String("value".to_string()),
204 },
205 DisplayLuaKV {
206 key: "key2".to_string(),
207 value: DisplayLuaValue::Boolean(true),
208 },
209 DisplayLuaKV {
210 key: "key3.key4".to_string(),
211 value: DisplayLuaValue::Boolean(true),
212 },
213 ]);
214 assert_eq!(
215 format!("{}", value),
216 "{\nkey = \"value\",\nkey2 = true,\n['key3.key4'] = true,\n}"
217 );
218 }
219}