Skip to main content

nook_vault/
manifest.rs

1use crate::{NookError, Result};
2use serde::{Deserialize, Serialize};
3use sha2::{Digest, Sha256};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
7pub enum ObjectType {
8    Manifest,
9    Content,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub enum NodeType {
14    #[serde(rename = "file")]
15    File,
16    #[serde(rename = "directory")]
17    Directory,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct Node {
22    pub node_id: u64,
23    pub parent_id: Option<u64>,
24    pub name: String,
25    pub node_type: NodeType,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub content_object_id: Option<[u8; 32]>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub wrapped_dek: Option<Vec<u8>>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub logical_size: Option<u64>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Manifest {
36    pub manifest_version: u32,
37    pub root_node_id: u64,
38    pub nodes: Vec<Node>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub previous_manifest_hash: Option<String>,
41    pub integrity_checksum: String,
42}
43
44#[derive(Serialize)]
45struct ManifestIntegrityView<'a> {
46    manifest_version: u32,
47    root_node_id: u64,
48    nodes: &'a [Node],
49    #[serde(skip_serializing_if = "Option::is_none")]
50    previous_manifest_hash: &'a Option<String>,
51}
52
53impl Manifest {
54    pub fn compute_integrity(&self) -> Result<String> {
55        let view = ManifestIntegrityView {
56            manifest_version: self.manifest_version,
57            root_node_id: self.root_node_id,
58            nodes: &self.nodes,
59            previous_manifest_hash: &self.previous_manifest_hash,
60        };
61        let bytes =
62            serde_json::to_vec(&view).map_err(|e| NookError::Serialization(e.to_string()))?;
63        Ok(hex::encode(Sha256::digest(bytes)))
64    }
65
66    pub fn validate_integrity(&self) -> Result<()> {
67        let expected = self.compute_integrity()?;
68        if expected != self.integrity_checksum {
69            return Err(NookError::Integrity("manifest checksum mismatch".into()));
70        }
71        Ok(())
72    }
73}