lint with clippy

fix problems reported by clippy excepts about unused functions
This commit is contained in:
qwjyh 2024-03-18 08:36:07 +09:00
parent 01f959b666
commit bac829c1f1
12 changed files with 296 additions and 313 deletions

View file

@ -174,7 +174,7 @@ impl Backup {
&self.name &self.name
} }
pub fn device<'a>(&'a self, devices: &'a Vec<Device>) -> Option<&Device> { pub fn device<'a>(&'a self, devices: &'a [Device]) -> Option<&Device> {
devices.iter().find(|dev| dev.name() == self.device) devices.iter().find(|dev| dev.name() == self.device)
} }
@ -190,7 +190,7 @@ impl Backup {
&self.command &self.command
} }
pub fn add_log(&mut self, newlog: BackupLog) -> () { pub fn add_log(&mut self, newlog: BackupLog) {
self.logs.push(newlog) self.logs.push(newlog)
} }

View file

@ -1,7 +1,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{self, stdout, Write}, io::{self, stdout, Write},
path::PathBuf, path::{PathBuf, Path},
}; };
use anyhow::{anyhow, Context, Ok, Result}; use anyhow::{anyhow, Context, Ok, Result};
@ -28,21 +28,21 @@ pub(crate) fn cmd_backup_add(
dest: PathBuf, dest: PathBuf,
cmd: BackupAddCommands, cmd: BackupAddCommands,
repo: Repository, repo: Repository,
config_dir: &PathBuf, config_dir: &Path,
storages: &Storages, storages: &Storages,
) -> Result<()> { ) -> Result<()> {
trace!("Canonicalize path: {:?}", src); trace!("Canonicalize path: {:?}", src);
let src = canonicalize(util::expand_tilde(src)?)?; let src = canonicalize(util::expand_tilde(src)?)?;
trace!("Canonicalize path: {:?}", dest); trace!("Canonicalize path: {:?}", dest);
let dest = canonicalize(util::expand_tilde(dest)?)?; let dest = canonicalize(util::expand_tilde(dest)?)?;
let device = devices::get_device(&config_dir)?; let device = devices::get_device(config_dir)?;
let new_backup = new_backup(name, src, dest, cmd, &device, storages)?; let new_backup = new_backup(name, src, dest, cmd, &device, storages)?;
let new_backup_name = new_backup.name().clone(); let new_backup_name = new_backup.name().clone();
let mut backups = Backups::read(&config_dir, &device)?; let mut backups = Backups::read(config_dir, &device)?;
println!("Backup config:"); println!("Backup config:");
serde_yaml::to_writer(stdout(), &new_backup)?; serde_yaml::to_writer(stdout(), &new_backup)?;
backups.add(new_backup)?; backups.add(new_backup)?;
backups.write(&config_dir, &device)?; backups.write(config_dir, &device)?;
add_and_commit( add_and_commit(
&repo, &repo,
@ -64,12 +64,12 @@ fn new_backup(
storages: &Storages, storages: &Storages,
) -> Result<Backup> { ) -> Result<Backup> {
let (src_parent, src_diff) = let (src_parent, src_diff) =
util::min_parent_storage(&src, &storages, &device).context(format!( util::min_parent_storage(&src, storages, device).context(format!(
"Coundn't find parent storage for src directory {}", "Coundn't find parent storage for src directory {}",
src.display() src.display()
))?; ))?;
let (dest_parent, dest_diff) = let (dest_parent, dest_diff) =
util::min_parent_storage(&dest, &storages, &device).context(format!( util::min_parent_storage(&dest, storages, device).context(format!(
"Couldn't find parent storage for dest directory: {}", "Couldn't find parent storage for dest directory: {}",
dest.display() dest.display()
))?; ))?;
@ -94,6 +94,166 @@ fn new_backup(
)) ))
} }
pub fn cmd_backup_list(
src_storage: Option<String>,
dest_storage: Option<String>,
device_name: Option<String>,
longprint: bool,
config_dir: &Path,
storages: &Storages,
) -> Result<()> {
let devices = devices::get_devices(config_dir)?;
let backups: HashMap<(String, String), Backup> = match device_name {
Some(device_name) => {
let device = devices
.iter()
.find(|dev| dev.name() == device_name)
.context(format!("Device with name {} doesn't exist", device_name))?;
let backups = Backups::read(config_dir, device)?;
let mut allbackups = HashMap::new();
for (name, backup) in backups.list {
if allbackups.insert((device.name(), name), backup).is_some() {
return Err(anyhow!("unexpected duplication in backups hashmap"));
};
}
allbackups
}
None => {
let mut allbackups = HashMap::new();
for device in &devices {
let backups = Backups::read(config_dir, device)?;
for (name, backup) in backups.list {
if allbackups.insert((device.name(), name), backup).is_some() {
return Err(anyhow!("unexpected duplication in backups hashmap"));
};
}
}
allbackups
}
};
// source/destination filtering
let backups: HashMap<(String, String), Backup> = backups
.into_iter()
.filter(|((_dev, _name), backup)| {
let src_matched = match &src_storage {
Some(src_storage) => &backup.source().storage == src_storage,
None => true,
};
let dest_matched = match &dest_storage {
Some(dest_storage) => &backup.destination().storage == dest_storage,
None => true,
};
src_matched && dest_matched
})
.collect();
let mut stdout = io::BufWriter::new(io::stdout());
write_backups_list(&mut stdout, backups, longprint, storages, &devices)?;
stdout.flush()?;
Ok(())
}
/// TODO: status printing
fn write_backups_list(
mut writer: impl io::Write,
backups: HashMap<(String, String), Backup>,
longprint: bool,
storages: &Storages,
devices: &[Device],
) -> Result<()> {
let mut name_width = 0;
let mut dev_width = 0;
let mut src_width = 0;
let mut src_storage_width = 0;
let mut dest_width = 0;
let mut dest_storage_width = 0;
let mut cmd_name_width = 0;
// get widths
for ((dev, _name), backup) in &backups {
let device = backup.device(devices).context(format!(
"Couldn't find device specified in backup config {}",
backup.name()
))?;
name_width = name_width.max(backup.name().width());
dev_width = dev_width.max(dev.width());
let src = backup.source().path(storages, device)?;
src_width = src_width.max(format!("{}", src.display()).width());
src_storage_width = src_storage_width.max(backup.source().storage.width());
let dest = backup.destination().path(storages, device)?;
dest_width = dest_width.max(format!("{}", dest.display()).width());
dest_storage_width = dest_storage_width.max(backup.destination().storage.width());
let cmd_name = backup.command().name();
cmd_name_width = cmd_name_width.max(cmd_name.width());
}
// main printing
for ((dev, _name), backup) in &backups {
let device = backup.device(devices).context(format!(
"Couldn't find device specified in backup config {}",
backup.name()
))?;
let src = backup.source().path(storages, device)?;
let dest = backup.destination().path(storages, device)?;
let cmd_name = backup.command().name();
let last_backup_elapsed = match backup.last_backup() {
Some(log) => {
let time = Local::now() - log.datetime;
util::format_summarized_duration(time)
}
None => "---".to_string(),
};
writeln!(
writer,
"{name:<name_width$} [{dev:<dev_width$}] {src:<src_storage_width$} → {dest:<dest_storage_width$} {last_backup_elapsed}",
name = backup.name(),
src = backup.source().storage,
dest = backup.destination().storage,
)?;
if longprint {
let cmd_note = backup.command().note();
writeln!(writer, " src : {src:<src_width$}", src = src.display())?;
writeln!(
writer,
" dest: {dest:<dest_width$}",
dest = dest.display()
)?;
writeln!(
writer,
" {cmd_name:<cmd_name_width$}({note})",
note = cmd_note,
)?;
}
}
Ok(())
}
pub fn cmd_backup_done(
name: String,
exit_status: u64,
log: Option<String>,
repo: Repository,
config_dir: &Path,
) -> Result<()> {
let device = devices::get_device(config_dir)?;
let mut backups = Backups::read(config_dir, &device)?;
let backup = backups
.get_mut(&name)
.context(format!("Failed to get backup with name {}", name))?;
trace!("Got backup: {:?}", backup);
let backup_name = backup.name().clone();
let status = BackupResult::from_exit_code(exit_status);
let new_log = BackupLog::new_with_current_time(status, log.unwrap_or("".to_string()));
trace!("New backup log: {:?}", new_log);
backup.add_log(new_log);
trace!("Added");
backups.write(config_dir, &device)?;
add_and_commit(
&repo,
&backups::backups_file(&device),
&format!("Done backup: {}", backup_name),
)?;
Ok(())
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::path::{Component, PathBuf}; use std::path::{Component, PathBuf};
@ -162,166 +322,3 @@ mod test {
Ok(()) Ok(())
} }
} }
pub fn cmd_backup_list(
src_storage: Option<String>,
dest_storage: Option<String>,
device_name: Option<String>,
longprint: bool,
config_dir: &PathBuf,
storages: &Storages,
) -> Result<()> {
let devices = devices::get_devices(&config_dir)?;
let backups: HashMap<(String, String), Backup> = match device_name {
Some(device_name) => {
let device = devices
.iter()
.find(|dev| dev.name() == device_name)
.context(format!("Device with name {} doesn't exist", device_name))?;
let backups = Backups::read(&config_dir, device)?;
let mut allbackups = HashMap::new();
for (name, backup) in backups.list {
if allbackups.insert((device.name(), name), backup).is_some() {
return Err(anyhow!("unexpected duplication in backups hashmap"));
};
}
allbackups
}
None => {
let mut allbackups = HashMap::new();
for device in &devices {
let backups = Backups::read(&config_dir, &device)?;
for (name, backup) in backups.list {
if allbackups.insert((device.name(), name), backup).is_some() {
return Err(anyhow!("unexpected duplication in backups hashmap"));
};
}
}
allbackups
}
};
// source/destination filtering
let backups: HashMap<(String, String), Backup> = backups
.into_iter()
.filter(|((_dev, _name), backup)| {
let src_matched = match &src_storage {
Some(src_storage) => &backup.source().storage == src_storage,
None => true,
};
let dest_matched = match &dest_storage {
Some(dest_storage) => &backup.destination().storage == dest_storage,
None => true,
};
src_matched && dest_matched
})
.collect();
let mut stdout = io::BufWriter::new(io::stdout());
write_backups_list(&mut stdout, backups, longprint, storages, &devices)?;
stdout.flush()?;
Ok(())
}
/// TODO: status printing
fn write_backups_list(
mut writer: impl io::Write,
backups: HashMap<(String, String), Backup>,
longprint: bool,
storages: &Storages,
devices: &Vec<Device>,
) -> Result<()> {
let mut name_width = 0;
let mut dev_width = 0;
let mut src_width = 0;
let mut src_storage_width = 0;
let mut dest_width = 0;
let mut dest_storage_width = 0;
let mut cmd_name_width = 0;
// get widths
for ((dev, _name), backup) in &backups {
let device = backup.device(devices).context(format!(
"Couldn't find device specified in backup config {}",
backup.name()
))?;
name_width = name_width.max(backup.name().width());
dev_width = dev_width.max(dev.width());
let src = backup.source().path(&storages, &device)?;
src_width = src_width.max(format!("{}", src.display()).width());
src_storage_width = src_storage_width.max(backup.source().storage.width());
let dest = backup.destination().path(&storages, &device)?;
dest_width = dest_width.max(format!("{}", dest.display()).width());
dest_storage_width = dest_storage_width.max(backup.destination().storage.width());
let cmd_name = backup.command().name();
cmd_name_width = cmd_name_width.max(cmd_name.width());
}
// main printing
for ((dev, _name), backup) in &backups {
let device = backup.device(devices).context(format!(
"Couldn't find device specified in backup config {}",
backup.name()
))?;
let src = backup.source().path(&storages, &device)?;
let dest = backup.destination().path(&storages, &device)?;
let cmd_name = backup.command().name();
let last_backup_elapsed = match backup.last_backup() {
Some(log) => {
let time = Local::now() - log.datetime;
util::format_summarized_duration(time)
}
None => {
format!("---")
}
};
writeln!(
writer,
"{name:<name_width$} [{dev:<dev_width$}] {src:<src_storage_width$} → {dest:<dest_storage_width$} {last_backup_elapsed}",
name = backup.name(),
src = backup.source().storage,
dest = backup.destination().storage,
)?;
if longprint {
let cmd_note = backup.command().note();
writeln!(writer, " src : {src:<src_width$}", src = src.display())?;
writeln!(
writer,
" dest: {dest:<dest_width$}",
dest = dest.display()
)?;
writeln!(
writer,
" {cmd_name:<cmd_name_width$}({note})",
note = cmd_note,
)?;
} else {
}
}
Ok(())
}
pub fn cmd_backup_done(
name: String,
exit_status: u64,
log: Option<String>,
repo: Repository,
config_dir: &PathBuf,
) -> Result<()> {
let device = devices::get_device(&config_dir)?;
let mut backups = Backups::read(&config_dir, &device)?;
let backup = backups
.get_mut(&name)
.context(format!("Failed to get backup with name {}", name))?;
trace!("Got backup: {:?}", backup);
let backup_name = backup.name().clone();
let status = BackupResult::from_exit_code(exit_status);
let new_log = BackupLog::new_with_current_time(status, log.unwrap_or("".to_string()));
trace!("New backup log: {:?}", new_log);
backup.add_log(new_log);
trace!("Added");
backups.write(&config_dir, &device)?;
add_and_commit(
&repo,
&backups::backups_file(&device),
&format!("Done backup: {}", backup_name),
)?;
Ok(())
}

