reddb_file/serverless/
manifest.rs1use 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}