use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum TransferOperation {
Put,
Get,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum TransferTransport {
Auto,
ExecRaw,
Sftp,
Scp,
Rsync,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum TransferKind {
File,
Directory,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RsyncOptions {
#[serde(default = "default_true")]
pub checksum: bool,
#[serde(default = "default_false")]
pub compress: bool,
#[serde(default = "default_false")]
pub delete: bool,
#[serde(default = "default_true")]
pub inplace: bool,
#[serde(default = "default_true")]
pub partial: bool,
pub bwlimit: Option<u32>,
}
impl Default for RsyncOptions {
fn default() -> Self {
Self {
checksum: true,
compress: false,
delete: false,
inplace: true,
partial: true,
bwlimit: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransferParams {
pub operation: TransferOperation,
pub local_path: String,
pub remote_path: String,
#[serde(default = "default_transport")]
pub transport: TransferTransport,
pub kind: Option<TransferKind>,
#[serde(default = "default_overwrite")]
pub overwrite: bool,
pub timeout_ms: Option<u64>,
#[serde(default)]
pub verbose: bool,
#[serde(default)]
pub rsync_options: RsyncOptions,
}
fn default_true() -> bool {
true
}
fn default_false() -> bool {
false
}
fn default_transport() -> TransferTransport {
TransferTransport::Auto
}
fn default_overwrite() -> bool {
false
}
impl Default for TransferParams {
fn default() -> Self {
Self {
operation: TransferOperation::Put,
local_path: String::new(),
remote_path: String::new(),
transport: default_transport(),
kind: None,
overwrite: default_overwrite(),
timeout_ms: None,
verbose: false,
rsync_options: RsyncOptions::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedPaths {
pub local_path: PathBuf,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TransferCounts {
pub bytes: u64,
pub files: u64,
pub directories: u64,
}
impl TransferCounts {
pub fn zero() -> Self {
Self {
bytes: 0,
files: 0,
directories: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransferStaging {
pub local: Option<StagingLocal>,
pub remote: Option<StagingRemote>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StagingLocal {
pub staging_path: String,
pub backup_path: Option<String>,
pub final_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StagingRemote {
pub staging_path: String,
pub backup_path: Option<String>,
pub final_path: String,
pub staging_base_home: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransferResponse {
pub ok: bool,
pub error: Option<String>,
pub params: TransferParams,
pub kind: Option<TransferKind>,
pub transport_used: TransferTransport,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub fallback_chain: Vec<TransferTransport>,
pub remote_home: Option<String>,
pub local_root: String,
pub resolved_paths: Option<ResolvedPaths>,
pub staging: Option<TransferStaging>,
pub counts: Option<TransferCounts>,
pub elapsed_ms: Option<u64>,
pub semantics: Option<String>,
}
impl TransferResponse {
pub fn ok_stub(
params: TransferParams,
transport_used: TransferTransport,
remote_home: &str,
local_root: &Path,
) -> Self {
Self {
ok: false,
error: None,
params,
kind: None,
transport_used,
fallback_chain: Vec::new(),
remote_home: Some(remote_home.to_string()),
local_root: local_root.display().to_string(),
resolved_paths: None,
staging: None,
counts: None,
elapsed_ms: None,
semantics: None,
}
}
pub fn error(params: TransferParams, local_root: &Path, msg: &str) -> Self {
Self {
ok: false,
error: Some(msg.to_string()),
transport_used: params.transport,
fallback_chain: Vec::new(),
remote_home: None,
local_root: local_root.display().to_string(),
params,
kind: None,
resolved_paths: None,
staging: None,
counts: None,
elapsed_ms: None,
semantics: None,
}
}
pub fn set_error(&mut self, msg: &str) {
self.ok = false;
self.error = Some(msg.to_string());
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompactTransferResponse {
pub ok: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub kind: Option<TransferKind>,
pub transport_used: TransferTransport,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub fallback_chain: Vec<TransferTransport>,
pub local_path: String,
pub remote_path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub counts: Option<TransferCounts>,
#[serde(skip_serializing_if = "Option::is_none")]
pub elapsed_ms: Option<u64>,
}
impl TransferResponse {
pub fn to_compact(&self) -> CompactTransferResponse {
CompactTransferResponse {
ok: self.ok,
error: self.error.clone(),
kind: self.kind,
transport_used: self.transport_used,
fallback_chain: self.fallback_chain.clone(),
local_path: self.params.local_path.clone(),
remote_path: self.params.remote_path.clone(),
counts: self.counts.clone(),
elapsed_ms: self.elapsed_ms,
}
}
pub fn to_json(&self, verbose: bool) -> Result<String, serde_json::Error> {
if verbose {
serde_json::to_string(self)
} else {
serde_json::to_string(&self.to_compact())
}
}
}