Skip to main content

camel_processor/data_format/
xml.rs

1use camel_api::body::Body;
2use camel_api::data_format::DataFormat;
3use camel_api::error::CamelError;
4use camel_api::xml_convert::{json_to_xml, xml_to_json};
5
6pub struct XmlDataFormat;
7
8impl DataFormat for XmlDataFormat {
9    fn name(&self) -> &str {
10        "xml"
11    }
12
13    fn marshal(&self, body: Body) -> Result<Body, CamelError> {
14        match body {
15            Body::Json(v) => {
16                let xml = json_to_xml(&v)?;
17                Ok(Body::Text(xml))
18            }
19            Body::Text(_) => Ok(body),
20            Body::Stream(_) => Err(CamelError::TypeConversionFailed(
21                "cannot marshal Body::Stream — add 'stream_cache' or 'convert_body_to' before this step".to_string(),
22            )),
23            Body::Empty | Body::Bytes(_) => Err(CamelError::TypeConversionFailed(
24                "XmlDataFormat::marshal only supports Body::Json and Body::Text".to_string(),
25            )),
26            Body::Xml(_) => Err(CamelError::TypeConversionFailed(
27                "XmlDataFormat::marshal does not accept Body::Xml — use unmarshal to convert XML to JSON"
28                    .to_string(),
29            )),
30        }
31    }
32
33    fn unmarshal(&self, body: Body) -> Result<Body, CamelError> {
34        match body {
35            Body::Json(_) => Ok(body),
36            Body::Text(s) => {
37                let v = xml_to_json(&s)?;
38                Ok(Body::Json(v))
39            }
40            Body::Bytes(b) => {
41                let s = String::from_utf8(b.to_vec()).map_err(|e| {
42                    CamelError::TypeConversionFailed(format!(
43                        "cannot unmarshal Body::Bytes as XML: {e}"
44                    ))
45                })?;
46                let v = xml_to_json(&s)?;
47                Ok(Body::Json(v))
48            }
49            Body::Xml(s) => {
50                let v = xml_to_json(&s)?;
51                Ok(Body::Json(v))
52            }
53            Body::Stream(_) => Err(CamelError::TypeConversionFailed(
54                "cannot unmarshal Body::Stream directly — use UnmarshalService which auto-materializes"
55                    .to_string(),
56            )),
57            Body::Empty => Err(CamelError::TypeConversionFailed(
58                "XmlDataFormat::unmarshal only supports Body::Json, Body::Text, Body::Bytes, and Body::Xml"
59                    .to_string(),
60            )),
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use bytes::Bytes;
69    use serde_json::json;
70
71    #[test]
72    fn test_name() {
73        assert_eq!(XmlDataFormat.name(), "xml");
74    }
75
76    #[test]
77    fn test_unmarshal_text_to_json() {
78        let body = Body::Text("<root><child>value</child></root>".to_string());
79        let result = XmlDataFormat.unmarshal(body).unwrap();
80        match result {
81            Body::Json(v) => assert_eq!(v["root"]["child"], json!("value")),
82            _ => panic!("expected Body::Json"),
83        }
84    }
85
86    #[test]
87    fn test_unmarshal_bytes_to_json() {
88        let body = Body::Bytes(Bytes::from_static(b"<root/>"));
89        let result = XmlDataFormat.unmarshal(body).unwrap();
90        match result {
91            Body::Json(v) => assert_eq!(v["root"], serde_json::Value::Null),
92            _ => panic!("expected Body::Json"),
93        }
94    }
95
96    #[test]
97    fn test_unmarshal_xml_body_to_json() {
98        let body = Body::Xml("<root><a>1</a></root>".to_string());
99        let result = XmlDataFormat.unmarshal(body).unwrap();
100        match result {
101            Body::Json(v) => assert_eq!(v["root"]["a"], json!("1")),
102            _ => panic!("expected Body::Json"),
103        }
104    }
105
106    #[test]
107    fn test_unmarshal_invalid_xml() {
108        let body = Body::Text("not xml".to_string());
109        let result = XmlDataFormat.unmarshal(body);
110        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
111    }
112
113    #[test]
114    fn test_unmarshal_json_noop() {
115        let body = Body::Json(json!({"x": 1}));
116        let result = XmlDataFormat.unmarshal(body).unwrap();
117        assert!(matches!(result, Body::Json(_)));
118    }
119
120    #[test]
121    fn test_unmarshal_empty_returns_error() {
122        let body = Body::Empty;
123        let result = XmlDataFormat.unmarshal(body);
124        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
125    }
126
127    #[test]
128    fn test_marshal_json_to_text() {
129        let body = Body::Json(json!({"root": {"name": "Alice"}}));
130        let result = XmlDataFormat.marshal(body).unwrap();
131        match result {
132            Body::Text(s) => {
133                assert!(s.contains("<root>"));
134                assert!(s.contains("<name>Alice</name>"));
135            }
136            _ => panic!("expected Body::Text"),
137        }
138    }
139
140    #[test]
141    fn test_marshal_text_noop() {
142        let body = Body::Text("already text".to_string());
143        let result = XmlDataFormat.marshal(body).unwrap();
144        assert_eq!(result, Body::Text("already text".to_string()));
145    }
146
147    #[test]
148    fn test_marshal_empty_returns_error() {
149        let body = Body::Empty;
150        let result = XmlDataFormat.marshal(body);
151        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
152    }
153
154    #[test]
155    fn test_marshal_xml_body_rejected() {
156        let body = Body::Xml("<root/>".to_string());
157        let result = XmlDataFormat.marshal(body);
158        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
159    }
160}