1use bytes::Bytes;
4
5use crate::{Codec, Error, Format, Value};
6
7#[derive(Clone)]
38#[non_exhaustive]
39pub enum Record {
40 #[non_exhaustive]
45 Raw {
46 bytes: Bytes,
48 format: Format,
50 },
51
52 Parsed(Value),
57}
58
59impl Record {
60 pub fn raw(bytes: impl Into<Bytes>, format: Format) -> Self {
64 Record::Raw {
65 bytes: bytes.into(),
66 format,
67 }
68 }
69
70 pub fn parsed(value: Value) -> Self {
72 Record::Parsed(value)
73 }
74
75 pub fn is_raw(&self) -> bool {
79 matches!(self, Record::Raw { .. })
80 }
81
82 pub fn is_parsed(&self) -> bool {
84 matches!(self, Record::Parsed(_))
85 }
86
87 pub fn format(&self) -> Format {
91 match self {
92 Record::Raw { format, .. } => format.clone(),
93 Record::Parsed(_) => Format::VALUE,
94 }
95 }
96
97 pub fn as_bytes(&self) -> Option<&Bytes> {
101 match self {
102 Record::Raw { bytes, .. } => Some(bytes),
103 Record::Parsed(_) => None,
104 }
105 }
106
107 pub fn as_value(&self) -> Option<&Value> {
111 match self {
112 Record::Raw { .. } => None,
113 Record::Parsed(v) => Some(v),
114 }
115 }
116
117 pub fn into_value(self, codec: &dyn Codec) -> Result<Value, Error> {
126 match self {
127 Record::Parsed(v) => Ok(v),
128 Record::Raw { bytes, format } => codec.decode(&bytes, &format),
129 }
130 }
131
132 pub fn into_bytes(self, codec: &dyn Codec, target_format: &Format) -> Result<Bytes, Error> {
140 match self {
141 Record::Raw { bytes, format } if &format == target_format => Ok(bytes),
142 Record::Raw { bytes, format } => {
143 let value = codec.decode(&bytes, &format)?;
145 codec.encode(&value, target_format)
146 }
147 Record::Parsed(v) => codec.encode(&v, target_format),
148 }
149 }
150
151 pub fn try_into_bytes(self, target_format: &Format) -> Result<Bytes, Self> {
155 match self {
156 Record::Raw { bytes, format } if &format == target_format => Ok(bytes),
157 other => Err(other),
158 }
159 }
160}
161
162impl std::fmt::Debug for Record {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 match self {
165 Record::Raw { bytes, format } => f
166 .debug_struct("Record::Raw")
167 .field("bytes_len", &bytes.len())
168 .field("format", format)
169 .finish(),
170 Record::Parsed(v) => f.debug_tuple("Record::Parsed").field(v).finish(),
171 }
172 }
173}
174
175impl From<Value> for Record {
176 fn from(v: Value) -> Self {
177 Record::Parsed(v)
178 }
179}
180
181impl From<Bytes> for Record {
182 fn from(bytes: Bytes) -> Self {
183 Record::Raw {
184 bytes,
185 format: Format::OCTET_STREAM,
186 }
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193 use std::collections::BTreeMap;
194
195 struct TestJsonCodec;
197
198 impl Codec for TestJsonCodec {
199 fn decode(&self, bytes: &Bytes, format: &Format) -> Result<Value, Error> {
200 if format != &Format::JSON {
201 return Err(Error::UnsupportedFormat(format.clone()));
202 }
203 let json: serde_json::Value = serde_json::from_slice(bytes)
204 .map_err(|e| Error::decode(format.clone(), e.to_string()))?;
205 Ok(json_to_value(json))
206 }
207
208 fn encode(&self, value: &Value, format: &Format) -> Result<Bytes, Error> {
209 if format != &Format::JSON {
210 return Err(Error::UnsupportedFormat(format.clone()));
211 }
212 let json = value_to_json(value);
213 let bytes = serde_json::to_vec(&json)
214 .map_err(|e| Error::encode(format.clone(), e.to_string()))?;
215 Ok(Bytes::from(bytes))
216 }
217
218 fn supports(&self, format: &Format) -> bool {
219 format == &Format::JSON
220 }
221 }
222
223 fn json_to_value(json: serde_json::Value) -> Value {
224 match json {
225 serde_json::Value::Null => Value::Null,
226 serde_json::Value::Bool(b) => Value::Bool(b),
227 serde_json::Value::Number(n) => {
228 if let Some(i) = n.as_i64() {
229 Value::Integer(i)
230 } else {
231 Value::Float(n.as_f64().unwrap_or(0.0))
232 }
233 }
234 serde_json::Value::String(s) => Value::String(s),
235 serde_json::Value::Array(arr) => {
236 Value::Array(arr.into_iter().map(json_to_value).collect())
237 }
238 serde_json::Value::Object(obj) => {
239 let map: BTreeMap<String, Value> = obj
240 .into_iter()
241 .map(|(k, v)| (k, json_to_value(v)))
242 .collect();
243 Value::Map(map)
244 }
245 }
246 }
247
248 fn value_to_json(value: &Value) -> serde_json::Value {
249 match value {
250 Value::Null => serde_json::Value::Null,
251 Value::Bool(b) => serde_json::Value::Bool(*b),
252 Value::Integer(i) => serde_json::Value::Number((*i).into()),
253 Value::Unsigned(i) => serde_json::Value::Number((*i).into()),
254 Value::Float(f) => serde_json::Number::from_f64(*f)
255 .map(serde_json::Value::Number)
256 .unwrap_or(serde_json::Value::Null),
257 Value::String(s) => serde_json::Value::String(s.clone()),
258 Value::Bytes(b) => serde_json::Value::String(format!("bytes:{}", b.len())),
259 Value::Array(arr) => serde_json::Value::Array(arr.iter().map(value_to_json).collect()),
260 Value::Map(map) => {
261 let obj: serde_json::Map<String, serde_json::Value> = map
262 .iter()
263 .map(|(k, v)| (k.clone(), value_to_json(v)))
264 .collect();
265 serde_json::Value::Object(obj)
266 }
267 }
268 }
269
270 #[test]
271 fn raw_record_inspection() {
272 let record = Record::raw(Bytes::from_static(b"hello"), Format::JSON);
273
274 assert!(record.is_raw());
275 assert!(!record.is_parsed());
276 assert_eq!(record.format(), Format::JSON);
277 assert_eq!(record.as_bytes(), Some(&Bytes::from_static(b"hello")));
278 assert_eq!(record.as_value(), None);
279 }
280
281 #[test]
282 fn parsed_record_inspection() {
283 let record = Record::parsed(Value::from("hello"));
284
285 assert!(!record.is_raw());
286 assert!(record.is_parsed());
287 assert_eq!(record.format(), Format::VALUE);
288 assert_eq!(record.as_bytes(), None);
289 assert_eq!(record.as_value(), Some(&Value::from("hello")));
290 }
291
292 #[test]
293 fn try_into_bytes_matching_format() {
294 let bytes = Bytes::from_static(b"hello");
295 let record = Record::raw(bytes.clone(), Format::JSON);
296
297 let result = record.try_into_bytes(&Format::JSON);
298 assert_eq!(result.unwrap(), bytes);
299 }
300
301 #[test]
302 fn try_into_bytes_mismatched_format() {
303 let record = Record::raw(Bytes::from_static(b"hello"), Format::JSON);
304
305 let result = record.try_into_bytes(&Format::PROTOBUF);
306 assert!(result.is_err()); }
308
309 #[test]
310 fn into_value_parsed() {
311 let codec = TestJsonCodec;
312 let record = Record::parsed(Value::from("hello"));
313 let value = record.into_value(&codec).unwrap();
314 assert_eq!(value, Value::String("hello".to_string()));
315 }
316
317 #[test]
318 fn into_value_raw() {
319 let codec = TestJsonCodec;
320 let record = Record::raw(Bytes::from_static(b"{\"name\":\"Alice\"}"), Format::JSON);
321 let value = record.into_value(&codec).unwrap();
322 match value {
323 Value::Map(map) => {
324 assert_eq!(map.get("name"), Some(&Value::String("Alice".to_string())));
325 }
326 _ => panic!("expected map"),
327 }
328 }
329
330 #[test]
331 fn into_bytes_raw_matching_format() {
332 let codec = TestJsonCodec;
333 let bytes = Bytes::from_static(b"{\"a\":1}");
334 let record = Record::raw(bytes.clone(), Format::JSON);
335 let result = record.into_bytes(&codec, &Format::JSON).unwrap();
336 assert_eq!(result, bytes);
337 }
338
339 #[test]
340 fn into_bytes_parsed() {
341 let codec = TestJsonCodec;
342 let record = Record::parsed(Value::from("hello"));
343 let result = record.into_bytes(&codec, &Format::JSON).unwrap();
344 assert_eq!(result, Bytes::from_static(b"\"hello\""));
345 }
346
347 #[test]
348 fn into_bytes_raw_different_format_error() {
349 let codec = TestJsonCodec;
350 let record = Record::raw(Bytes::from_static(b"data"), Format::JSON);
351 let result = record.into_bytes(&codec, &Format::PROTOBUF);
353 assert!(result.is_err());
354 }
355
356 #[test]
357 fn try_into_bytes_parsed_returns_err() {
358 let record = Record::parsed(Value::Null);
359 let result = record.try_into_bytes(&Format::JSON);
360 assert!(result.is_err());
361 }
362
363 #[test]
364 fn debug_raw_record() {
365 let record = Record::raw(Bytes::from_static(b"hello"), Format::JSON);
366 let debug = format!("{:?}", record);
367 assert!(debug.contains("Record::Raw"));
368 assert!(debug.contains("bytes_len"));
369 assert!(debug.contains("format"));
370 }
371
372 #[test]
373 fn debug_parsed_record() {
374 let record = Record::parsed(Value::from(42));
375 let debug = format!("{:?}", record);
376 assert!(debug.contains("Record::Parsed"));
377 }
378
379 #[test]
380 fn from_value_impl() {
381 let value = Value::from("test");
382 let record: Record = value.into();
383 assert!(record.is_parsed());
384 assert_eq!(record.as_value(), Some(&Value::String("test".to_string())));
385 }
386
387 #[test]
388 fn from_bytes_impl() {
389 let bytes = Bytes::from_static(b"test data");
390 let record: Record = bytes.clone().into();
391 assert!(record.is_raw());
392 assert_eq!(record.format(), Format::OCTET_STREAM);
393 assert_eq!(record.as_bytes(), Some(&bytes));
394 }
395
396 #[test]
397 fn clone_raw_record() {
398 let record = Record::raw(Bytes::from_static(b"data"), Format::JSON);
399 let cloned = record.clone();
400 assert!(cloned.is_raw());
401 assert_eq!(cloned.format(), Format::JSON);
402 }
403
404 #[test]
405 fn clone_parsed_record() {
406 let record = Record::parsed(Value::from(123));
407 let cloned = record.clone();
408 assert!(cloned.is_parsed());
409 assert_eq!(cloned.as_value(), Some(&Value::Integer(123)));
410 }
411
412 #[test]
413 fn raw_with_vec_bytes() {
414 let vec = vec![1u8, 2, 3, 4];
415 let record = Record::raw(vec, Format::OCTET_STREAM);
416 assert!(record.is_raw());
417 assert_eq!(record.as_bytes().map(|b| b.len()), Some(4));
418 }
419
420 struct MultiFormatCodec;
422
423 impl Codec for MultiFormatCodec {
424 fn decode(&self, bytes: &Bytes, format: &Format) -> Result<Value, Error> {
425 if format == &Format::JSON {
426 let json: serde_json::Value = serde_json::from_slice(bytes)
427 .map_err(|e| Error::decode(format.clone(), e.to_string()))?;
428 Ok(json_to_value(json))
429 } else if format.as_str() == "text/plain" {
430 let s = String::from_utf8_lossy(bytes);
431 Ok(Value::String(s.to_string()))
432 } else {
433 Err(Error::UnsupportedFormat(format.clone()))
434 }
435 }
436
437 fn encode(&self, value: &Value, format: &Format) -> Result<Bytes, Error> {
438 if format == &Format::JSON {
439 let json = value_to_json(value);
440 let bytes = serde_json::to_vec(&json)
441 .map_err(|e| Error::encode(format.clone(), e.to_string()))?;
442 Ok(Bytes::from(bytes))
443 } else if format.as_str() == "text/plain" {
444 match value {
445 Value::String(s) => Ok(Bytes::from(s.clone())),
446 _ => Ok(Bytes::from(format!("{:?}", value))),
447 }
448 } else {
449 Err(Error::UnsupportedFormat(format.clone()))
450 }
451 }
452
453 fn supports(&self, format: &Format) -> bool {
454 format == &Format::JSON || format.as_str() == "text/plain"
455 }
456 }
457
458 #[test]
459 fn into_bytes_transcode() {
460 let codec = MultiFormatCodec;
462 let text_format = Format::new("text/plain");
463
464 let record = Record::raw(Bytes::from_static(b"\"hello world\""), Format::JSON);
466
467 let result = record.into_bytes(&codec, &text_format).unwrap();
469
470 assert_eq!(result.as_ref(), b"hello world");
472 }
473
474 #[test]
475 fn into_bytes_transcode_decode_error() {
476 let codec = MultiFormatCodec;
478 let text_format = Format::new("text/plain");
479
480 let record = Record::raw(Bytes::from_static(b"not valid json {{{"), Format::JSON);
482
483 let result = record.into_bytes(&codec, &text_format);
485 assert!(result.is_err());
486 }
487
488 #[test]
489 fn into_value_decode_error() {
490 let codec = TestJsonCodec;
491 let record = Record::raw(Bytes::from_static(b"not valid json"), Format::JSON);
492 let result = record.into_value(&codec);
493 assert!(result.is_err());
494 }
495
496 #[test]
497 fn into_bytes_parsed_encode_error() {
498 let codec = TestJsonCodec;
499 let record = Record::parsed(Value::from("test"));
500 let result = record.into_bytes(&codec, &Format::PROTOBUF);
502 assert!(result.is_err());
503 }
504
505 #[test]
506 fn format_raw_format_clone() {
507 let record = Record::raw(Bytes::from_static(b"data"), Format::new("custom/format"));
509 let format = record.format();
510 assert_eq!(format.as_str(), "custom/format");
511 }
512
513 #[test]
514 fn into_value_unsupported_format() {
515 let codec = TestJsonCodec;
516 let record = Record::raw(Bytes::from_static(b"data"), Format::PROTOBUF);
517 let result = record.into_value(&codec);
518 assert!(result.is_err());
519 }
520}