1use anyhow::{anyhow, Result};
21use serde_json::Value as JsonValue;
22
23#[derive(Debug, Clone, PartialEq)]
25pub enum ContentType {
26 Json,
27 Xml,
28 Yaml,
29 Text,
30}
31
32impl ContentType {
33 pub fn from_header(header: Option<&str>) -> Self {
35 match header {
36 Some(h) => {
37 let lower = h.to_lowercase();
38 if lower.contains("application/json") || lower.contains("text/json") {
39 ContentType::Json
40 } else if lower.contains("application/xml") || lower.contains("text/xml") {
41 ContentType::Xml
42 } else if lower.contains("application/x-yaml")
43 || lower.contains("text/yaml")
44 || lower.contains("application/yaml")
45 {
46 ContentType::Yaml
47 } else if lower.contains("text/plain") {
48 ContentType::Text
49 } else {
50 ContentType::Json
52 }
53 }
54 None => ContentType::Json,
55 }
56 }
57}
58
59pub fn parse_content(body: &[u8], content_type: ContentType) -> Result<JsonValue> {
64 match content_type {
65 ContentType::Json => parse_json(body),
66 ContentType::Xml => parse_xml(body),
67 ContentType::Yaml => parse_yaml(body),
68 ContentType::Text => parse_text(body),
69 }
70}
71
72fn parse_json(body: &[u8]) -> Result<JsonValue> {
74 serde_json::from_slice(body).map_err(|e| anyhow!("Failed to parse JSON: {e}"))
75}
76
77fn parse_yaml(body: &[u8]) -> Result<JsonValue> {
79 let yaml_value: serde_yaml::Value =
80 serde_yaml::from_slice(body).map_err(|e| anyhow!("Failed to parse YAML: {e}"))?;
81 yaml_to_json(yaml_value)
82}
83
84fn yaml_to_json(yaml: serde_yaml::Value) -> Result<JsonValue> {
86 match yaml {
87 serde_yaml::Value::Null => Ok(JsonValue::Null),
88 serde_yaml::Value::Bool(b) => Ok(JsonValue::Bool(b)),
89 serde_yaml::Value::Number(n) => {
90 if let Some(i) = n.as_i64() {
91 Ok(JsonValue::Number(i.into()))
92 } else if let Some(f) = n.as_f64() {
93 Ok(serde_json::Number::from_f64(f)
94 .map(JsonValue::Number)
95 .unwrap_or(JsonValue::Null))
96 } else {
97 Ok(JsonValue::Null)
98 }
99 }
100 serde_yaml::Value::String(s) => Ok(JsonValue::String(s)),
101 serde_yaml::Value::Sequence(seq) => {
102 let arr: Result<Vec<JsonValue>> = seq.into_iter().map(yaml_to_json).collect();
103 Ok(JsonValue::Array(arr?))
104 }
105 serde_yaml::Value::Mapping(map) => {
106 let mut obj = serde_json::Map::new();
107 for (k, v) in map {
108 let key = match k {
109 serde_yaml::Value::String(s) => s,
110 serde_yaml::Value::Number(n) => n.to_string(),
111 serde_yaml::Value::Bool(b) => b.to_string(),
112 _ => continue,
113 };
114 obj.insert(key, yaml_to_json(v)?);
115 }
116 Ok(JsonValue::Object(obj))
117 }
118 serde_yaml::Value::Tagged(tagged) => yaml_to_json(tagged.value),
119 }
120}
121
122fn parse_xml(body: &[u8]) -> Result<JsonValue> {
124 let xml_str = std::str::from_utf8(body).map_err(|e| anyhow!("Invalid UTF-8 in XML: {e}"))?;
125 xml_to_json(xml_str)
126}
127
128fn xml_to_json(xml: &str) -> Result<JsonValue> {
136 use quick_xml::events::Event;
137 use quick_xml::Reader;
138
139 let mut reader = Reader::from_str(xml);
140 reader.config_mut().trim_text(true);
141
142 let mut stack: Vec<(String, JsonValue)> = vec![(
143 "root".to_string(),
144 JsonValue::Object(serde_json::Map::new()),
145 )];
146
147 loop {
148 match reader.read_event() {
149 Ok(Event::Start(e)) => {
150 let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
151 let mut obj = serde_json::Map::new();
152
153 for attr in e.attributes().flatten() {
155 let key = format!("@{}", String::from_utf8_lossy(attr.key.as_ref()));
156 let value = String::from_utf8_lossy(&attr.value).to_string();
157 obj.insert(key, JsonValue::String(value));
158 }
159
160 stack.push((name, JsonValue::Object(obj)));
161 }
162 Ok(Event::End(_)) if stack.len() > 1 => {
163 if let Some((name, value)) = stack.pop() {
164 if let Some((_, JsonValue::Object(parent_obj))) = stack.last_mut() {
165 if let Some(existing) = parent_obj.get_mut(&name) {
167 match existing {
168 JsonValue::Array(arr) => arr.push(value),
169 _ => {
170 let prev = existing.take();
171 *existing = JsonValue::Array(vec![prev, value]);
172 }
173 }
174 } else {
175 parent_obj.insert(name, value);
176 }
177 }
178 }
179 }
180 Ok(Event::Text(e)) => {
181 let text = e
182 .unescape()
183 .map_err(|e| anyhow!("XML unescape error: {e}"))?;
184 let text = text.trim();
185 if !text.is_empty() {
186 if let Some((_, current)) = stack.last_mut() {
187 if let JsonValue::Object(obj) = current {
188 if obj.is_empty() {
189 *current = JsonValue::String(text.to_string());
191 } else {
192 obj.insert(
193 "_text".to_string(),
194 JsonValue::String(text.to_string()),
195 );
196 }
197 }
198 }
199 }
200 }
201 Ok(Event::Empty(e)) => {
202 let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
203 let mut obj = serde_json::Map::new();
204
205 for attr in e.attributes().flatten() {
206 let key = format!("@{}", String::from_utf8_lossy(attr.key.as_ref()));
207 let value = String::from_utf8_lossy(&attr.value).to_string();
208 obj.insert(key, JsonValue::String(value));
209 }
210
211 let value = if obj.is_empty() {
212 JsonValue::Null
213 } else {
214 JsonValue::Object(obj)
215 };
216
217 if let Some((_, JsonValue::Object(parent_obj))) = stack.last_mut() {
218 if let Some(existing) = parent_obj.get_mut(&name) {
219 match existing {
220 JsonValue::Array(arr) => arr.push(value),
221 _ => {
222 let prev = existing.take();
223 *existing = JsonValue::Array(vec![prev, value]);
224 }
225 }
226 } else {
227 parent_obj.insert(name, value);
228 }
229 }
230 }
231 Ok(Event::Eof) => break,
232 Err(e) => return Err(anyhow!("XML parse error: {e}")),
233 _ => {}
234 }
235 }
236
237 let Some((_, JsonValue::Object(mut root))) = stack.pop() else {
239 return Err(anyhow!("Failed to parse XML structure"));
240 };
241
242 if root.len() == 1 {
244 Ok(root
245 .into_iter()
246 .next()
247 .map(|(_, v)| v)
248 .unwrap_or(JsonValue::Null))
249 } else {
250 Ok(JsonValue::Object(root))
251 }
252}
253
254fn parse_text(body: &[u8]) -> Result<JsonValue> {
256 let text =
257 std::str::from_utf8(body).map_err(|e| anyhow!("Invalid UTF-8 in text content: {e}"))?;
258 Ok(JsonValue::String(text.to_string()))
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_content_type_from_header() {
267 assert_eq!(
268 ContentType::from_header(Some("application/json")),
269 ContentType::Json
270 );
271 assert_eq!(
272 ContentType::from_header(Some("application/json; charset=utf-8")),
273 ContentType::Json
274 );
275 assert_eq!(
276 ContentType::from_header(Some("text/json")),
277 ContentType::Json
278 );
279 assert_eq!(
280 ContentType::from_header(Some("application/xml")),
281 ContentType::Xml
282 );
283 assert_eq!(ContentType::from_header(Some("text/xml")), ContentType::Xml);
284 assert_eq!(
285 ContentType::from_header(Some("application/x-yaml")),
286 ContentType::Yaml
287 );
288 assert_eq!(
289 ContentType::from_header(Some("text/yaml")),
290 ContentType::Yaml
291 );
292 assert_eq!(
293 ContentType::from_header(Some("text/plain")),
294 ContentType::Text
295 );
296 assert_eq!(ContentType::from_header(None), ContentType::Json);
297 assert_eq!(
298 ContentType::from_header(Some("unknown/type")),
299 ContentType::Json
300 );
301 }
302
303 #[test]
304 fn test_parse_json() {
305 let json = r#"{"name": "test", "value": 42, "nested": {"key": "value"}}"#;
306 let result = parse_content(json.as_bytes(), ContentType::Json).unwrap();
307
308 assert_eq!(result["name"], "test");
309 assert_eq!(result["value"], 42);
310 assert_eq!(result["nested"]["key"], "value");
311 }
312
313 #[test]
314 fn test_parse_json_array() {
315 let json = r#"[1, 2, 3]"#;
316 let result = parse_content(json.as_bytes(), ContentType::Json).unwrap();
317
318 assert!(result.is_array());
319 assert_eq!(result.as_array().unwrap().len(), 3);
320 }
321
322 #[test]
323 fn test_parse_yaml() {
324 let yaml = r#"
325name: test
326value: 42
327nested:
328 key: value
329items:
330 - one
331 - two
332"#;
333 let result = parse_content(yaml.as_bytes(), ContentType::Yaml).unwrap();
334
335 assert_eq!(result["name"], "test");
336 assert_eq!(result["value"], 42);
337 assert_eq!(result["nested"]["key"], "value");
338 assert_eq!(result["items"][0], "one");
339 assert_eq!(result["items"][1], "two");
340 }
341
342 #[test]
343 fn test_parse_xml_simple() {
344 let xml = r#"<root><name>test</name><value>42</value></root>"#;
345 let result = parse_content(xml.as_bytes(), ContentType::Xml).unwrap();
346
347 assert_eq!(result["name"], "test");
348 assert_eq!(result["value"], "42");
349 }
350
351 #[test]
352 fn test_parse_xml_with_attributes() {
353 let xml = r#"<item id="123" type="test">content</item>"#;
354 let result = parse_content(xml.as_bytes(), ContentType::Xml).unwrap();
355
356 assert_eq!(result["@id"], "123");
357 assert_eq!(result["@type"], "test");
358 assert_eq!(result["_text"], "content");
359 }
360
361 #[test]
362 fn test_parse_xml_repeated_elements() {
363 let xml = r#"<root><item>one</item><item>two</item><item>three</item></root>"#;
364 let result = parse_content(xml.as_bytes(), ContentType::Xml).unwrap();
365
366 assert!(result["item"].is_array());
367 let items = result["item"].as_array().unwrap();
368 assert_eq!(items.len(), 3);
369 assert_eq!(items[0], "one");
370 assert_eq!(items[1], "two");
371 assert_eq!(items[2], "three");
372 }
373
374 #[test]
375 fn test_parse_text() {
376 let text = "Hello, World!";
377 let result = parse_content(text.as_bytes(), ContentType::Text).unwrap();
378
379 assert_eq!(result, JsonValue::String("Hello, World!".to_string()));
380 }
381
382 #[test]
383 fn test_parse_invalid_json() {
384 let invalid = "not valid json";
385 let result = parse_content(invalid.as_bytes(), ContentType::Json);
386 assert!(result.is_err());
387 }
388
389 #[test]
390 fn test_parse_invalid_yaml() {
391 let invalid = "key: [unclosed";
392 let result = parse_content(invalid.as_bytes(), ContentType::Yaml);
393 assert!(result.is_err());
394 }
395
396 #[test]
397 fn test_parse_invalid_xml() {
398 let invalid = "<root><unclosed></root>";
400 let result = parse_content(invalid.as_bytes(), ContentType::Xml);
401 assert!(result.is_err());
402 }
403}