Skip to main content

camel_processor/data_format/
json.rs

1use camel_api::body::Body;
2use camel_api::data_format::DataFormat;
3use camel_api::error::CamelError;
4
5pub struct JsonDataFormat;
6
7impl DataFormat for JsonDataFormat {
8    fn name(&self) -> &str {
9        "json"
10    }
11
12    fn marshal(&self, body: Body) -> Result<Body, CamelError> {
13        match body {
14            Body::Json(v) => {
15                let s = serde_json::to_string(&v).map_err(|e| {
16                    CamelError::TypeConversionFailed(format!(
17                        "cannot marshal Body::Json to text: {e}"
18                    ))
19                })?;
20                Ok(Body::Text(s))
21            }
22            Body::Text(_) => Ok(body),
23            Body::Stream(_) => Err(CamelError::TypeConversionFailed(
24                "cannot marshal Body::Stream — materialize with into_bytes() first".to_string(),
25            )),
26            Body::Empty | Body::Xml(_) => Err(CamelError::TypeConversionFailed(
27                "JsonDataFormat::marshal only supports Body::Json, Body::Text, and Body::Bytes"
28                    .to_string(),
29            )),
30            Body::Bytes(b) => {
31                let v: serde_json::Value = serde_json::from_slice(&b).map_err(|e| {
32                    CamelError::TypeConversionFailed(format!(
33                        "cannot marshal Body::Bytes as JSON: {e}"
34                    ))
35                })?;
36                Ok(Body::Text(serde_json::to_string(&v).map_err(|e| {
37                    CamelError::TypeConversionFailed(format!(
38                        "cannot serialize JSON value to text: {e}"
39                    ))
40                })?))
41            }
42        }
43    }
44
45    fn unmarshal(&self, body: Body) -> Result<Body, CamelError> {
46        match body {
47            Body::Json(_) => Ok(body),
48            Body::Text(s) => {
49                let v = serde_json::from_str(&s).map_err(|e| {
50                    CamelError::TypeConversionFailed(format!(
51                        "cannot unmarshal Body::Text as JSON: {e}"
52                    ))
53                })?;
54                Ok(Body::Json(v))
55            }
56            Body::Bytes(b) => {
57                let v = serde_json::from_slice(&b).map_err(|e| {
58                    CamelError::TypeConversionFailed(format!(
59                        "cannot unmarshal Body::Bytes as JSON: {e}"
60                    ))
61                })?;
62                Ok(Body::Json(v))
63            }
64            Body::Stream(_) => Err(CamelError::TypeConversionFailed(
65                "cannot unmarshal Body::Stream — materialize with into_bytes() first".to_string(),
66            )),
67            Body::Empty | Body::Xml(_) => Err(CamelError::TypeConversionFailed(
68                "JsonDataFormat::unmarshal only supports Body::Json, Body::Text, and Body::Bytes"
69                    .to_string(),
70            )),
71        }
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use bytes::Bytes;
79    use serde_json::json;
80
81    #[test]
82    fn test_name() {
83        assert_eq!(JsonDataFormat.name(), "json");
84    }
85
86    #[test]
87    fn test_unmarshal_text_to_json() {
88        let body = Body::Text(r#"{"a":1}"#.to_string());
89        let result = JsonDataFormat.unmarshal(body).unwrap();
90        assert!(matches!(result, Body::Json(_)));
91        if let Body::Json(v) = result {
92            assert_eq!(v["a"], json!(1));
93        }
94    }
95
96    #[test]
97    fn test_unmarshal_bytes_to_json() {
98        let body = Body::Bytes(Bytes::from_static(b"{\"b\":2}"));
99        let result = JsonDataFormat.unmarshal(body).unwrap();
100        assert!(matches!(result, Body::Json(_)));
101    }
102
103    #[test]
104    fn test_unmarshal_invalid_json() {
105        let body = Body::Text("not json".to_string());
106        let result = JsonDataFormat.unmarshal(body);
107        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
108    }
109
110    #[test]
111    fn test_unmarshal_json_noop() {
112        let body = Body::Json(json!({"x": 1}));
113        let result = JsonDataFormat.unmarshal(body).unwrap();
114        assert!(matches!(result, Body::Json(_)));
115    }
116
117    #[test]
118    fn test_unmarshal_unsupported_variant() {
119        let body = Body::Empty;
120        let result = JsonDataFormat.unmarshal(body);
121        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
122    }
123
124    #[test]
125    fn test_unmarshal_stream_rejected() {
126        use camel_api::body::{StreamBody, StreamMetadata};
127        use futures::stream;
128        use std::sync::Arc;
129        use tokio::sync::Mutex;
130
131        let stream = stream::iter(vec![Ok(Bytes::from_static(b"data"))]);
132        let body = Body::Stream(StreamBody {
133            stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
134            metadata: StreamMetadata::default(),
135        });
136        let result = JsonDataFormat.unmarshal(body);
137        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
138    }
139
140    #[test]
141    fn test_marshal_json_to_text() {
142        let body = Body::Json(json!({"key": "value"}));
143        let result = JsonDataFormat.marshal(body).unwrap();
144        match result {
145            Body::Text(s) => assert!(s.contains("\"key\"")),
146            _ => panic!("expected Body::Text"),
147        }
148    }
149
150    #[test]
151    fn test_marshal_text_noop() {
152        let body = Body::Text("already text".to_string());
153        let result = JsonDataFormat.marshal(body).unwrap();
154        assert_eq!(result, Body::Text("already text".to_string()));
155    }
156
157    #[test]
158    fn test_marshal_unsupported_variant() {
159        let body = Body::Xml("<root/>".to_string());
160        let result = JsonDataFormat.marshal(body);
161        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
162    }
163
164    #[test]
165    fn test_marshal_bytes_to_text() {
166        let body = Body::Bytes(Bytes::from_static(b"{\"key\":\"val\"}"));
167        let result = JsonDataFormat.marshal(body).unwrap();
168        match result {
169            Body::Text(s) => assert!(s.contains("\"key\"")),
170            _ => panic!("expected Body::Text"),
171        }
172    }
173
174    #[test]
175    fn test_marshal_invalid_bytes_returns_error() {
176        let body = Body::Bytes(Bytes::from_static(b"not json"));
177        let result = JsonDataFormat.marshal(body);
178        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
179    }
180}