1use aster_forge_cloud_files_core::{
4 CloudItem, CloudItemKey, CloudScope, MutationIntent, SessionGeneration,
5};
6use async_trait::async_trait;
7
8use crate::{
9 LinuxFileAccess, LinuxInodeGeneration, LinuxInodeRecord, LinuxNodeKind, LinuxWriteSession,
10 Result, validate_linux_name,
11};
12
13pub type LinuxNamespaceStoreResult<T> = std::result::Result<T, LinuxNamespaceMutationStoreError>;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum LinuxNamespaceMutationStoreErrorKind {
19 NotFound,
21 AlreadyExists,
23 DirectoryNotEmpty,
25 IsDirectory,
27 NotDirectory,
29 Unsupported,
31 Fenced,
33 Conflict,
35 PersistenceFailure,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
41#[error("linux namespace store {kind:?}: {context}")]
42pub struct LinuxNamespaceMutationStoreError {
43 kind: LinuxNamespaceMutationStoreErrorKind,
44 context: String,
45}
46
47impl LinuxNamespaceMutationStoreError {
48 pub fn new(kind: LinuxNamespaceMutationStoreErrorKind, context: impl Into<String>) -> Self {
50 Self {
51 kind,
52 context: context.into(),
53 }
54 }
55
56 #[must_use]
58 pub const fn kind(&self) -> LinuxNamespaceMutationStoreErrorKind {
59 self.kind
60 }
61
62 #[must_use]
64 pub fn context(&self) -> &str {
65 &self.context
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct LinuxCreateFileRequest {
72 parent_key: CloudItemKey,
73 name: String,
74 mode: u32,
75 umask: u32,
76 access: LinuxFileAccess,
77 session_generation: SessionGeneration,
78}
79
80impl LinuxCreateFileRequest {
81 pub fn new(
87 parent_key: CloudItemKey,
88 name: impl Into<String>,
89 mode: u32,
90 umask: u32,
91 access: LinuxFileAccess,
92 session_generation: SessionGeneration,
93 ) -> Result<Self> {
94 let name = name.into();
95 validate_linux_name(&name)?;
96 Ok(Self {
97 parent_key,
98 name,
99 mode,
100 umask,
101 access,
102 session_generation,
103 })
104 }
105
106 #[must_use]
108 pub const fn parent_key(&self) -> &CloudItemKey {
109 &self.parent_key
110 }
111
112 #[must_use]
114 pub fn name(&self) -> &str {
115 &self.name
116 }
117
118 #[must_use]
120 pub const fn mode(&self) -> u32 {
121 self.mode
122 }
123
124 #[must_use]
126 pub const fn umask(&self) -> u32 {
127 self.umask
128 }
129
130 #[must_use]
132 pub const fn access(&self) -> LinuxFileAccess {
133 self.access
134 }
135
136 #[must_use]
138 pub const fn session_generation(&self) -> SessionGeneration {
139 self.session_generation
140 }
141}
142
143#[derive(Debug, Clone, PartialEq, Eq)]
145pub struct LinuxCreatedFile {
146 item: CloudItem,
147 inode_record: LinuxInodeRecord,
148 mutation_intent: MutationIntent,
149}
150
151impl LinuxCreatedFile {
152 #[must_use]
154 pub const fn new(
155 item: CloudItem,
156 inode_record: LinuxInodeRecord,
157 mutation_intent: MutationIntent,
158 ) -> Self {
159 Self {
160 item,
161 inode_record,
162 mutation_intent,
163 }
164 }
165
166 #[must_use]
168 pub const fn item(&self) -> &CloudItem {
169 &self.item
170 }
171
172 #[must_use]
174 pub const fn inode_record(&self) -> &LinuxInodeRecord {
175 &self.inode_record
176 }
177
178 #[must_use]
180 pub const fn mutation_intent(&self) -> &MutationIntent {
181 &self.mutation_intent
182 }
183
184 #[must_use]
186 pub fn into_parts(self) -> (CloudItem, LinuxInodeRecord, MutationIntent) {
187 (self.item, self.inode_record, self.mutation_intent)
188 }
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
193pub struct LinuxCreateFileAcceptance {
194 created: LinuxCreatedFile,
195 session: LinuxWriteSession,
196}
197
198impl LinuxCreateFileAcceptance {
199 #[must_use]
201 pub const fn new(created: LinuxCreatedFile, session: LinuxWriteSession) -> Self {
202 Self { created, session }
203 }
204
205 #[must_use]
207 pub const fn created(&self) -> &LinuxCreatedFile {
208 &self.created
209 }
210
211 #[must_use]
213 pub const fn session(&self) -> &LinuxWriteSession {
214 &self.session
215 }
216
217 #[must_use]
219 pub fn into_parts(self) -> (LinuxCreatedFile, LinuxWriteSession) {
220 (self.created, self.session)
221 }
222}
223
224#[derive(Debug, Clone, PartialEq, Eq)]
226pub struct LinuxNamespaceItem {
227 item: CloudItem,
228 inode_record: LinuxInodeRecord,
229 mutation_intent: MutationIntent,
230}
231
232impl LinuxNamespaceItem {
233 #[must_use]
235 pub const fn new(
236 item: CloudItem,
237 inode_record: LinuxInodeRecord,
238 mutation_intent: MutationIntent,
239 ) -> Self {
240 Self {
241 item,
242 inode_record,
243 mutation_intent,
244 }
245 }
246
247 #[must_use]
249 pub const fn item(&self) -> &CloudItem {
250 &self.item
251 }
252
253 #[must_use]
255 pub const fn inode_record(&self) -> &LinuxInodeRecord {
256 &self.inode_record
257 }
258
259 #[must_use]
261 pub const fn mutation_intent(&self) -> &MutationIntent {
262 &self.mutation_intent
263 }
264
265 #[must_use]
267 pub fn into_parts(self) -> (CloudItem, LinuxInodeRecord, MutationIntent) {
268 (self.item, self.inode_record, self.mutation_intent)
269 }
270}
271
272impl From<LinuxCreatedFile> for LinuxNamespaceItem {
273 fn from(value: LinuxCreatedFile) -> Self {
274 let (item, inode_record, mutation_intent) = value.into_parts();
275 Self::new(item, inode_record, mutation_intent)
276 }
277}
278
279#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct LinuxNamespaceTombstone {
282 key: CloudItemKey,
283 inode_generation: LinuxInodeGeneration,
284 parent_key: CloudItemKey,
285 name: String,
286 kind: LinuxNodeKind,
287 mutation_intent: MutationIntent,
288}
289
290impl LinuxNamespaceTombstone {
291 pub fn new(
297 key: CloudItemKey,
298 inode_generation: LinuxInodeGeneration,
299 parent_key: CloudItemKey,
300 name: impl Into<String>,
301 kind: LinuxNodeKind,
302 mutation_intent: MutationIntent,
303 ) -> Result<Self> {
304 let name = name.into();
305 validate_linux_name(&name)?;
306 Ok(Self {
307 key,
308 inode_generation,
309 parent_key,
310 name,
311 kind,
312 mutation_intent,
313 })
314 }
315
316 #[must_use]
318 pub const fn key(&self) -> &CloudItemKey {
319 &self.key
320 }
321
322 #[must_use]
324 pub const fn inode_generation(&self) -> LinuxInodeGeneration {
325 self.inode_generation
326 }
327
328 #[must_use]
330 pub const fn parent_key(&self) -> &CloudItemKey {
331 &self.parent_key
332 }
333
334 #[must_use]
336 pub fn name(&self) -> &str {
337 &self.name
338 }
339
340 #[must_use]
342 pub const fn kind(&self) -> LinuxNodeKind {
343 self.kind
344 }
345
346 #[must_use]
348 pub const fn mutation_intent(&self) -> &MutationIntent {
349 &self.mutation_intent
350 }
351}
352
353#[derive(Debug, Clone, Default, PartialEq, Eq)]
355pub struct LinuxNamespaceOverlay {
356 items: Vec<LinuxNamespaceItem>,
357 tombstones: Vec<LinuxNamespaceTombstone>,
358}
359
360impl LinuxNamespaceOverlay {
361 #[must_use]
363 pub const fn new(
364 items: Vec<LinuxNamespaceItem>,
365 tombstones: Vec<LinuxNamespaceTombstone>,
366 ) -> Self {
367 Self { items, tombstones }
368 }
369
370 #[must_use]
372 pub fn items(&self) -> &[LinuxNamespaceItem] {
373 &self.items
374 }
375
376 #[must_use]
378 pub fn tombstones(&self) -> &[LinuxNamespaceTombstone] {
379 &self.tombstones
380 }
381
382 #[must_use]
384 pub fn into_parts(self) -> (Vec<LinuxNamespaceItem>, Vec<LinuxNamespaceTombstone>) {
385 (self.items, self.tombstones)
386 }
387}
388
389#[derive(Debug, Clone, PartialEq, Eq)]
391pub struct LinuxCreateDirectoryRequest {
392 parent_key: CloudItemKey,
393 name: String,
394 mode: u32,
395 umask: u32,
396 session_generation: SessionGeneration,
397}
398
399impl LinuxCreateDirectoryRequest {
400 pub fn new(
406 parent_key: CloudItemKey,
407 name: impl Into<String>,
408 mode: u32,
409 umask: u32,
410 session_generation: SessionGeneration,
411 ) -> Result<Self> {
412 let name = name.into();
413 validate_linux_name(&name)?;
414 Ok(Self {
415 parent_key,
416 name,
417 mode,
418 umask,
419 session_generation,
420 })
421 }
422
423 #[must_use]
424 pub const fn parent_key(&self) -> &CloudItemKey {
425 &self.parent_key
426 }
427
428 #[must_use]
429 pub fn name(&self) -> &str {
430 &self.name
431 }
432
433 #[must_use]
434 pub const fn mode(&self) -> u32 {
435 self.mode
436 }
437
438 #[must_use]
439 pub const fn umask(&self) -> u32 {
440 self.umask
441 }
442
443 #[must_use]
444 pub const fn session_generation(&self) -> SessionGeneration {
445 self.session_generation
446 }
447}
448
449#[derive(Debug, Clone, PartialEq, Eq)]
451pub struct LinuxRenameRequest {
452 key: CloudItemKey,
453 inode_generation: LinuxInodeGeneration,
454 kind: LinuxNodeKind,
455 old_parent_key: CloudItemKey,
456 old_name: String,
457 new_parent_key: CloudItemKey,
458 new_name: String,
459 destination: Option<LinuxRenameDestination>,
460 no_replace: bool,
461 session_generation: SessionGeneration,
462}
463
464impl LinuxRenameRequest {
465 #[expect(
466 clippy::too_many_arguments,
467 reason = "the request preserves exact native source, destination, and generation facts"
468 )]
469 pub fn new(
474 key: CloudItemKey,
475 inode_generation: LinuxInodeGeneration,
476 kind: LinuxNodeKind,
477 old_parent_key: CloudItemKey,
478 old_name: impl Into<String>,
479 new_parent_key: CloudItemKey,
480 new_name: impl Into<String>,
481 destination: Option<LinuxRenameDestination>,
482 no_replace: bool,
483 session_generation: SessionGeneration,
484 ) -> Result<Self> {
485 let old_name = old_name.into();
486 let new_name = new_name.into();
487 validate_linux_name(&old_name)?;
488 validate_linux_name(&new_name)?;
489 Ok(Self {
490 key,
491 inode_generation,
492 kind,
493 old_parent_key,
494 old_name,
495 new_parent_key,
496 new_name,
497 destination,
498 no_replace,
499 session_generation,
500 })
501 }
502
503 #[must_use]
504 pub const fn key(&self) -> &CloudItemKey {
505 &self.key
506 }
507
508 #[must_use]
509 pub const fn inode_generation(&self) -> LinuxInodeGeneration {
510 self.inode_generation
511 }
512
513 #[must_use]
514 pub const fn kind(&self) -> LinuxNodeKind {
515 self.kind
516 }
517
518 #[must_use]
519 pub const fn old_parent_key(&self) -> &CloudItemKey {
520 &self.old_parent_key
521 }
522
523 #[must_use]
524 pub fn old_name(&self) -> &str {
525 &self.old_name
526 }
527
528 #[must_use]
529 pub const fn new_parent_key(&self) -> &CloudItemKey {
530 &self.new_parent_key
531 }
532
533 #[must_use]
534 pub fn new_name(&self) -> &str {
535 &self.new_name
536 }
537
538 #[must_use]
539 pub const fn destination(&self) -> Option<&LinuxRenameDestination> {
540 self.destination.as_ref()
541 }
542
543 #[must_use]
544 pub const fn no_replace(&self) -> bool {
545 self.no_replace
546 }
547
548 #[must_use]
549 pub const fn session_generation(&self) -> SessionGeneration {
550 self.session_generation
551 }
552}
553
554#[derive(Debug, Clone, PartialEq, Eq)]
556pub struct LinuxRenameDestination {
557 key: CloudItemKey,
558 inode_generation: LinuxInodeGeneration,
559 kind: LinuxNodeKind,
560}
561
562impl LinuxRenameDestination {
563 #[must_use]
564 pub const fn new(
565 key: CloudItemKey,
566 inode_generation: LinuxInodeGeneration,
567 kind: LinuxNodeKind,
568 ) -> Self {
569 Self {
570 key,
571 inode_generation,
572 kind,
573 }
574 }
575
576 #[must_use]
577 pub const fn key(&self) -> &CloudItemKey {
578 &self.key
579 }
580
581 #[must_use]
582 pub const fn inode_generation(&self) -> LinuxInodeGeneration {
583 self.inode_generation
584 }
585
586 #[must_use]
587 pub const fn kind(&self) -> LinuxNodeKind {
588 self.kind
589 }
590}
591
592#[derive(Debug, Clone, PartialEq, Eq)]
594pub struct LinuxRenameAcceptance {
595 item: LinuxNamespaceItem,
596 source: LinuxNamespaceTombstone,
597 replaced: Option<LinuxNamespaceTombstone>,
598}
599
600impl LinuxRenameAcceptance {
601 #[must_use]
602 pub const fn new(
603 item: LinuxNamespaceItem,
604 source: LinuxNamespaceTombstone,
605 replaced: Option<LinuxNamespaceTombstone>,
606 ) -> Self {
607 Self {
608 item,
609 source,
610 replaced,
611 }
612 }
613
614 #[must_use]
615 pub const fn item(&self) -> &LinuxNamespaceItem {
616 &self.item
617 }
618
619 #[must_use]
620 pub const fn source(&self) -> &LinuxNamespaceTombstone {
621 &self.source
622 }
623
624 #[must_use]
625 pub const fn replaced(&self) -> Option<&LinuxNamespaceTombstone> {
626 self.replaced.as_ref()
627 }
628
629 #[must_use]
630 pub fn into_parts(
631 self,
632 ) -> (
633 LinuxNamespaceItem,
634 LinuxNamespaceTombstone,
635 Option<LinuxNamespaceTombstone>,
636 ) {
637 (self.item, self.source, self.replaced)
638 }
639}
640
641#[derive(Debug, Clone, PartialEq, Eq)]
643pub struct LinuxRemoveRequest {
644 key: CloudItemKey,
645 inode_generation: LinuxInodeGeneration,
646 parent_key: CloudItemKey,
647 name: String,
648 kind: LinuxNodeKind,
649 session_generation: SessionGeneration,
650}
651
652impl LinuxRemoveRequest {
653 pub fn new(
658 key: CloudItemKey,
659 inode_generation: LinuxInodeGeneration,
660 parent_key: CloudItemKey,
661 name: impl Into<String>,
662 kind: LinuxNodeKind,
663 session_generation: SessionGeneration,
664 ) -> Result<Self> {
665 let name = name.into();
666 validate_linux_name(&name)?;
667 Ok(Self {
668 key,
669 inode_generation,
670 parent_key,
671 name,
672 kind,
673 session_generation,
674 })
675 }
676
677 #[must_use]
678 pub const fn key(&self) -> &CloudItemKey {
679 &self.key
680 }
681
682 #[must_use]
683 pub const fn inode_generation(&self) -> LinuxInodeGeneration {
684 self.inode_generation
685 }
686
687 #[must_use]
688 pub const fn parent_key(&self) -> &CloudItemKey {
689 &self.parent_key
690 }
691
692 #[must_use]
693 pub fn name(&self) -> &str {
694 &self.name
695 }
696
697 #[must_use]
698 pub const fn kind(&self) -> LinuxNodeKind {
699 self.kind
700 }
701
702 #[must_use]
703 pub const fn session_generation(&self) -> SessionGeneration {
704 self.session_generation
705 }
706}
707
708#[async_trait]
714pub trait LinuxNamespaceMutationStore: Send + Sync {
715 async fn activate_namespace(
717 &self,
718 scope: &CloudScope,
719 session_generation: SessionGeneration,
720 ) -> LinuxNamespaceStoreResult<Vec<LinuxCreatedFile>>;
721
722 async fn create_file(
724 &self,
725 request: &LinuxCreateFileRequest,
726 ) -> LinuxNamespaceStoreResult<LinuxCreateFileAcceptance>;
727
728 async fn open_created_file(
730 &self,
731 key: &CloudItemKey,
732 session_generation: SessionGeneration,
733 ) -> LinuxNamespaceStoreResult<LinuxWriteSession>;
734
735 async fn activate_namespace_overlay(
737 &self,
738 _scope: &CloudScope,
739 _session_generation: SessionGeneration,
740 ) -> LinuxNamespaceStoreResult<LinuxNamespaceOverlay> {
741 Ok(LinuxNamespaceOverlay::default())
742 }
743
744 async fn create_directory(
746 &self,
747 _request: &LinuxCreateDirectoryRequest,
748 ) -> LinuxNamespaceStoreResult<LinuxNamespaceItem> {
749 Err(LinuxNamespaceMutationStoreError::new(
750 LinuxNamespaceMutationStoreErrorKind::Unsupported,
751 "directory create is not implemented by this namespace store",
752 ))
753 }
754
755 async fn rename(
757 &self,
758 _request: &LinuxRenameRequest,
759 ) -> LinuxNamespaceStoreResult<LinuxRenameAcceptance> {
760 Err(LinuxNamespaceMutationStoreError::new(
761 LinuxNamespaceMutationStoreErrorKind::Unsupported,
762 "rename is not implemented by this namespace store",
763 ))
764 }
765
766 async fn remove(
768 &self,
769 _request: &LinuxRemoveRequest,
770 ) -> LinuxNamespaceStoreResult<LinuxNamespaceTombstone> {
771 Err(LinuxNamespaceMutationStoreError::new(
772 LinuxNamespaceMutationStoreErrorKind::Unsupported,
773 "remove is not implemented by this namespace store",
774 ))
775 }
776}