Skip to main content

reddb_file/serverless/
manifest.rs

1use super::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct ServerlessManifestEntry {
5    pub kind: ServerlessPackKind,
6    pub relative_path: PathBuf,
7    pub bytes: u64,
8    pub checksum: u32,
9    pub content_hash: ServerlessContentHash,
10}
11
12impl ServerlessManifestEntry {
13    pub fn new(
14        kind: ServerlessPackKind,
15        relative_path: impl Into<PathBuf>,
16        bytes: u64,
17        checksum: u32,
18    ) -> Self {
19        Self {
20            kind,
21            relative_path: relative_path.into(),
22            bytes,
23            checksum,
24            content_hash: ServerlessContentHash::ZERO,
25        }
26    }
27
28    pub fn from_bytes(
29        kind: ServerlessPackKind,
30        relative_path: impl Into<PathBuf>,
31        payload: &[u8],
32    ) -> Self {
33        Self {
34            kind,
35            relative_path: relative_path.into(),
36            bytes: payload.len() as u64,
37            checksum: crc32(payload),
38            content_hash: ServerlessContentHash::from_bytes(payload),
39        }
40    }
41
42    pub fn with_content_hash(mut self, content_hash: ServerlessContentHash) -> Self {
43        self.content_hash = content_hash;
44        self
45    }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct ServerlessManifest {
50    pub namespace: String,
51    pub generation: u64,
52    pub entries: Vec<ServerlessManifestEntry>,
53}
54
55impl ServerlessManifest {
56    pub fn new(namespace: impl Into<String>, generation: u64) -> Self {
57        Self {
58            namespace: namespace.into(),
59            generation,
60            entries: Vec::new(),
61        }
62    }
63
64    pub fn push(&mut self, entry: ServerlessManifestEntry) {
65        self.entries.push(entry);
66        self.entries.sort_by_key(|entry| {
67            (
68                u8::from(entry.kind),
69                entry.relative_path.to_string_lossy().to_string(),
70            )
71        });
72    }
73
74    pub fn write_to_path(&self, path: impl AsRef<Path>) -> RdbFileResult<()> {
75        write_bytes(path, &self.encode())
76    }
77
78    pub fn read_from_path(path: impl AsRef<Path>) -> RdbFileResult<Self> {
79        Self::decode(&fs::read(path)?)
80    }
81
82    pub fn encode(&self) -> Vec<u8> {
83        let mut out = Vec::new();
84        out.extend_from_slice(SERVERLESS_MANIFEST_MAGIC);
85        put_u16(&mut out, SERVERLESS_ARTIFACT_VERSION);
86        put_u64(&mut out, self.generation);
87        put_string(&mut out, &self.namespace);
88        put_u32(&mut out, self.entries.len() as u32);
89        for entry in &self.entries {
90            out.push(u8::from(entry.kind));
91            put_string(&mut out, &entry.relative_path.to_string_lossy());
92            put_u64(&mut out, entry.bytes);
93            put_u32(&mut out, entry.checksum);
94            put_content_hash(&mut out, entry.content_hash);
95        }
96        let checksum = crc32(&out);
97        put_u32(&mut out, checksum);
98        out
99    }
100
101    pub fn decode(bytes: &[u8]) -> RdbFileResult<Self> {
102        verify_checksum(bytes)?;
103        let mut cursor = 0usize;
104        expect_magic(bytes, &mut cursor, SERVERLESS_MANIFEST_MAGIC)?;
105        let version = take_u16(bytes, &mut cursor)?;
106        if version != SERVERLESS_ARTIFACT_VERSION {
107            return Err(RdbFileError::InvalidOperation(format!(
108                "unsupported serverless manifest version {version}"
109            )));
110        }
111        let generation = take_u64(bytes, &mut cursor)?;
112        let namespace = take_string(bytes, &mut cursor)?;
113        let count = take_u32(bytes, &mut cursor)? as usize;
114        let mut entries = Vec::with_capacity(count);
115        for _ in 0..count {
116            let kind = ServerlessPackKind::try_from(take_u8(bytes, &mut cursor)?)?;
117            let relative_path = PathBuf::from(take_string(bytes, &mut cursor)?);
118            let bytes_len = take_u64(bytes, &mut cursor)?;
119            let checksum = take_u32(bytes, &mut cursor)?;
120            let content_hash = take_content_hash(bytes, &mut cursor)?;
121            entries.push(ServerlessManifestEntry {
122                kind,
123                relative_path,
124                bytes: bytes_len,
125                checksum,
126                content_hash,
127            });
128        }
129        reject_trailing_bytes(bytes, cursor)?;
130        Ok(Self {
131            namespace,
132            generation,
133            entries,
134        })
135    }
136}