diff --git a/CHANGELOG.md b/CHANGELOG.md index 28021d9..f19446b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This project follows [semantic versioning](http://semver.org). ### UNRELEASED * [added] Added `copy` subcommand * [modified] Also documenting common flags in subcommands +* [modified] Using repository aliases (**conversion needed**) ### v0.3.2 (2017-05-11) diff --git a/docs/man/zvault.1.md b/docs/man/zvault.1.md index 02d57e1..1a1845f 100644 --- a/docs/man/zvault.1.md +++ b/docs/man/zvault.1.md @@ -76,8 +76,10 @@ location. ### Path syntax 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` -will be used instead. +If the given repository path is absolute, this path will be used as is. +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 done via the syntax `repository::backup_name` where `repository` is the path to diff --git a/src/cli/args.rs b/src/cli/args.rs index f61fdc7..b769415 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -1,13 +1,13 @@ use ::prelude::*; use super::*; -use std::path::Path; +use std::path::{Path, PathBuf}; use log::LogLevel; use clap::{App, AppSettings, Arg, SubCommand}; pub enum Arguments { Init { - repo_path: String, + repo_path: PathBuf, bundle_size: usize, chunker: ChunkerType, compression: Option, @@ -16,7 +16,7 @@ pub enum Arguments { remote_path: String }, Backup { - repo_path: String, + repo_path: PathBuf, backup_name: String, src_path: String, full: bool, @@ -28,20 +28,20 @@ pub enum Arguments { tar: bool }, Restore { - repo_path: String, + repo_path: PathBuf, backup_name: String, inode: Option, dst_path: String, tar: bool }, Remove { - repo_path: String, + repo_path: PathBuf, backup_name: String, inode: Option, force: bool }, Prune { - repo_path: String, + repo_path: PathBuf, prefix: String, daily: usize, weekly: usize, @@ -50,13 +50,13 @@ pub enum Arguments { force: bool }, Vacuum { - repo_path: String, + repo_path: PathBuf, ratio: f32, force: bool, combine: bool }, Check { - repo_path: String, + repo_path: PathBuf, backup_name: Option, inode: Option, bundles: bool, @@ -65,56 +65,56 @@ pub enum Arguments { repair: bool }, List { - repo_path: String, + repo_path: PathBuf, backup_name: Option, inode: Option }, Info { - repo_path: String, + repo_path: PathBuf, backup_name: Option, inode: Option }, Copy { - repo_path_src: String, + repo_path_src: PathBuf, backup_name_src: String, - repo_path_dst: String, + repo_path_dst: PathBuf, backup_name_dst: String, }, Mount { - repo_path: String, + repo_path: PathBuf, backup_name: Option, inode: Option, mount_point: String }, Versions { - repo_path: String, + repo_path: PathBuf, path: String }, Diff { - repo_path_old: String, + repo_path_old: PathBuf, backup_name_old: String, inode_old: Option, - repo_path_new: String, + repo_path_new: PathBuf, backup_name_new: String, inode_new: Option }, Analyze { - repo_path: String + repo_path: PathBuf }, BundleList { - repo_path: String + repo_path: PathBuf }, BundleInfo { - repo_path: String, + repo_path: PathBuf, bundle_id: BundleId }, Import { - repo_path: String, + repo_path: PathBuf, remote_path: String, key_files: Vec }, Config { - repo_path: String, + repo_path: PathBuf, bundle_size: Option, chunker: Option, compression: Option>, @@ -126,7 +126,7 @@ pub enum Arguments { password: Option }, AddKey { - repo_path: String, + repo_path: PathBuf, file: Option, password: Option, set_default: bool @@ -142,16 +142,25 @@ pub enum Arguments { } -fn parse_repo_path(repo_path: &str, existing: bool, backup_restr: Option, path_restr: Option) -> Result<(&str, Option<&str>, Option<&str>), String> { - let mut parts = repo_path.splitn(3, "::"); - let mut repo = parts.next().unwrap_or(&DEFAULT_REPOSITORY); - if repo.is_empty() { - repo = &DEFAULT_REPOSITORY; +fn convert_repo_path(mut path_str: &str) -> PathBuf { + if path_str.is_empty() { + path_str = "default"; } - 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, path_restr: Option) -> 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()); } - if !existing && Path::new(repo).exists() { + if !existing && repo.exists() { return Err("The specified repository already exists".to_string()); } 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(), encryption: args.is_present("encrypt"), 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() } }, ("backup", Some(args)) => { let (repository, backup, _inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), Some(false)).unwrap(); Arguments::Backup { - repo_path: repository.to_string(), + repo_path: repository, backup_name: backup.unwrap().to_string(), full: args.is_present("full"), same_device: !args.is_present("cross_device"), @@ -484,7 +493,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { ("restore", Some(args)) => { let (repository, backup, inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), None).unwrap(); Arguments::Restore { - repo_path: repository.to_string(), + repo_path: repository, backup_name: backup.unwrap().to_string(), inode: inode.map(|v| v.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)) => { let (repository, backup, inode) = parse_repo_path(args.value_of("BACKUP").unwrap(), true, Some(true), None).unwrap(); Arguments::Remove { - repo_path: repository.to_string(), + repo_path: repository, backup_name: backup.unwrap().to_string(), inode: inode.map(|v| v.to_string()), force: args.is_present("force") @@ -503,7 +512,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { ("prune", Some(args)) => { let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); Arguments::Prune { - repo_path: repository.to_string(), + repo_path: repository, prefix: args.value_of("prefix").unwrap_or("").to_string(), force: args.is_present("force"), 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)) => { let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); Arguments::Vacuum { - repo_path: repository.to_string(), + repo_path: repository, force: args.is_present("force"), combine: args.is_present("combine"), 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)) => { let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); Arguments::Check { - repo_path: repository.to_string(), + repo_path: repository, backup_name: backup.map(|v| v.to_string()), inode: inode.map(|v| v.to_string()), bundles: args.is_present("bundles"), @@ -536,7 +545,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { ("list", Some(args)) => { let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); Arguments::List { - repo_path: repository.to_string(), + repo_path: repository, backup_name: backup.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)) => { let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); Arguments::BundleList { - repo_path: repository.to_string(), + repo_path: repository, } }, ("bundleinfo", Some(args)) => { let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); Arguments::BundleInfo { - repo_path: repository.to_string(), + repo_path: repository, bundle_id: try!(parse_bundle_id(args.value_of("BUNDLE").unwrap())) } }, ("info", Some(args)) => { let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); Arguments::Info { - repo_path: repository.to_string(), + repo_path: repository, backup_name: backup.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_dst, backup_dst, _inode) = parse_repo_path(args.value_of("DST").unwrap(), true, Some(true), Some(false)).unwrap(); Arguments::Copy { - repo_path_src: repository_src.to_string(), + repo_path_src: repository_src, 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(), } }, ("mount", Some(args)) => { let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); Arguments::Mount { - repo_path: repository.to_string(), + repo_path: repository, backup_name: backup.map(|v| v.to_string()), inode: inode.map(|v| v.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)) => { let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); Arguments::Versions { - repo_path: repository.to_string(), + repo_path: repository, 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_new, backup_new, inode_new) = parse_repo_path(args.value_of("NEW").unwrap(), true, Some(true), None).unwrap(); Arguments::Diff { - repo_path_old: repository_old.to_string(), + repo_path_old: repository_old, backup_name_old: backup_old.unwrap().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(), inode_new: inode_new.map(|v| v.to_string()), } @@ -603,13 +612,13 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { ("analyze", Some(args)) => { let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); Arguments::Analyze { - repo_path: repository.to_string() + repo_path: repository } }, ("import", Some(args)) => { let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), false, Some(false), Some(false)).unwrap(); Arguments::Import { - repo_path: repository.to_string(), + repo_path: repository, 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![]) } @@ -622,7 +631,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { compression: args.value_of("compression").map(|v| parse_compression(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()), - repo_path: repository.to_string(), + repo_path: repository, } }, ("genkey", Some(args)) => { @@ -634,7 +643,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { ("addkey", Some(args)) => { let (repository, _backup, _inode) = parse_repo_path(args.value_of("REPO").unwrap(), true, Some(false), Some(false)).unwrap(); Arguments::AddKey { - repo_path: repository.to_string(), + repo_path: repository, set_default: args.is_present("set_default"), password: args.value_of("password").map(|v| v.to_string()), file: args.value_of("FILE").map(|v| v.to_string()) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 7aaaa1d..7c339d6 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -12,7 +12,7 @@ use std::io::{BufReader, BufRead}; use std::fs::File; use std::env; use std::str; -use std::path::Path; +use std::path::{Path, PathBuf}; 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_VACUUM_RATIO_STR: &'static str = "0"; lazy_static! { - pub static ref DEFAULT_REPOSITORY: String = { - env::home_dir().unwrap().join(".zvault").to_string_lossy().to_string() + pub static ref ZVAULT_FOLDER: PathBuf = { + env::home_dir().unwrap().join(".zvault") }; } @@ -93,7 +93,7 @@ macro_rules! checked { }; } -fn open_repository(path: &str) -> Result { +fn open_repository(path: &Path) -> Result { Ok(checked!(Repository::open(path), "load repository", ErrorCode::LoadRepository)) }