Skip to content

Commit 19be529

Browse files
committed
replace option
1 parent 302f6fd commit 19be529

File tree

3 files changed

+48
-13
lines changed

3 files changed

+48
-13
lines changed

src/cli.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@ pub struct Cli {
1212
pub enum Commands {
1313
#[command(about = "Copy a file to a remote host")]
1414
Receive {
15-
#[arg(help = "Remote source to copy from")]
15+
#[clap(help = "Remote source to copy from")]
1616
source: String,
1717

18-
#[arg(help = "Local destination to copy to")]
18+
#[clap(help = "Local destination to copy to")]
1919
destination: String,
2020

21-
#[arg(long, help = "Remote host to connect to")]
21+
#[clap(long, help = "Remote host to connect to")]
2222
host: String,
2323

24-
#[arg(short, long, help = "Remote username to connect as")]
24+
#[clap(short, long, help = "Remote username to connect as")]
2525
user: String,
2626

27-
#[arg(short, long, help = "Path to private key")]
27+
#[clap(short, long, help = "Path to private key")]
2828
private_key: Option<PathBuf>,
29+
30+
#[clap(help = "Replace the file if it exists", long, default_value = "false")]
31+
replace: bool,
2932
},
3033
}

src/run.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::cli::{Cli, Commands};
22
use crate::error::ScpError;
3-
use crate::scp::{Connect, SshOpts};
3+
use crate::scp::{Connect, Mode, SshOpts};
44
use crate::utils::get_private_key_path;
55
use clap::Parser;
66
use std::path::PathBuf;
@@ -15,6 +15,7 @@ pub async fn run() -> anyhow::Result<(), ScpError> {
1515
host,
1616
user: username,
1717
private_key,
18+
replace,
1819
} => {
1920
let private_key = get_private_key_path(&private_key)?;
2021

@@ -24,7 +25,9 @@ pub async fn run() -> anyhow::Result<(), ScpError> {
2425
username,
2526
};
2627

27-
return Connect::new(scp_opts)?
28+
let mode = if replace { Mode::Replace } else { Mode::Ignore };
29+
30+
return Connect::new(scp_opts, mode)?
2831
.receive(&PathBuf::from(source), &PathBuf::from(destination))
2932
.await;
3033
}

src/scp.rs

+35-6
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@ use crate::{error::ScpError, utils::with_retry};
1313
pub struct Connect {
1414
session: Session,
1515
ssh_opts: SshOpts,
16+
mode: Mode,
1617
}
1718

1819
impl Connect {
19-
pub fn new(ssh_opts: SshOpts) -> anyhow::Result<Self, ScpError> {
20+
pub fn new(ssh_opts: SshOpts, mode: Mode) -> anyhow::Result<Self, ScpError> {
2021
let session = create_session(&ssh_opts)?;
2122

22-
Ok(Self { session, ssh_opts })
23+
Ok(Self {
24+
session,
25+
ssh_opts,
26+
mode,
27+
})
2328
}
2429

2530
pub async fn receive(&self, from: &PathBuf, to: &PathBuf) -> anyhow::Result<(), ScpError> {
@@ -34,8 +39,10 @@ impl Connect {
3439
let item_clone = item.clone();
3540
let ssh_opts = self.ssh_opts.clone();
3641
let pb = pb.clone();
42+
let mode = self.mode.clone();
3743
let handle = tokio::task::spawn(async move {
38-
let result = copy_file_from_remote(&ssh_opts, item_clone.clone(), to_path).await;
44+
let result =
45+
copy_file_from_remote(&ssh_opts, item_clone.clone(), to_path, &mode).await;
3946
pb.inc(1);
4047
result
4148
});
@@ -110,10 +117,20 @@ pub struct SshOpts {
110117
pub private_key: PathBuf,
111118
}
112119

120+
/// Mode to use when copying files
121+
/// Replace will overwrite the file if it exists
122+
/// Ignore will skip the file if it exists
123+
#[derive(Clone)]
124+
pub enum Mode {
125+
Replace,
126+
Ignore,
127+
}
128+
113129
async fn copy_file_from_remote(
114130
ssh_opts: &SshOpts,
115131
remote_file_path: PathBuf,
116132
local_file_path: PathBuf,
133+
mode: &Mode,
117134
) -> anyhow::Result<(), ScpError> {
118135
let create_session = || create_session(ssh_opts);
119136
let session = with_retry(create_session, 10)?;
@@ -126,9 +143,21 @@ async fn copy_file_from_remote(
126143
// make the dir if not exists
127144
fs::create_dir_all(local_file_path.parent().unwrap())?;
128145

129-
// Create local file and write to it
130-
let mut local_file = File::create(local_file_path)?;
131-
local_file.write_all(&contents)?;
146+
match mode {
147+
Mode::Replace => {
148+
let mut local_file = File::create(&local_file_path)?;
149+
local_file.write_all(&contents)?;
150+
}
151+
Mode::Ignore => {
152+
if local_file_path.exists() {
153+
return Ok(());
154+
}
155+
156+
let mut local_file = File::create(local_file_path)?;
157+
local_file.write_all(&contents)?;
158+
}
159+
}
160+
132161
session.disconnect(None, "Bye", None)?;
133162

134163
Ok(())

0 commit comments

Comments
 (0)