1use std::path::{Path, PathBuf};
2use std::sync::Arc;
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "kebab-case")]
8pub enum TransferOperation {
9 Put,
10 Get,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "kebab-case")]
15pub enum TransferTransport {
16 Auto,
17 ExecRaw,
18 Sftp,
19 Scp,
20 Rsync,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "kebab-case")]
25pub enum TransferKind {
26 File,
27 Directory,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct RsyncOptions {
33 #[serde(default = "default_true")]
35 pub checksum: bool,
36 #[serde(default = "default_false")]
38 pub compress: bool,
39 #[serde(default = "default_false")]
41 pub delete: bool,
42 #[serde(default = "default_true")]
44 pub inplace: bool,
45 #[serde(default = "default_true")]
47 pub partial: bool,
48 pub bwlimit: Option<u32>,
50}
51
52impl Default for RsyncOptions {
53 fn default() -> Self {
54 Self {
55 checksum: true,
56 compress: false,
57 delete: false,
58 inplace: true,
59 partial: true,
60 bwlimit: None,
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct TransferParams {
67 pub operation: TransferOperation,
68
69 pub local_path: String,
74
75 pub remote_path: String,
80
81 #[serde(default = "default_transport")]
82 pub transport: TransferTransport,
83
84 pub kind: Option<TransferKind>,
86
87 #[serde(default = "default_overwrite")]
95 pub overwrite: bool,
96
97 pub timeout_ms: Option<u64>,
99
100 #[serde(default)]
102 pub background: bool,
103
104 #[serde(default)]
107 pub verbose: bool,
108
109 #[serde(default)]
111 pub rsync_options: RsyncOptions,
112}
113
114fn default_true() -> bool {
115 true
116}
117
118fn default_false() -> bool {
119 false
120}
121
122fn default_transport() -> TransferTransport {
123 TransferTransport::Auto
124}
125
126fn default_overwrite() -> bool {
127 false
128}
129
130impl Default for TransferParams {
131 fn default() -> Self {
132 Self {
133 operation: TransferOperation::Put,
134 local_path: String::new(),
135 remote_path: String::new(),
136 transport: default_transport(),
137 kind: None,
138 overwrite: default_overwrite(),
139 timeout_ms: None,
140 background: false,
141 verbose: false,
142 rsync_options: RsyncOptions::default(),
143 }
144 }
145}
146
147#[derive(Debug, Clone)]
148pub(crate) enum TransferProgressTarget {
149 Local(PathBuf),
150 Remote(String),
151}
152
153#[derive(Debug, Clone)]
154pub(crate) enum TransferEvent {
155 Preparing,
156 Transferring(TransferTransport),
157 FileStage {
158 target: TransferProgressTarget,
159 total_bytes: Option<u64>,
160 },
161 Finalizing,
162}
163
164#[derive(Clone)]
165pub(crate) struct TransferEventSink(Arc<dyn Fn(TransferEvent) + Send + Sync>);
166
167impl std::fmt::Debug for TransferEventSink {
168 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 formatter.write_str("TransferEventSink(..)")
170 }
171}
172
173impl TransferEventSink {
174 pub(crate) fn new(callback: impl Fn(TransferEvent) + Send + Sync + 'static) -> Self {
175 Self(Arc::new(callback))
176 }
177
178 pub(crate) fn emit(&self, event: TransferEvent) {
179 (self.0)(event);
180 }
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct ResolvedPaths {
185 pub local_path: PathBuf,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
189pub struct TransferCounts {
190 pub bytes: u64,
191 pub files: u64,
192 pub directories: u64,
193}
194
195impl TransferCounts {
196 pub fn zero() -> Self {
197 Self {
198 bytes: 0,
199 files: 0,
200 directories: 0,
201 }
202 }
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct TransferStaging {
207 pub local: Option<StagingLocal>,
208 pub remote: Option<StagingRemote>,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct StagingLocal {
213 pub staging_path: String,
214 pub backup_path: Option<String>,
215 pub final_path: String,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct StagingRemote {
220 pub staging_path: String,
221 pub backup_path: Option<String>,
222 pub final_path: String,
223 pub staging_base_home: String,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct TransferResponse {
228 pub ok: bool,
229 pub error: Option<String>,
230
231 pub params: TransferParams,
232 pub kind: Option<TransferKind>,
233
234 pub transport_used: TransferTransport,
235 #[serde(skip_serializing_if = "Vec::is_empty", default)]
236 pub fallback_chain: Vec<TransferTransport>,
237 pub remote_home: Option<String>,
238 pub local_root: String,
239
240 pub resolved_paths: Option<ResolvedPaths>,
241 pub staging: Option<TransferStaging>,
242 pub counts: Option<TransferCounts>,
243
244 pub elapsed_ms: Option<u64>,
245 pub semantics: Option<String>,
246}
247
248impl TransferResponse {
249 pub fn ok_stub(
250 params: TransferParams,
251 transport_used: TransferTransport,
252 remote_home: &str,
253 local_root: &Path,
254 ) -> Self {
255 Self {
256 ok: false,
257 error: None,
258 params,
259 kind: None,
260 transport_used,
261 fallback_chain: Vec::new(),
262 remote_home: Some(remote_home.to_string()),
263 local_root: local_root.display().to_string(),
264 resolved_paths: None,
265 staging: None,
266 counts: None,
267 elapsed_ms: None,
268 semantics: None,
269 }
270 }
271
272 pub fn error(params: TransferParams, local_root: &Path, msg: &str) -> Self {
273 Self {
274 ok: false,
275 error: Some(msg.to_string()),
276 transport_used: params.transport,
277 fallback_chain: Vec::new(),
278 remote_home: None,
279 local_root: local_root.display().to_string(),
280 params,
281 kind: None,
282 resolved_paths: None,
283 staging: None,
284 counts: None,
285 elapsed_ms: None,
286 semantics: None,
287 }
288 }
289
290 pub fn set_error(&mut self, msg: &str) {
291 self.ok = false;
292 self.error = Some(msg.to_string());
293 }
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct CompactTransferResponse {
300 pub ok: bool,
301 #[serde(skip_serializing_if = "Option::is_none")]
302 pub error: Option<String>,
303 pub kind: Option<TransferKind>,
304 pub transport_used: TransferTransport,
305 #[serde(skip_serializing_if = "Vec::is_empty", default)]
306 pub fallback_chain: Vec<TransferTransport>,
307 pub local_path: String,
309 pub remote_path: String,
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub counts: Option<TransferCounts>,
312 #[serde(skip_serializing_if = "Option::is_none")]
313 pub elapsed_ms: Option<u64>,
314}
315
316impl TransferResponse {
317 pub fn to_compact(&self) -> CompactTransferResponse {
319 CompactTransferResponse {
320 ok: self.ok,
321 error: self.error.clone(),
322 kind: self.kind,
323 transport_used: self.transport_used,
324 fallback_chain: self.fallback_chain.clone(),
325 local_path: self.params.local_path.clone(),
326 remote_path: self.params.remote_path.clone(),
327 counts: self.counts.clone(),
328 elapsed_ms: self.elapsed_ms,
329 }
330 }
331
332 pub fn to_json(&self, verbose: bool) -> Result<String, serde_json::Error> {
334 if verbose {
335 serde_json::to_string(self)
336 } else {
337 serde_json::to_string(&self.to_compact())
338 }
339 }
340}