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 {value}"
26 )))
27 }
28 }
29}
30
31pub fn deserialize_vec_from_lua_array_or_string<'de, D, T>(
36 deserializer: D,
37) -> std::result::Result<Vec<T>, D::Error>
38where
39 D: Deserializer<'de>,
40 T: From<String>,
41{
42 let values = serde_json::Value::deserialize(deserializer)?;
43 if values.is_string() {
44 let value = T::from(values.as_str().unwrap().into());
45 Ok(vec![value])
46 } else {
47 mlua_json_value_to_vec(values).map_err(de::Error::custom)
48 }
49}
50
51#[derive(Error, Debug)]
52#[error("expected list of strings")]
53pub struct ExpectedListOfStrings;
54
55#[derive(Error, Debug)]
56#[error("expected table with strings as keys")]
57pub struct ExpectedTableOfStrings;
58
59pub fn mlua_json_value_to_map<T>(
62 values: serde_json::Value,
63) -> Result<HashMap<String, T>, ExpectedTableOfStrings>
64where
65 T: From<String>,
66{
67 values
68 .as_object()
69 .ok_or(ExpectedTableOfStrings)?
70 .clone()
71 .into_iter()
72 .map(|(key, value)| {
73 let value: String = value
74 .as_str()
75 .map(|s| s.into())
76 .ok_or(ExpectedTableOfStrings)?;
77
78 Ok((key, value.into()))
79 })
80 .try_collect()
81}
82
83pub fn mlua_json_value_to_vec<T>(values: serde_json::Value) -> Result<Vec<T>, ExpectedListOfStrings>
86where
87 T: From<String>,
88{
89 if let Some(values_as_obj) = values.as_object() {
92 if values_as_obj.is_empty() {
93 return Ok(Vec::default());
94 }
95 }
96 values
97 .as_array()
98 .ok_or(ExpectedListOfStrings)?
99 .iter()
100 .map(|val| {
101 let str: String = val
102 .as_str()
103 .map(|s| s.into())
104 .ok_or(ExpectedListOfStrings)?;
105 Ok(str.into())
106 })
107 .try_collect()
108}
109
110pub(crate) enum DisplayLuaValue {
111 Boolean(bool),
115 String(String),
116 List(Vec<Self>),
117 Table(Vec<DisplayLuaKV>),
118}
119
120pub(crate) struct DisplayLuaKV {
121 pub(crate) key: String,
122 pub(crate) value: DisplayLuaValue,
123}
124
125impl Display for DisplayLuaValue {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 use std::fmt::Write;
128 let mut buf = String::new();
129 match self {
130 DisplayLuaValue::Boolean(b) => write!(buf, "{b}")?,
133 DisplayLuaValue::String(s) => write!(buf, "\"{s}\"")?,
134 DisplayLuaValue::List(l) => {
135 writeln!(buf, "{{")?;
136 for item in l {
137 writeln!(buf, "{item},")?;
138 }
139 write!(buf, "}}")?;
140 }
141 DisplayLuaValue::Table(t) => {
142 writeln!(buf, "{{")?;
143
144 for item in t {
145 writeln!(buf, "{item},")?;
146 }
147
148 write!(buf, "}}")?;
149 }
150 };
151 let output = match stylua_lib::format_code(
152 &buf,
153 stylua_lib::Config::default(),
154 None,
155 stylua_lib::OutputVerification::Full,
156 ) {
157 Ok(formatted_code) => formatted_code,
158 Err(_) => buf,
159 };
160 write!(f, "{output}")
161 }
162}
163
164impl Display for DisplayLuaKV {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 if !self
167 .key
168 .chars()
169 .all(|c| c == '_' || c.is_ascii_alphanumeric())
170 {
171 write!(f, "['{}'] = {}", self.key, self.value)
172 } else {
173 write!(f, "{} = {}", self.key, self.value)
174 }
175 }
176}
177
178pub(crate) trait DisplayAsLuaKV {
180 fn display_lua(&self) -> DisplayLuaKV;
181}
182
183pub(crate) trait DisplayAsLuaValue {
184 fn display_lua_value(&self) -> DisplayLuaValue;
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn display_lua_value() {
193 let value = DisplayLuaValue::String("hello".to_string());
194 assert_eq!(format!("{value}"), "\"hello\"");
195
196 let value = DisplayLuaValue::Boolean(true);
197 assert_eq!(format!("{value}"), "true");
198
199 let value = DisplayLuaValue::List(vec![
200 DisplayLuaValue::String("hello".to_string()),
201 DisplayLuaValue::Boolean(true),
202 ]);
203 assert_eq!(format!("{value}"), "{\n\"hello\",\ntrue,\n}");
204
205 let value = DisplayLuaValue::Table(vec![
206 DisplayLuaKV {
207 key: "key".to_string(),
208 value: DisplayLuaValue::String("value".to_string()),
209 },
210 DisplayLuaKV {
211 key: "key2".to_string(),
212 value: DisplayLuaValue::Boolean(true),
213 },
214 DisplayLuaKV {
215 key: "key3.key4".to_string(),
216 value: DisplayLuaValue::Boolean(true),
217 },
218 ]);
219 assert_eq!(
220 format!("{value}"),
221 "{\nkey = \"value\",\nkey2 = true,\n['key3.key4'] = true,\n}"
222 );
223 }
224}