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 — add 'stream_cache' or 'convert_body_to' before this step".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 directly — use UnmarshalService which auto-materializes"
66                    .to_string(),
67            )),
68            Body::Empty | Body::Xml(_) => Err(CamelError::TypeConversionFailed(
69                "JsonDataFormat::unmarshal only supports Body::Json, Body::Text, and Body::Bytes"
70                    .to_string(),
71            )),
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use bytes::Bytes;
80    use serde_json::json;
81
82    #[test]
83    fn test_name() {
84        assert_eq!(JsonDataFormat.name(), "json");
85    }
86
87    #[test]
88    fn test_unmarshal_text_to_json() {
89        let body = Body::Text(r#"{"a":1}"#.to_string());
90        let result = JsonDataFormat.unmarshal(body).unwrap();
91        assert!(matches!(result, Body::Json(_)));
92        if let Body::Json(v) = result {
93            assert_eq!(v["a"], json!(1));
94        }
95    }
96
97    #[test]
98    fn test_unmarshal_bytes_to_json() {
99        let body = Body::Bytes(Bytes::from_static(b"{\"b\":2}"));
100        let result = JsonDataFormat.unmarshal(body).unwrap();
101        assert!(matches!(result, Body::Json(_)));
102    }
103
104    #[test]
105    fn test_unmarshal_invalid_json() {
106        let body = Body::Text("not json".to_string());
107        let result = JsonDataFormat.unmarshal(body);
108        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
109    }
110
111    #[test]
112    fn test_unmarshal_json_noop() {
113        let body = Body::Json(json!({"x": 1}));
114        let result = JsonDataFormat.unmarshal(body).unwrap();
115        assert!(matches!(result, Body::Json(_)));
116    }
117
118    #[test]
119    fn test_unmarshal_unsupported_variant() {
120        let body = Body::Empty;
121        let result = JsonDataFormat.unmarshal(body);
122        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
123    }
124
125    #[test]
126    fn test_unmarshal_stream_rejected() {
127        use camel_api::body::{StreamBody, StreamMetadata};
128        use futures::stream;
129        use std::sync::Arc;
130        use tokio::sync::Mutex;
131
132        let stream = stream::iter(vec![Ok(Bytes::from_static(b"data"))]);
133        let body = Body::Stream(StreamBody {
134            stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
135            metadata: StreamMetadata::default(),
136        });
137        let result = JsonDataFormat.unmarshal(body);
138        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
139    }
140
141    #[test]
142    fn test_marshal_json_to_text() {
143        let body = Body::Json(json!({"key": "value"}));
144        let result = JsonDataFormat.marshal(body).unwrap();
145        match result {
146            Body::Text(s) => assert!(s.contains("\"key\"")),
147            _ => panic!("expected Body::Text"),
148        }
149    }
150
151    #[test]
152    fn test_marshal_text_noop() {
153        let body = Body::Text("already text".to_string());
154        let result = JsonDataFormat.marshal(body).unwrap();
155        assert_eq!(result, Body::Text("already text".to_string()));
156    }
157
158    #[test]
159    fn test_marshal_unsupported_variant() {
160        let body = Body::Xml("<root/>".to_string());
161        let result = JsonDataFormat.marshal(body);
162        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
163    }
164
165    #[test]
166    fn test_marshal_bytes_to_text() {
167        let body = Body::Bytes(Bytes::from_static(b"{\"key\":\"val\"}"));
168        let result = JsonDataFormat.marshal(body).unwrap();
169        match result {
170            Body::Text(s) => assert!(s.contains("\"key\"")),
171            _ => panic!("expected Body::Text"),
172        }
173    }
174
175    #[test]
176    fn test_marshal_invalid_bytes_returns_error() {
177        let body = Body::Bytes(Bytes::from_static(b"not json"));
178        let result = JsonDataFormat.marshal(body);
179        assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
180    }
181}