jsonette/converter.rs
1/*
2 * Copyright (c) 2026 DevEtte.
3 *
4 * This project is dual-licensed under both the MIT License and the
5 * Apache License, Version 2.0 (the "License"). You may not use this
6 * file except in compliance with one of these licenses.
7 *
8 * You may obtain a copy of the Licenses at:
9 * - MIT: https://opensource.org
10 * - Apache 2.0: http://apache.org
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the licenses.
16 */
17
18//! Data conversion module for JSON ↔ YAML, TOML, XML.
19//!
20//! Uses `serde_json::Value` as the intermediate data model to translate between
21//! various structured data formats. When converting TO JSON, the output is formatted
22//! using the core engine's `format` implementation to respect user settings.
23
24use std::str::FromStr;
25
26/// The supported data formats for conversion.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum DataFormat {
29 /// JSON format
30 Json,
31 /// YAML format
32 Yaml,
33 /// TOML format
34 Toml,
35 /// XML format
36 Xml,
37}
38
39impl FromStr for DataFormat {
40 type Err = String;
41
42 fn from_str(s: &str) -> Result<Self, Self::Err> {
43 match s.to_lowercase().as_str() {
44 "json" => Ok(DataFormat::Json),
45 "yaml" | "yml" => Ok(DataFormat::Yaml),
46 "toml" => Ok(DataFormat::Toml),
47 "xml" => Ok(DataFormat::Xml),
48 _ => Err(format!("Unsupported format: {}", s)),
49 }
50 }
51}
52
53/// Converts a string payload from one data format to another.
54///
55/// # Arguments
56///
57/// * `input` - The input string payload.
58/// * `from` - The source format.
59/// * `to` - The target format.
60///
61/// # Returns
62///
63/// The converted string or an error message.
64pub fn convert(input: &str, from: DataFormat, to: DataFormat) -> Result<String, String> {
65 // 1. Parse input to intermediate serde_json::Value
66 let value: serde_json::Value = match from {
67 DataFormat::Json => {
68 serde_json::from_str(input).map_err(|e| format!("JSON Parse Error: {}", e))?
69 }
70 DataFormat::Yaml => {
71 serde_yml::from_str(input).map_err(|e| format!("YAML Parse Error: {}", e))?
72 }
73 DataFormat::Toml => {
74 toml::from_str(input).map_err(|e| format!("TOML Parse Error: {}", e))?
75 }
76 DataFormat::Xml => {
77 quick_xml::de::from_str(input).map_err(|e| format!("XML Parse Error: {}", e))?
78 }
79 };
80
81 // 2. Serialize value to target format
82 match to {
83 DataFormat::Json => {
84 let raw_json = serde_json::to_string(&value)
85 .map_err(|e| format!("JSON Serialize Error: {}", e))?;
86 // Re-parse and format using our engine to respect configuration rules
87 match crate::parse(&raw_json) {
88 Ok(node) => Ok(crate::format(&node)),
89 Err(_) => Ok(raw_json), // fallback to raw
90 }
91 }
92 DataFormat::Yaml => {
93 serde_yml::to_string(&value).map_err(|e| format!("YAML Serialize Error: {}", e))
94 }
95 DataFormat::Toml => {
96 let toml_value = if value.is_array() {
97 let mut map = serde_json::Map::new();
98 map.insert("data".to_string(), value.clone());
99 serde_json::Value::Object(map)
100 } else {
101 value.clone()
102 };
103 toml::to_string_pretty(&toml_value).map_err(|e| format!("TOML Serialize Error: {}", e))
104 }
105 DataFormat::Xml => {
106 // Wrap the value in a root element so quick-xml can serialize it
107 #[derive(serde::Serialize)]
108 struct XmlRoot<'a> {
109 #[serde(rename = "$value")]
110 value: &'a serde_json::Value,
111 }
112 quick_xml::se::to_string_with_root("root", &value)
113 .or_else(|_| quick_xml::se::to_string(&XmlRoot { value: &value }))
114 .map_err(|e| format!("XML Serialize Error: {}", e))
115 }
116 }
117}