From bac3a8c69e7eec2328cffd87cc23df479fbf4c49 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Mon, 5 Feb 2024 02:36:33 -0500 Subject: [PATCH] Add support for TOML config files --- Cargo.lock | 75 ++++++++++++++++++++++ Cargo.toml | 1 + examples/config-templates/config-all.toml | 6 +- examples/config-templates/config-home.toml | 2 +- examples/config-templates/config-root.toml | 2 +- src/args.rs | 36 ++++++++++- src/controller.rs | 2 + src/main.rs | 28 ++++---- 8 files changed, 134 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b164951..e10522c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,6 +224,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.8" @@ -245,6 +251,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" version = "0.4.1" @@ -280,6 +292,16 @@ dependencies = [ "cc", ] +[[package]] +name = "indexmap" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is-terminal" version = "0.4.10" @@ -437,6 +459,7 @@ dependencies = [ "prettytable-rs", "serde", "sqlite", + "toml", ] [[package]] @@ -484,6 +507,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "sqlite" version = "0.33.0" @@ -562,6 +594,40 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -736,3 +802,12 @@ name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index f7c668c..a4411f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ md5 = "0.7.0" chrono = "0.4.33" prettytable-rs = "0.10.0" anyhow = "1.0.79" +toml = "0.8.9" [profile.release] diff --git a/examples/config-templates/config-all.toml b/examples/config-templates/config-all.toml index f8aeccf..65daec3 100644 --- a/examples/config-templates/config-all.toml +++ b/examples/config-templates/config-all.toml @@ -1,12 +1,12 @@ -# Example configuration file for rustnapshot +# Example configuration file for rusnapshot # These are all the current available options # Snapshots directory dest_dir = "/mnt/defvol/_snapshots/" -# Directory to be snapshoted +# Directory to be snapshot-ed source_dir = "/mnt/defvol/_active/root/" # SQLite database file PATH, created if no exists, recommended to be in the snapshots dir -database_file = "/mnt/defvol/_snapshots/rustnapshot.sqlite" +database_file = "/mnt/defvol/_snapshots/rusnapshot.sqlite" # Prefix for the snapshots, they will be saved in the format prefix-$current_date snapshot_prefix = "root" # Snapshots identifier diff --git a/examples/config-templates/config-home.toml b/examples/config-templates/config-home.toml index 288450d..1849876 100644 --- a/examples/config-templates/config-home.toml +++ b/examples/config-templates/config-home.toml @@ -1,5 +1,5 @@ dest_dir = "/mnt/defvol/_snapshots/" source_dir = "/home/" -database_file = "/mnt/defvol/_snapshots/rustnapshot.sqlite" +database_file = "/mnt/defvol/_snapshots/rusnapshot.sqlite" snapshot_prefix = "home" keep_only = "2" diff --git a/examples/config-templates/config-root.toml b/examples/config-templates/config-root.toml index 2228e95..293099f 100644 --- a/examples/config-templates/config-root.toml +++ b/examples/config-templates/config-root.toml @@ -1,5 +1,5 @@ dest_dir = "/mnt/defvol/_snapshots/" source_dir = "/" -database_file = "/mnt/defvol/_snapshots/rustnapshot.sqlite" +database_file = "/mnt/defvol/_snapshots/rusnapshot.sqlite" snapshot_prefix = "root" keep_only = "2" diff --git a/src/args.rs b/src/args.rs index cf9b501..7cc1c09 100644 --- a/src/args.rs +++ b/src/args.rs @@ -4,6 +4,7 @@ use { clap::Parser, serde::{Deserialize, Serialize}, sqlite::Connection, + std::collections::BTreeMap, }; /// Simple and handy btrfs snapshoting tool. @@ -46,7 +47,7 @@ pub struct Args { #[clap(long = "create", conflicts_with_all = &["restore_snapshot", "delete_snapshot", "list_snapshots", "clean_snapshots"])] pub create_snapshot: bool, /// Enable snapshots cleaning, will keep only the last X snapshots specified with -k/--keep. - #[clap(long = "clean")] + #[clap(long = "clean", requires_all = &["keep_only", "snapshot_kind", "snapshot_prefix"])] pub clean_snapshots: bool, /// Delete a snapshot. #[clap(long = "del", requires_all = &["snapshot_id"])] @@ -95,3 +96,36 @@ impl Args { Ok(()) } } + +// Deserialize the configuration file. +impl Args { + /// Deserialize the configuration file. + pub fn from_config_file(&mut self) -> Result<()> { + let config_file = std::fs::read_to_string(self.config_file.as_deref().unwrap())?; + let config: BTreeMap = toml::from_str(&config_file)?; + + if let Some(dest_dir) = config.get("dest_dir") { + self.dest_dir = dest_dir.as_str().unwrap().to_string(); + } + if let Some(source_dir) = config.get("source_dir") { + self.source_dir = source_dir.as_str().unwrap().to_string(); + } + if let Some(snapshot_prefix) = config.get("snapshot_prefix") { + self.snapshot_prefix = snapshot_prefix.as_str().unwrap().to_string(); + } + if let Some(snapshot_kind) = config.get("snapshot_kind") { + self.snapshot_kind = snapshot_kind.as_str().unwrap().to_string(); + } + if let Some(database_file) = config.get("database_file") { + self.database_file = database_file.as_str().unwrap().to_string(); + } + if let Some(keep_only) = config.get("keep_only") { + self.keep_only = keep_only.as_integer().unwrap() as usize; + } + if let Some(timeout) = config.get("timeout") { + self.timeout = timeout.as_integer().unwrap() as usize; + } + + Ok(()) + } +} diff --git a/src/controller.rs b/src/controller.rs index abb8cc5..3704ba9 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -95,9 +95,11 @@ pub fn manage_listing(database_connection: &Connection) -> Result<()> { pub fn keep_only_x(args: &mut Args, extra_args: &mut ExtraArgs) -> Result<()> { let snaps_data = database::return_only_x_items(&extra_args.database_connection, args)?; + for data in &snaps_data { extra_args.snapshot_name = data.destination.clone() + &data.name; args.snapshot_id = data.snap_id.clone(); + manage_deletion(args, extra_args)?; } diff --git a/src/main.rs b/src/main.rs index 223fc65..f4978e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,10 @@ use rusnapshot::{args, controller, structs::ExtraArgs}; fn try_run() -> Result<()> { let mut arguments = args::Args::parse(); + if arguments.config_file.is_some() { + arguments.from_config_file()?; + } + let mut extra_args = ExtraArgs { snapshot_name: format!( "{}-{}", @@ -19,19 +23,19 @@ fn try_run() -> Result<()> { arguments.init(&extra_args)?; - // It's required to have a trailing slash for the source and destination directories. - // Otherwise, when we retrieve the snapshot data from the database, we won't be able to - // restore/delete the snapshot because the source/destination paths won't match. - if !arguments.source_dir.is_empty() && !arguments.source_dir.ends_with('/') { - arguments.source_dir += "/"; - } - if !arguments.dest_dir.is_empty() && !arguments.dest_dir.ends_with('/') { - arguments.dest_dir += "/"; - } - - // Now we need to make sure that the paths for source and destination are full paths. - // If they are not, we will use the current working directory to build the full path. if arguments.create_snapshot { + // It's required to have a trailing slash for the source and destination directories. + // Otherwise, when we retrieve the snapshot data from the database, we won't be able to + // restore/delete the snapshot because the source/destination paths won't match. + if !arguments.source_dir.is_empty() && !arguments.source_dir.ends_with('/') { + arguments.source_dir += "/"; + } + if !arguments.dest_dir.is_empty() && !arguments.dest_dir.ends_with('/') { + arguments.dest_dir += "/"; + } + + // Now we need to make sure that the paths for source and destination are full paths. + // If they are not, we will use the current working directory to build the full path. if !Path::new(&arguments.source_dir).is_absolute() { arguments.source_dir = std::env::current_dir()? .join(&arguments.source_dir)