camel_processor/data_format/
json.rs1use camel_api::body::Body;
2use camel_api::data_format::DataFormat;
3use camel_api::error::CamelError;
4
5const MAX_JSON_BYTES: usize = 16 * 1024 * 1024; pub struct JsonDataFormat;
11
12impl DataFormat for JsonDataFormat {
13 fn name(&self) -> &str {
14 "json"
15 }
16
17 fn marshal(&self, body: Body) -> Result<Body, CamelError> {
18 match body {
19 Body::Json(v) => {
20 let s = serde_json::to_string(&v).map_err(|e| {
21 CamelError::TypeConversionFailed(format!(
22 "cannot marshal Body::Json to text: {e}"
23 ))
24 })?;
25 Ok(Body::Text(s))
26 }
27 Body::Text(_) => Ok(body),
28 Body::Stream(_) => Err(CamelError::TypeConversionFailed(
29 "cannot marshal Body::Stream — add 'stream_cache' or 'convert_body_to' before this step".to_string(),
30 )),
31 Body::Empty => Ok(Body::Empty),
35 Body::Xml(_) => Err(CamelError::TypeConversionFailed(
36 "JsonDataFormat::marshal only supports Body::Json, Body::Text, Body::Bytes, \
37 and Body::Empty"
38 .to_string(),
39 )),
40 Body::Bytes(b) => {
41 let v: serde_json::Value = serde_json::from_slice(&b).map_err(|e| {
42 CamelError::TypeConversionFailed(format!(
43 "cannot marshal Body::Bytes as JSON: {e}"
44 ))
45 })?;
46 Ok(Body::Text(serde_json::to_string(&v).map_err(|e| {
47 CamelError::TypeConversionFailed(format!(
48 "cannot serialize JSON value to text: {e}"
49 ))
50 })?))
51 }
52 }
53 }
54
55 fn unmarshal(&self, body: Body) -> Result<Body, CamelError> {
56 match body {
57 Body::Json(_) => Ok(body),
58 Body::Text(s) => {
59 if s.len() > MAX_JSON_BYTES {
60 return Err(CamelError::TypeConversionFailed(format!(
61 "JSON unmarshal input {} bytes exceeds max {MAX_JSON_BYTES}",
62 s.len()
63 )));
64 }
65 let v = serde_json::from_str(&s).map_err(|e| {
66 CamelError::TypeConversionFailed(format!(
67 "cannot unmarshal Body::Text as JSON: {e}"
68 ))
69 })?;
70 Ok(Body::Json(v))
71 }
72 Body::Bytes(b) => {
73 if b.len() > MAX_JSON_BYTES {
74 return Err(CamelError::TypeConversionFailed(format!(
75 "JSON unmarshal input {} bytes exceeds max {MAX_JSON_BYTES}",
76 b.len()
77 )));
78 }
79 let v = serde_json::from_slice(&b).map_err(|e| {
80 CamelError::TypeConversionFailed(format!(
81 "cannot unmarshal Body::Bytes as JSON: {e}"
82 ))
83 })?;
84 Ok(Body::Json(v))
85 }
86 Body::Stream(_) => Err(CamelError::TypeConversionFailed(
87 "cannot unmarshal Body::Stream directly — use UnmarshalService which auto-materializes"
88 .to_string(),
89 )),
90 Body::Empty => Ok(Body::Empty),
94 Body::Xml(_) => Err(CamelError::TypeConversionFailed(
95 "JsonDataFormat::unmarshal only supports Body::Json, Body::Text, Body::Bytes, \
96 and Body::Empty"
97 .to_string(),
98 )),
99 }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use bytes::Bytes;
107 use serde_json::json;
108
109 #[test]
110 fn test_name() {
111 assert_eq!(JsonDataFormat.name(), "json");
112 }
113
114 #[test]
115 fn test_unmarshal_text_to_json() {
116 let body = Body::Text(r#"{"a":1}"#.to_string());
117 let result = JsonDataFormat.unmarshal(body).unwrap();
118 assert!(matches!(result, Body::Json(_)));
119 if let Body::Json(v) = result {
120 assert_eq!(v["a"], json!(1));
121 }
122 }
123
124 #[test]
125 fn test_unmarshal_bytes_to_json() {
126 let body = Body::Bytes(Bytes::from_static(b"{\"b\":2}"));
127 let result = JsonDataFormat.unmarshal(body).unwrap();
128 assert!(matches!(result, Body::Json(_)));
129 }
130
131 #[test]
132 fn test_unmarshal_invalid_json() {
133 let body = Body::Text("not json".to_string());
134 let result = JsonDataFormat.unmarshal(body);
135 assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
136 }
137
138 #[test]
139 fn test_unmarshal_json_noop() {
140 let body = Body::Json(json!({"x": 1}));
141 let result = JsonDataFormat.unmarshal(body).unwrap();
142 assert!(matches!(result, Body::Json(_)));
143 }
144
145 #[test]
146 fn test_unmarshal_unsupported_variant() {
147 let body = Body::Xml("<root/>".to_string());
148 let result = JsonDataFormat.unmarshal(body);
149 assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
150 }
151
152 #[test]
153 fn test_unmarshal_empty_is_noop() {
154 let result = JsonDataFormat.unmarshal(Body::Empty).unwrap();
156 assert!(matches!(result, Body::Empty));
157 }
158
159 #[test]
160 fn test_marshal_empty_is_noop() {
161 let result = JsonDataFormat.marshal(Body::Empty).unwrap();
163 assert!(matches!(result, Body::Empty));
164 }
165
166 #[test]
167 fn test_unmarshal_stream_rejected() {
168 use camel_api::body::{StreamBody, StreamMetadata};
169 use futures::stream;
170 use std::sync::Arc;
171 use tokio::sync::Mutex;
172
173 let stream = stream::iter(vec![Ok(Bytes::from_static(b"data"))]);
174 let body = Body::Stream(StreamBody {
175 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
176 metadata: StreamMetadata::default(),
177 });
178 let result = JsonDataFormat.unmarshal(body);
179 assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
180 }
181
182 #[test]
183 fn test_marshal_json_to_text() {
184 let body = Body::Json(json!({"key": "value"}));
185 let result = JsonDataFormat.marshal(body).unwrap();
186 match result {
187 Body::Text(s) => assert!(s.contains("\"key\"")),
188 _ => panic!("expected Body::Text"),
189 }
190 }
191
192 #[test]
193 fn test_marshal_text_noop() {
194 let body = Body::Text("already text".to_string());
195 let result = JsonDataFormat.marshal(body).unwrap();
196 assert_eq!(result, Body::Text("already text".to_string()));
197 }
198
199 #[test]
200 fn test_marshal_unsupported_variant() {
201 let body = Body::Xml("<root/>".to_string());
202 let result = JsonDataFormat.marshal(body);
203 assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
204 }
205
206 #[test]
207 fn test_marshal_bytes_to_text() {
208 let body = Body::Bytes(Bytes::from_static(b"{\"key\":\"val\"}"));
209 let result = JsonDataFormat.marshal(body).unwrap();
210 match result {
211 Body::Text(s) => assert!(s.contains("\"key\"")),
212 _ => panic!("expected Body::Text"),
213 }
214 }
215
216 #[test]
217 fn test_marshal_invalid_bytes_returns_error() {
218 let body = Body::Bytes(Bytes::from_static(b"not json"));
219 let result = JsonDataFormat.marshal(body);
220 assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
221 }
222
223 #[test]
224 fn test_unmarshal_text_size_cap() {
225 let big = format!(r#"{{"k":"{}"}}"#, "x".repeat(16 * 1024 * 1024 + 10));
227 let body = Body::Text(big);
228 let result = JsonDataFormat.unmarshal(body);
229 assert!(result.is_err(), "expected error for oversized JSON text");
230 let msg = format!("{}", result.unwrap_err());
231 assert!(
232 msg.contains("exceeds") && msg.contains("max"),
233 "error should mention size cap: {msg}"
234 );
235 }
236
237 #[test]
238 fn test_unmarshal_bytes_size_cap() {
239 let big = format!(r#"{{"k":"{}"}}"#, "x".repeat(16 * 1024 * 1024 + 10));
240 let body = Body::Bytes(bytes::Bytes::from(big));
241 let result = JsonDataFormat.unmarshal(body);
242 assert!(result.is_err());
243 }
244}