Merge pull request #241 from rustic-rs/clap-v4

update clap to v4
This commit is contained in:
aawsome 2023-04-16 08:45:21 +02:00 committed by GitHub
commit fa3604b98e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 314 additions and 231 deletions

151
Cargo.lock generated
View File

@ -75,6 +75,55 @@ dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
[[package]]
name = "anstyle-parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
]
[[package]]
name = "anyhow"
version = "1.0.70"
@ -87,17 +136,6 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -258,51 +296,55 @@ dependencies = [
[[package]]
name = "clap"
version = "3.2.23"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a"
dependencies = [
"atty",
"bitflags",
"clap_builder",
"clap_derive",
"clap_lex",
"indexmap",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6"
dependencies = [
"anstream",
"anstyle",
"bitflags",
"clap_lex",
"strsim",
"termcolor",
"textwrap",
"terminal_size",
]
[[package]]
name = "clap_complete"
version = "3.2.5"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f7a2e0a962c45ce25afce14220bc24f9dade0a1787f185cecf96bfba7847cd8"
checksum = "01c22dcfb410883764b29953103d9ef7bb8fe21b3fa1158bc99986c2067294bd"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "3.2.18"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.14",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
[[package]]
name = "codespan-reporting"
@ -314,6 +356,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "comfy-table"
version = "6.1.4"
@ -932,15 +980,6 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
@ -1175,6 +1214,18 @@ version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi 0.3.1",
"io-lifetimes",
"rustix",
"windows-sys 0.48.0",
]
[[package]]
name = "itertools"
version = "0.10.5"
@ -1413,12 +1464,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "os_str_bytes"
version = "6.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
[[package]]
name = "owo-colors"
version = "3.5.0"
@ -2302,10 +2347,14 @@ dependencies = [
]
[[package]]
name = "textwrap"
version = "0.16.0"
name = "terminal_size"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
dependencies = [
"rustix",
"windows-sys 0.48.0",
]
[[package]]
name = "thiserror"
@ -2573,6 +2622,12 @@ dependencies = [
"log",
]
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"

View File

@ -70,8 +70,8 @@ semver = "1"
dirs = "5"
cachedir = "0.3"
# commands
clap = { version = "3", features = ["derive", "env"] }
clap_complete = "3.2.4"
clap = { version = "4", features = ["derive", "env", "wrap_help"] }
clap_complete = "4"
directories = "5"
nom = "7"
toml = "0.7"

View File

@ -3,5 +3,7 @@ Changes in version x.x.x:
Breaking changes:
Bugs fixed:
- restore: Warm-up options given by the command line didn't work. This has been fixed.
New features:
- Updated to clap v4

View File

@ -46,42 +46,42 @@ pub struct LocalSourceOptions {
ignore_devid: bool,
/// Glob pattern to exclude/include (can be specified multiple times)
#[clap(long, help_heading = "EXCLUDE OPTIONS")]
#[clap(long, help_heading = "Exclude options")]
#[merge(strategy = merge::vec::overwrite_empty)]
glob: Vec<String>,
/// Same as --glob pattern but ignores the casing of filenames
#[clap(long, value_name = "GLOB", help_heading = "EXCLUDE OPTIONS")]
#[clap(long, value_name = "GLOB", help_heading = "Exclude options")]
#[merge(strategy = merge::vec::overwrite_empty)]
iglob: Vec<String>,
/// Read glob patterns to exclude/include from this file (can be specified multiple times)
#[clap(long, value_name = "FILE", help_heading = "EXCLUDE OPTIONS")]
#[clap(long, value_name = "FILE", help_heading = "Exclude options")]
#[merge(strategy = merge::vec::overwrite_empty)]
glob_file: Vec<String>,
/// Same as --glob-file ignores the casing of filenames in patterns
#[clap(long, value_name = "FILE", help_heading = "EXCLUDE OPTIONS")]
#[clap(long, value_name = "FILE", help_heading = "Exclude options")]
#[merge(strategy = merge::vec::overwrite_empty)]
iglob_file: Vec<String>,
/// Ignore files based on .gitignore files
#[clap(long, help_heading = "EXCLUDE OPTIONS")]
#[clap(long, help_heading = "Exclude options")]
#[merge(strategy = merge::bool::overwrite_false)]
git_ignore: bool,
/// Exclude contents of directories containing this filename (can be specified multiple times)
#[clap(long, value_name = "FILE", help_heading = "EXCLUDE OPTIONS")]
#[clap(long, value_name = "FILE", help_heading = "Exclude options")]
#[merge(strategy = merge::vec::overwrite_empty)]
exclude_if_present: Vec<String>,
/// Exclude other file systems, don't cross filesystem boundaries and subvolumes
#[clap(long, short = 'x', help_heading = "EXCLUDE OPTIONS")]
#[clap(long, short = 'x', help_heading = "Exclude options")]
#[merge(strategy = merge::bool::overwrite_false)]
one_file_system: bool,
/// Maximum size of files to be backuped. Larger files will be excluded.
#[clap(long, value_name = "SIZE", help_heading = "EXCLUDE OPTIONS")]
#[clap(long, value_name = "SIZE", help_heading = "Exclude options")]
#[serde_as(as = "Option<DisplayFromStr>")]
exclude_larger_than: Option<ByteSize>,
}

View File

@ -109,19 +109,19 @@ impl IntoIterator for Tree {
#[derive(Default, Clone, Parser)]
pub struct TreeStreamerOptions {
/// Glob pattern to exclude/include (can be specified multiple times)
#[clap(long, help_heading = "EXCLUDE OPTIONS")]
#[clap(long, help_heading = "Exclude options")]
glob: Vec<String>,
/// Same as --glob pattern but ignores the casing of filenames
#[clap(long, value_name = "GLOB", help_heading = "EXCLUDE OPTIONS")]
#[clap(long, value_name = "GLOB", help_heading = "Exclude options")]
iglob: Vec<String>,
/// Read glob patterns to exclude/include from this file (can be specified multiple times)
#[clap(long, value_name = "FILE", help_heading = "EXCLUDE OPTIONS")]
#[clap(long, value_name = "FILE", help_heading = "Exclude options")]
glob_file: Vec<String>,
/// Same as --glob-file ignores the casing of filenames in patterns
#[clap(long, value_name = "FILE", help_heading = "EXCLUDE OPTIONS")]
#[clap(long, value_name = "FILE", help_heading = "Exclude options")]
iglob_file: Vec<String>,
}

View File

@ -3,7 +3,7 @@ use std::str::FromStr;
use anyhow::{bail, Result};
use chrono::Local;
use clap::{AppSettings, Parser};
use clap::Parser;
use log::*;
use merge::Merge;
use path_dedot::ParseDot;
@ -20,13 +20,14 @@ use crate::repofile::{
use crate::repository::OpenRepository;
#[derive(Clone, Default, Parser, Deserialize, Merge)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
#[serde(default, rename_all = "kebab-case")]
pub(super) struct Opts {
/// Output generated snapshot in json format
#[clap(long)]
#[merge(strategy = merge::bool::overwrite_false)]
json: bool,
/// Backup source (can be specified multiple times), use - for stdin. If no source is given, uses all
/// sources defined in the config file
#[clap(value_name = "SOURCE")]
#[merge(skip)]
#[serde(skip)]
cli_sources: Vec<String>,
/// Do not upload or write any data, just show what would be done
#[clap(long, short = 'n')]
@ -34,25 +35,48 @@ pub(super) struct Opts {
dry_run: bool,
/// Group snapshots by any combination of host,label,paths,tags to find a suitable parent (default: host,label,paths)
#[clap(long, short = 'g', value_name = "CRITERION")]
#[clap(
long,
short = 'g',
value_name = "CRITERION",
help_heading = "Options for parent processing"
)]
group_by: Option<SnapshotGroupCriterion>,
/// Snapshot to use as parent
#[clap(long, value_name = "SNAPSHOT", conflicts_with = "force")]
#[clap(
long,
value_name = "SNAPSHOT",
conflicts_with = "force",
help_heading = "Options for parent processing"
)]
parent: Option<String>,
/// Use no parent, read all files
#[clap(long, short, conflicts_with = "parent")]
#[clap(
long,
short,
conflicts_with = "parent",
help_heading = "Options for parent processing"
)]
#[merge(strategy = merge::bool::overwrite_false)]
force: bool,
/// Ignore ctime changes when checking for modified files
#[clap(long, conflicts_with = "force")]
#[clap(
long,
conflicts_with = "force",
help_heading = "Options for parent processing"
)]
#[merge(strategy = merge::bool::overwrite_false)]
ignore_ctime: bool,
/// Ignore inode number changes when checking for modified files
#[clap(long, conflicts_with = "force")]
#[clap(
long,
conflicts_with = "force",
help_heading = "Options for parent processing"
)]
#[merge(strategy = merge::bool::overwrite_false)]
ignore_inode: bool,
@ -65,20 +89,18 @@ pub(super) struct Opts {
#[clap(long, value_name = "PATH")]
as_path: Option<PathBuf>,
#[clap(flatten)]
#[serde(flatten)]
snap_opts: SnapshotOptions,
#[clap(flatten)]
#[serde(flatten)]
ignore_opts: LocalSourceOptions,
/// Backup source (can be specified multiple times), use - for stdin. If no source is given, uses all
/// sources defined in the config file
#[clap(value_name = "SOURCE")]
#[merge(skip)]
#[serde(skip)]
cli_sources: Vec<String>,
#[clap(flatten, next_help_heading = "Snapshot options")]
#[serde(flatten)]
snap_opts: SnapshotOptions,
/// Output generated snapshot in json format
#[clap(long)]
#[merge(strategy = merge::bool::overwrite_false)]
json: bool,
// This is a hack to support serde(deny_unknown_fields) for deserializing the backup options from TOML
// while still being able to use [[backup.sources]] in the config file.

View File

@ -43,12 +43,15 @@ struct IdOpt {
#[derive(Parser)]
struct TreeOpts {
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS (when using latest)")]
filter: SnapshotFilter,
/// Snapshot/path of the tree to display
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
#[clap(
flatten,
next_help_heading = "Snapshot filter options (when using latest)"
)]
filter: SnapshotFilter,
}
pub(super) fn execute(repo: OpenRepository, opts: Opts, config_file: RusticConfig) -> Result<()> {

View File

@ -24,7 +24,7 @@ use crate::repository::OpenRepository;
#[derive(Parser)]
pub(super) struct Opts {
/// Don't verify the data saved in the cache
#[clap(long, conflicts_with = "no-cache")]
#[clap(long, conflicts_with = "no_cache")]
trust_cache: bool,
/// Read all data blobs

View File

@ -5,6 +5,7 @@ use clap_complete::{generate, shells, Generator};
#[derive(Parser)]
pub(super) struct Opts {
/// Shell to generate completions for
#[clap(value_enum)]
sh: Variant,
}

View File

@ -1,6 +1,6 @@
use anyhow::{bail, Result};
use bytesize::ByteSize;
use clap::{AppSettings, Parser};
use clap::Parser;
use crate::backend::{DecryptBackend, DecryptWriteBackend};
use crate::repofile::ConfigFile;
@ -40,7 +40,6 @@ pub(super) fn execute(mut repo: OpenRepository, opts: Opts) -> Result<()> {
}
#[derive(Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
pub(super) struct ConfigOpts {
/// Set compression level. Allowed levels are 1 to 22 and -1 to -7, see <https://facebook.github.io/zstd/>.
/// Note that 0 equals to no compression

View File

@ -1,7 +1,7 @@
use std::collections::BTreeSet;
use anyhow::{bail, Result};
use clap::{AppSettings, Parser};
use clap::Parser;
use log::*;
use rayon::prelude::*;
@ -13,22 +13,20 @@ use crate::repofile::{Id, SnapshotFile, SnapshotFilter};
use crate::repository::{OpenRepository, Repository, RepositoryOptions};
#[derive(Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
pub(super) struct Opts {
/// Snapshots to copy. If none is given, use filter options to filter from all snapshots.
#[clap(value_name = "ID")]
ids: Vec<String>,
/// Don't copy any snapshot, only show what would be done
#[clap(long, short = 'n')]
dry_run: bool,
#[clap(
flatten,
help_heading = "SNAPSHOT FILTER OPTIONS (if no snapshot is given)"
next_help_heading = "Snapshot filter options (if no snapshot is given)"
)]
filter: SnapshotFilter,
/// Snapshots to copy. If none is given, use filter to filter from all
/// snapshots.
#[clap(value_name = "ID")]
ids: Vec<String>,
}
pub(super) fn execute(

View File

@ -15,12 +15,6 @@ use crate::repository::OpenRepository;
#[derive(Parser)]
pub(super) struct Opts {
#[clap(flatten)]
ignore_opts: LocalSourceOptions,
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS (when using latest)")]
filter: SnapshotFilter,
/// Reference snapshot/path
#[clap(value_name = "SNAPSHOT1[:PATH1]")]
snap1: String,
@ -36,6 +30,15 @@ pub(super) struct Opts {
/// don't check for different file contents
#[clap(long)]
no_content: bool,
#[clap(flatten)]
ignore_opts: LocalSourceOptions,
#[clap(
flatten,
next_help_heading = "Snapshot filter options (when using latest)"
)]
filter: SnapshotFilter,
}
pub(super) fn execute(

View File

@ -12,12 +12,15 @@ use super::{progress_counter, RusticConfig};
#[derive(Parser)]
pub(super) struct Opts {
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS (when using latest)")]
filter: SnapshotFilter,
/// file from snapshot to dump
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
#[clap(
flatten,
next_help_heading = "Snapshot filter options (when using latest)"
)]
filter: SnapshotFilter,
}
pub(super) fn execute(

View File

@ -2,7 +2,7 @@ use std::str::FromStr;
use anyhow::Result;
use chrono::{DateTime, Datelike, Duration, Local, Timelike};
use clap::{AppSettings, Parser};
use clap::Parser;
use derivative::Derivative;
use merge::Merge;
use serde::Deserialize;
@ -16,41 +16,44 @@ use crate::repofile::{
use crate::repository::OpenRepository;
#[derive(Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
pub(super) struct Opts {
/// Snapshots to forget. If none is given, use filter options to filter from all snapshots
#[clap(value_name = "ID")]
ids: Vec<String>,
#[clap(flatten)]
config: ConfigOpts,
/// Also prune the repository
#[clap(long)]
prune: bool,
#[clap(flatten, help_heading = "PRUNE OPTIONS (only when used with --prune)")]
#[clap(
flatten,
next_help_heading = "PRUNE OPTIONS (only when used with --prune)"
)]
prune_opts: prune::Opts,
/// Don't remove anything, only show what would be done
#[clap(skip)]
dry_run: bool,
/// Snapshots to forget
ids: Vec<String>,
}
#[serde_as]
#[derive(Default, Parser, Deserialize, Merge)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
#[serde(default, rename_all = "kebab-case")]
struct ConfigOpts {
/// Group snapshots by any combination of host,label,paths,tags (default: "host,label,paths")
#[clap(long, short = 'g', value_name = "CRITERION")]
#[serde_as(as = "Option<DisplayFromStr>")]
group_by: Option<SnapshotGroupCriterion>,
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS")]
/// Also prune the repository
#[clap(long)]
#[merge(strategy = merge::bool::overwrite_false)]
prune: bool,
#[clap(flatten, next_help_heading = "Snapshot filter options")]
#[serde(flatten)]
filter: SnapshotFilter,
#[clap(flatten, help_heading = "RETENTION OPTIONS")]
#[clap(flatten, next_help_heading = "Retention options")]
#[serde(flatten)]
keep: KeepOptions,
}
@ -153,7 +156,7 @@ pub(super) fn execute(
}
}
if opts.prune {
if opts.config.prune {
prune::execute(repo, opts.prune_opts, forget_snaps)?;
}

View File

@ -92,8 +92,8 @@ pub fn warm_up_wait(
packs: impl ExactSizeIterator<Item = Id>,
wait: bool,
) -> Result<()> {
if repo.opts.warm_up_command.is_some() {
warm_up_command(packs, repo.opts.warm_up_command.as_ref().unwrap())?;
if let Some(command) = &repo.opts.warm_up_command {
warm_up_command(packs, command)?;
} else if repo.opts.warm_up {
warm_up(&repo.be, packs)?;
}

View File

@ -13,10 +13,10 @@ use crate::repofile::{ConfigFile, KeyFile};
#[derive(Parser)]
pub(super) struct Opts {
#[clap(flatten, help_heading = "KEY OPTIONS")]
#[clap(flatten, next_help_heading = "Key options")]
key_opts: KeyOpts,
#[clap(flatten, help_heading = "CONFIG OPTIONS")]
#[clap(flatten, next_help_heading = "Config options")]
config_opts: ConfigOpts,
}

View File

@ -2,7 +2,7 @@ use std::fs::File;
use std::io::BufReader;
use anyhow::Result;
use clap::{AppSettings, Parser, Subcommand};
use clap::{Parser, Subcommand};
use rpassword::{prompt_password, read_password_from_bufread};
use crate::backend::{FileType, WriteBackend};
@ -18,11 +18,11 @@ pub(super) struct Opts {
#[derive(Subcommand)]
enum Command {
/// Add a new key to the repository
Add(AddOpts),
}
#[derive(Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
pub(crate) struct AddOpts {
/// File from which to read the new password
#[clap(long)]
@ -33,7 +33,6 @@ pub(crate) struct AddOpts {
}
#[derive(Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
pub(crate) struct KeyOpts {
/// Set 'hostname' in public key information
#[clap(long)]

View File

@ -9,7 +9,7 @@ use crate::repository::OpenRepository;
#[derive(Parser)]
pub(super) struct Opts {
/// File type to list
#[clap(possible_values=["blobs", "index", "packs", "snapshots", "keys"])]
#[clap(value_parser=["blobs", "index", "packs", "snapshots", "keys"])]
tpe: String,
}

View File

@ -11,19 +11,22 @@ use crate::repository::OpenRepository;
#[derive(Parser)]
pub(super) struct Opts {
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS (when using latest)")]
filter: SnapshotFilter,
/// Snapshot/path to list
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
/// recursively list the dir (default when no PATH is given)
#[clap(long)]
recursive: bool,
#[clap(
flatten,
next_help_heading = "Snapshot filter options (when using latest)"
)]
filter: SnapshotFilter,
#[clap(flatten)]
streamer_opts: TreeStreamerOptions,
/// Snapshot/path to list
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
}
pub(super) fn execute(

View File

@ -1,6 +1,6 @@
use anyhow::Result;
use chrono::Local;
use clap::{AppSettings, Parser};
use clap::Parser;
use log::*;
use crate::backend::{DecryptWriteBackend, FileType};
@ -13,8 +13,11 @@ use super::helpers::{progress_counter, progress_spinner};
use super::rustic_config::RusticConfig;
#[derive(Default, Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
pub(super) struct Opts {
/// Snapshots to merge. If none is given, use filter options to filter from all snapshots.
#[clap(value_name = "ID")]
ids: Vec<String>,
/// Output generated snapshot in json format
#[clap(long)]
json: bool,
@ -23,15 +26,11 @@ pub(super) struct Opts {
#[clap(long)]
delete: bool,
#[clap(flatten)]
#[clap(flatten, next_help_heading = "Snapshot options")]
snap_opts: SnapshotOptions,
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS")]
#[clap(flatten, next_help_heading = "Snapshot filter options")]
filter: SnapshotFilter,
/// Snapshots to merge. If none is given, use filter to filter from all snapshots.
#[clap(value_name = "ID")]
ids: Vec<String>,
}
pub(super) fn execute(

View File

@ -40,24 +40,25 @@ mod tag;
use rustic_config::RusticConfig;
#[derive(Parser)]
#[clap(about, name="rustic", version = option_env!("PROJECT_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")))]
#[clap(about, version, name="rustic", version = option_env!("PROJECT_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")))]
struct Opts {
#[clap(flatten, help_heading = "GLOBAL OPTIONS")]
global: GlobalOpts,
#[clap(flatten, help_heading = "REPOSITORY OPTIONS")]
repository: RepositoryOptions,
/// Config profile to use. This parses the file `<PROFILE>.toml` in the config directory.
#[clap(
short = 'P',
long,
value_name = "PROFILE",
global = true,
default_value = "rustic"
default_value = "rustic",
help_heading = "Global options"
)]
config_profile: String,
#[clap(flatten, next_help_heading = "Global options")]
global: GlobalOpts,
#[clap(flatten, next_help_heading = "Repository options")]
repository: RepositoryOptions,
#[clap(subcommand)]
command: Command,
}
@ -87,7 +88,7 @@ struct GlobalOpts {
global = true,
env = "RUSTIC_PROGRESS_INTERVAL",
value_name = "DURATION",
conflicts_with = "no-progress"
conflicts_with = "no_progress"
)]
#[serde_as(as = "Option<DisplayFromStr>")]
progress_interval: Option<humantime::Duration>,
@ -110,11 +111,10 @@ enum Command {
/// Check the repository
Check(check::Opts),
/// Copy snapshots to another repository
/// Copy snapshots to other repositories. Note: The target repositories must be given in the config file!
Copy(copy::Opts),
/// Compare two snapshots/paths
///
/// Note that the exclude options only apply for comparison with a local path
Diff(diff::Opts),
@ -167,12 +167,12 @@ pub fn execute() -> Result<()> {
// get global options from command line / env and config file
let config_file = RusticConfig::new(&args.config_profile)?;
let mut opts = args.global;
config_file.merge_into("global", &mut opts)?;
let mut gopts = args.global;
config_file.merge_into("global", &mut gopts)?;
// start logger
let level_filter = opts.log_level.unwrap_or(LevelFilter::Info);
match opts.log_file {
let level_filter = gopts.log_level.unwrap_or(LevelFilter::Info);
match gopts.log_file {
None => TermLogger::init(
level_filter,
ConfigBuilder::new()
@ -199,12 +199,12 @@ pub fn execute() -> Result<()> {
])?,
}
if opts.no_progress {
if gopts.no_progress {
let mut no_progress = NO_PROGRESS.lock().unwrap();
*no_progress = true;
}
if let Some(duration) = opts.progress_interval {
if let Some(duration) = gopts.progress_interval {
let mut interval = PROGRESS_INTERVAL.lock().unwrap();
*interval = *duration;
}
@ -263,3 +263,9 @@ pub fn execute() -> Result<()> {
Ok(())
}
#[test]
fn verify_cli() {
use clap::CommandFactory;
Opts::command().debug_assert()
}

View File

@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex};
use anyhow::{anyhow, bail, Result};
use bytesize::ByteSize;
use chrono::{DateTime, Duration, Local};
use clap::{AppSettings, Parser};
use clap::Parser;
use derive_more::Add;
use itertools::Itertools;
use log::*;
@ -24,7 +24,7 @@ use crate::repofile::{HeaderEntry, IndexBlob, IndexFile, IndexPack, SnapshotFile
use crate::repository::OpenRepository;
#[derive(Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
#[group(id = "prune_opts")]
pub(super) struct Opts {
/// Don't remove anything, only show what would be done
#[clap(long, short = 'n')]
@ -59,7 +59,7 @@ pub(super) struct Opts {
/// Repack packs containing uncompressed blobs. This cannot be used with --fast-repack.
/// Implies --max-unused=0.
#[clap(long, conflicts_with = "fast-repack")]
#[clap(long, conflicts_with = "fast_repack")]
repack_uncompressed: bool,
/// Only repack packs which are cacheable [default: true for a hot/cold repository, else false]
@ -139,6 +139,7 @@ pub(super) fn execute(repo: OpenRepository, opts: Opts, ignore_snaps: Vec<Id>) -
Ok(())
}
#[derive(Clone)]
enum LimitOption {
Size(ByteSize),
Percentage(u64),

View File

@ -1,7 +1,7 @@
use std::collections::{HashMap, HashSet};
use anyhow::Result;
use clap::{AppSettings, Parser, Subcommand};
use clap::{Parser, Subcommand};
use log::*;
use crate::backend::{
@ -46,9 +46,8 @@ struct IndexOpts {
}
#[derive(Default, Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
struct SnapOpts {
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS")]
#[clap(flatten, next_help_heading = "Snapshot filter options")]
filter: SnapshotFilter,
/// Only show what would be repaired

View File

@ -4,9 +4,9 @@ use std::io::Read;
use std::num::NonZeroU32;
use std::path::{Path, PathBuf};
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{anyhow, Context, Result};
use chrono::{DateTime, Local, Utc};
use clap::{AppSettings, Parser};
use clap::Parser;
use derive_getters::Dissolve;
use ignore::{DirEntry, WalkBuilder};
use log::*;
@ -24,20 +24,21 @@ use crate::repofile::{SnapshotFile, SnapshotFilter};
use crate::repository::OpenRepository;
#[derive(Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
pub(super) struct Opts {
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS (when using latest)")]
filter: SnapshotFilter,
/// Snapshot/path to restore
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
/// Restore destination
#[clap(value_name = "DESTINATION")]
dest: String,
/// Dry-run: don't restore, only show what would be done
#[clap(long, short = 'n')]
dry_run: bool,
#[clap(flatten)]
streamer_opts: TreeStreamerOptions,
/// Remove all files/dirs in destination which are not contained in snapshot.
/// WARNING: Use with care, maybe first try this first with --dry-run?
/// WARNING: Use with care, maybe first try this with --dry-run?
#[clap(long)]
delete: bool,
@ -46,32 +47,21 @@ pub(super) struct Opts {
numeric_id: bool,
/// Don't restore ownership (user/group)
#[clap(long, conflicts_with = "numeric-id")]
#[clap(long, conflicts_with = "numeric_id")]
no_ownership: bool,
/// Warm up needed data pack files by only requesting them without processing
#[clap(long)]
warm_up: bool,
/// Always read and verify existing files (don't trust correct modification time and file size)
#[clap(long)]
verify_existing: bool,
/// Warm up needed data pack files by running the command with %id replaced by pack id
#[clap(long, conflicts_with = "warm-up")]
warm_up_command: Option<String>,
#[clap(flatten)]
streamer_opts: TreeStreamerOptions,
/// Duration (e.g. 10m) to wait after warm up before doing the actual restore
#[clap(long, value_name = "DURATION", conflicts_with = "dry-run")]
warm_up_wait: Option<humantime::Duration>,
/// Snapshot/path to restore
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
/// Restore destination
#[clap(value_name = "DESTINATION")]
dest: String,
#[clap(
flatten,
next_help_heading = "Snapshot filter options (when using latest)"
)]
filter: SnapshotFilter,
}
pub(super) fn execute(
@ -82,13 +72,6 @@ pub(super) fn execute(
let be = &repo.dbe;
config_file.merge_into("snapshot-filter", &mut opts.filter)?;
if let Some(command) = &opts.warm_up_command {
if !command.contains("%id") {
bail!("warm-up command must contain %id!");
}
info!("using warm-up command {command}");
}
let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, ""));
let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?;

View File

@ -14,8 +14,9 @@ use crate::repository::OpenRepository;
#[derive(Parser)]
pub(super) struct Opts {
#[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS")]
filter: SnapshotFilter,
/// Snapshots to show. If none is given, use filter options to filter from all snapshots
#[clap(value_name = "ID")]
ids: Vec<String>,
/// Group snapshots by any combination of host,label,paths,tags
#[clap(
@ -38,9 +39,8 @@ pub(super) struct Opts {
#[clap(long, conflicts_with_all = &["long", "json"])]
all: bool,
/// Snapshots to show
#[clap(value_name = "ID")]
ids: Vec<String>,
#[clap(flatten, next_help_heading = "Snapshot filter options")]
filter: SnapshotFilter,
}
pub(super) fn execute(

View File

@ -1,6 +1,6 @@
use anyhow::Result;
use chrono::{Duration, Local};
use clap::{AppSettings, Parser};
use clap::Parser;
use super::{progress_counter, RusticConfig};
use crate::backend::{DecryptWriteBackend, FileType};
@ -9,15 +9,19 @@ use crate::repofile::{DeleteOption, SnapshotFile, SnapshotFilter, StringList};
use crate::repository::OpenRepository;
#[derive(Parser)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
pub(super) struct Opts {
/// Snapshots to change tags. If none is given, use filter to filter from all
/// snapshots.
#[clap(value_name = "ID")]
ids: Vec<String>,
/// Don't change any snapshot, only show which would be modified
#[clap(long, short = 'n')]
dry_run: bool,
#[clap(
flatten,
help_heading = "SNAPSHOT FILTER OPTIONS (if no snapshot is given)"
next_help_heading = "Snapshot filter options (if no snapshot is given)"
)]
filter: SnapshotFilter,
@ -26,12 +30,12 @@ pub(super) struct Opts {
long,
value_name = "TAG[,TAG,..]",
conflicts_with = "remove",
help_heading = "TAG OPTIONS"
help_heading = "Tag options"
)]
add: Vec<StringList>,
/// Tags to remove (can be specified multiple times)
#[clap(long, value_name = "TAG[,TAG,..]", help_heading = "TAG OPTIONS")]
#[clap(long, value_name = "TAG[,TAG,..]", help_heading = "Tag options")]
remove: Vec<StringList>,
/// Tag list to set (can be specified multiple times)
@ -39,34 +43,29 @@ pub(super) struct Opts {
long,
value_name = "TAG[,TAG,..]",
conflicts_with = "remove",
help_heading = "TAG OPTIONS"
help_heading = "Tag options"
)]
set: Vec<StringList>,
/// Remove any delete mark
#[clap(
long,
conflicts_with_all = &["set-delete-never", "set-delete-after"],
help_heading = "DELETE MARK OPTIONS"
conflicts_with_all = &["set_delete_never", "set_delete_after"],
help_heading = "Delete mark options"
)]
remove_delete: bool,
/// Mark snapshot as uneraseable
#[clap(
long,
conflicts_with = "set-delete-after",
help_heading = "DELETE MARK OPTIONS"
conflicts_with = "set_delete_after",
help_heading = "Delete mark options"
)]
set_delete_never: bool,
/// Mark snapshot to be deleted after given duration (e.g. 10d)
#[clap(long, value_name = "DURATION", help_heading = "DELETE MARK OPTIONS")]
#[clap(long, value_name = "DURATION", help_heading = "Delete mark options")]
set_delete_after: Option<humantime::Duration>,
/// Snapshots to change tags. If none is given, use filter to filter from all
/// snapshots.
#[clap(value_name = "ID")]
ids: Vec<String>,
}
pub(super) fn execute(

View File

@ -5,7 +5,7 @@ use std::{cmp::Ordering, fmt::Display};
use anyhow::{anyhow, bail, Result};
use chrono::{DateTime, Duration, Local};
use clap::{AppSettings, Parser};
use clap::Parser;
use derivative::Derivative;
use dunce::canonicalize;
use gethostname::gethostname;
@ -25,7 +25,6 @@ use crate::repository::parse_command;
#[serde_as]
#[derive(Clone, Default, Parser, Deserialize, Merge)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
pub struct SnapshotOptions {
/// Label snapshot with given label
@ -47,7 +46,7 @@ pub struct SnapshotOptions {
description_from: Option<PathBuf>,
/// Mark snapshot as uneraseable
#[clap(long, conflicts_with = "delete-after")]
#[clap(long, conflicts_with = "delete_after")]
#[merge(strategy = merge::bool::overwrite_false)]
delete_never: bool,
@ -420,6 +419,7 @@ impl Ord for SnapshotFile {
}
}
#[derive(Clone)]
struct SnapshotFn(FnPtr, AST);
impl FromStr for SnapshotFn {
type Err = anyhow::Error;

View File

@ -49,7 +49,6 @@ pub struct RepositoryOptions {
short,
long,
global = true,
parse(from_os_str),
env = "RUSTIC_PASSWORD_FILE",
conflicts_with = "password"
)]
@ -60,7 +59,7 @@ pub struct RepositoryOptions {
long,
global = true,
env = "RUSTIC_PASSWORD_COMMAND",
conflicts_with_all = &["password", "password-file"],
conflicts_with_all = &["password", "password_file"],
)]
password_command: Option<String>,
@ -73,8 +72,7 @@ pub struct RepositoryOptions {
#[clap(
long,
global = true,
parse(from_os_str),
conflicts_with = "no-cache",
conflicts_with = "no_cache",
env = "RUSTIC_CACHE_DIR"
)]
cache_dir: Option<PathBuf>,
@ -85,7 +83,7 @@ pub struct RepositoryOptions {
pub(crate) warm_up: bool,
/// Warm up needed data pack files by running the command with %id replaced by pack id
#[clap(long, global = true, conflicts_with = "warm-up")]
#[clap(long, global = true, conflicts_with = "warm_up")]
pub(crate) warm_up_command: Option<String>,
/// Duration (e.g. 10m) to wait after warm up
@ -149,6 +147,13 @@ impl Repository {
None => bail!("No repository given. Please use the --repository option."),
};
if let Some(command) = &opts.warm_up_command {
if !command.contains("%id") {
bail!("warm-up command must contain %id!");
}
info!("using warm-up command {command}");
}
let be_hot = opts
.repo_hot
.as_ref()