Skip to main content

qubit_fs/temp/
persist_options.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Temporary resource persistence options.
9
10use crate::copy::MetadataPreservePolicy;
11use crate::error::FsError;
12use crate::error::FsErrorKind;
13use crate::error::FsOperation;
14use crate::metadata::AtomicityRequirement;
15use crate::metadata::FileSystemCapabilities;
16use crate::metadata::FileSystemCapability;
17
18/// Options controlling temporary resource persistence.
19///
20/// # Examples
21///
22/// ```rust
23/// use qubit_fs::temp::PersistOptions;
24///
25/// assert!(!PersistOptions::default().overwrite());
26/// ```
27#[non_exhaustive]
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct PersistOptions {
30    /// Whether the destination may be overwritten.
31    overwrite: bool,
32    /// Required atomicity level.
33    atomicity: AtomicityRequirement,
34    /// Metadata preservation policy.
35    preserve_metadata: MetadataPreservePolicy,
36    /// Whether a missing destination parent is created before publication.
37    create_parent: bool,
38}
39
40impl PersistOptions {
41    /// Returns whether the destination may be overwritten.
42    #[inline]
43    #[must_use]
44    pub const fn overwrite(&self) -> bool {
45        self.overwrite
46    }
47
48    /// Returns the required atomicity level.
49    #[inline]
50    #[must_use]
51    pub const fn atomicity(&self) -> AtomicityRequirement {
52        self.atomicity
53    }
54
55    /// Returns the metadata preservation policy.
56    #[inline]
57    #[must_use]
58    pub const fn preserve_metadata(&self) -> MetadataPreservePolicy {
59        self.preserve_metadata
60    }
61
62    /// Returns whether missing destination parents are created.
63    #[inline]
64    #[must_use]
65    pub const fn creates_parent(&self) -> bool {
66        self.create_parent
67    }
68
69    /// Enables recursive creation of a missing destination parent.
70    #[inline]
71    #[must_use]
72    pub const fn with_create_parent(mut self) -> Self {
73        self.create_parent = true;
74        self
75    }
76
77    /// Replaces whether the destination may be overwritten.
78    #[inline]
79    #[must_use]
80    pub const fn with_overwrite(mut self, overwrite: bool) -> Self {
81        self.overwrite = overwrite;
82        self
83    }
84
85    /// Replaces the required atomicity level.
86    #[inline]
87    #[must_use]
88    pub const fn with_atomicity(mut self, atomicity: AtomicityRequirement) -> Self {
89        self.atomicity = atomicity;
90        self
91    }
92
93    /// Replaces the metadata preservation policy.
94    #[inline]
95    #[must_use]
96    pub const fn with_preserve_metadata(mut self, preserve_metadata: MetadataPreservePolicy) -> Self {
97        self.preserve_metadata = preserve_metadata;
98        self
99    }
100
101    /// Validates required persistence guarantees before provider side effects.
102    ///
103    /// # Errors
104    /// Returns [`FsErrorKind::RequirementNotMet`] when atomic persistence is
105    /// required but the configured filesystem does not guarantee it.
106    pub fn validate_against(&self, capabilities: FileSystemCapabilities) -> Result<(), FsError> {
107        if self.atomicity() == AtomicityRequirement::Required
108            && !capabilities.supports(FileSystemCapability::AtomicTempPersist)
109        {
110            return Err(FsError::new(
111                FsErrorKind::RequirementNotMet,
112                FsOperation::PersistTemp,
113                "atomic temporary persistence is required but not supported",
114            )
115            .with_required_capability(FileSystemCapability::AtomicTempPersist));
116        }
117        Ok(())
118    }
119}
120
121impl Default for PersistOptions {
122    #[inline]
123    fn default() -> Self {
124        Self {
125            overwrite: false,
126            atomicity: AtomicityRequirement::Required,
127            preserve_metadata: MetadataPreservePolicy::Portable,
128            create_parent: false,
129        }
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::PersistOptions;
136    use crate::copy::MetadataPreservePolicy;
137    use crate::metadata::AtomicityRequirement;
138
139    #[test]
140    fn option_accessors_are_executed_at_runtime() {
141        let options = PersistOptions::default()
142            .with_overwrite(true)
143            .with_atomicity(AtomicityRequirement::NotRequired)
144            .with_preserve_metadata(MetadataPreservePolicy::All)
145            .with_create_parent();
146
147        assert!(options.overwrite());
148        assert_eq!(options.atomicity(), AtomicityRequirement::NotRequired);
149        assert_eq!(options.preserve_metadata(), MetadataPreservePolicy::All);
150        assert!(options.creates_parent());
151    }
152}