1use crate::error::{Error, ErrorKind, Result, Span};
4use crate::json::{Config as JsonConfig, Parser as JsonParser};
5use crate::toml::Parser as TomlParser;
6use crate::value::{Object, TomlDatetime, Value};
7use crate::xml::model::{Content as XmlContent, Document as XmlDocument, Element as XmlElement};
8use crate::xml::parser::Parser as XmlParser;
9use crate::yaml::Parser as YamlParser;
10use indexmap::IndexMap;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum Format {
14 Json,
15 Toml,
16 Yaml,
17 Xml,
18}
19
20#[derive(Clone, Debug, Default)]
22pub struct ConvertOptions {
23 pub json: JsonConfig,
24}
25
26pub fn convert(input: &str, from: Format, to: Format) -> Result<String> {
28 convert_with_options(input, from, to, &ConvertOptions::default())
29}
30
31pub fn convert_with_options(
33 input: &str,
34 from: Format,
35 to: Format,
36 options: &ConvertOptions,
37) -> Result<String> {
38 if from == to {
39 return Ok(input.to_string());
40 }
41
42 match (from, to) {
43 (Format::Xml, _) => {
44 let mut parser = XmlParser::new(input.as_bytes());
45 let doc = parser.parse()?;
46 let value = xml_to_value(&doc);
47 serialize_value(&value, to)
48 }
49 (_, Format::Xml) => {
50 let value = parse_value(input, from, options)?;
51 let doc = value_to_xml(&value);
52 Ok(serialize_xml(&doc))
53 }
54 _ => {
55 let value = parse_value(input, from, options)?;
56 serialize_value(&value, to)
57 }
58 }
59}
60
61fn parse_value(input: &str, format: Format, options: &ConvertOptions) -> Result<Value> {
62 match format {
63 Format::Json => {
64 let mut parser = JsonParser::with_config(input.as_bytes(), options.json);
65 parser.parse_value()
66 }
67 Format::Toml => {
68 let mut parser = TomlParser::new(input.as_bytes());
69 parser.parse()
70 }
71 Format::Yaml => {
72 let mut parser = YamlParser::new(input.as_bytes());
73 parser.parse()
74 }
75 Format::Xml => Err(Error::with_message(
76 ErrorKind::InvalidToken,
77 Span::empty(),
78 "xml requires xml parser".to_string(),
79 )),
80 }
81}
82
83fn serialize_value(value: &Value, format: Format) -> Result<String> {
84 match format {
85 Format::Json => Ok(serialize_json(value)),
86 Format::Toml => serialize_toml(value),
87 Format::Yaml => Ok(serialize_yaml(value, 0)),
88 Format::Xml => Err(Error::with_message(
89 ErrorKind::InvalidToken,
90 Span::empty(),
91 "xml requires xml serializer".to_string(),
92 )),
93 }
94}
95
96fn serialize_json(value: &Value) -> String {
97 match value {
98 Value::Null => "null".to_string(),
99 Value::Bool(b) => b.to_string(),
100 Value::Number(n) => {
101 if n.is_finite() {
102 n.to_string()
103 } else {
104 "null".to_string()
105 }
106 }
107 Value::String(s) => format!("\"{}\"", escape_json(s)),
108 Value::Array(arr) => {
109 let items: Vec<String> = arr.iter().map(serialize_json).collect();
110 format!("[{}]", items.join(","))
111 }
112 Value::Object(obj) => {
113 let pairs: Vec<String> = obj
114 .iter()
115 .map(|(k, v)| format!("\"{}\":{}", escape_json(k), serialize_json(v)))
116 .collect();
117 format!("{{{}}}", pairs.join(","))
118 }
119 Value::Datetime(dt) => format!("\"{}\"", format_datetime(dt)),
120 }
121}
122
123fn escape_json(input: &str) -> String {
124 input
125 .chars()
126 .flat_map(|ch| match ch {
127 '\\' => "\\\\".chars().collect::<Vec<_>>(),
128 '"' => "\\\"".chars().collect::<Vec<_>>(),
129 '\n' => "\\n".chars().collect::<Vec<_>>(),
130 '\r' => "\\r".chars().collect::<Vec<_>>(),
131 '\t' => "\\t".chars().collect::<Vec<_>>(),
132 _ => vec![ch],
133 })
134 .collect()
135}
136
137fn serialize_toml(value: &Value) -> Result<String> {
138 match value {
139 Value::Object(obj) => Ok(serialize_toml_object(obj)),
140 _ => Err(Error::with_message(
141 ErrorKind::InvalidToken,
142 Span::empty(),
143 "toml root must be object".to_string(),
144 )),
145 }
146}
147
148fn serialize_toml_object(obj: &Object) -> String {
149 let mut lines = Vec::new();
150 for (key, value) in obj.iter() {
151 lines.push(format!("{key} = {}", serialize_toml_value(value)));
152 }
153 lines.join("\n")
154}
155
156fn serialize_toml_value(value: &Value) -> String {
157 match value {
158 Value::Null => "null".to_string(),
159 Value::Bool(b) => b.to_string(),
160 Value::Number(n) => {
161 if n.is_finite() {
162 n.to_string()
163 } else {
164 "nan".to_string()
165 }
166 }
167 Value::String(s) => format!("\"{}\"", escape_toml(s)),
168 Value::Array(arr) => {
169 let items: Vec<String> = arr.iter().map(serialize_toml_value).collect();
170 format!("[{}]", items.join(", "))
171 }
172 Value::Object(obj) => {
173 let entries: Vec<String> = obj
174 .iter()
175 .map(|(k, v)| format!("{k} = {}", serialize_toml_value(v)))
176 .collect();
177 format!("{{{}}}", entries.join(", "))
178 }
179 Value::Datetime(dt) => format_datetime(dt),
180 }
181}
182
183fn escape_toml(input: &str) -> String {
184 input
185 .chars()
186 .flat_map(|ch| match ch {
187 '\\' => "\\\\".chars().collect::<Vec<_>>(),
188 '"' => "\\\"".chars().collect::<Vec<_>>(),
189 '\n' => "\\n".chars().collect::<Vec<_>>(),
190 '\r' => "\\r".chars().collect::<Vec<_>>(),
191 '\t' => "\\t".chars().collect::<Vec<_>>(),
192 _ => vec![ch],
193 })
194 .collect()
195}
196
197fn serialize_yaml(value: &Value, indent: usize) -> String {
198 let pad = " ".repeat(indent);
199 match value {
200 Value::Null => format!("{pad}null"),
201 Value::Bool(b) => format!("{pad}{b}"),
202 Value::Number(n) => format!("{pad}{n}"),
203 Value::String(s) => format!("{pad}\"{}\"", escape_yaml(s)),
204 Value::Datetime(dt) => format!("{pad}{}", format_datetime(dt)),
205 Value::Array(arr) => arr
206 .iter()
207 .map(|v| {
208 let item = serialize_yaml(v, indent + 2);
209 format!("{pad}- {}", item.trim_start())
210 })
211 .collect::<Vec<_>>()
212 .join("\n"),
213 Value::Object(obj) => obj
214 .iter()
215 .map(|(k, v)| {
216 let value = serialize_yaml(v, indent + 2);
217 if matches!(v, Value::Array(_) | Value::Object(_)) {
218 format!("{pad}{k}:\n{value}")
219 } else {
220 format!("{pad}{k}: {}", value.trim_start())
221 }
222 })
223 .collect::<Vec<_>>()
224 .join("\n"),
225 }
226}
227
228fn escape_yaml(input: &str) -> String {
229 input
230 .chars()
231 .flat_map(|ch| match ch {
232 '\\' => "\\\\".chars().collect::<Vec<_>>(),
233 '"' => "\\\"".chars().collect::<Vec<_>>(),
234 '\n' => "\\n".chars().collect::<Vec<_>>(),
235 '\r' => "\\r".chars().collect::<Vec<_>>(),
236 '\t' => "\\t".chars().collect::<Vec<_>>(),
237 _ => vec![ch],
238 })
239 .collect()
240}
241
242fn format_datetime(dt: &TomlDatetime) -> String {
243 use time::format_description::well_known::Rfc3339;
244 use time::macros::format_description;
245 match dt {
246 TomlDatetime::OffsetDateTime(value) => value
247 .format(&Rfc3339)
248 .unwrap_or_else(|_| "1979-05-27T07:32:00Z".to_string()),
249 TomlDatetime::LocalDateTime(value) => value
250 .format(&format_description!(
251 "[year]-[month]-[day]T[hour]:[minute]:[second]"
252 ))
253 .unwrap_or_else(|_| "1979-05-27T07:32:00".to_string()),
254 TomlDatetime::LocalDate(value) => value
255 .format(&format_description!("[year]-[month]-[day]"))
256 .unwrap_or_else(|_| "1979-05-27".to_string()),
257 TomlDatetime::LocalTime(value) => value
258 .format(&format_description!("[hour]:[minute]:[second]"))
259 .unwrap_or_else(|_| "07:32:00".to_string()),
260 }
261}
262
263fn xml_to_value(doc: &XmlDocument) -> Value {
264 let mut root = Object::new();
265 root.insert(&doc.root.name, element_to_value(&doc.root));
266 Value::Object(root)
267}
268
269fn element_to_value(element: &XmlElement) -> Value {
270 let mut obj = Object::new();
271
272 if !element.attributes.is_empty() {
273 let mut attrs = Object::new();
274 for (key, value) in element.attributes.iter() {
275 attrs.insert(key, value.clone());
276 }
277 obj.insert("@attributes", Value::Object(attrs));
278 }
279
280 let mut text = String::new();
281 for child in &element.children {
282 if let XmlContent::Text(value) = child {
283 text.push_str(value);
284 }
285 }
286 if !text.trim().is_empty() {
287 obj.insert("#text", Value::String(text));
288 }
289
290 for child in &element.children {
291 if let XmlContent::Element(child) = child {
292 let value = element_to_value(child);
293 match obj.get(&child.name) {
294 Some(Value::Array(arr)) => {
295 let mut items = arr.clone();
296 items.push(value);
297 obj.insert(&child.name, Value::Array(items));
298 }
299 Some(existing) => {
300 let items = vec![existing.clone(), value];
301 obj.insert(&child.name, Value::Array(items.into()));
302 }
303 None => {
304 obj.insert(&child.name, value);
305 }
306 }
307 }
308 }
309
310 if obj.is_empty() {
311 Value::Object(Object::new())
312 } else {
313 Value::Object(obj)
314 }
315}
316
317fn value_to_xml(value: &Value) -> XmlDocument {
318 let root = XmlElement {
319 name: "root".to_string(),
320 attributes: IndexMap::new(),
321 children: value_to_children(value),
322 };
323 XmlDocument { root }
324}
325
326fn value_to_children(value: &Value) -> Vec<XmlContent> {
327 match value {
328 Value::Object(obj) => obj
329 .iter()
330 .flat_map(|(key, value)| value_to_elements(key, value))
331 .map(XmlContent::Element)
332 .collect(),
333 Value::Array(arr) => arr.iter().flat_map(value_to_children).collect(),
334 Value::String(text) => vec![XmlContent::Text(text.clone())],
335 Value::Number(n) => vec![XmlContent::Text(n.to_string())],
336 Value::Bool(b) => vec![XmlContent::Text(b.to_string())],
337 Value::Null => Vec::new(),
338 Value::Datetime(dt) => vec![XmlContent::Text(format_datetime(dt))],
339 }
340}
341
342fn value_to_elements(name: &str, value: &Value) -> Vec<XmlElement> {
343 match value {
344 Value::Array(arr) => arr
345 .iter()
346 .flat_map(|value| value_to_elements(name, value))
347 .collect(),
348 Value::Object(obj) => {
349 let mut attributes = IndexMap::new();
350 let mut children = Vec::new();
351
352 if let Some(Value::Object(attrs)) = obj.get("@attributes") {
353 for (key, value) in attrs.iter() {
354 if let Value::String(text) = value {
355 attributes.insert(key.clone(), text.clone());
356 } else {
357 attributes.insert(key.clone(), serialize_json(value));
358 }
359 }
360 }
361
362 if let Some(Value::String(text)) = obj.get("#text") {
363 children.push(XmlContent::Text(text.clone()));
364 }
365
366 for (key, value) in obj.iter() {
367 if key == "@attributes" || key == "#text" {
368 continue;
369 }
370 for element in value_to_elements(key, value) {
371 children.push(XmlContent::Element(element));
372 }
373 }
374
375 vec![XmlElement {
376 name: name.to_string(),
377 attributes,
378 children,
379 }]
380 }
381 _ => vec![XmlElement {
382 name: name.to_string(),
383 attributes: IndexMap::new(),
384 children: value_to_children(value),
385 }],
386 }
387}
388
389fn serialize_xml(doc: &XmlDocument) -> String {
390 let mut output = String::new();
391 serialize_element(&doc.root, &mut output);
392 output
393}
394
395fn serialize_element(element: &XmlElement, output: &mut String) {
396 output.push('<');
397 output.push_str(&element.name);
398
399 for (key, value) in element.attributes.iter() {
400 output.push(' ');
401 output.push_str(key);
402 output.push_str("=\"");
403 output.push_str(&escape_xml(value));
404 output.push('"');
405 }
406
407 if element.children.is_empty() {
408 output.push_str("/>");
409 return;
410 }
411
412 output.push('>');
413 for child in &element.children {
414 match child {
415 XmlContent::Element(child) => serialize_element(child, output),
416 XmlContent::Text(text) => output.push_str(&escape_xml(text)),
417 }
418 }
419 output.push_str("</");
420 output.push_str(&element.name);
421 output.push('>');
422}
423
424fn escape_xml(input: &str) -> String {
425 input
426 .replace('&', "&")
427 .replace('<', "<")
428 .replace('>', ">")
429 .replace('"', """)
430 .replace('\'', "'")
431}