1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "kebab-case")]
7pub enum TransferOperation {
8 Put,
9 Get,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "kebab-case")]
14pub enum TransferTransport {
15 Auto,
16 ExecRaw,
17 Sftp,
18 Scp,
19 Rsync,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "kebab-case")]
24pub enum TransferKind {
25 File,
26 Directory,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct RsyncOptions {
32 #[serde(default = "default_true")]
34 pub checksum: bool,
35 #[serde(default = "default_false")]
37 pub compress: bool,
38 #[serde(default = "default_false")]
40 pub delete: bool,
41 #[serde(default = "default_true")]
43 pub inplace: bool,
44 #[serde(default = "default_true")]
46 pub partial: bool,
47 pub bwlimit: Option<u32>,
49}
50
51impl Default for RsyncOptions {
52 fn default() -> Self {
53 Self {
54 checksum: true,
55 compress: false,
56 delete: false,
57 inplace: true,
58 partial: true,
59 bwlimit: None,
60 }
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct TransferParams {
66 pub operation: TransferOperation,
67
68 pub local_path: String,
73
74 pub remote_path: String,
79
80 #[serde(default = "default_transport")]
81 pub transport: TransferTransport,
82
83 pub kind: Option<TransferKind>,
85
86 #[serde(default = "default_overwrite")]
94 pub overwrite: bool,
95
96 pub timeout_ms: Option<u64>,
98
99 #[serde(default)]
102 pub verbose: bool,
103
104 #[serde(default)]
106 pub rsync_options: RsyncOptions,
107}
108
109fn default_true() -> bool {
110 true
111}
112
113fn default_false() -> bool {
114 false
115}
116
117fn default_transport() -> TransferTransport {
118 TransferTransport::Auto
119}
120
121fn default_overwrite() -> bool {
122 false
123}
124
125impl Default for TransferParams {
126 fn default() -> Self {
127 Self {
128 operation: TransferOperation::Put,
129 local_path: String::new(),
130 remote_path: String::new(),
131 transport: default_transport(),
132 kind: None,
133 overwrite: default_overwrite(),
134 timeout_ms: None,
135 verbose: false,
136 rsync_options: RsyncOptions::default(),
137 }
138 }
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct ResolvedPaths {
143 pub local_path: PathBuf,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
147pub struct TransferCounts {
148 pub bytes: u64,
149 pub files: u64,
150 pub directories: u64,
151}
152
153impl TransferCounts {
154 pub fn zero() -> Self {
155 Self {
156 bytes: 0,
157 files: 0,
158 directories: 0,
159 }
160 }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct TransferStaging {
165 pub local: Option<StagingLocal>,
166 pub remote: Option<StagingRemote>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct StagingLocal {
171 pub staging_path: String,
172 pub backup_path: Option<String>,
173 pub final_path: String,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct StagingRemote {
178 pub staging_path: String,
179 pub backup_path: Option<String>,
180 pub final_path: String,
181 pub staging_base_home: String,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct TransferResponse {
186 pub ok: bool,
187 pub error: Option<String>,
188
189 pub params: TransferParams,
190 pub kind: Option<TransferKind>,
191
192 pub transport_used: TransferTransport,
193 #[serde(skip_serializing_if = "Vec::is_empty", default)]
194 pub fallback_chain: Vec<TransferTransport>,
195 pub remote_home: Option<String>,
196 pub local_root: String,
197
198 pub resolved_paths: Option<ResolvedPaths>,
199 pub staging: Option<TransferStaging>,
200 pub counts: Option<TransferCounts>,
201
202 pub elapsed_ms: Option<u64>,
203 pub semantics: Option<String>,
204}
205
206impl TransferResponse {
207 pub fn ok_stub(
208 params: TransferParams,
209 transport_used: TransferTransport,
210 remote_home: &str,
211 local_root: &Path,
212 ) -> Self {
213 Self {
214 ok: false,
215 error: None,
216 params,
217 kind: None,
218 transport_used,
219 fallback_chain: Vec::new(),
220 remote_home: Some(remote_home.to_string()),
221 local_root: local_root.display().to_string(),
222 resolved_paths: None,
223 staging: None,
224 counts: None,
225 elapsed_ms: None,
226 semantics: None,
227 }
228 }
229
230 pub fn error(params: TransferParams, local_root: &Path, msg: &str) -> Self {
231 Self {
232 ok: false,
233 error: Some(msg.to_string()),
234 transport_used: params.transport,
235 fallback_chain: Vec::new(),
236 remote_home: None,
237 local_root: local_root.display().to_string(),
238 params,
239 kind: None,
240 resolved_paths: None,
241 staging: None,
242 counts: None,
243 elapsed_ms: None,
244 semantics: None,
245 }
246 }
247
248 pub fn set_error(&mut self, msg: &str) {
249 self.ok = false;
250 self.error = Some(msg.to_string());
251 }
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct CompactTransferResponse {
258 pub ok: bool,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub error: Option<String>,
261 pub kind: Option<TransferKind>,
262 pub transport_used: TransferTransport,
263 #[serde(skip_serializing_if = "Vec::is_empty", default)]
264 pub fallback_chain: Vec<TransferTransport>,
265 pub local_path: String,
267 pub remote_path: String,
268 #[serde(skip_serializing_if = "Option::is_none")]
269 pub counts: Option<TransferCounts>,
270 #[serde(skip_serializing_if = "Option::is_none")]
271 pub elapsed_ms: Option<u64>,
272}
273
274impl TransferResponse {
275 pub fn to_compact(&self) -> CompactTransferResponse {
277 CompactTransferResponse {
278 ok: self.ok,
279 error: self.error.clone(),
280 kind: self.kind,
281 transport_used: self.transport_used,
282 fallback_chain: self.fallback_chain.clone(),
283 local_path: self.params.local_path.clone(),
284 remote_path: self.params.remote_path.clone(),
285 counts: self.counts.clone(),
286 elapsed_ms: self.elapsed_ms,
287 }
288 }
289
290 pub fn to_json(&self, verbose: bool) -> Result<String, serde_json::Error> {
292 if verbose {
293 serde_json::to_string(self)
294 } else {
295 serde_json::to_string(&self.to_compact())
296 }
297 }
298}