1use std::io::Read;
2
3use csv::ReaderBuilder;
4use serde_json::{Map, Value};
5use xmltree::{Element, XMLNode};
6
7use crate::error::ToonifyError;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum SourceFormat {
11 Json,
12 Yaml,
13 Xml,
14 Csv,
15}
16
17pub fn load_from_reader<R: Read>(
18 mut reader: R,
19 format: SourceFormat,
20) -> Result<Value, ToonifyError> {
21 let mut buf = String::new();
22 reader.read_to_string(&mut buf)?;
23 load_from_str(&buf, format)
24}
25
26pub fn load_from_str(input: &str, format: SourceFormat) -> Result<Value, ToonifyError> {
27 match format {
28 SourceFormat::Json => serde_json::from_str(input)
29 .map_err(|err| ToonifyError::parse_err(SourceFormat::Json, err)),
30 SourceFormat::Yaml => serde_yaml::from_str(input)
31 .map_err(|err| ToonifyError::parse_err(SourceFormat::Yaml, err)),
32 SourceFormat::Xml => parse_xml(input),
33 SourceFormat::Csv => parse_csv(input),
34 }
35}
36
37fn parse_csv(input: &str) -> Result<Value, ToonifyError> {
38 let mut reader = ReaderBuilder::new()
39 .has_headers(true)
40 .trim(csv::Trim::Fields)
41 .from_reader(input.as_bytes());
42
43 let headers = reader
44 .headers()
45 .map_err(|err| ToonifyError::parse_err(SourceFormat::Csv, err))?
46 .clone();
47
48 let mut rows = Vec::new();
49 for record in reader.records() {
50 let record = record.map_err(|err| ToonifyError::parse_err(SourceFormat::Csv, err))?;
51 let mut row = Map::with_capacity(headers.len());
52 for (idx, header) in headers.iter().enumerate() {
53 let cell = record.get(idx).unwrap_or_default();
54 row.insert(header.to_string(), parse_csv_cell(cell));
55 }
56 rows.push(Value::Object(row));
57 }
58
59 Ok(Value::Array(rows))
60}
61
62fn parse_csv_cell(cell: &str) -> Value {
63 if cell.is_empty() {
64 return Value::String(String::new());
65 }
66
67 if let Ok(Value::Bool(boolean)) = serde_json::from_str(cell) {
68 return Value::Bool(boolean);
69 }
70
71 if let Ok(Value::Number(number)) = serde_json::from_str(cell) {
72 return Value::Number(number);
73 }
74
75 if let Ok(Value::Null) = serde_json::from_str(cell) {
76 return Value::Null;
77 }
78
79 Value::String(cell.to_string())
80}
81
82fn parse_xml(input: &str) -> Result<Value, ToonifyError> {
83 let root = Element::parse(input.as_bytes())
84 .map_err(|err| ToonifyError::parse_err(SourceFormat::Xml, err))?;
85
86 let root_value = Value::Object({
87 let mut map = Map::new();
88 map.insert(root.name.clone(), element_to_value(&root));
89 map
90 });
91
92 Ok(root_value)
93}
94
95fn element_to_value(element: &Element) -> Value {
96 let mut object = Map::new();
97
98 for (attr, value) in &element.attributes {
99 object.insert(format!("@{}", attr), Value::String(value.clone()));
100 }
101
102 let mut child_groups: indexmap::IndexMap<String, Vec<Value>> = indexmap::IndexMap::new();
103 let mut text_content = Vec::new();
104
105 for child in &element.children {
106 match child {
107 XMLNode::Element(child_el) => {
108 child_groups
109 .entry(child_el.name.clone())
110 .or_default()
111 .push(element_to_value(child_el));
112 }
113 XMLNode::Text(text) | XMLNode::CData(text) => {
114 let trimmed = text.trim();
115 if !trimmed.is_empty() {
116 text_content.push(trimmed.to_string());
117 }
118 }
119 _ => {}
120 }
121 }
122
123 let combined_text = text_content.join(" ");
124 if child_groups.is_empty() && object.is_empty() {
125 if combined_text.is_empty() {
126 Value::Null
127 } else {
128 Value::String(combined_text)
129 }
130 } else {
131 if !combined_text.is_empty() {
132 object.insert("_text".into(), Value::String(combined_text));
133 }
134
135 for (name, values) in child_groups {
136 if values.len() == 1 {
137 object.insert(name, values.into_iter().next().unwrap());
138 } else {
139 object.insert(name, Value::Array(values));
140 }
141 }
142 Value::Object(object)
143 }
144}