1use super::{PreparedCommit, ResolvedBinding, ValidatedOp};
5use loonfs_api::wire::wal::WalDelta;
6use loonfs_api::{ContentRef, InodeId, InodeKind, RevisionNo};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub struct MaterializedCommitDelta {
11 pub semantic_op_index: u32,
12 pub delta_index: u32,
13 pub wal_delta: WalDelta,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub(crate) struct MaterializedCommit {
18 pub prepared: PreparedCommit,
19 pub committed_at_ms: u64,
24 pub deltas: Vec<MaterializedCommitDelta>,
25 pub results: Vec<CommitOpResult>,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(tag = "kind", rename_all = "snake_case")]
30pub enum CommitOpResult {
31 CreateDirectory {
32 op_index: u32,
33 inode_id: InodeId,
34 },
35 CreateFile {
36 op_index: u32,
37 inode_id: InodeId,
38 revision_no: RevisionNo,
39 content_ref: ContentRef,
40 },
41 ReplaceFile {
42 op_index: u32,
43 inode_id: InodeId,
44 revision_no: RevisionNo,
45 content_ref: ContentRef,
46 },
47 RestoreRevision {
48 op_index: u32,
49 inode_id: InodeId,
50 source_revision_no: RevisionNo,
51 revision_no: RevisionNo,
52 content_ref: ContentRef,
53 },
54 DeleteFile {
55 op_index: u32,
56 inode_id: InodeId,
57 },
58 Rename {
59 op_index: u32,
60 inode_id: InodeId,
61 },
62 DeleteSubtree {
63 op_index: u32,
64 root_inode_id: InodeId,
65 },
66 Undelete {
67 op_index: u32,
68 inode_id: InodeId,
69 },
70}
71
72pub(crate) fn materialize_commit(
73 prepared: PreparedCommit,
74 committed_at_ms: u64,
75) -> MaterializedCommit {
76 let mut deltas = Vec::new();
77 let mut results = Vec::with_capacity(prepared.plan.validated_ops.len());
78 for op in &prepared.plan.validated_ops {
79 let (mut op_deltas, result) = materialize_validated_op(op);
80 deltas.append(&mut op_deltas);
81 results.push(result);
82 }
83
84 MaterializedCommit {
85 prepared,
86 committed_at_ms,
87 deltas,
88 results,
89 }
90}
91
92pub(super) fn materialize_validated_op(
93 op: &ValidatedOp,
94) -> (Vec<MaterializedCommitDelta>, CommitOpResult) {
95 let mut deltas = Vec::new();
96 let result = match op {
97 ValidatedOp::CreateDir {
98 op_index,
99 parent_inode_id,
100 display_name,
101 name_key,
102 child_inode_id,
103 create_inode_delta_index,
104 bind_delta_index,
105 } => {
106 push_delta(
107 &mut deltas,
108 *op_index,
109 WalDelta::CreateInode {
110 delta_index: *create_inode_delta_index,
111 inode_id: *child_inode_id,
112 inode_kind: InodeKind::Directory,
113 },
114 );
115 push_delta(
116 &mut deltas,
117 *op_index,
118 WalDelta::BindDirentry {
119 delta_index: *bind_delta_index,
120 parent_inode_id: *parent_inode_id,
121 name_key: name_key.clone(),
122 display_name: display_name.clone(),
123 child_inode_id: *child_inode_id,
124 },
125 );
126 CommitOpResult::CreateDirectory {
127 op_index: *op_index,
128 inode_id: *child_inode_id,
129 }
130 }
131 ValidatedOp::CreateFile {
132 op_index,
133 parent_inode_id,
134 display_name,
135 name_key,
136 child_inode_id,
137 content_ref,
138 create_inode_delta_index,
139 bind_delta_index,
140 revision_delta_index,
141 } => {
142 push_delta(
143 &mut deltas,
144 *op_index,
145 WalDelta::CreateInode {
146 delta_index: *create_inode_delta_index,
147 inode_id: *child_inode_id,
148 inode_kind: InodeKind::File,
149 },
150 );
151 push_delta(
152 &mut deltas,
153 *op_index,
154 WalDelta::BindDirentry {
155 delta_index: *bind_delta_index,
156 parent_inode_id: *parent_inode_id,
157 name_key: name_key.clone(),
158 display_name: display_name.clone(),
159 child_inode_id: *child_inode_id,
160 },
161 );
162 push_delta(
163 &mut deltas,
164 *op_index,
165 WalDelta::AppendFileRevision {
166 delta_index: *revision_delta_index,
167 inode_id: *child_inode_id,
168 revision_no: RevisionNo(1),
169 content_ref: content_ref.clone(),
170 },
171 );
172 CommitOpResult::CreateFile {
173 op_index: *op_index,
174 inode_id: *child_inode_id,
175 revision_no: RevisionNo(1),
176 content_ref: content_ref.clone(),
177 }
178 }
179 ValidatedOp::ReplaceFile {
180 op_index,
181 inode_id,
182 revision_no,
183 content_ref,
184 revision_delta_index,
185 } => {
186 push_delta(
187 &mut deltas,
188 *op_index,
189 WalDelta::AppendFileRevision {
190 delta_index: *revision_delta_index,
191 inode_id: *inode_id,
192 revision_no: *revision_no,
193 content_ref: content_ref.clone(),
194 },
195 );
196 CommitOpResult::ReplaceFile {
197 op_index: *op_index,
198 inode_id: *inode_id,
199 revision_no: *revision_no,
200 content_ref: content_ref.clone(),
201 }
202 }
203 ValidatedOp::RestoreRevision {
204 op_index,
205 inode_id,
206 source_revision_no,
207 revision_no,
208 content_ref,
209 revision_delta_index,
210 } => {
211 push_delta(
212 &mut deltas,
213 *op_index,
214 WalDelta::AppendFileRevision {
215 delta_index: *revision_delta_index,
216 inode_id: *inode_id,
217 revision_no: *revision_no,
218 content_ref: content_ref.clone(),
219 },
220 );
221 CommitOpResult::RestoreRevision {
222 op_index: *op_index,
223 inode_id: *inode_id,
224 source_revision_no: *source_revision_no,
225 revision_no: *revision_no,
226 content_ref: content_ref.clone(),
227 }
228 }
229 ValidatedOp::DeleteFile {
230 op_index,
231 inode_id,
232 source_binding,
233 unbind_delta_index,
234 tombstone_delta_index,
235 } => {
236 push_unbind_delta(&mut deltas, *op_index, *unbind_delta_index, source_binding);
237 push_delta(
238 &mut deltas,
239 *op_index,
240 WalDelta::TombstoneSubtree {
241 delta_index: *tombstone_delta_index,
242 root_inode_id: *inode_id,
243 parent_inode_id: Some(source_binding.parent_inode_id),
244 name_key: Some(source_binding.name_key.clone()),
245 display_name: Some(source_binding.display_name.clone()),
246 },
247 );
248 CommitOpResult::DeleteFile {
249 op_index: *op_index,
250 inode_id: *inode_id,
251 }
252 }
253 ValidatedOp::Rename {
254 op_index,
255 inode_id,
256 new_parent_inode_id,
257 new_display_name,
258 new_name_key,
259 source_binding,
260 unbind_delta_index,
261 bind_delta_index,
262 } => {
263 push_unbind_delta(&mut deltas, *op_index, *unbind_delta_index, source_binding);
264 push_delta(
265 &mut deltas,
266 *op_index,
267 WalDelta::BindDirentry {
268 delta_index: *bind_delta_index,
269 parent_inode_id: *new_parent_inode_id,
270 name_key: new_name_key.clone(),
271 display_name: new_display_name.clone(),
272 child_inode_id: *inode_id,
273 },
274 );
275 CommitOpResult::Rename {
276 op_index: *op_index,
277 inode_id: *inode_id,
278 }
279 }
280 ValidatedOp::DeleteSubtree {
281 op_index,
282 root_inode_id,
283 source_binding,
284 unbind_delta_index,
285 tombstone_delta_index,
286 } => {
287 push_unbind_delta(&mut deltas, *op_index, *unbind_delta_index, source_binding);
288 push_delta(
289 &mut deltas,
290 *op_index,
291 WalDelta::TombstoneSubtree {
292 delta_index: *tombstone_delta_index,
293 root_inode_id: *root_inode_id,
294 parent_inode_id: Some(source_binding.parent_inode_id),
295 name_key: Some(source_binding.name_key.clone()),
296 display_name: Some(source_binding.display_name.clone()),
297 },
298 );
299 CommitOpResult::DeleteSubtree {
300 op_index: *op_index,
301 root_inode_id: *root_inode_id,
302 }
303 }
304 ValidatedOp::Undelete {
305 op_index,
306 inode_id,
307 parent_inode_id,
308 display_name,
309 name_key,
310 target_seq,
311 target_delta_index,
312 revoke_tombstone_delta_index,
313 bind_delta_index,
314 } => {
315 push_delta(
319 &mut deltas,
320 *op_index,
321 WalDelta::RevokeSubtreeTombstone {
322 delta_index: *revoke_tombstone_delta_index,
323 root_inode_id: *inode_id,
324 target_seq: *target_seq,
325 target_delta_index: *target_delta_index,
326 },
327 );
328 push_delta(
329 &mut deltas,
330 *op_index,
331 WalDelta::BindDirentry {
332 delta_index: *bind_delta_index,
333 parent_inode_id: *parent_inode_id,
334 name_key: name_key.clone(),
335 display_name: display_name.clone(),
336 child_inode_id: *inode_id,
337 },
338 );
339 CommitOpResult::Undelete {
340 op_index: *op_index,
341 inode_id: *inode_id,
342 }
343 }
344 };
345
346 (deltas, result)
347}
348
349fn push_unbind_delta(
350 deltas: &mut Vec<MaterializedCommitDelta>,
351 semantic_op_index: u32,
352 delta_index: u32,
353 binding: &ResolvedBinding,
354) {
355 push_delta(
356 deltas,
357 semantic_op_index,
358 WalDelta::UnbindDirentry {
359 delta_index,
360 parent_inode_id: binding.parent_inode_id,
361 name_key: binding.name_key.clone(),
362 display_name: binding.display_name.clone(),
363 child_inode_id: binding.child_inode_id,
364 bind_seq: binding.bind_seq,
365 bind_delta_index: binding.bind_delta_index,
366 },
367 )
368}
369
370fn push_delta(
371 deltas: &mut Vec<MaterializedCommitDelta>,
372 semantic_op_index: u32,
373 wal_delta: WalDelta,
374) {
375 let delta_index = wal_delta_index(&wal_delta);
376 deltas.push(MaterializedCommitDelta {
377 semantic_op_index,
378 delta_index,
379 wal_delta,
380 });
381}
382
383fn wal_delta_index(wal_delta: &WalDelta) -> u32 {
384 match wal_delta {
385 WalDelta::CreateInode { delta_index, .. }
386 | WalDelta::BindDirentry { delta_index, .. }
387 | WalDelta::UnbindDirentry { delta_index, .. }
388 | WalDelta::AppendFileRevision { delta_index, .. }
389 | WalDelta::TombstoneSubtree { delta_index, .. }
390 | WalDelta::RevokeSubtreeTombstone { delta_index, .. } => *delta_index,
391 }
392}