Skip to main content

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}