Skip to main content

xml_disassembler/parsers/
parse_to_xml_object.rs

1//! Parse file to XmlElement - supports XML, YAML, JSON, TOML, INI.
2
3use crate::parsers::{
4    extract_xml_declaration_from_raw, extract_xmlns_from_raw, parse_xml_from_str,
5};
6use crate::types::XmlElement;
7use serde_json::Value;
8use tokio::fs;
9
10pub async fn parse_to_xml_object(file_path: &str) -> Option<XmlElement> {
11    if file_path.to_lowercase().ends_with(".xml") {
12        let content = fs::read_to_string(file_path).await.ok()?;
13        let mut parsed = parse_xml_from_str(&content, file_path)?;
14        if let Some(obj) = parsed.as_object_mut() {
15            // quickxml_to_serde drops the declaration - extract from raw and add at top level
16            if let Some(decl) = extract_xml_declaration_from_raw(&content) {
17                obj.insert("?xml".to_string(), decl);
18            }
19            // quickxml_to_serde drops xmlns - extract from raw and add to root element
20            if let Some(xmlns) = extract_xmlns_from_raw(&content) {
21                let root_key = obj.keys().find(|k| *k != "?xml")?.clone();
22                if let Some(root_val) = obj.get_mut(&root_key) {
23                    if let Some(root_obj) = root_val.as_object_mut() {
24                        if !root_obj.contains_key("@xmlns") {
25                            root_obj.insert("@xmlns".to_string(), Value::String(xmlns));
26                        }
27                    }
28                }
29            }
30        }
31        return Some(parsed);
32    }
33
34    let content = fs::read_to_string(file_path).await.ok()?;
35
36    if file_path.to_lowercase().ends_with(".yaml") || file_path.to_lowercase().ends_with(".yml") {
37        return serde_yaml::from_str(&content).ok();
38    }
39
40    if file_path.to_lowercase().ends_with(".json5") {
41        return json5::from_str(&content).ok();
42    }
43
44    if file_path.to_lowercase().ends_with(".json") {
45        return serde_json::from_str(&content).ok();
46    }
47
48    if file_path.to_lowercase().ends_with(".toml") {
49        let toml_val: toml::Value = toml::from_str(&content).ok()?;
50        return serde_json::from_str(&serde_json::to_string(&toml_val).ok()?).ok();
51    }
52
53    if file_path.to_lowercase().ends_with(".ini") {
54        return parse_ini_to_value(&content);
55    }
56
57    None
58}
59
60fn parse_ini_to_value(content: &str) -> Option<XmlElement> {
61    let mut result = serde_json::Map::new();
62    let mut current_section = String::new();
63    let mut section_map = serde_json::Map::new();
64
65    for line in content.lines() {
66        let line = line.trim();
67        if line.is_empty() || line.starts_with(';') || line.starts_with('#') {
68            continue;
69        }
70        if line.starts_with('[') && line.ends_with(']') {
71            if !current_section.is_empty() {
72                result.insert(current_section.clone(), Value::Object(section_map.clone()));
73            }
74            current_section = line[1..line.len() - 1].to_string();
75            section_map = serde_json::Map::new();
76        } else if let Some((key, val)) = line.split_once('=') {
77            let key = key.trim().to_string();
78            let val = val.trim().to_string();
79            section_map.insert(key, Value::String(val));
80        }
81    }
82    if !current_section.is_empty() {
83        result.insert(current_section, Value::Object(section_map));
84    }
85    Some(Value::Object(result))
86}