qubit_fs/copy/
copy_options.rs1use std::time::Duration;
11
12use crate::copy::CopyConflictPolicy;
13use crate::copy::CopyMode;
14use crate::copy::MetadataPreservePolicy;
15use crate::copy::ServerSidePreference;
16use crate::error::FsError;
17use crate::error::FsErrorKind;
18use crate::error::FsOperation;
19use crate::metadata::AtomicityRequirement;
20use crate::metadata::DurabilityRequirement;
21use crate::metadata::FileSystemCapabilities;
22use crate::metadata::FileSystemCapability;
23use crate::metadata::SymlinkPolicy;
24
25#[non_exhaustive]
36#[derive(Clone, Debug, PartialEq)]
37pub struct CopyOptions {
38 mode: CopyMode,
40 conflict: CopyConflictPolicy,
42 preserve_metadata: MetadataPreservePolicy,
44 server_side: ServerSidePreference,
46 symlink_policy: Option<SymlinkPolicy>,
48 create_parent: bool,
50 continue_on_error: bool,
52 atomicity: AtomicityRequirement,
54 durability: DurabilityRequirement,
56 max_depth: Option<usize>,
58 max_entries: Option<usize>,
64 max_bytes: Option<u64>,
66 deadline: Option<Duration>,
73}
74
75impl CopyOptions {
76 #[inline]
78 #[must_use]
79 pub const fn with_mode(mut self, mode: CopyMode) -> Self {
80 self.mode = mode;
81 self
82 }
83
84 #[inline]
86 #[must_use]
87 pub const fn mode(&self) -> CopyMode {
88 self.mode
89 }
90
91 #[inline]
93 #[must_use]
94 pub const fn with_conflict(mut self, conflict: CopyConflictPolicy) -> Self {
95 self.conflict = conflict;
96 self
97 }
98
99 #[inline]
101 #[must_use]
102 pub const fn conflict(&self) -> CopyConflictPolicy {
103 self.conflict
104 }
105
106 #[inline]
108 #[must_use]
109 pub const fn with_preserve_metadata(mut self, preserve_metadata: MetadataPreservePolicy) -> Self {
110 self.preserve_metadata = preserve_metadata;
111 self
112 }
113
114 #[inline]
116 #[must_use]
117 pub const fn preserve_metadata(&self) -> MetadataPreservePolicy {
118 self.preserve_metadata
119 }
120
121 #[inline]
123 #[must_use]
124 pub const fn with_server_side(mut self, server_side: ServerSidePreference) -> Self {
125 self.server_side = server_side;
126 self
127 }
128
129 #[inline]
131 #[must_use]
132 pub const fn server_side(&self) -> ServerSidePreference {
133 self.server_side
134 }
135
136 #[inline]
138 #[must_use]
139 pub const fn with_symlink_policy(mut self, policy: SymlinkPolicy) -> Self {
140 self.symlink_policy = Some(policy);
141 self
142 }
143
144 #[inline]
146 #[must_use]
147 pub const fn symlink_policy_override(&self) -> Option<SymlinkPolicy> {
148 self.symlink_policy
149 }
150
151 #[inline]
153 #[must_use]
154 pub const fn with_create_parent(mut self, create: bool) -> Self {
155 self.create_parent = create;
156 self
157 }
158
159 #[inline]
161 #[must_use]
162 pub const fn create_parent(&self) -> bool {
163 self.create_parent
164 }
165
166 #[inline]
168 #[must_use]
169 pub const fn with_continue_on_error(mut self, continue_on_error: bool) -> Self {
170 self.continue_on_error = continue_on_error;
171 self
172 }
173
174 #[inline]
176 #[must_use]
177 pub const fn continue_on_error(&self) -> bool {
178 self.continue_on_error
179 }
180
181 #[inline]
183 #[must_use]
184 pub const fn with_atomicity(mut self, atomicity: AtomicityRequirement) -> Self {
185 self.atomicity = atomicity;
186 self
187 }
188
189 #[inline]
191 #[must_use]
192 pub const fn atomicity(&self) -> AtomicityRequirement {
193 self.atomicity
194 }
195
196 #[inline]
198 #[must_use]
199 pub const fn with_durability(mut self, durability: DurabilityRequirement) -> Self {
200 self.durability = durability;
201 self
202 }
203
204 #[inline]
206 #[must_use]
207 pub const fn durability(&self) -> DurabilityRequirement {
208 self.durability
209 }
210
211 #[inline]
213 #[must_use]
214 pub const fn with_max_depth(mut self, max_depth: Option<usize>) -> Self {
215 self.max_depth = max_depth;
216 self
217 }
218
219 #[inline]
221 #[must_use]
222 pub const fn max_depth(&self) -> Option<usize> {
223 self.max_depth
224 }
225
226 #[inline]
232 #[must_use]
233 pub const fn with_max_entries(mut self, max_entries: Option<usize>) -> Self {
234 self.max_entries = max_entries;
235 self
236 }
237
238 #[inline]
245 #[must_use]
246 pub const fn max_entries(&self) -> Option<usize> {
247 self.max_entries
248 }
249
250 #[inline]
252 #[must_use]
253 pub const fn with_max_bytes(mut self, max_bytes: Option<u64>) -> Self {
254 self.max_bytes = max_bytes;
255 self
256 }
257
258 #[inline]
260 #[must_use]
261 pub const fn max_bytes(&self) -> Option<u64> {
262 self.max_bytes
263 }
264
265 #[inline]
267 #[must_use]
268 pub const fn with_deadline(mut self, deadline: Option<Duration>) -> Self {
269 self.deadline = deadline;
270 self
271 }
272
273 #[inline]
275 #[must_use]
276 pub const fn deadline(&self) -> Option<Duration> {
277 self.deadline
278 }
279
280 #[inline]
285 #[must_use]
286 pub fn file() -> Self {
287 Self {
288 mode: CopyMode::File,
289 ..Self::default()
290 }
291 }
292
293 #[inline]
298 #[must_use]
299 pub fn tree() -> Self {
300 Self {
301 mode: CopyMode::Tree,
302 ..Self::default()
303 }
304 }
305
306 pub fn validate_against(&self, capabilities: FileSystemCapabilities) -> Result<(), FsError> {
318 if self.server_side == ServerSidePreference::Require
319 && !capabilities.supports(FileSystemCapability::ServerSideCopy)
320 {
321 return Err(FsError::new(
322 FsErrorKind::RequirementNotMet,
323 FsOperation::Copy,
324 "server-side copy is required but not supported",
325 )
326 .with_required_capability(FileSystemCapability::ServerSideCopy));
327 }
328 let atomic_capability = match self.mode {
329 CopyMode::Auto => FileSystemCapability::AtomicFileCopy,
330 CopyMode::File => FileSystemCapability::AtomicFileCopy,
331 CopyMode::Tree => FileSystemCapability::AtomicTreeCopy,
332 };
333 let atomic_supported = match self.mode {
334 CopyMode::Auto => {
335 capabilities.supports(FileSystemCapability::AtomicFileCopy)
336 || capabilities.supports(FileSystemCapability::AtomicTreeCopy)
337 }
338 CopyMode::File | CopyMode::Tree => capabilities.supports(atomic_capability),
339 };
340 if self.atomicity == AtomicityRequirement::Required && !atomic_supported {
341 return Err(FsError::new(
342 FsErrorKind::RequirementNotMet,
343 FsOperation::Copy,
344 "atomic copy publication is required but not supported",
345 )
346 .with_required_capability(atomic_capability));
347 }
348 let durable_capability = match self.mode {
349 CopyMode::Auto => FileSystemCapability::DurableFileCopy,
350 CopyMode::File => FileSystemCapability::DurableFileCopy,
351 CopyMode::Tree => FileSystemCapability::DurableTreeCopy,
352 };
353 let durable_supported = match self.mode {
354 CopyMode::Auto => {
355 capabilities.supports(FileSystemCapability::DurableFileCopy)
356 || capabilities.supports(FileSystemCapability::DurableTreeCopy)
357 }
358 CopyMode::File | CopyMode::Tree => capabilities.supports(durable_capability),
359 };
360 if self.durability == DurabilityRequirement::Required && !durable_supported {
361 return Err(FsError::new(
362 FsErrorKind::RequirementNotMet,
363 FsOperation::Copy,
364 "durable copy publication is required but not supported",
365 )
366 .with_required_capability(durable_capability));
367 }
368 Ok(())
369 }
370}
371
372impl Default for CopyOptions {
373 #[inline]
374 fn default() -> Self {
375 Self {
376 mode: CopyMode::Auto,
377 conflict: CopyConflictPolicy::Fail,
378 preserve_metadata: MetadataPreservePolicy::None,
379 server_side: ServerSidePreference::Disable,
380 symlink_policy: None,
381 create_parent: false,
382 continue_on_error: false,
383 atomicity: AtomicityRequirement::NotRequired,
384 durability: DurabilityRequirement::NotRequired,
385 max_depth: None,
386 max_entries: None,
387 max_bytes: None,
388 deadline: None,
389 }
390 }
391}