zvault/src/cli/args.rs

621 lines
29 KiB
Rust
Raw Normal View History

2017-03-21 10:28:11 +00:00
use ::prelude::*;
2017-03-22 08:19:16 +00:00
use super::*;
2017-03-16 19:05:58 +00:00
2017-04-07 09:05:28 +00:00
use std::path::Path;
2017-04-05 10:41:39 +00:00
use clap::{App, AppSettings, Arg, SubCommand};
2017-03-16 19:05:58 +00:00
pub enum Arguments {
Init {
repo_path: String,
bundle_size: usize,
chunker: ChunkerType,
2017-03-17 10:03:07 +00:00
compression: Option<Compression>,
2017-03-18 16:22:11 +00:00
encryption: bool,
2017-03-22 13:10:42 +00:00
hash: HashMethod,
2017-03-22 13:42:27 +00:00
remote_path: String
2017-03-16 19:05:58 +00:00
},
Backup {
repo_path: String,
backup_name: String,
src_path: String,
2017-03-20 21:24:53 +00:00
full: bool,
2017-03-24 08:26:55 +00:00
reference: Option<String>,
2017-03-24 10:00:20 +00:00
same_device: bool,
excludes: Vec<String>,
2017-03-26 18:33:32 +00:00
excludes_from: Option<String>,
2017-04-03 13:18:06 +00:00
no_default_excludes: bool,
tar: bool
2017-03-16 19:05:58 +00:00
},
Restore {
repo_path: String,
backup_name: String,
inode: Option<String>,
2017-04-03 13:18:06 +00:00
dst_path: String,
tar: bool
2017-03-16 19:05:58 +00:00
},
2017-03-17 11:58:22 +00:00
Remove {
repo_path: String,
backup_name: String,
inode: Option<String>
},
2017-03-20 14:38:33 +00:00
Prune {
repo_path: String,
prefix: String,
daily: Option<usize>,
weekly: Option<usize>,
monthly: Option<usize>,
yearly: Option<usize>,
2017-03-20 17:11:03 +00:00
force: bool
2017-03-20 14:38:33 +00:00
},
2017-03-17 11:58:22 +00:00
Vacuum {
repo_path: String,
2017-03-20 13:03:29 +00:00
ratio: f32,
2017-03-20 17:11:03 +00:00
force: bool
2017-03-17 11:58:22 +00:00
},
2017-03-16 19:05:58 +00:00
Check {
repo_path: String,
backup_name: Option<String>,
inode: Option<String>,
full: bool
},
List {
repo_path: String,
backup_name: Option<String>,
inode: Option<String>
},
Info {
repo_path: String,
backup_name: Option<String>,
inode: Option<String>
},
2017-03-26 09:34:16 +00:00
Mount {
repo_path: String,
backup_name: Option<String>,
inode: Option<String>,
mount_point: String
},
Versions {
repo_path: String,
path: String
},
2017-03-29 21:24:26 +00:00
Diff {
repo_path_old: String,
backup_name_old: String,
inode_old: Option<String>,
repo_path_new: String,
backup_name_new: String,
inode_new: Option<String>
},
2017-03-25 11:43:49 +00:00
Analyze {
repo_path: String
},
2017-03-24 11:52:01 +00:00
BundleList {
2017-03-16 19:05:58 +00:00
repo_path: String
},
2017-03-24 11:52:01 +00:00
BundleInfo {
repo_path: String,
bundle_id: BundleId
},
2017-03-17 11:58:22 +00:00
Import {
repo_path: String,
2017-03-22 16:28:45 +00:00
remote_path: String,
key_files: Vec<String>
2017-03-17 11:58:22 +00:00
},
2017-03-26 18:33:32 +00:00
Config {
2017-03-18 16:22:11 +00:00
repo_path: String,
bundle_size: Option<usize>,
chunker: Option<ChunkerType>,
compression: Option<Option<Compression>>,
encryption: Option<Option<PublicKey>>,
hash: Option<HashMethod>
},
GenKey {
2017-03-22 16:28:45 +00:00
file: Option<String>
2017-03-18 16:22:11 +00:00
},
AddKey {
repo_path: String,
2017-03-22 16:28:45 +00:00
file: Option<String>,
2017-03-18 16:22:11 +00:00
set_default: bool
},
2017-03-16 19:05:58 +00:00
AlgoTest {
2017-03-17 10:03:07 +00:00
file: String,
bundle_size: usize,
chunker: ChunkerType,
compression: Option<Compression>,
2017-03-18 16:22:11 +00:00
encrypt: bool,
2017-03-17 10:03:07 +00:00
hash: HashMethod
2017-03-16 19:05:58 +00:00
}
}
2017-04-07 09:05:28 +00:00
fn parse_repo_path(repo_path: &str, existing: bool, backup_restr: Option<bool>, path_restr: Option<bool>) -> Result<(&str, Option<&str>, Option<&str>), String> {
2017-03-31 16:44:27 +00:00
let mut parts = repo_path.splitn(3, "::");
let mut repo = parts.next().unwrap_or(&DEFAULT_REPOSITORY);
if repo.is_empty() {
repo = &DEFAULT_REPOSITORY;
}
2017-04-07 09:05:28 +00:00
if existing && !Path::new(repo).join("config.yaml").exists() {
return Err("The specified repository does not exist".to_string());
}
if !existing && Path::new(repo).exists() {
return Err("The specified repository already exists".to_string());
}
2017-03-31 16:44:27 +00:00
let mut backup = parts.next();
if let Some(val) = backup {
if val.is_empty() {
backup = None
}
}
let mut path = parts.next();
if let Some(val) = path {
if val.is_empty() {
path = None
}
}
2017-03-26 09:34:16 +00:00
if let Some(restr) = backup_restr {
if !restr && backup.is_some() {
2017-04-07 09:05:28 +00:00
return Err("No backup may be given here".to_string());
2017-03-26 09:34:16 +00:00
}
if restr && backup.is_none() {
2017-04-07 09:05:28 +00:00
return Err("A backup must be specified".to_string());
2017-03-26 09:34:16 +00:00
}
}
if let Some(restr) = path_restr {
if !restr && path.is_some() {
2017-04-07 09:05:28 +00:00
return Err("No subpath may be given here".to_string());
2017-03-26 09:34:16 +00:00
}
if restr && path.is_none() {
2017-04-07 09:05:28 +00:00
return Err("A subpath must be specified".to_string());
2017-03-26 09:34:16 +00:00
}
}
2017-04-03 13:18:06 +00:00
Ok((repo, backup, path))
2017-03-26 09:34:16 +00:00
}
2017-04-07 09:05:28 +00:00
#[allow(unknown_lints,needless_pass_by_value)]
fn validate_repo_path(repo_path: String, existing: bool, backup_restr: Option<bool>, path_restr: Option<bool>) -> Result<(), String> {
parse_repo_path(&repo_path, existing, backup_restr, path_restr).map(|_| ())
}
fn parse_num(num: &str) -> Result<u64, String> {
2017-03-17 10:03:07 +00:00
if let Ok(num) = num.parse::<u64>() {
2017-04-03 13:18:06 +00:00
Ok(num)
2017-03-17 10:03:07 +00:00
} else {
2017-04-07 09:05:28 +00:00
Err("Must be a number".to_string())
2017-03-17 10:03:07 +00:00
}
}
2017-04-07 09:05:28 +00:00
#[allow(unknown_lints,needless_pass_by_value)]
fn validate_num(val: String) -> Result<(), String> {
parse_num(&val).map(|_| ())
}
fn parse_chunker(val: &str) -> Result<ChunkerType, String> {
2017-03-18 16:22:11 +00:00
if let Ok(chunker) = ChunkerType::from_string(val) {
2017-04-03 13:18:06 +00:00
Ok(chunker)
2017-03-17 10:03:07 +00:00
} else {
2017-04-07 09:05:28 +00:00
Err("Invalid chunker method/size".to_string())
2017-03-17 10:03:07 +00:00
}
}
2017-04-07 09:05:28 +00:00
#[allow(unknown_lints,needless_pass_by_value)]
fn validate_chunker(val: String) -> Result<(), String> {
parse_chunker(&val).map(|_| ())
}
fn parse_compression(val: &str) -> Result<Option<Compression>, String> {
2017-03-17 10:03:07 +00:00
if val == "none" {
2017-04-03 13:18:06 +00:00
return Ok(None)
2017-03-17 10:03:07 +00:00
}
if let Ok(compression) = Compression::from_string(val) {
2017-04-03 13:18:06 +00:00
Ok(Some(compression))
2017-03-17 10:03:07 +00:00
} else {
2017-04-07 09:05:28 +00:00
Err("Invalid compression method/level".to_string())
2017-03-17 10:03:07 +00:00
}
}
2017-04-07 09:05:28 +00:00
#[allow(unknown_lints,needless_pass_by_value)]
fn validate_compression(val: String) -> Result<(), String> {
parse_compression(&val).map(|_| ())
}
fn parse_public_key(val: &str) -> Result<Option<PublicKey>, String> {
if val.to_lowercase() == "none" {
return Ok(None);
}
2017-03-18 16:22:11 +00:00
let bytes = match parse_hex(val) {
Ok(bytes) => bytes,
Err(_) => {
2017-04-07 09:05:28 +00:00
return Err("Invalid hexadecimal".to_string());
2017-03-18 16:22:11 +00:00
}
};
if let Some(key) = PublicKey::from_slice(&bytes) {
2017-04-07 09:05:28 +00:00
Ok(Some(key))
2017-03-18 16:22:11 +00:00
} else {
2017-04-07 09:05:28 +00:00
return Err("Invalid key".to_string())
2017-03-18 16:22:11 +00:00
}
}
2017-04-07 09:05:28 +00:00
#[allow(unknown_lints,needless_pass_by_value)]
fn validate_public_key(val: String) -> Result<(), String> {
parse_public_key(&val).map(|_| ())
}
fn parse_hash(val: &str) -> Result<HashMethod, String> {
2017-03-18 16:22:11 +00:00
if let Ok(hash) = HashMethod::from(val) {
2017-04-03 13:18:06 +00:00
Ok(hash)
2017-03-17 10:03:07 +00:00
} else {
2017-04-07 09:05:28 +00:00
Err("Invalid hash method".to_string())
2017-03-17 10:03:07 +00:00
}
}
2017-04-07 09:05:28 +00:00
#[allow(unknown_lints,needless_pass_by_value)]
fn validate_hash(val: String) -> Result<(), String> {
parse_hash(&val).map(|_| ())
}
2017-04-03 13:18:06 +00:00
fn parse_bundle_id(val: &str) -> Result<BundleId, ErrorCode> {
2017-03-24 11:52:01 +00:00
if let Ok(hash) = Hash::from_string(val) {
2017-04-03 13:18:06 +00:00
Ok(BundleId(hash))
2017-03-24 11:52:01 +00:00
} else {
error!("Invalid bundle id: {}", val);
2017-04-03 13:18:06 +00:00
Err(ErrorCode::InvalidArgs)
2017-03-24 11:52:01 +00:00
}
}
2017-04-07 09:05:28 +00:00
#[allow(unknown_lints,needless_pass_by_value)]
fn validate_existing_path(val: String) -> Result<(), String> {
if !Path::new(&val).exists() {
Err("Path does not exist".to_string())
} else {
Ok(())
}
}
2017-03-25 11:43:49 +00:00
#[allow(unknown_lints,cyclomatic_complexity)]
2017-04-03 13:18:06 +00:00
pub fn parse() -> Result<Arguments, ErrorCode> {
2017-04-05 10:41:39 +00:00
let args = App::new("zvault").version(crate_version!()).author(crate_authors!(",\n")).about(crate_description!())
2017-04-05 14:02:16 +00:00
.settings(&[AppSettings::AllowMissingPositional, AppSettings::VersionlessSubcommands, AppSettings::SubcommandRequiredElseHelp])
2017-04-05 10:41:39 +00:00
.global_settings(&[AppSettings::UnifiedHelpMessage, AppSettings::ColoredHelp, AppSettings::ColorAuto])
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("init").about("Initialize a new repository")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[bundle_size] --bundle-size [SIZE] 'Set the target bundle size in MiB'")
.default_value(DEFAULT_BUNDLE_SIZE_STR).validator(validate_num))
.arg(Arg::from_usage("--chunker [CHUNKER] 'Set the chunker algorithm and target chunk size'")
.default_value(DEFAULT_CHUNKER).validator(validate_chunker))
.arg(Arg::from_usage("-c --compression [COMPRESSION] 'Set the compression method and level'")
.default_value(DEFAULT_COMPRESSION).validator(validate_compression))
2017-04-06 11:38:09 +00:00
.arg(Arg::from_usage("-e --encrypt 'Generate a keypair and enable encryption'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("--hash [HASH] 'Set the hash method'")
.default_value(DEFAULT_HASH).validator(validate_hash))
.arg(Arg::from_usage("-r --remote <REMOTE> 'Set the path to the mounted remote storage'")
.validator(validate_existing_path))
.arg(Arg::from_usage("[REPO] 'The path for the new repository'")
.default_value("").validator(|val| validate_repo_path(val, false, Some(false), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("backup").about("Create a new backup")
.arg(Arg::from_usage("--full 'Create a full backup without using a reference'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[reference] --ref [REF] 'Base the new backup on this reference'")
.conflicts_with("full"))
2017-04-06 11:39:24 +00:00
.arg(Arg::from_usage("[cross_device] -x --xdev 'Allow to cross filesystem boundaries'"))
2017-04-05 14:02:16 +00:00
.arg(Arg::from_usage("-e --exclude [PATTERN]... 'Exclude this path or file pattern'"))
2017-04-06 11:39:24 +00:00
.arg(Arg::from_usage("[excludes_from] --excludes-from [FILE] 'Read the list of excludes from this file'"))
.arg(Arg::from_usage("[no_default_excludes] --no-default-excludes 'Do not load the default excludes file'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("--tar 'Read the source data from a tar file'")
.conflicts_with_all(&["reference", "exclude", "excludes_from"]))
.arg(Arg::from_usage("<SRC> 'Source path to backup'")
.validator(validate_existing_path))
.arg(Arg::from_usage("<BACKUP> 'Backup path, [repository]::backup'")
.validator(|val| validate_repo_path(val, true, Some(true), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("restore").about("Restore a backup or subtree")
.arg(Arg::from_usage("--tar 'Restore in form of a tar file'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("<BACKUP> 'The backup/subtree path, [repository]::backup[::subtree]'")
.validator(|val| validate_repo_path(val, true, Some(true), None)))
.arg(Arg::from_usage("<DST> 'Destination path for backup'")
.validator(validate_existing_path)))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("remove").aliases(&["rm", "delete", "del"]).about("Remove a backup or a subtree")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("<BACKUP> 'The backup/subtree path, [repository]::backup[::subtree]'")
.validator(|val| validate_repo_path(val, true, Some(true), None))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("prune").about("Remove backups based on age")
.arg(Arg::from_usage("-p --prefix [PREFIX] 'Only consider backups starting with this prefix'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("-d --daily [NUM] 'Keep this number of daily backups'")
.default_value("0").validator(validate_num))
.arg(Arg::from_usage("-w --weekly [NUM] 'Keep this number of weekly backups'")
.default_value("0").validator(validate_num))
.arg(Arg::from_usage("-m --monthly [NUM] 'Keep this number of monthly backups'")
.default_value("0").validator(validate_num))
.arg(Arg::from_usage("-y --yearly [NUM] 'Keep this number of yearly backups'")
.default_value("0").validator(validate_num))
2017-04-05 14:02:16 +00:00
.arg(Arg::from_usage("-f --force 'Actually run the prune instead of simulating it'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[REPO] 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("vacuum").about("Reclaim space by rewriting bundles")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("-r --ratio [NUM] 'Ratio in % of unused space in a bundle to rewrite that bundle'")
.default_value(DEFAULT_VACUUM_RATIO_STR).validator(validate_num))
2017-04-05 14:02:16 +00:00
.arg(Arg::from_usage("-f --force 'Actually run the vacuum instead of simulating it'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[REPO] 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("check").about("Check the repository, a backup or a backup subtree")
.arg(Arg::from_usage("--full 'Also check file contents (slow)'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[PATH] 'Path of the repository/backup/subtree, [repository][::backup[::subtree]]'")
.validator(|val| validate_repo_path(val, true, None, None))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("list").alias("ls").about("List backups or backup contents")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[PATH] 'Path of the repository/backup/subtree, [repository][::backup[::subtree]]'")
.validator(|val| validate_repo_path(val, true, None, None))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("mount").about("Mount the repository, a backup or a subtree")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[PATH] 'Path of the repository/backup/subtree, [repository][::backup[::subtree]]'")
.validator(|val| validate_repo_path(val, true, None, None)))
.arg(Arg::from_usage("<MOUNTPOINT> 'Existing mount point'")
.validator(validate_existing_path)))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("bundlelist").about("List bundles in a repository")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[REPO] 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("bundleinfo").about("Display information on a bundle")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[REPO] 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false))))
2017-04-05 14:02:16 +00:00
.arg(Arg::from_usage("<BUNDLE> 'Id of the bundle'")))
.subcommand(SubCommand::with_name("import").about("Reconstruct a repository from the remote storage")
.arg(Arg::from_usage("-k --key [FILE]... 'Key file needed to read the bundles'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("<REMOTE> 'Remote repository path'")
.validator(validate_existing_path))
.arg(Arg::from_usage("[REPO] 'The path for the new repository'")
.validator(|val| validate_repo_path(val, false, Some(false), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("info").about("Display information on a repository, a backup or a subtree")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[PATH] 'Path of the repository/backup/subtree, [repository][::backup[::subtree]]'")
.validator(|val| validate_repo_path(val, true, None, None))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("analyze").about("Analyze the used and reclaimable space of bundles")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[REPO] 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("versions").about("Find different versions of a file in all backups")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[REPO] 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false))))
2017-04-05 14:02:16 +00:00
.arg(Arg::from_usage("<PATH> 'Path of the file'")))
.subcommand(SubCommand::with_name("diff").about("Display differences between two backup versions")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("<OLD> 'Old version, [repository]::backup[::subpath]'")
.validator(|val| validate_repo_path(val, true, Some(true), None)))
.arg(Arg::from_usage("<NEW> 'New version, [repository]::backup[::subpath]'")
.validator(|val| validate_repo_path(val, true, Some(true), None))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("config").about("Display or change the configuration")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[bundle_size] --bundle-size [SIZE] 'Set the target bundle size in MiB'")
.validator(validate_num))
.arg(Arg::from_usage("--chunker [CHUNKER] 'Set the chunker algorithm and target chunk size'")
.validator(validate_chunker))
.arg(Arg::from_usage("-c --compression [COMPRESSION] 'Set the compression method and level'")
.validator(validate_compression))
.arg(Arg::from_usage("-e --encryption [PUBLIC_KEY] 'The public key to use for encryption'")
.validator(validate_public_key))
.arg(Arg::from_usage("--hash [HASH] 'Set the hash method'")
.validator(validate_hash))
.arg(Arg::from_usage("[REPO] 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("genkey").about("Generate a new key pair")
.arg(Arg::from_usage("[FILE] 'Destination file for the keypair'")))
.subcommand(SubCommand::with_name("addkey").about("Add a key pair to the repository")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("-g --generate 'Generate a new key pair'")
.conflicts_with("FILE"))
2017-04-06 11:39:24 +00:00
.arg(Arg::from_usage("[set_default] --default -d 'Set the key pair as default'"))
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[FILE] 'File containing the keypair'")
.required_unless("generate").validator(validate_existing_path))
.arg(Arg::from_usage("[REPO] 'Path of the repository'")
.validator(|val| validate_repo_path(val, true, Some(false), Some(false)))))
2017-04-05 14:02:16 +00:00
.subcommand(SubCommand::with_name("algotest").about("Test a specific algorithm combination")
2017-04-07 09:05:28 +00:00
.arg(Arg::from_usage("[bundle_size] --bundle-size [SIZE] 'Set the target bundle size in MiB'")
.default_value(DEFAULT_BUNDLE_SIZE_STR).validator(validate_num))
.arg(Arg::from_usage("--chunker [CHUNKER] 'Set the chunker algorithm and target chunk size'")
.default_value(DEFAULT_CHUNKER).validator(validate_chunker))
.arg(Arg::from_usage("-c --compression [COMPRESSION] 'Set the compression method and level'")
.default_value(DEFAULT_COMPRESSION).validator(validate_compression))
.arg(Arg::from_usage("-e --encrypt 'Generate a keypair and enable encryption'"))
.arg(Arg::from_usage("--hash [HASH] 'Set the hash method'")
.default_value(DEFAULT_HASH).validator(validate_hash))
.arg(Arg::from_usage("<FILE> 'File with test data'")
.validator(validate_existing_path))).get_matches();
2017-03-17 06:15:19 +00:00
if let Some(args) = args.subcommand_matches("init") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), false, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Init {
2017-04-07 09:05:28 +00:00
bundle_size: (parse_num(args.value_of("bundle_size").unwrap()).unwrap() * 1024 * 1024) as usize,
chunker: parse_chunker(args.value_of("chunker").unwrap()).unwrap(),
compression: parse_compression(args.value_of("compression").unwrap()).unwrap(),
2017-04-06 11:38:09 +00:00
encryption: args.is_present("encrypt"),
2017-04-07 09:05:28 +00:00
hash: parse_hash(args.value_of("hash").unwrap()).unwrap(),
2017-03-17 10:03:07 +00:00
repo_path: repository.to_string(),
2017-03-22 13:42:27 +00:00
remote_path: args.value_of("remote").unwrap().to_string()
2017-04-03 13:18:06 +00:00
})
2017-03-17 10:03:07 +00:00
}
if let Some(args) = args.subcommand_matches("backup") {
2017-04-07 09:05:28 +00:00
let (repository, backup, _inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Backup {
2017-03-17 10:03:07 +00:00
repo_path: repository.to_string(),
backup_name: backup.unwrap().to_string(),
full: args.is_present("full"),
2017-03-29 20:13:28 +00:00
same_device: !args.is_present("cross_device"),
2017-03-24 10:00:20 +00:00
excludes: args.values_of("exclude").map(|v| v.map(|k| k.to_string()).collect()).unwrap_or_else(|| vec![]),
excludes_from: args.value_of("excludes_from").map(|v| v.to_string()),
2017-03-20 21:24:53 +00:00
src_path: args.value_of("SRC").unwrap().to_string(),
2017-03-26 18:33:32 +00:00
reference: args.value_of("reference").map(|v| v.to_string()),
2017-04-03 13:18:06 +00:00
no_default_excludes: args.is_present("no_default_excludes"),
tar: args.is_present("tar")
})
2017-03-17 10:03:07 +00:00
}
if let Some(args) = args.subcommand_matches("restore") {
2017-04-07 09:05:28 +00:00
let (repository, backup, inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), None).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Restore {
2017-03-17 10:03:07 +00:00
repo_path: repository.to_string(),
backup_name: backup.unwrap().to_string(),
inode: inode.map(|v| v.to_string()),
2017-04-03 13:18:06 +00:00
dst_path: args.value_of("DST").unwrap().to_string(),
tar: args.is_present("tar")
})
2017-03-17 10:03:07 +00:00
}
2017-03-17 11:58:22 +00:00
if let Some(args) = args.subcommand_matches("remove") {
2017-04-07 09:05:28 +00:00
let (repository, backup, inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), None).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Remove {
2017-03-17 11:58:22 +00:00
repo_path: repository.to_string(),
backup_name: backup.unwrap().to_string(),
inode: inode.map(|v| v.to_string())
2017-04-03 13:18:06 +00:00
})
2017-03-17 11:58:22 +00:00
}
2017-03-20 14:38:33 +00:00
if let Some(args) = args.subcommand_matches("prune") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), true, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Prune {
2017-03-20 14:38:33 +00:00
repo_path: repository.to_string(),
prefix: args.value_of("prefix").unwrap_or("").to_string(),
2017-03-20 17:11:03 +00:00
force: args.is_present("force"),
2017-04-03 13:18:06 +00:00
daily: match args.value_of("daily") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some(parse_num(v).unwrap() as usize)
2017-04-03 13:18:06 +00:00
},
weekly: match args.value_of("weekly") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some(parse_num(v).unwrap() as usize)
2017-04-03 13:18:06 +00:00
},
monthly: match args.value_of("monthly") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some(parse_num(v).unwrap() as usize)
2017-04-03 13:18:06 +00:00
},
yearly: match args.value_of("yearly") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some(parse_num(v).unwrap() as usize)
2017-04-03 13:18:06 +00:00
}
})
2017-03-20 14:38:33 +00:00
}
2017-03-17 11:58:22 +00:00
if let Some(args) = args.subcommand_matches("vacuum") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), true, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Vacuum {
2017-03-17 11:58:22 +00:00
repo_path: repository.to_string(),
2017-03-20 17:11:03 +00:00
force: args.is_present("force"),
2017-04-07 09:05:28 +00:00
ratio: parse_num(args.value_of("ratio").unwrap()).unwrap() as f32 / 100.0
2017-04-03 13:18:06 +00:00
})
2017-03-17 11:58:22 +00:00
}
2017-03-17 10:03:07 +00:00
if let Some(args) = args.subcommand_matches("check") {
2017-04-07 09:05:28 +00:00
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap_or(""), true, None, None).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Check {
2017-03-17 10:03:07 +00:00
repo_path: repository.to_string(),
backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string()),
full: args.is_present("full")
2017-04-03 13:18:06 +00:00
})
2017-03-17 10:03:07 +00:00
}
if let Some(args) = args.subcommand_matches("list") {
2017-04-07 09:05:28 +00:00
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap_or(""), true, None, None).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::List {
2017-03-17 10:03:07 +00:00
repo_path: repository.to_string(),
backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string())
2017-04-03 13:18:06 +00:00
})
2017-03-17 10:03:07 +00:00
}
2017-03-24 11:52:01 +00:00
if let Some(args) = args.subcommand_matches("bundlelist") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), true, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::BundleList {
2017-03-24 11:52:01 +00:00
repo_path: repository.to_string(),
2017-04-03 13:18:06 +00:00
})
2017-03-24 11:52:01 +00:00
}
if let Some(args) = args.subcommand_matches("bundleinfo") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), true, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::BundleInfo {
2017-03-17 10:03:07 +00:00
repo_path: repository.to_string(),
2017-04-03 13:18:06 +00:00
bundle_id: try!(parse_bundle_id(args.value_of("BUNDLE").unwrap()))
})
2017-03-17 10:03:07 +00:00
}
if let Some(args) = args.subcommand_matches("info") {
2017-04-07 09:05:28 +00:00
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap_or(""), true, None, None).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Info {
2017-03-17 10:03:07 +00:00
repo_path: repository.to_string(),
backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string())
2017-04-03 13:18:06 +00:00
})
2017-03-17 10:03:07 +00:00
}
2017-03-26 09:34:16 +00:00
if let Some(args) = args.subcommand_matches("mount") {
2017-04-07 09:05:28 +00:00
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap_or(""), true, None, None).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Mount {
2017-03-26 09:34:16 +00:00
repo_path: repository.to_string(),
backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string()),
mount_point: args.value_of("MOUNTPOINT").unwrap().to_string()
2017-04-03 13:18:06 +00:00
})
2017-03-26 09:34:16 +00:00
}
if let Some(args) = args.subcommand_matches("versions") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), true, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Versions {
repo_path: repository.to_string(),
path: args.value_of("PATH").unwrap().to_string()
2017-04-03 13:18:06 +00:00
})
}
2017-03-29 21:24:26 +00:00
if let Some(args) = args.subcommand_matches("diff") {
2017-04-07 09:05:28 +00:00
let (repository_old, backup_old, inode_old) = parse_repo_path(args.value_of("OLD").unwrap(), true, Some(true), None).unwrap();
let (repository_new, backup_new, inode_new) = parse_repo_path(args.value_of("NEW").unwrap(), true, Some(true), None).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Diff {
2017-03-29 21:24:26 +00:00
repo_path_old: repository_old.to_string(),
backup_name_old: backup_old.unwrap().to_string(),
inode_old: inode_old.map(|v| v.to_string()),
repo_path_new: repository_new.to_string(),
backup_name_new: backup_new.unwrap().to_string(),
2017-03-31 16:44:27 +00:00
inode_new: inode_new.map(|v| v.to_string()),
2017-04-03 13:18:06 +00:00
})
2017-03-29 21:24:26 +00:00
}
2017-03-26 09:34:16 +00:00
if let Some(args) = args.subcommand_matches("analyze") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), true, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Analyze {
2017-03-25 11:43:49 +00:00
repo_path: repository.to_string()
2017-04-03 13:18:06 +00:00
})
2017-03-25 11:43:49 +00:00
}
2017-03-17 11:58:22 +00:00
if let Some(args) = args.subcommand_matches("import") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), false, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Import {
2017-03-17 11:58:22 +00:00
repo_path: repository.to_string(),
2017-03-22 16:28:45 +00:00
remote_path: args.value_of("REMOTE").unwrap().to_string(),
key_files: args.values_of("key").map(|v| v.map(|k| k.to_string()).collect()).unwrap_or_else(|| vec![])
2017-04-03 13:18:06 +00:00
})
2017-03-17 11:58:22 +00:00
}
2017-03-26 18:33:32 +00:00
if let Some(args) = args.subcommand_matches("config") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), true, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::Config {
bundle_size: match args.value_of("bundle_size") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some((parse_num(v).unwrap() * 1024 * 1024) as usize)
2017-04-03 13:18:06 +00:00
},
chunker: match args.value_of("chunker") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some(parse_chunker(v).unwrap())
2017-04-03 13:18:06 +00:00
},
compression: match args.value_of("compression") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some(parse_compression(v).unwrap())
2017-04-03 13:18:06 +00:00
},
encryption: match args.value_of("encryption") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some(parse_public_key(v).unwrap())
2017-04-03 13:18:06 +00:00
},
hash: match args.value_of("hash") {
None => None,
2017-04-07 09:05:28 +00:00
Some(v) => Some(parse_hash(v).unwrap())
2017-04-03 13:18:06 +00:00
},
2017-03-18 16:22:11 +00:00
repo_path: repository.to_string(),
2017-04-03 13:18:06 +00:00
})
2017-03-18 16:22:11 +00:00
}
2017-03-22 16:28:45 +00:00
if let Some(args) = args.subcommand_matches("genkey") {
2017-04-03 13:18:06 +00:00
return Ok(Arguments::GenKey {
2017-03-22 16:28:45 +00:00
file: args.value_of("FILE").map(|v| v.to_string())
2017-04-03 13:18:06 +00:00
})
2017-03-18 16:22:11 +00:00
}
if let Some(args) = args.subcommand_matches("addkey") {
2017-04-07 09:05:28 +00:00
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap_or(""), true, Some(false), Some(false)).unwrap();
2017-04-03 13:18:06 +00:00
return Ok(Arguments::AddKey {
2017-03-18 16:22:11 +00:00
repo_path: repository.to_string(),
set_default: args.is_present("set_default"),
2017-03-22 16:28:45 +00:00
file: args.value_of("FILE").map(|v| v.to_string())
2017-04-03 13:18:06 +00:00
})
2017-03-18 16:22:11 +00:00
}
2017-03-17 10:03:07 +00:00
if let Some(args) = args.subcommand_matches("algotest") {
2017-04-03 13:18:06 +00:00
return Ok(Arguments::AlgoTest {
2017-04-07 09:05:28 +00:00
bundle_size: (parse_num(args.value_of("bundle_size").unwrap()).unwrap() * 1024 * 1024) as usize,
chunker: parse_chunker(args.value_of("chunker").unwrap()).unwrap(),
compression: parse_compression(args.value_of("compression").unwrap()).unwrap(),
2017-03-18 16:22:11 +00:00
encrypt: args.is_present("encrypt"),
2017-04-07 09:05:28 +00:00
hash: parse_hash(args.value_of("hash").unwrap()).unwrap(),
2017-03-17 10:03:07 +00:00
file: args.value_of("FILE").unwrap().to_string(),
2017-04-03 13:18:06 +00:00
})
2017-03-17 06:15:19 +00:00
}
2017-03-17 10:03:07 +00:00
error!("No subcommand given");
2017-04-03 13:18:06 +00:00
Err(ErrorCode::InvalidArgs)
2017-03-17 06:15:19 +00:00
}