From 52d01e962e0f1eed8ef05349fd16d8ac6894655e Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sat, 4 Mar 2023 13:08:19 +0100 Subject: [PATCH] TOML config: Abort with error on unknown fields --- changelog/new.txt | 4 ++++ src/backend/ignore.rs | 2 +- src/commands/backup.rs | 2 +- src/commands/forget.rs | 4 ++-- src/commands/mod.rs | 3 +-- src/commands/rustic_config.rs | 9 +++++++-- src/repofile/snapshotfile.rs | 2 +- src/repository/mod.rs | 2 +- 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/changelog/new.txt b/changelog/new.txt index bc67e06..593f45a 100644 --- a/changelog/new.txt +++ b/changelog/new.txt @@ -1,5 +1,9 @@ Changes in version x.x.x: +Breaking changes: +- Repository options in the config file must now be given under the `[repository]` section. + Bugs fixed: New features: +- Extra or wrong fields in the config file now lead to rustic complaining and aborting. \ No newline at end of file diff --git a/src/backend/ignore.rs b/src/backend/ignore.rs index 3c365e5..475b57c 100644 --- a/src/backend/ignore.rs +++ b/src/backend/ignore.rs @@ -25,7 +25,7 @@ pub struct LocalSource { #[serde_as] #[derive(Default, Clone, Parser, Deserialize, Merge)] -#[serde(default, rename_all = "kebab-case")] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] pub struct LocalSourceOptions { /// Save access time for files and directories #[clap(long)] diff --git a/src/commands/backup.rs b/src/commands/backup.rs index dd0cc2d..f908836 100644 --- a/src/commands/backup.rs +++ b/src/commands/backup.rs @@ -24,7 +24,7 @@ use crate::repository::OpenRepository; #[serde_as] #[derive(Clone, Default, Parser, Deserialize, Merge)] #[clap(global_setting(AppSettings::DeriveDisplayOrder))] -#[serde(default, rename_all = "kebab-case")] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] pub(super) struct Opts { /// Output generated snapshot in json format #[clap(long)] diff --git a/src/commands/forget.rs b/src/commands/forget.rs index 98a54cd..b829af7 100644 --- a/src/commands/forget.rs +++ b/src/commands/forget.rs @@ -39,7 +39,7 @@ pub(super) struct Opts { #[serde_as] #[derive(Default, Parser, Deserialize, Merge)] #[clap(global_setting(AppSettings::DeriveDisplayOrder))] -#[serde(default, rename_all = "kebab-case")] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] struct ConfigOpts { /// Group snapshots by any combination of host,label,paths,tags (default: "host,label,paths") #[clap(long, short = 'g', value_name = "CRITERION")] @@ -163,7 +163,7 @@ pub(super) fn execute( #[serde_as] #[derive(Clone, PartialEq, Derivative, Parser, Deserialize, Merge)] #[derivative(Default)] -#[serde(default, rename_all = "kebab-case")] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] pub(super) struct KeepOptions { /// Keep snapshots with this taglist (can be specified multiple times) #[clap(long, value_name = "TAG[,TAG,..]")] diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 1cd730c..48634b3 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -62,7 +62,7 @@ struct Opts { #[serde_as] #[derive(Default, Parser, Deserialize, Merge)] -#[serde(default, rename_all = "kebab-case")] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] struct GlobalOpts { /// Use this log level [default: info] #[clap(long, global = true, env = "RUSTIC_LOG_LEVEL")] @@ -193,7 +193,6 @@ pub fn execute() -> Result<()> { let mut repo_opts = args.repository; config_file.merge_into("repository", &mut repo_opts)?; - config_file.merge_into("global", &mut repo_opts)?; // deprecated, but repo-options were originally under [global] let repo = Repository::new(repo_opts)?; if let Command::Init(opts) = args.command { diff --git a/src/commands/rustic_config.rs b/src/commands/rustic_config.rs index 81cef4e..09c8a7b 100644 --- a/src/commands/rustic_config.rs +++ b/src/commands/rustic_config.rs @@ -30,7 +30,9 @@ impl RusticConfig { Value::Array(Vec::new()) }; - Ok(RusticConfig(config)) + Ok(RusticConfig( + config.try_into().context("reading config file")?, + )) } fn get_value(&self, section: &str) -> Option<&Value> { @@ -45,7 +47,10 @@ impl RusticConfig { Opts: Merge + Deserialize<'de>, { if let Some(value) = self.get_value(section) { - let config: Opts = value.clone().try_into()?; + let config: Opts = value + .clone() + .try_into() + .with_context(|| format!("reading section [{section}] in config file"))?; opts.merge(config); } Ok(()) diff --git a/src/repofile/snapshotfile.rs b/src/repofile/snapshotfile.rs index b336675..6be8687 100644 --- a/src/repofile/snapshotfile.rs +++ b/src/repofile/snapshotfile.rs @@ -309,7 +309,7 @@ impl Ord for SnapshotFile { #[serde_as] #[derive(Default, Parser, Deserialize, Merge)] -#[serde(default, rename_all = "kebab-case")] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] pub struct SnapshotFilter { /// Hostname to filter (can be specified multiple times) #[clap(long, value_name = "HOSTNAME")] diff --git a/src/repository/mod.rs b/src/repository/mod.rs index 7ad059d..ecb7234 100644 --- a/src/repository/mod.rs +++ b/src/repository/mod.rs @@ -30,7 +30,7 @@ use crate::repofile::{find_key_in_backend, ConfigFile}; #[serde_as] #[derive(Default, Parser, Deserialize, Merge)] -#[serde(default, rename_all = "kebab-case")] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] pub struct RepositoryOptions { /// Repository to use #[clap(short, long, global = true, env = "RUSTIC_REPOSITORY")]