Skip to main content

gonzalo_domain/
codec.rs

1//! Mapping between typed domain structs and generic record bodies.
2
3use gonzalo_core::{Body, CoreError, Result};
4use serde::{Serialize, de::DeserializeOwned};
5
6/// A typed value that can be stored in a record body as JSON.
7pub 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            // A `Body::Blob` carries only the content hash; the referenced JSON
19            // lives out-of-line and must be fetched via `BlobStore::get_blob`.
20            // `from_body` has no `BlobStore`, so decoding a blob here is
21            // impossible — fail explicitly rather than misparse the hash bytes
22            // as JSON (which yields a misleading serde error).
23            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        // A `Body::Blob` carries only the content hash, not the referenced JSON.
55        // `from_body` has no `BlobStore`, so it must fail explicitly rather than
56        // try to parse the hash string as JSON (a misleading serde error).
57        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}