urna_format/sections/
provenance.rs1use super::codec::Cursor;
8use crate::error::UrnaError;
9use crate::layout::{SECTION_PAYLOAD_PREFIX_SIZE, SECTION_PAYLOAD_VERSION, SECTION_PROVENANCE};
10
11pub fn encode_provenance(value: &serde_json::Value) -> crate::Result<Vec<u8>> {
12 let json = serde_json::to_vec(value)?;
13 let json_len = u32::try_from(json.len())
14 .map_err(|_| UrnaError::InvalidInput("provenance JSON too large".into()))?;
15 let mut buf = Vec::with_capacity(SECTION_PAYLOAD_PREFIX_SIZE + json.len());
16 buf.extend_from_slice(&SECTION_PAYLOAD_VERSION.to_le_bytes());
17 buf.extend_from_slice(&(json_len as u64).to_le_bytes());
18 buf.extend_from_slice(&json);
19 Ok(buf)
20}
21
22pub fn decode_provenance(data: &[u8]) -> crate::Result<serde_json::Value> {
23 let mut c = Cursor::new(data, SECTION_PROVENANCE);
24 if c.data.len() < SECTION_PAYLOAD_PREFIX_SIZE {
25 return Err(c.malformed("payload shorter than prefix"));
26 }
27 let version = c.read_u32()?;
28 if version != SECTION_PAYLOAD_VERSION {
29 return Err(UrnaError::UnsupportedSectionVersion {
30 section_id: SECTION_PROVENANCE,
31 version,
32 });
33 }
34 let json_len = c.read_u64()? as usize;
35 let json = c.read_bytes(json_len)?;
36 let value: serde_json::Value = serde_json::from_slice(json).map_err(UrnaError::Json)?;
37 c.finish()?;
38 Ok(value)
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn roundtrip() {
47 let v = serde_json::json!({"source": "demo", "ingested_at": "2026-01-01"});
48 let bytes = encode_provenance(&v).unwrap();
49 let back = decode_provenance(&bytes).unwrap();
50 assert_eq!(v, back);
51 }
52}