1#![allow(clippy::tabs_in_doc_comments)]
36
37use indexmap::IndexMap;
38use serde::Deserialize;
39use serde_json::{from_str, Result, Value};
40
41#[derive(Deserialize)]
42#[serde(untagged)]
43enum Json {
44 Sequence(Vec<Value>),
45 Map(IndexMap<String, Value>),
46}
47
48pub fn parse(json: &str) -> Result<String> {
70 let mut lua = String::from("{\n");
71
72 match from_str(json)? {
73 Json::Sequence(json) => {
74 for value in json {
75 lua.push_str(&walk(None, &value, 1));
76 }
77 }
78 Json::Map(json) => {
79 for (key, value) in json {
80 lua.push_str(&walk(Some(&validate_string(&key)), &value, 1));
81 }
82 }
83 }
84
85 lua.push('}');
86
87 Ok(lua)
88}
89
90fn walk(key: Option<&str>, value: &Value, depth: usize) -> String {
91 let mut lua = String::new();
92
93 lua.push_str(&get_indent(depth));
94
95 if let Some(key) = key {
96 lua.push_str(&format!("[\"{}\"] = ", validate_string(key)));
97 }
98
99 match value {
100 Value::String(s) => lua.push_str(&format!("\"{}\"", &validate_string(s))),
101 Value::Number(n) => lua.push_str(&n.to_string()),
102 Value::Bool(b) => lua.push_str(&b.to_string()),
103 Value::Null => lua.push_str("nil"),
104 Value::Array(a) => {
105 lua.push_str("{\n");
106
107 for v in a {
108 lua.push_str(&walk(None, v, depth + 1));
109 }
110
111 lua.push_str(&get_indent(depth));
112 lua.push('}');
113 }
114 Value::Object(o) => {
115 lua.push_str("{\n");
116
117 for (k, v) in o {
118 lua.push_str(&walk(Some(k), v, depth + 1));
119 }
120
121 lua.push_str(&get_indent(depth));
122 lua.push('}');
123 }
124 }
125
126 lua.push_str(",\n");
127
128 lua
129}
130
131fn get_indent(depth: usize) -> String {
132 let mut indent = String::new();
133
134 for _ in 0..depth {
135 indent.push('\t');
136 }
137
138 indent
139}
140
141fn validate_string(string: &str) -> String {
142 let mut validated = String::new();
143
144 for char in string.chars() {
145 match char {
146 '\n' => validated.push_str("\\n"),
147 '\t' => validated.push_str("\\t"),
148 '\r' => validated.push_str("\\r"),
149 '\\' => validated.push_str("\\\\"),
150 '"' => validated.push_str("\\\""),
151 _ => validated.push(char),
152 }
153 }
154
155 validated
156}
157
158#[cfg(test)]
159mod test {
160 #[test]
161 fn all_values() {
162 use crate::parse;
163
164 let json = r#"{
165 "string": "str",
166 "int": 420,
167 "float": 4.2,
168 "bool": true,
169 "null": null,
170 "array": [
171 "string",
172 12345,
173 false,
174 {
175 "k": "v"
176 }
177 ],
178 "object": {
179 "key": "value"
180 }
181}"#;
182
183 let lua = r#"{
184 ["string"] = "str",
185 ["int"] = 420,
186 ["float"] = 4.2,
187 ["bool"] = true,
188 ["null"] = nil,
189 ["array"] = {
190 "string",
191 12345,
192 false,
193 {
194 ["k"] = "v",
195 },
196 },
197 ["object"] = {
198 ["key"] = "value",
199 },
200}"#;
201
202 assert_eq!(parse(json).unwrap(), lua);
203 }
204
205 #[test]
206 fn malformed_strings() {
207 use crate::parse;
208
209 let json = r#"{
210 "1": "..\n..",
211 "2": "..\t..",
212 "3": "..\r..",
213 "4": "..\\..",
214 "5": "..\".."
215}"#;
216
217 let lua = r#"{
218 ["1"] = "..\n..",
219 ["2"] = "..\t..",
220 ["3"] = "..\r..",
221 ["4"] = "..\\..",
222 ["5"] = "..\"..",
223}"#;
224
225 assert_eq!(parse(json).unwrap(), lua);
226 }
227
228 #[test]
229 fn root_array() {
230 use crate::parse;
231
232 let json = r#"["a", "b", "c"]"#;
233
234 let lua = r#"{
235 "a",
236 "b",
237 "c",
238}"#;
239
240 assert_eq!(parse(json).unwrap(), lua);
241 }
242}