Using repository aliases

pull/10/head
Dennis Schwerdel 2017-05-17 09:43:14 +02:00
parent 645022ce9c
commit 2fe62cbe27
4 changed files with 68 additions and 56 deletions

View File

@ -6,6 +6,7 @@ This project follows [semantic versioning](http://semver.org).
### UNRELEASED ### UNRELEASED
* [added] Added `copy` subcommand * [added] Added `copy` subcommand
* [modified] Also documenting common flags in subcommands * [modified] Also documenting common flags in subcommands
* [modified] Using repository aliases (**conversion needed**)
### v0.3.2 (2017-05-11) ### v0.3.2 (2017-05-11)

View File

@ -76,8 +76,10 @@ location.
### Path syntax ### Path syntax
Most subcommands work with a repository that has to be specified as a parameter. Most subcommands work with a repository that has to be specified as a parameter.
If this repository is specified as `::`, the default repository in `~/.zvault` If the given repository path is absolute, this path will be used as is.
will be used instead. If the given path is relative, the repository will be located in
`~/.zvault/repos`. If the path is empty (specified as `::`), the default
repository in `~/.zvault/repos/default` will be used.
Some subcommands need to reference a specific backup in the repository. This is Some subcommands need to reference a specific backup in the repository. This is
done via the syntax `repository::backup_name` where `repository` is the path to done via the syntax `repository::backup_name` where `repository` is the path to

View File

@ -1,13 +1,13 @@
use ::prelude::*; use ::prelude::*;
use super::*; use super::*;
use std::path::Path; use std::path::{Path, PathBuf};
use log::LogLevel; use log::LogLevel;
use clap::{App, AppSettings, Arg, SubCommand}; use clap::{App, AppSettings, Arg, SubCommand};
pub enum Arguments { pub enum Arguments {
Init { Init {
repo_path: String, repo_path: PathBuf,
bundle_size: usize, bundle_size: usize,
chunker: ChunkerType, chunker: ChunkerType,
compression: Option<Compression>, compression: Option<Compression>,
@ -16,7 +16,7 @@ pub enum Arguments {
remote_path: String remote_path: String
}, },
Backup { Backup {
repo_path: String, repo_path: PathBuf,
backup_name: String, backup_name: String,
src_path: String, src_path: String,
full: bool, full: bool,
@ -28,20 +28,20 @@ pub enum Arguments {
tar: bool tar: bool
}, },
Restore { Restore {
repo_path: String, repo_path: PathBuf,
backup_name: String, backup_name: String,
inode: Option<String>, inode: Option<String>,
dst_path: String, dst_path: String,
tar: bool tar: bool
}, },
Remove { Remove {
repo_path: String, repo_path: PathBuf,
backup_name: String, backup_name: String,
inode: Option<String>, inode: Option<String>,
force: bool force: bool
}, },
Prune { Prune {
repo_path: String, repo_path: PathBuf,
prefix: String, prefix: String,
daily: usize, daily: usize,
weekly: usize, weekly: usize,
@ -50,13 +50,13 @@ pub enum Arguments {
force: bool force: bool
}, },
Vacuum { Vacuum {
repo_path: String, repo_path: PathBuf,
ratio: f32, ratio: f32,
force: bool, force: bool,
combine: bool combine: bool
}, },
Check { Check {
repo_path: String, repo_path: PathBuf,
backup_name: Option<String>, backup_name: Option<String>,
inode: Option<String>, inode: Option<String>,
bundles: bool, bundles: bool,
@ -65,56 +65,56 @@ pub enum Arguments {
repair: bool repair: bool
}, },
List { List {
repo_path: String, repo_path: PathBuf,
backup_name: Option<String>, backup_name: Option<String>,
inode: Option<String> inode: Option<String>
}, },
Info { Info {
repo_path: String, repo_path: PathBuf,
backup_name: Option<String>, backup_name: Option<String>,
inode: Option<String> inode: Option<String>
}, },
Copy { Copy {
repo_path_src: String, repo_path_src: PathBuf,
backup_name_src: String, backup_name_src: String,
repo_path_dst: String, repo_path_dst: PathBuf,
backup_name_dst: String, backup_name_dst: String,
}, },
Mount { Mount {
repo_path: String, repo_path: PathBuf,
backup_name: Option<String>, backup_name: Option<String>,
inode: Option<String>, inode: Option<String>,
mount_point: String mount_point: String
}, },
Versions { Versions {
repo_path: String, repo_path: PathBuf,
path: String path: String
}, },
Diff { Diff {
repo_path_old: String, repo_path_old: PathBuf,
backup_name_old: String, backup_name_old: String,
inode_old: Option<String>, inode_old: Option<String>,
repo_path_new: String, repo_path_new: PathBuf,
backup_name_new: String, backup_name_new: String,
inode_new: Option<String> inode_new: Option<String>
}, },
Analyze { Analyze {
repo_path: String repo_path: PathBuf
}, },
BundleList { BundleList {
repo_path: String repo_path: PathBuf
}, },
BundleInfo { BundleInfo {
repo_path: String, repo_path: PathBuf,
bundle_id: BundleId bundle_id: BundleId
}, },
Import { Import {
repo_path: String, repo_path: PathBuf,
remote_path: String, remote_path: String,
key_files: Vec<String> key_files: Vec<String>
}, },
Config { Config {
repo_path: String, repo_path: PathBuf,
bundle_size: Option<usize>, bundle_size: Option<usize>,
chunker: Option<ChunkerType>, chunker: Option<ChunkerType>,
compression: Option<Option<Compression>>, compression: Option<Option<Compression>>,
@ -126,7 +126,7 @@ pub enum Arguments {
password: Option<String> password: Option<String>
}, },
AddKey { AddKey {
repo_path: String, repo_path: PathBuf,
file: Option<String>, file: Option<String>,
password: Option<String>, password: Option<String>,
set_default: bool set_default: bool
@ -142,16 +142,25 @@ pub enum Arguments {
} }
fn parse_repo_path(repo_path: &str, existing: bool, backup_restr: Option<bool>, path_restr: Option<bool>) -> Result<(&str, Option<&str>, Option<&str>), String> { fn convert_repo_path(mut path_str: &str) -> PathBuf {
let mut parts = repo_path.splitn(3, "::"); if path_str.is_empty() {
let mut repo = parts.next().unwrap_or(&DEFAULT_REPOSITORY); path_str = "default";
if repo.is_empty() {
repo = &DEFAULT_REPOSITORY;
} }
if existing && !Path::new(repo).join("config.yaml").exists() { let path = Path::new(path_str);
if path.is_absolute() {
path.to_path_buf()
} else {
ZVAULT_FOLDER.join("repos").join(path)
}
}
fn parse_repo_path(repo_path: &str, existing: bool, backup_restr: Option<bool>, path_restr: Option<bool>) -> Result<(PathBuf, Option<&str>, Option<&str>), String> {
let mut parts = repo_path.splitn(3, "::");
let repo = convert_repo_path(parts.next().unwrap_or(""));
if existing && !repo.join("config.yaml").exists() {
return Err("The specified repository does not exist".to_string()); return Err("The specified repository does not exist".to_string());
} }
if !existing && Path::new(repo).exists() { if !existing && repo.exists() {
return Err("The specified repository already exists".to_string()); return Err("The specified repository already exists".to_string());
} }
let mut backup = parts.next(); let mut backup = parts.next();
@ -462,14 +471,14 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
compression: parse_compression(args.value_of("compression").unwrap()).unwrap(), compression: parse_compression(args.value_of("compression").unwrap()).unwrap(),
encryption: args.is_present("encrypt"), encryption: args.is_present("encrypt"),
hash: parse_hash(args.value_of("hash").unwrap()).unwrap(), hash: parse_hash(args.value_of("hash").unwrap()).unwrap(),
repo_path: repository.to_string(), repo_path: repository,
remote_path: args.value_of("remote").unwrap().to_string() remote_path: args.value_of("remote").unwrap().to_string()
} }
}, },
("backup", Some(args)) => { ("backup", Some(args)) => {
let (repository, backup, _inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), Some(false)).unwrap(); let (repository, backup, _inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), Some(false)).unwrap();
Arguments::Backup { Arguments::Backup {
repo_path: repository.to_string(), repo_path: repository,
backup_name: backup.unwrap().to_string(), backup_name: backup.unwrap().to_string(),
full: args.is_present("full"), full: args.is_present("full"),
same_device: !args.is_present("cross_device"), same_device: !args.is_present("cross_device"),
@ -484,7 +493,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("restore", Some(args)) => { ("restore", Some(args)) => {
let (repository, backup, inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), None).unwrap(); let (repository, backup, inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), None).unwrap();
Arguments::Restore { Arguments::Restore {
repo_path: repository.to_string(), repo_path: repository,
backup_name: backup.unwrap().to_string(), backup_name: backup.unwrap().to_string(),
inode: inode.map(|v| v.to_string()), inode: inode.map(|v| v.to_string()),
dst_path: args.value_of("DST").unwrap().to_string(), dst_path: args.value_of("DST").unwrap().to_string(),
@ -494,7 +503,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("remove", Some(args)) => { ("remove", Some(args)) => {
let (repository, backup, inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), None).unwrap(); let (repository, backup, inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), None).unwrap();
Arguments::Remove { Arguments::Remove {
repo_path: repository.to_string(), repo_path: repository,
backup_name: backup.unwrap().to_string(), backup_name: backup.unwrap().to_string(),
inode: inode.map(|v| v.to_string()), inode: inode.map(|v| v.to_string()),
force: args.is_present("force") force: args.is_present("force")
@ -503,7 +512,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("prune", Some(args)) => { ("prune", Some(args)) => {
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap();
Arguments::Prune { Arguments::Prune {
repo_path: repository.to_string(), repo_path: repository,
prefix: args.value_of("prefix").unwrap_or("").to_string(), prefix: args.value_of("prefix").unwrap_or("").to_string(),
force: args.is_present("force"), force: args.is_present("force"),
daily: parse_num(args.value_of("daily").unwrap()).unwrap() as usize, daily: parse_num(args.value_of("daily").unwrap()).unwrap() as usize,
@ -515,7 +524,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("vacuum", Some(args)) => { ("vacuum", Some(args)) => {
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap();
Arguments::Vacuum { Arguments::Vacuum {
repo_path: repository.to_string(), repo_path: repository,
force: args.is_present("force"), force: args.is_present("force"),
combine: args.is_present("combine"), combine: args.is_present("combine"),
ratio: parse_num(args.value_of("ratio").unwrap()).unwrap() as f32 / 100.0 ratio: parse_num(args.value_of("ratio").unwrap()).unwrap() as f32 / 100.0
@ -524,7 +533,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("check", Some(args)) => { ("check", Some(args)) => {
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap();
Arguments::Check { Arguments::Check {
repo_path: repository.to_string(), repo_path: repository,
backup_name: backup.map(|v| v.to_string()), backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string()), inode: inode.map(|v| v.to_string()),
bundles: args.is_present("bundles"), bundles: args.is_present("bundles"),
@ -536,7 +545,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("list", Some(args)) => { ("list", Some(args)) => {
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap();
Arguments::List { Arguments::List {
repo_path: repository.to_string(), repo_path: repository,
backup_name: backup.map(|v| v.to_string()), backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string()) inode: inode.map(|v| v.to_string())
} }
@ -544,20 +553,20 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("bundlelist", Some(args)) => { ("bundlelist", Some(args)) => {
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap();
Arguments::BundleList { Arguments::BundleList {
repo_path: repository.to_string(), repo_path: repository,
} }
}, },
("bundleinfo", Some(args)) => { ("bundleinfo", Some(args)) => {
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap();
Arguments::BundleInfo { Arguments::BundleInfo {
repo_path: repository.to_string(), repo_path: repository,
bundle_id: try!(parse_bundle_id(args.value_of("BUNDLE").unwrap())) bundle_id: try!(parse_bundle_id(args.value_of("BUNDLE").unwrap()))
} }
}, },
("info", Some(args)) => { ("info", Some(args)) => {
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap();
Arguments::Info { Arguments::Info {
repo_path: repository.to_string(), repo_path: repository,
backup_name: backup.map(|v| v.to_string()), backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string()) inode: inode.map(|v| v.to_string())
} }
@ -566,16 +575,16 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
let (repository_src, backup_src, _inode) = parse_repo_path(args.value_of("SRC").unwrap(), true, Some(true), Some(false)).unwrap(); let (repository_src, backup_src, _inode) = parse_repo_path(args.value_of("SRC").unwrap(), true, Some(true), Some(false)).unwrap();
let (repository_dst, backup_dst, _inode) = parse_repo_path(args.value_of("DST").unwrap(), true, Some(true), Some(false)).unwrap(); let (repository_dst, backup_dst, _inode) = parse_repo_path(args.value_of("DST").unwrap(), true, Some(true), Some(false)).unwrap();
Arguments::Copy { Arguments::Copy {
repo_path_src: repository_src.to_string(), repo_path_src: repository_src,
backup_name_src: backup_src.unwrap().to_string(), backup_name_src: backup_src.unwrap().to_string(),
repo_path_dst: repository_dst.to_string(), repo_path_dst: repository_dst,
backup_name_dst: backup_dst.unwrap().to_string(), backup_name_dst: backup_dst.unwrap().to_string(),
} }
}, },
("mount", Some(args)) => { ("mount", Some(args)) => {
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap();
Arguments::Mount { Arguments::Mount {
repo_path: repository.to_string(), repo_path: repository,
backup_name: backup.map(|v| v.to_string()), backup_name: backup.map(|v| v.to_string()),
inode: inode.map(|v| v.to_string()), inode: inode.map(|v| v.to_string()),
mount_point: args.value_of("MOUNTPOINT").unwrap().to_string() mount_point: args.value_of("MOUNTPOINT").unwrap().to_string()
@ -584,7 +593,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("versions", Some(args)) => { ("versions", Some(args)) => {
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap();
Arguments::Versions { Arguments::Versions {
repo_path: repository.to_string(), repo_path: repository,
path: args.value_of("PATH").unwrap().to_string() path: args.value_of("PATH").unwrap().to_string()
} }
}, },
@ -592,10 +601,10 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
let (repository_old, backup_old, inode_old) = parse_repo_path(args.value_of("OLD").unwrap(), true, Some(true), None).unwrap(); 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(); let (repository_new, backup_new, inode_new) = parse_repo_path(args.value_of("NEW").unwrap(), true, Some(true), None).unwrap();
Arguments::Diff { Arguments::Diff {
repo_path_old: repository_old.to_string(), repo_path_old: repository_old,
backup_name_old: backup_old.unwrap().to_string(), backup_name_old: backup_old.unwrap().to_string(),
inode_old: inode_old.map(|v| v.to_string()), inode_old: inode_old.map(|v| v.to_string()),
repo_path_new: repository_new.to_string(), repo_path_new: repository_new,
backup_name_new: backup_new.unwrap().to_string(), backup_name_new: backup_new.unwrap().to_string(),
inode_new: inode_new.map(|v| v.to_string()), inode_new: inode_new.map(|v| v.to_string()),
} }
@ -603,13 +612,13 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("analyze", Some(args)) => { ("analyze", Some(args)) => {
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap();
Arguments::Analyze { Arguments::Analyze {
repo_path: repository.to_string() repo_path: repository
} }
}, },
("import", Some(args)) => { ("import", Some(args)) => {
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), false, Some(false), Some(false)).unwrap(); let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), false, Some(false), Some(false)).unwrap();
Arguments::Import { Arguments::Import {
repo_path: repository.to_string(), repo_path: repository,
remote_path: args.value_of("REMOTE").unwrap().to_string(), 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![]) key_files: args.values_of("key").map(|v| v.map(|k| k.to_string()).collect()).unwrap_or_else(|| vec![])
} }
@ -622,7 +631,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
compression: args.value_of("compression").map(|v| parse_compression(v).unwrap()), compression: args.value_of("compression").map(|v| parse_compression(v).unwrap()),
encryption: args.value_of("encryption").map(|v| parse_public_key(v).unwrap()), encryption: args.value_of("encryption").map(|v| parse_public_key(v).unwrap()),
hash: args.value_of("hash").map(|v| parse_hash(v).unwrap()), hash: args.value_of("hash").map(|v| parse_hash(v).unwrap()),
repo_path: repository.to_string(), repo_path: repository,
} }
}, },
("genkey", Some(args)) => { ("genkey", Some(args)) => {
@ -634,7 +643,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
("addkey", Some(args)) => { ("addkey", Some(args)) => {
let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap();
Arguments::AddKey { Arguments::AddKey {
repo_path: repository.to_string(), repo_path: repository,
set_default: args.is_present("set_default"), set_default: args.is_present("set_default"),
password: args.value_of("password").map(|v| v.to_string()), password: args.value_of("password").map(|v| v.to_string()),
file: args.value_of("FILE").map(|v| v.to_string()) file: args.value_of("FILE").map(|v| v.to_string())

View File

@ -12,7 +12,7 @@ use std::io::{BufReader, BufRead};
use std::fs::File; use std::fs::File;
use std::env; use std::env;
use std::str; use std::str;
use std::path::Path; use std::path::{Path, PathBuf};
use self::args::Arguments; use self::args::Arguments;
@ -76,8 +76,8 @@ pub const DEFAULT_COMPRESSION: &'static str = "brotli/3";
pub const DEFAULT_BUNDLE_SIZE_STR: &'static str = "25"; pub const DEFAULT_BUNDLE_SIZE_STR: &'static str = "25";
pub const DEFAULT_VACUUM_RATIO_STR: &'static str = "0"; pub const DEFAULT_VACUUM_RATIO_STR: &'static str = "0";
lazy_static! { lazy_static! {
pub static ref DEFAULT_REPOSITORY: String = { pub static ref ZVAULT_FOLDER: PathBuf = {
env::home_dir().unwrap().join(".zvault").to_string_lossy().to_string() env::home_dir().unwrap().join(".zvault")
}; };
} }
@ -93,7 +93,7 @@ macro_rules! checked {
}; };
} }
fn open_repository(path: &str) -> Result<Repository, ErrorCode> { fn open_repository(path: &Path) -> Result<Repository, ErrorCode> {
Ok(checked!(Repository::open(path), "load repository", ErrorCode::LoadRepository)) Ok(checked!(Repository::open(path), "load repository", ErrorCode::LoadRepository))
} }