file_engine/operations/
copy.rs1use std::path::PathBuf;
2use std::time::Instant;
3
4use tokio_util::sync::CancellationToken;
5
6use crate::error::Result;
7use crate::handle::Handle;
8use crate::planner::{BatchConfig, ErrorStrategy, OperationOutcome, SortOrder};
9use crate::profiler::DEFAULT_SMALL_FILE_THRESHOLD;
10use crate::progress::ProgressReporter;
11
12use super::default_concurrency;
13use super::pipeline::run_copy_pipeline;
14
15pub struct CopyBuilder {
16 source: PathBuf,
17 dest: PathBuf,
18 overwrite: bool,
19 preserve_permissions: bool,
22 allow_filesystem_integrity_risk: bool,
23 small_file_threshold: Option<u64>,
24 batch_config: BatchConfig,
25 concurrency: Option<usize>,
26}
27
28impl CopyBuilder {
29 pub(crate) fn new(source: impl Into<PathBuf>, dest: impl Into<PathBuf>) -> Self {
30 Self {
31 source: source.into(),
32 dest: dest.into(),
33 overwrite: false,
34 preserve_permissions: false,
35 allow_filesystem_integrity_risk: false,
36 small_file_threshold: None,
37 batch_config: BatchConfig::default(),
38 concurrency: None,
39 }
40 }
41
42 pub fn overwrite(mut self, overwrite: bool) -> Self {
43 self.overwrite = overwrite;
44 self
45 }
46
47 #[cfg(all(unix, feature = "permissions"))]
48 pub fn preserve_permissions(mut self, preserve: bool) -> Self {
49 self.preserve_permissions = preserve;
50 self
51 }
52
53 pub fn allow_filesystem_integrity_risk(mut self, allow: bool) -> Self {
59 self.allow_filesystem_integrity_risk = allow;
60 self
61 }
62
63 pub fn small_file_threshold(mut self, bytes: u64) -> Self {
64 self.small_file_threshold = Some(bytes);
65 self
66 }
67
68 pub fn max_bytes_per_batch(mut self, bytes: u64) -> Self {
69 self.batch_config.max_bytes_per_batch = bytes;
70 self
71 }
72
73 pub fn max_files_per_batch(mut self, n: usize) -> Self {
74 self.batch_config.max_files_per_batch = Some(n);
75 self
76 }
77
78 pub fn batch_sort_order(mut self, order: SortOrder) -> Self {
79 self.batch_config.sort_order = order;
80 self
81 }
82
83 pub fn on_error(mut self, strategy: ErrorStrategy) -> Self {
84 self.batch_config.error_strategy = strategy;
85 self
86 }
87
88 pub fn batch_concurrency(mut self, n: usize) -> Self {
89 self.concurrency = Some(n);
90 self
91 }
92
93 pub fn start(self) -> Result<Handle<OperationOutcome>> {
94 let cancel = CancellationToken::new();
95 let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
96 let reporter = ProgressReporter::new(tx);
97
98 let concurrency = self.concurrency.unwrap_or_else(default_concurrency);
99 let threshold = self
100 .small_file_threshold
101 .unwrap_or(DEFAULT_SMALL_FILE_THRESHOLD);
102 let cancel_for_task = cancel.clone();
103
104 let join_handle = tokio::spawn(async move {
105 let started = Instant::now();
106 let mut outcome = run_copy_pipeline(
107 &self.source,
108 &self.dest,
109 self.overwrite,
110 self.preserve_permissions,
111 self.allow_filesystem_integrity_risk,
112 threshold,
113 &self.batch_config,
114 concurrency,
115 cancel_for_task,
116 reporter,
117 )
118 .await?;
119 outcome.duration = started.elapsed();
120 Ok(outcome)
121 });
122
123 Ok(Handle::new(join_handle, rx, cancel))
124 }
125}