View file

@ -1,4 +1,4 @@
use std::path::PathBuf; use std::path::Path;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
@ -8,16 +8,16 @@ use crate::{
storages::{Storage, StorageExt, Storages}, storages::{Storage, StorageExt, Storages},
}; };
pub(crate) fn cmd_check(config_dir: &PathBuf) -> Result<()> { pub(crate) fn cmd_check(config_dir: &Path) -> Result<()> {
info!("Config dir: {}", &config_dir.display()); info!("Config dir: {}", &config_dir.display());
let device = devices::get_device(&config_dir)?; let device = devices::get_device(config_dir)?;
info!("Current device: {:?}", device); info!("Current device: {:?}", device);
let devices = devices::get_devices(&config_dir)?; let devices = devices::get_devices(config_dir)?;
info!("Configured devices: {:?}", devices); info!("Configured devices: {:?}", devices);
let storages = Storages::read(&config_dir)?; let storages = Storages::read(config_dir)?;
info!("Storages: {:?}", storages); info!("Storages: {:?}", storages);
if !(storages.list.iter().all(|(_name, storage)| match storage { if !(storages.list.iter().all(|(_name, storage)| match storage {
Storage::SubDirectory(storage) => storage.parent(&storages).is_some(), Storage::SubDirectory(storage) => storage.parent(&storages).is_some(),
@ -30,7 +30,7 @@ pub(crate) fn cmd_check(config_dir: &PathBuf) -> Result<()> {
info!("All SubDirectory's parent exists."); info!("All SubDirectory's parent exists.");
for device in &devices { for device in &devices {
let backups = Backups::read(&config_dir, &device)?; let backups = Backups::read(config_dir, device)?;
for (name, backup) in &backups.list { for (name, backup) in &backups.list {
if name != backup.name() { if name != backup.name() {
return Err(anyhow!( return Err(anyhow!(

View file

@ -15,7 +15,7 @@ use std::io::{BufWriter, Write};
use std::path::{self, Path, PathBuf}; use std::path::{self, Path, PathBuf};
fn clone_repo( fn clone_repo(
repo_url: &String, repo_url: &str,
use_sshagent: bool, use_sshagent: bool,
ssh_key: Option<PathBuf>, ssh_key: Option<PathBuf>,
config_dir: &path::PathBuf, config_dir: &path::PathBuf,
@ -24,7 +24,7 @@ fn clone_repo(
if ssh_key.is_none() && !use_sshagent { if ssh_key.is_none() && !use_sshagent {
info!("No authentication will be used."); info!("No authentication will be used.");
info!("Use either ssh_key or ssh-agent to access private repository"); info!("Use either ssh_key or ssh-agent to access private repository");
return Ok(Repository::clone(&repo_url, &config_dir)?); return Ok(Repository::clone(repo_url, config_dir)?);
} }
// using credentials // using credentials
@ -68,7 +68,7 @@ fn clone_repo(
let mut builder = git2::build::RepoBuilder::new(); let mut builder = git2::build::RepoBuilder::new();
builder.fetch_options(fo); builder.fetch_options(fo);
Ok(builder.clone(&repo_url, config_dir)?) Ok(builder.clone(repo_url, config_dir)?)
} }
pub(crate) fn cmd_init( pub(crate) fn cmd_init(
@ -91,22 +91,21 @@ pub(crate) fn cmd_init(
let repo = match repo_url { let repo = match repo_url {
Some(repo_url) => { Some(repo_url) => {
trace!("repo: {}", repo_url); trace!("repo: {}", repo_url);
let repo = clone_repo(&repo_url, use_sshagent, ssh_key, config_dir)?; clone_repo(&repo_url, use_sshagent, ssh_key, config_dir)?
repo
} }
None => { None => {
trace!("No repo provided"); trace!("No repo provided");
println!("Initializing for the first device..."); println!("Initializing for the first device...");
// create repository // create repository
let repo = Repository::init(&config_dir)?; let repo = Repository::init(config_dir)?;
// set up gitignore // set up gitignore
{ {
let f = File::create(&config_dir.join(".gitignore"))?; let f = File::create(config_dir.join(".gitignore"))?;
{ {
let mut buf = BufWriter::new(f); let mut buf = BufWriter::new(f);
buf.write("devname".as_bytes())?; buf.write_all("devname".as_bytes())?;
} }
add_and_commit(&repo, Path::new(".gitignore"), "Add devname to gitignore.")?; add_and_commit(&repo, Path::new(".gitignore"), "Add devname to gitignore.")?;
full_status(&repo)?; full_status(&repo)?;
@ -115,7 +114,7 @@ pub(crate) fn cmd_init(
// TDOO: wrap up below into one commit? // TDOO: wrap up below into one commit?
// set up devices.yml // set up devices.yml
let devices: Vec<Device> = vec![]; let devices: Vec<Device> = vec![];
write_devices(&config_dir, devices)?; write_devices(config_dir, devices)?;
add_and_commit( add_and_commit(
&repo, &repo,
Path::new(DEVICESFILE), Path::new(DEVICESFILE),
@ -123,7 +122,7 @@ pub(crate) fn cmd_init(
)?; )?;
// set up storages.yml // set up storages.yml
let storages = Storages::new(); let storages = Storages::new();
storages.write(&config_dir)?; storages.write(config_dir)?;
add_and_commit( add_and_commit(
&repo, &repo,
Path::new(STORAGESFILE), Path::new(STORAGESFILE),
@ -131,7 +130,7 @@ pub(crate) fn cmd_init(
)?; )?;
// set up directory for backups // set up directory for backups
DirBuilder::new().create(&config_dir.join(backups::BACKUPSDIR))?; DirBuilder::new().create(config_dir.join(backups::BACKUPSDIR))?;
repo repo
} }
@ -155,7 +154,7 @@ pub(crate) fn cmd_init(
// Add new device to devices.yml // Add new device to devices.yml
{ {
let mut devices: Vec<Device> = get_devices(&config_dir)?; let mut devices: Vec<Device> = get_devices(config_dir)?;
trace!("devices: {:?}", devices); trace!("devices: {:?}", devices);
if devices.iter().any(|x| x.name() == device.name()) { if devices.iter().any(|x| x.name() == device.name()) {
error!("Device name `{}` is already used.", device.name()); error!("Device name `{}` is already used.", device.name());
@ -164,21 +163,21 @@ pub(crate) fn cmd_init(
} }
devices.push(device.clone()); devices.push(device.clone());
trace!("Devices: {:?}", devices); trace!("Devices: {:?}", devices);
write_devices(&config_dir, devices)?; write_devices(config_dir, devices)?;
} }
full_status(&repo)?; full_status(&repo)?;
// commit // commit
add_and_commit( add_and_commit(
&repo, &repo,
&Path::new(DEVICESFILE), Path::new(DEVICESFILE),
&format!("Add new device: {}", &device.name()), &format!("Add new device: {}", &device.name()),
)?; )?;
// backups/[device].yml // backups/[device].yml
{ {
let backups = Backups::new(); let backups = Backups::new();
backups.write(&config_dir, &device)?; backups.write(config_dir, &device)?;
} }
add_and_commit( add_and_commit(
&repo, &repo,

View file

@ -27,14 +27,14 @@ use crate::{
pub(crate) fn cmd_storage_add( pub(crate) fn cmd_storage_add(
args: StorageAddCommands, args: StorageAddCommands,
repo: Repository, repo: Repository,
config_dir: &PathBuf, config_dir: &Path,
) -> Result<()> { ) -> Result<()> {
trace!("Storage Add with args: {:?}", args); trace!("Storage Add with args: {:?}", args);
// Get storages // Get storages
let mut storages = Storages::read(&config_dir)?; let mut storages = Storages::read(config_dir)?;
trace!("found storages: {:?}", storages); trace!("found storages: {:?}", storages);
let device = devices::get_device(&config_dir)?; let device = devices::get_device(config_dir)?;
let storage = match args { let storage = match args {
StorageAddCommands::Physical { name, path } => { StorageAddCommands::Physical { name, path } => {
if !is_unique_name(&name, &storages) { if !is_unique_name(&name, &storages) {
@ -54,7 +54,7 @@ pub(crate) fn cmd_storage_add(
)? )?
}; };
println!("storage: {}: {:?}", storage.name(), storage); println!("storage: {}: {:?}", storage.name(), storage);
Storage::PhysicalStorage(storage) Storage::Physical(storage)
} }
StorageAddCommands::Directory { StorageAddCommands::Directory {
name, name,
@ -113,12 +113,12 @@ pub(crate) fn cmd_storage_add(
trace!("updated storages: {:?}", storages); trace!("updated storages: {:?}", storages);
// write to file // write to file
storages.write(&config_dir)?; storages.write(config_dir)?;
// commit // commit
add_and_commit( add_and_commit(
&repo, &repo,
&Path::new(storages::STORAGESFILE), Path::new(storages::STORAGESFILE),
&format!( &format!(
"Add new storage({}): {}", "Add new storage({}): {}",
new_storage_type, new_storage_name new_storage_type, new_storage_name
@ -163,19 +163,19 @@ fn manually_construct_physical_drive_partition(
fs, fs,
is_removable, is_removable,
local_info, local_info,
&device, device,
)) ))
} }
pub(crate) fn cmd_storage_list(config_dir: &PathBuf, with_note: bool) -> Result<()> { pub(crate) fn cmd_storage_list(config_dir: &Path, with_note: bool) -> Result<()> {
// Get storages // Get storages
let storages = Storages::read(&config_dir)?; let storages = Storages::read(config_dir)?;
trace!("found storages: {:?}", storages); trace!("found storages: {:?}", storages);
if storages.list.is_empty() { if storages.list.is_empty() {
println!("No storages found"); println!("No storages found");
return Ok(()); return Ok(());
} }
let device = devices::get_device(&config_dir)?; let device = devices::get_device(config_dir)?;
let mut stdout = io::BufWriter::new(io::stdout()); let mut stdout = io::BufWriter::new(io::stdout());
write_storages_list(&mut stdout, &storages, &device, with_note)?; write_storages_list(&mut stdout, &storages, &device, with_note)?;
stdout.flush()?; stdout.flush()?;
@ -190,12 +190,12 @@ fn write_storages_list(
) -> Result<()> { ) -> Result<()> {
let name_width = storages let name_width = storages
.list .list
.iter() .values()
.map(|(_k, v)| v.name().width()) .map(|v| v.name().width())
.max() .max()
.unwrap(); .unwrap();
trace!("name widths: {}", name_width); trace!("name widths: {}", name_width);
for (_k, storage) in &storages.list { for storage in storages.list.values() {
let size_str = match storage.capacity() { let size_str = match storage.capacity() {
Some(b) => Byte::from_bytes(b.into()) Some(b) => Byte::from_bytes(b.into())
.get_appropriate_unit(true) .get_appropriate_unit(true)
@ -203,7 +203,7 @@ fn write_storages_list(
.to_string(), .to_string(),
None => "".to_string(), None => "".to_string(),
}; };
let isremovable = if let Storage::PhysicalStorage(s) = storage { let isremovable = if let Storage::Physical(s) = storage {
if s.is_removable() { if s.is_removable() {
"+" "+"
} else { } else {
@ -212,7 +212,7 @@ fn write_storages_list(
} else { } else {
" " " "
}; };
let path = storage.mount_path(&device).map_or_else( let path = storage.mount_path(device).map_or_else(
|e| { |e| {
info!("Not found: {}", e); info!("Not found: {}", e);
"".to_string() "".to_string()
@ -220,7 +220,7 @@ fn write_storages_list(
|v| v.display().to_string(), |v| v.display().to_string(),
); );
let parent_name = if let Storage::SubDirectory(s) = storage { let parent_name = if let Storage::SubDirectory(s) = storage {
s.parent(&storages) s.parent(storages)
.context(format!("Failed to get parent of storage {}", s))? .context(format!("Failed to get parent of storage {}", s))?
.name() .name()
} else { } else {
@ -238,7 +238,7 @@ fn write_storages_list(
)?; )?;
if long_display { if long_display {
let note = match storage { let note = match storage {
Storage::PhysicalStorage(s) => s.kind(), Storage::Physical(s) => s.kind(),
Storage::SubDirectory(s) => &s.notes, Storage::SubDirectory(s) => &s.notes,
Storage::Online(s) => &s.provider, Storage::Online(s) => &s.provider,
}; };
@ -253,11 +253,11 @@ pub(crate) fn cmd_storage_bind(
new_alias: String, new_alias: String,
mount_point: PathBuf, mount_point: PathBuf,
repo: Repository, repo: Repository,
config_dir: &PathBuf, config_dir: &Path,
) -> Result<()> { ) -> Result<()> {
let device = devices::get_device(&config_dir)?; let device = devices::get_device(config_dir)?;
// get storages // get storages
let mut storages = Storages::read(&config_dir)?; let mut storages = Storages::read(config_dir)?;
let commit_comment = { let commit_comment = {
// find matching storage // find matching storage
let storage = &mut storages let storage = &mut storages
@ -284,11 +284,11 @@ pub(crate) fn cmd_storage_bind(
trace!("bound new system name to the storage"); trace!("bound new system name to the storage");
trace!("storages: {:#?}", storages); trace!("storages: {:#?}", storages);
storages.write(&config_dir)?; storages.write(config_dir)?;
// commit // commit
add_and_commit( add_and_commit(
&repo, &repo,
&Path::new(storages::STORAGESFILE), Path::new(storages::STORAGESFILE),
&format!("Bound new storage name to storage ({})", commit_comment), &format!("Bound new storage name to storage ({})", commit_comment),
)?; )?;
println!("Bound new storage name to storage ({})", commit_comment); println!("Bound new storage name to storage ({})", commit_comment);

View file

@ -27,7 +27,7 @@ impl Device {
pub fn new(name: String) -> Device { pub fn new(name: String) -> Device {
let sys = System::new(); let sys = System::new();
Device { Device {
name: name, name,
os_name: sys.name().unwrap_or_else(|| { os_name: sys.name().unwrap_or_else(|| {
warn!("Failed to get OS name. Saving as \"unknown\"."); warn!("Failed to get OS name. Saving as \"unknown\".");
"unknown".to_string() "unknown".to_string()
@ -49,17 +49,6 @@ impl Device {
} }
} }
#[cfg(test)]
mod tests {
use super::Device;
#[test]
fn get_name() {
let device = Device::new("test".to_string());
assert_eq!("test".to_string(), device.name());
}
}
/// Get devname of the device from file `devname`. /// Get devname of the device from file `devname`.
fn get_devname(config_dir: &Path) -> Result<String> { fn get_devname(config_dir: &Path) -> Result<String> {
let f = File::open(config_dir.join("devname")).context("Failed to open devname file")?; let f = File::open(config_dir.join("devname")).context("Failed to open devname file")?;
@ -80,8 +69,7 @@ pub fn get_device(config_dir: &Path) -> Result<Device> {
trace!("devices: {:?}", devices); trace!("devices: {:?}", devices);
devices devices
.into_iter() .into_iter()
.filter(|dev| dev.name() == devname) .find(|dev| dev.name() == devname)
.next()
.context("Couldn't find Device in devices.yml") .context("Couldn't find Device in devices.yml")
} }
@ -93,7 +81,7 @@ pub fn get_devices(config_dir: &Path) -> Result<Vec<Device>> {
let reader = BufReader::new(f); let reader = BufReader::new(f);
let yaml: Vec<Device> = let yaml: Vec<Device> =
serde_yaml::from_reader(reader).context("Failed to parse devices.yml")?; serde_yaml::from_reader(reader).context("Failed to parse devices.yml")?;
return Ok(yaml); Ok(yaml)
} }
/// Write `devices` to yaml file in `config_dir`. /// Write `devices` to yaml file in `config_dir`.
@ -106,3 +94,14 @@ pub fn write_devices(config_dir: &Path, devices: Vec<Device>) -> Result<()> {
let writer = BufWriter::new(f); let writer = BufWriter::new(f);
serde_yaml::to_writer(writer, &devices).map_err(|e| anyhow!(e)) serde_yaml::to_writer(writer, &devices).map_err(|e| anyhow!(e))
} }
#[cfg(test)]
mod tests {
use super::Device;
#[test]
fn get_name() {
let device = Device::new("test".to_string());
assert_eq!("test".to_string(), device.name());
}
}

View file

@ -156,7 +156,7 @@ fn add_and_commit(repo: &Repository, path: &Path, message: &str) -> Result<Oid,
config.get_entry("user.email")?.value().unwrap(), config.get_entry("user.email")?.value().unwrap(),
)?; )?;
trace!("git signature: {}", signature); trace!("git signature: {}", signature);
let parent_commit = find_last_commit(&repo)?; let parent_commit = find_last_commit(repo)?;
let result = match parent_commit { let result = match parent_commit {
Some(parent_commit) => repo.commit( Some(parent_commit) => repo.commit(
Some("HEAD"), Some("HEAD"),

View file

@ -27,7 +27,7 @@ pub enum StorageType {
/// All storage types. /// All storage types.
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum Storage { pub enum Storage {
PhysicalStorage(PhysicalDrivePartition), Physical(PhysicalDrivePartition),
SubDirectory(Directory), SubDirectory(Directory),
Online(OnlineStorage), Online(OnlineStorage),
} }
@ -36,7 +36,7 @@ impl Storage {
/// Full type name like "PhysicalStorage". /// Full type name like "PhysicalStorage".
pub fn typename(&self) -> &str { pub fn typename(&self) -> &str {
match self { match self {
Self::PhysicalStorage(_) => "PhysicalStorage", Self::Physical(_) => "PhysicalStorage",
Self::SubDirectory(_) => "SubDirectory", Self::SubDirectory(_) => "SubDirectory",
Self::Online(_) => "OnlineStorage", Self::Online(_) => "OnlineStorage",
} }
@ -45,7 +45,7 @@ impl Storage {
/// Short type name with one letter like "P". /// Short type name with one letter like "P".
pub fn shorttypename(&self) -> &str { pub fn shorttypename(&self) -> &str {
match self { match self {
Self::PhysicalStorage(_) => "P", Self::Physical(_) => "P",
Self::SubDirectory(_) => "S", Self::SubDirectory(_) => "S",
Self::Online(_) => "O", Self::Online(_) => "O",
} }
@ -55,7 +55,7 @@ impl Storage {
impl StorageExt for Storage { impl StorageExt for Storage {
fn name(&self) -> &String { fn name(&self) -> &String {
match self { match self {
Self::PhysicalStorage(s) => s.name(), Self::Physical(s) => s.name(),
Self::SubDirectory(s) => s.name(), Self::SubDirectory(s) => s.name(),
Self::Online(s) => s.name(), Self::Online(s) => s.name(),
} }
@ -63,7 +63,7 @@ impl StorageExt for Storage {
fn local_info(&self, device: &devices::Device) -> Option<&local_info::LocalInfo> { fn local_info(&self, device: &devices::Device) -> Option<&local_info::LocalInfo> {
match self { match self {
Self::PhysicalStorage(s) => s.local_info(device), Self::Physical(s) => s.local_info(device),
Self::SubDirectory(s) => s.local_info(device), Self::SubDirectory(s) => s.local_info(device),
Self::Online(s) => s.local_info(device), Self::Online(s) => s.local_info(device),
} }
@ -71,9 +71,9 @@ impl StorageExt for Storage {
fn mount_path(&self, device: &devices::Device) -> Result<path::PathBuf> { fn mount_path(&self, device: &devices::Device) -> Result<path::PathBuf> {
match self { match self {
Self::PhysicalStorage(s) => s.mount_path(&device), Self::Physical(s) => s.mount_path(device),
Self::SubDirectory(s) => s.mount_path(&device), Self::SubDirectory(s) => s.mount_path(device),
Self::Online(s) => s.mount_path(&device), Self::Online(s) => s.mount_path(device),
} }
} }
@ -84,7 +84,7 @@ impl StorageExt for Storage {
device: &devices::Device, device: &devices::Device,
) -> Result<()> { ) -> Result<()> {
match self { match self {
Storage::PhysicalStorage(s) => s.bound_on_device(alias, mount_point, device), Storage::Physical(s) => s.bound_on_device(alias, mount_point, device),
Storage::SubDirectory(s) => s.bound_on_device(alias, mount_point, device), Storage::SubDirectory(s) => s.bound_on_device(alias, mount_point, device),
Storage::Online(s) => s.bound_on_device(alias, mount_point, device), Storage::Online(s) => s.bound_on_device(alias, mount_point, device),
} }
@ -92,7 +92,7 @@ impl StorageExt for Storage {
fn capacity(&self) -> Option<u64> { fn capacity(&self) -> Option<u64> {
match self { match self {
Storage::PhysicalStorage(s) => s.capacity(), Storage::Physical(s) => s.capacity(),
Storage::SubDirectory(s) => s.capacity(), Storage::SubDirectory(s) => s.capacity(),
Storage::Online(s) => s.capacity(), Storage::Online(s) => s.capacity(),
} }
@ -100,7 +100,7 @@ impl StorageExt for Storage {
fn parent<'a>(&'a self, storages: &'a Storages) -> Option<&'a Storage> { fn parent<'a>(&'a self, storages: &'a Storages) -> Option<&'a Storage> {
match self { match self {
Storage::PhysicalStorage(s) => s.parent(storages), Storage::Physical(s) => s.parent(storages),
Storage::SubDirectory(s) => s.parent(storages), Storage::SubDirectory(s) => s.parent(storages),
Storage::Online(s) => s.parent(storages), Storage::Online(s) => s.parent(storages),
} }
@ -110,7 +110,7 @@ impl StorageExt for Storage {
impl fmt::Display for Storage { impl fmt::Display for Storage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::PhysicalStorage(s) => s.fmt(f), Self::Physical(s) => s.fmt(f),
Self::SubDirectory(s) => s.fmt(f), Self::SubDirectory(s) => s.fmt(f),
Self::Online(s) => s.fmt(f), Self::Online(s) => s.fmt(f),
} }

View file

@ -52,7 +52,7 @@ impl Directory {
device: &devices::Device, device: &devices::Device,
storages: &Storages, storages: &Storages,
) -> Result<Directory> { ) -> Result<Directory> {
let (parent, diff_path) = util::min_parent_storage(&path, storages, &device) let (parent, diff_path) = util::min_parent_storage(&path, storages, device)
.context("Failed to compare diff of paths")?; .context("Failed to compare diff of paths")?;
trace!("Selected parent: {}", parent.name()); trace!("Selected parent: {}", parent.name());
let local_info = LocalInfo::new(alias, path); let local_info = LocalInfo::new(alias, path);
@ -78,9 +78,9 @@ impl Directory {
/// Resolve mount path of directory with current device. /// Resolve mount path of directory with current device.
fn mount_path(&self, device: &devices::Device, storages: &Storages) -> Result<path::PathBuf> { fn mount_path(&self, device: &devices::Device, storages: &Storages) -> Result<path::PathBuf> {
let parent_mount_path = self let parent_mount_path = self
.parent(&storages) .parent(storages)
.context("Can't find parent storage")? .context("Can't find parent storage")?
.mount_path(&device)?; .mount_path(device)?;
Ok(parent_mount_path.join(self.relative_path.clone())) Ok(parent_mount_path.join(self.relative_path.clone()))
} }
} }
@ -181,7 +181,7 @@ mod test {
); );
let mut storages = Storages::new(); let mut storages = Storages::new();
storages storages
.add(storages::Storage::PhysicalStorage(physical)) .add(storages::Storage::Physical(physical))
.unwrap(); .unwrap();
storages.add(Storage::SubDirectory(directory)).unwrap(); storages.add(Storage::SubDirectory(directory)).unwrap();
// assert_eq!(directory.name(), "test_name"); // assert_eq!(directory.name(), "test_name");

View file

@ -5,9 +5,8 @@ use crate::devices::Device;
use crate::storages::{Storage, StorageExt, Storages}; use crate::storages::{Storage, StorageExt, Storages};
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use byte_unit::Byte; use byte_unit::Byte;
use inquire::Text;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path; use std::path::{self, Path};
use std::{collections::HashMap, fmt}; use std::{collections::HashMap, fmt};
use sysinfo::{Disk, DiskExt, SystemExt}; use sysinfo::{Disk, DiskExt, SystemExt};
@ -61,7 +60,7 @@ impl PhysicalDrivePartition {
let fs = std::str::from_utf8(fs)?; let fs = std::str::from_utf8(fs)?;
let local_info = LocalInfo::new(alias, disk.mount_point().to_path_buf()); let local_info = LocalInfo::new(alias, disk.mount_point().to_path_buf());
Ok(PhysicalDrivePartition { Ok(PhysicalDrivePartition {
name: name, name,
kind: format!("{:?}", disk.kind()), kind: format!("{:?}", disk.kind()),
capacity: disk.total_space(), capacity: disk.total_space(),
fs: fs.to_string(), fs: fs.to_string(),
@ -71,12 +70,8 @@ impl PhysicalDrivePartition {
}) })
} }
pub fn bind_device( pub fn bind_device(&mut self, disk: &sysinfo::Disk, config_dir: &Path) -> Result<()> {
&mut self, let device = devices::get_device(config_dir)?;
disk: &sysinfo::Disk,
config_dir: &std::path::PathBuf,
) -> Result<()> {
let device = devices::get_device(&config_dir)?;
let alias = match disk.name().to_str() { let alias = match disk.name().to_str() {
Some(s) => s.to_string(), Some(s) => s.to_string(),
None => return Err(anyhow!("Failed to convert storage name to valid str.")), None => return Err(anyhow!("Failed to convert storage name to valid str.")),
@ -101,33 +96,6 @@ impl PhysicalDrivePartition {
} }
} }
#[cfg(test)]
mod test {
use crate::{
devices::Device,
storages::{local_info::LocalInfo, StorageExt},
};
use std::path::PathBuf;
use super::PhysicalDrivePartition;
#[test]
fn test_new() {
let localinfo = LocalInfo::new("alias".to_string(), PathBuf::from("/mnt/sample"));
let storage = PhysicalDrivePartition::new(
"name".to_string(),
"SSD".to_string(),
100,
"ext_4".to_string(),
true,
localinfo,
&Device::new("test_device".to_string()),
);
assert_eq!(storage.name(), "name");
assert_eq!(storage.capacity(), Some(100));
}
}
impl StorageExt for PhysicalDrivePartition { impl StorageExt for PhysicalDrivePartition {
fn name(&self) -> &String { fn name(&self) -> &String {
&self.name &self.name
@ -203,7 +171,7 @@ pub fn select_physical_storage(
trace!("{:?}", disk) trace!("{:?}", disk)
} }
let disk = select_sysinfo_disk(&sys_disks)?; let disk = select_sysinfo_disk(&sys_disks)?;
let storage = PhysicalDrivePartition::try_from_sysinfo_disk(&disk, disk_name, device)?; let storage = PhysicalDrivePartition::try_from_sysinfo_disk(disk, disk_name, device)?;
Ok(storage) Ok(storage)
} }
@ -213,10 +181,7 @@ fn select_sysinfo_disk(sysinfo: &sysinfo::System) -> Result<&Disk> {
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, disk)| { .map(|(i, disk)| {
let name = match disk.name().to_str() { let name = disk.name().to_str().unwrap_or("");
Some(s) => s,
None => "",
};
let fs: &str = std::str::from_utf8(disk.file_system()).unwrap_or("unknown"); let fs: &str = std::str::from_utf8(disk.file_system()).unwrap_or("unknown");
let kind = format!("{:?}", disk.kind()); let kind = format!("{:?}", disk.kind());
let mount_path = disk.mount_point(); let mount_path = disk.mount_point();
@ -246,3 +211,30 @@ fn select_sysinfo_disk(sysinfo: &sysinfo::System) -> Result<&Disk> {
trace!("selected disk: {:?}", disk); trace!("selected disk: {:?}", disk);
Ok(disk) Ok(disk)
} }
#[cfg(test)]
mod test {
use crate::{
devices::Device,
storages::{local_info::LocalInfo, StorageExt},
};
use std::path::PathBuf;
use super::PhysicalDrivePartition;
#[test]
fn test_new() {
let localinfo = LocalInfo::new("alias".to_string(), PathBuf::from("/mnt/sample"));
let storage = PhysicalDrivePartition::new(
"name".to_string(),
"SSD".to_string(),
100,
"ext_4".to_string(),
true,
localinfo,
&Device::new("test_device".to_string()),
);
assert_eq!(storage.name(), "name");
assert_eq!(storage.capacity(), Some(100));
}
}

View file

@ -21,7 +21,7 @@ pub fn min_parent_storage<'a>(
Ok(path) => path, Ok(path) => path,
Err(_) => return None, Err(_) => return None,
}; };
let diff = pathdiff::diff_paths(&path, storage_path)?; let diff = pathdiff::diff_paths(path, storage_path)?;
if diff.components().any(|c| c == path::Component::ParentDir) { if diff.components().any(|c| c == path::Component::ParentDir) {
None None
} else { } else {
@ -115,14 +115,12 @@ mod test {
.unwrap() .unwrap()
.eq(&PathBuf::from("/aaa/bbb/ccc"))); .eq(&PathBuf::from("/aaa/bbb/ccc")));
let expanded = expand_tilde(PathBuf::from("~/aaa/bbb/ccc")); let expanded = expand_tilde(PathBuf::from("~/aaa/bbb/ccc"));
match expanded { if let Ok(path) = expanded {
Ok(path) => assert!(path.eq(&dirs::home_dir().unwrap().join("aaa/bbb/ccc"))), assert!(path.eq(&dirs::home_dir().unwrap().join("aaa/bbb/ccc")))
Err(_) => (),
} }
let expanded = expand_tilde(PathBuf::from("aaa/~/bbb")); let expanded = expand_tilde(PathBuf::from("aaa/~/bbb"));
match expanded { if let Ok(path) = expanded {
Ok(path) => assert_eq!(path, PathBuf::from("aaa/~/bbb")), assert_eq!(path, PathBuf::from("aaa/~/bbb"))
Err(_) => (),
} }
Ok(()) Ok(())
} }

View file

@ -41,7 +41,7 @@ mod cmd_init {
let repo_1 = Repository::open(&config_dir_1)?; let repo_1 = Repository::open(&config_dir_1)?;
let upstream_name = "remote"; let upstream_name = "remote";
let mut repo_1_remote = let mut repo_1_remote =
repo_1.remote(upstream_name, &bare_repo_dir.path().to_str().unwrap())?; repo_1.remote(upstream_name, bare_repo_dir.path().to_str().unwrap())?;
repo_1_remote.push(&["refs/heads/main"], None)?; repo_1_remote.push(&["refs/heads/main"], None)?;
trace!("bare repo {:?}", bare_repo_dir.display()); trace!("bare repo {:?}", bare_repo_dir.display());
println!("{:?}", bare_repo_dir.read_dir()?); println!("{:?}", bare_repo_dir.read_dir()?);
@ -112,7 +112,7 @@ mod cmd_init {
let repo_1 = Repository::open(&config_dir_1)?; let repo_1 = Repository::open(&config_dir_1)?;
let upstream_name = "remote"; let upstream_name = "remote";
let mut repo_1_remote = let mut repo_1_remote =
repo_1.remote(upstream_name, &bare_repo_dir.path().to_str().unwrap())?; repo_1.remote(upstream_name, bare_repo_dir.path().to_str().unwrap())?;
repo_1_remote.push(&["refs/heads/main"], None)?; repo_1_remote.push(&["refs/heads/main"], None)?;
trace!("bare repo {:?}", bare_repo_dir.display()); trace!("bare repo {:?}", bare_repo_dir.display());
println!("{:?}", bare_repo_dir.read_dir()?); println!("{:?}", bare_repo_dir.read_dir()?);
@ -182,9 +182,7 @@ mod cmd_init {
.stdout(predicate::str::contains("")); .stdout(predicate::str::contains(""));
// Add storage (directory) // Add storage (directory)
let sample_directory = &sample_storage.join("foo").join("bar"); let sample_directory = &sample_storage.join("foo").join("bar");
DirBuilder::new() DirBuilder::new().recursive(true).create(sample_directory)?;
.recursive(true)
.create(&sample_directory)?;
Command::cargo_bin("xdbm")? Command::cargo_bin("xdbm")?
.arg("-c") .arg("-c")
.arg(config_dir_1.path()) .arg(config_dir_1.path())
@ -194,7 +192,7 @@ mod cmd_init {
.arg("--alias") .arg("--alias")
.arg("docs") .arg("docs")
.arg("gdrive_docs") .arg("gdrive_docs")
.arg(&sample_directory) .arg(sample_directory)
.assert() .assert()
.success() .success()
.stdout(predicate::str::contains("")); .stdout(predicate::str::contains(""));
@ -222,7 +220,7 @@ mod cmd_init {
.arg("--alias") .arg("--alias")
.arg("gdocs") .arg("gdocs")
.arg("--path") .arg("--path")
.arg(&sample_directory) .arg(sample_directory)
.arg("gdrive_docs") .arg("gdrive_docs")
.assert() .assert()
.success() .success()
@ -232,7 +230,7 @@ mod cmd_init {
let sample_storage_2 = assert_fs::TempDir::new()?; let sample_storage_2 = assert_fs::TempDir::new()?;
Command::cargo_bin("xdbm")? Command::cargo_bin("xdbm")?
.arg("-c") .arg("-c")
.arg(&config_dir_2.path()) .arg(config_dir_2.path())
.arg("storage") .arg("storage")
.arg("add") .arg("add")
.arg("online") .arg("online")
@ -243,13 +241,13 @@ mod cmd_init {
.arg("--alias") .arg("--alias")
.arg("nas") .arg("nas")
.arg("nas") .arg("nas")
.arg(&sample_storage_2.path()) .arg(sample_storage_2.path())
.assert() .assert()
.success(); .success();
Command::cargo_bin("xdbm")? Command::cargo_bin("xdbm")?
.arg("-c") .arg("-c")
.arg(&config_dir_2.path()) .arg(config_dir_2.path())
.arg("storage") .arg("storage")
.arg("list") .arg("list")
.arg("-l") .arg("-l")
@ -257,18 +255,18 @@ mod cmd_init {
.success(); .success();
// backup add // backup add
let backup_src = &sample_storage_2.join("foo").join("bar"); let backup_src = &sample_storage_2.join("foo").join("bar");
DirBuilder::new().recursive(true).create(&backup_src)?; DirBuilder::new().recursive(true).create(backup_src)?;
let backup_dest = &sample_directory.join("docs"); let backup_dest = &sample_directory.join("docs");
DirBuilder::new().recursive(true).create(&backup_dest)?; DirBuilder::new().recursive(true).create(backup_dest)?;
Command::cargo_bin("xdbm")? Command::cargo_bin("xdbm")?
.arg("-c") .arg("-c")
.arg(&config_dir_2.path()) .arg(config_dir_2.path())
.arg("backup") .arg("backup")
.arg("add") .arg("add")
.arg("--src") .arg("--src")
.arg(&backup_src) .arg(backup_src)
.arg("--dest") .arg("--dest")
.arg(&backup_dest) .arg(backup_dest)
.arg("foodoc") .arg("foodoc")
.arg("external") .arg("external")
.arg("rsync") .arg("rsync")
@ -278,13 +276,13 @@ mod cmd_init {
Command::cargo_bin("xdbm")? Command::cargo_bin("xdbm")?
.arg("-c") .arg("-c")
.arg(&config_dir_2.path()) .arg(config_dir_2.path())
.arg("backup") .arg("backup")
.arg("add") .arg("add")
.arg("--src") .arg("--src")
.arg(&backup_src) .arg(backup_src)
.arg("--dest") .arg("--dest")
.arg(&backup_dest) .arg(backup_dest)
.arg("foodoc") .arg("foodoc")
.arg("external") .arg("external")
.arg("rsync") .arg("rsync")