1use gonzalo_core::{Body, CoreError, Result};
4use serde::{Serialize, de::DeserializeOwned};
5
6pub trait RecordCodec: Serialize + DeserializeOwned {
8 fn to_body(&self) -> Result<Body> {
9 let bytes = serde_json::to_vec(self).map_err(|e| CoreError::Serde(e.to_string()))?;
10 Ok(Body::Inline(bytes))
11 }
12
13 fn from_body(body: &Body) -> Result<Self> {
14 match body {
15 Body::Inline(bytes) => {
16 serde_json::from_slice(bytes).map_err(|e| CoreError::Serde(e.to_string()))
17 }
18 Body::Blob { .. } => Err(CoreError::Backend(
24 "cannot decode a blob-backed body without a BlobStore".to_string(),
25 )),
26 }
27 }
28}
29
30#[cfg(test)]
31mod tests {
32 use super::*;
33 use serde::Deserialize;
34
35 #[derive(Debug, PartialEq, Serialize, Deserialize)]
36 struct Demo {
37 n: u32,
38 s: String,
39 }
40 impl RecordCodec for Demo {}
41
42 #[test]
43 fn roundtrips_through_body() {
44 let d = Demo {
45 n: 7,
46 s: "x".into(),
47 };
48 let body = d.to_body().unwrap();
49 assert_eq!(Demo::from_body(&body).unwrap(), d);
50 }
51
52 #[test]
53 fn from_body_rejects_a_blob_body_instead_of_misparsing_its_hash() {
54 let body = Body::blob(br#"{"n":7,"s":"x"}"#);
58 let err = Demo::from_body(&body).unwrap_err();
59 let msg = err.to_string();
60 assert!(
61 msg.contains("blob"),
62 "error should mention a blob body, got: {msg}"
63 );
64 }
65}