use std::time::Duration;
use crate::copy::CopyConflictPolicy;
use crate::copy::CopyMode;
use crate::copy::MetadataPreservePolicy;
use crate::copy::ServerSidePreference;
use crate::error::FsError;
use crate::error::FsErrorKind;
use crate::error::FsOperation;
use crate::metadata::AtomicityRequirement;
use crate::metadata::DurabilityRequirement;
use crate::metadata::FileSystemCapabilities;
use crate::metadata::FileSystemCapability;
use crate::metadata::SymlinkPolicy;
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub struct CopyOptions {
mode: CopyMode,
conflict: CopyConflictPolicy,
preserve_metadata: MetadataPreservePolicy,
server_side: ServerSidePreference,
symlink_policy: Option<SymlinkPolicy>,
create_parent: bool,
continue_on_error: bool,
atomicity: AtomicityRequirement,
durability: DurabilityRequirement,
max_depth: Option<usize>,
max_entries: Option<usize>,
max_bytes: Option<u64>,
deadline: Option<Duration>,
}
impl CopyOptions {
#[inline]
#[must_use]
pub const fn with_mode(mut self, mode: CopyMode) -> Self {
self.mode = mode;
self
}
#[inline]
#[must_use]
pub const fn mode(&self) -> CopyMode {
self.mode
}
#[inline]
#[must_use]
pub const fn with_conflict(mut self, conflict: CopyConflictPolicy) -> Self {
self.conflict = conflict;
self
}
#[inline]
#[must_use]
pub const fn conflict(&self) -> CopyConflictPolicy {
self.conflict
}
#[inline]
#[must_use]
pub const fn with_preserve_metadata(mut self, preserve_metadata: MetadataPreservePolicy) -> Self {
self.preserve_metadata = preserve_metadata;
self
}
#[inline]
#[must_use]
pub const fn preserve_metadata(&self) -> MetadataPreservePolicy {
self.preserve_metadata
}
#[inline]
#[must_use]
pub const fn with_server_side(mut self, server_side: ServerSidePreference) -> Self {
self.server_side = server_side;
self
}
#[inline]
#[must_use]
pub const fn server_side(&self) -> ServerSidePreference {
self.server_side
}
#[inline]
#[must_use]
pub const fn with_symlink_policy(mut self, policy: SymlinkPolicy) -> Self {
self.symlink_policy = Some(policy);
self
}
#[inline]
#[must_use]
pub const fn symlink_policy_override(&self) -> Option<SymlinkPolicy> {
self.symlink_policy
}
#[inline]
#[must_use]
pub const fn with_create_parent(mut self, create: bool) -> Self {
self.create_parent = create;
self
}
#[inline]
#[must_use]
pub const fn create_parent(&self) -> bool {
self.create_parent
}
#[inline]
#[must_use]
pub const fn with_continue_on_error(mut self, continue_on_error: bool) -> Self {
self.continue_on_error = continue_on_error;
self
}
#[inline]
#[must_use]
pub const fn continue_on_error(&self) -> bool {
self.continue_on_error
}
#[inline]
#[must_use]
pub const fn with_atomicity(mut self, atomicity: AtomicityRequirement) -> Self {
self.atomicity = atomicity;
self
}
#[inline]
#[must_use]
pub const fn atomicity(&self) -> AtomicityRequirement {
self.atomicity
}
#[inline]
#[must_use]
pub const fn with_durability(mut self, durability: DurabilityRequirement) -> Self {
self.durability = durability;
self
}
#[inline]
#[must_use]
pub const fn durability(&self) -> DurabilityRequirement {
self.durability
}
#[inline]
#[must_use]
pub const fn with_max_depth(mut self, max_depth: Option<usize>) -> Self {
self.max_depth = max_depth;
self
}
#[inline]
#[must_use]
pub const fn max_depth(&self) -> Option<usize> {
self.max_depth
}
#[inline]
#[must_use]
pub const fn with_max_entries(mut self, max_entries: Option<usize>) -> Self {
self.max_entries = max_entries;
self
}
#[inline]
#[must_use]
pub const fn max_entries(&self) -> Option<usize> {
self.max_entries
}
#[inline]
#[must_use]
pub const fn with_max_bytes(mut self, max_bytes: Option<u64>) -> Self {
self.max_bytes = max_bytes;
self
}
#[inline]
#[must_use]
pub const fn max_bytes(&self) -> Option<u64> {
self.max_bytes
}
#[inline]
#[must_use]
pub const fn with_deadline(mut self, deadline: Option<Duration>) -> Self {
self.deadline = deadline;
self
}
#[inline]
#[must_use]
pub const fn deadline(&self) -> Option<Duration> {
self.deadline
}
#[inline]
#[must_use]
pub fn file() -> Self {
Self {
mode: CopyMode::File,
..Self::default()
}
}
#[inline]
#[must_use]
pub fn tree() -> Self {
Self {
mode: CopyMode::Tree,
..Self::default()
}
}
pub fn validate_against(&self, capabilities: FileSystemCapabilities) -> Result<(), FsError> {
if self.server_side == ServerSidePreference::Require
&& !capabilities.supports(FileSystemCapability::ServerSideCopy)
{
return Err(FsError::new(
FsErrorKind::RequirementNotMet,
FsOperation::Copy,
"server-side copy is required but not supported",
)
.with_required_capability(FileSystemCapability::ServerSideCopy));
}
let atomic_capability = match self.mode {
CopyMode::Auto => FileSystemCapability::AtomicFileCopy,
CopyMode::File => FileSystemCapability::AtomicFileCopy,
CopyMode::Tree => FileSystemCapability::AtomicTreeCopy,
};
let atomic_supported = match self.mode {
CopyMode::Auto => {
capabilities.supports(FileSystemCapability::AtomicFileCopy)
|| capabilities.supports(FileSystemCapability::AtomicTreeCopy)
}
CopyMode::File | CopyMode::Tree => capabilities.supports(atomic_capability),
};
if self.atomicity == AtomicityRequirement::Required && !atomic_supported {
return Err(FsError::new(
FsErrorKind::RequirementNotMet,
FsOperation::Copy,
"atomic copy publication is required but not supported",
)
.with_required_capability(atomic_capability));
}
let durable_capability = match self.mode {
CopyMode::Auto => FileSystemCapability::DurableFileCopy,
CopyMode::File => FileSystemCapability::DurableFileCopy,
CopyMode::Tree => FileSystemCapability::DurableTreeCopy,
};
let durable_supported = match self.mode {
CopyMode::Auto => {
capabilities.supports(FileSystemCapability::DurableFileCopy)
|| capabilities.supports(FileSystemCapability::DurableTreeCopy)
}
CopyMode::File | CopyMode::Tree => capabilities.supports(durable_capability),
};
if self.durability == DurabilityRequirement::Required && !durable_supported {
return Err(FsError::new(
FsErrorKind::RequirementNotMet,
FsOperation::Copy,
"durable copy publication is required but not supported",
)
.with_required_capability(durable_capability));
}
Ok(())
}
}
impl Default for CopyOptions {
#[inline]
fn default() -> Self {
Self {
mode: CopyMode::Auto,
conflict: CopyConflictPolicy::Fail,
preserve_metadata: MetadataPreservePolicy::None,
server_side: ServerSidePreference::Disable,
symlink_policy: None,
create_parent: false,
continue_on_error: false,
atomicity: AtomicityRequirement::NotRequired,
durability: DurabilityRequirement::NotRequired,
max_depth: None,
max_entries: None,
max_bytes: None,
deadline: None,
}
}
}