Deserialize a provenance graph from bytes, returning the graph and its hash. The hash is computed over the input bytes (the entire serialized form).
(data: &[u8])
| 268 | /// |
| 269 | /// The hash is computed over the input bytes (the entire serialized form). |
| 270 | pub fn deserialize(data: &[u8]) -> Result<(Self, Hash), ProvenanceGraphError> { |
| 271 | if data.len() < MAGIC.len() + 1 { |
| 272 | return Err(ProvenanceGraphError::Codec { |
| 273 | reason: format!( |
| 274 | "data too short: {} bytes (minimum {})", |
| 275 | data.len(), |
| 276 | MAGIC.len() + 1 |
| 277 | ), |
| 278 | }); |
| 279 | } |
| 280 | |
| 281 | if &data[..4] != MAGIC { |
| 282 | return Err(ProvenanceGraphError::Codec { |
| 283 | reason: format!("invalid magic: expected {:?}, got {:?}", MAGIC, &data[..4]), |
| 284 | }); |
| 285 | } |
| 286 | |
| 287 | // Peek at the version byte without fully deserializing. |
| 288 | // The first field in the postcard payload is `version: u8`. |
| 289 | let version_byte = data[4]; |
| 290 | |
| 291 | if version_byte > SCHEMA_VERSION { |
| 292 | return Err(ProvenanceGraphError::UnsupportedVersion { |
| 293 | version: version_byte, |
| 294 | max_supported: SCHEMA_VERSION, |
| 295 | }); |
| 296 | } |
| 297 | |
| 298 | let graph: Self = if version_byte <= 1 { |
| 299 | // v1 payload: no `profile` field. Deserialize into the v1 shim |
| 300 | // struct (identical to the current struct minus `profile`) and |
| 301 | // upgrade to v2 in-memory with `profile: None`. |
| 302 | let v1: ProvenanceGraphV1 = |
| 303 | postcard::from_bytes(&data[4..]).map_err(|e| ProvenanceGraphError::Codec { |
| 304 | reason: format!("postcard deserialize failed (v1): {}", e), |
| 305 | })?; |
| 306 | ProvenanceGraph { |
| 307 | version: SCHEMA_VERSION, |
| 308 | timestamp: v1.timestamp, |
| 309 | session_id: v1.session_id, |
| 310 | agent_name: v1.agent_name, |
| 311 | agent_display_name: v1.agent_display_name, |
| 312 | agent_vendor: v1.agent_vendor, |
| 313 | nodes: v1.nodes, |
| 314 | edges: v1.edges, |
| 315 | changes_explained: v1.changes_explained, |
| 316 | previous: v1.previous, |
| 317 | stats: v1.stats, |
| 318 | profile: None, |
| 319 | plan_id: None, |
| 320 | todos: Vec::new(), |
| 321 | } |
| 322 | } else if version_byte == 2 { |
| 323 | let v2: ProvenanceGraphV2 = |
| 324 | postcard::from_bytes(&data[4..]).map_err(|e| ProvenanceGraphError::Codec { |
| 325 | reason: format!("postcard deserialize failed (v2): {}", e), |
| 326 | })?; |
| 327 | ProvenanceGraph { |