use std::fmt;
use std::path::PathBuf;
use anyhow::Context;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use structopt::StructOpt;
use rsyn::{Client, Options, Result};
#[derive(Debug, StructOpt)]
#[structopt()]
struct Opt {
path: String,
#[structopt(long, env = "RSYN_LOG_FILE")]
log_file: Option<PathBuf>,
#[structopt(long, env = "RSYN_RSYNC_PATH")]
rsync_path: Option<String>,
#[structopt(long, short = "e", env = "RSYN_RSH")]
rsh: Option<String>,
#[structopt(long, short = "r")]
recursive: bool,
#[structopt(long)]
list_only: bool,
#[structopt(short = "v", parse(from_occurrences))]
verbose: u32,
}
impl Opt {
fn to_options(&self) -> Options {
Options {
recursive: self.recursive,
list_only: self.list_only,
verbose: self.verbose,
rsync_command: self.rsync_path.as_ref().map(|p| {
shell_words::split(&p).expect("Failed to split shell words from rsync_command")
}),
ssh_command: self.rsh.as_ref().map(|p| {
shell_words::split(&p).expect("Failed to split shell words from ssh_command")
}),
}
}
}
fn main() -> Result<()> {
let opt = Opt::from_args();
configure_logging(&opt)?;
let mut client = Client::from_str(&opt.path).expect("Failed to parse path");
*client.mut_options() = opt.to_options();
let (file_list, _stats) = client.list_files()?;
for entry in file_list {
println!("{}", &entry)
}
debug!("that's all folks");
Ok(())
}
fn configure_logging(opt: &Opt) -> Result<()> {
let mut to_file = fern::Dispatch::new()
.level(log::LevelFilter::Debug)
.format(format_log);
if let Some(ref log_file) = opt.log_file {
to_file = to_file.chain(fern::log_file(log_file).context("Failed to open log file")?);
}
let console_level = match opt.verbose {
0 => log::LevelFilter::Warn,
1 => log::LevelFilter::Info,
2 => log::LevelFilter::Debug,
_ => log::LevelFilter::Trace,
};
let to_console = fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!("[{:<8}] {}", record.level(), message))
})
.level(console_level)
.chain(std::io::stderr());
fern::Dispatch::new()
.chain(to_console)
.chain(to_file)
.apply()
.expect("Failed to configure logger");
Ok(())
}
fn format_log(out: fern::FormatCallback<'_>, args: &fmt::Arguments<'_>, record: &log::Record<'_>) {
out.finish(format_args!(
"[{}] [{:<30}][{}] {}",
chrono::Local::now().format("%m-%d %H:%M:%S"),
record.target(),
record.level().to_string().chars().next().unwrap(),
args
))
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn rsync_path_option() {
let opt = Opt::from_iter(&[
"rsyn",
"--rsync-path=rsync --wibble --wobble",
"-vv",
"/example",
]);
assert_eq!(
opt.rsync_path.as_deref().unwrap(),
"rsync --wibble --wobble"
);
let options = opt.to_options();
assert_eq!(
options.rsync_command.unwrap(),
["rsync", "--wibble", "--wobble"]
);
}
#[test]
fn rsh_option() {
let opt = Opt::from_iter(&["rsyn", "--rsh=ssh -OFoo -OBar=123 -v -A", "-vv", "/example"]);
assert!(opt.rsync_path.is_none());
let options = opt.to_options();
assert!(options.rsync_command.is_none());
assert_eq!(
options.ssh_command.unwrap(),
["ssh", "-OFoo", "-OBar=123", "-v", "-A"]
);
}
}