Add support for TOML config files

This commit is contained in:
Eduard Tolosa 2024-02-05 02:36:33 -05:00
parent c32742b355
commit bac3a8c69e
8 changed files with 134 additions and 18 deletions

75
Cargo.lock generated
View file

@ -224,6 +224,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.8" version = "0.3.8"
@ -245,6 +251,12 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
@ -280,6 +292,16 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "indexmap"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.10" version = "0.4.10"
@ -437,6 +459,7 @@ dependencies = [
"prettytable-rs", "prettytable-rs",
"serde", "serde",
"sqlite", "sqlite",
"toml",
] ]
[[package]] [[package]]
@ -484,6 +507,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "sqlite" name = "sqlite"
version = "0.33.0" version = "0.33.0"
@ -562,6 +594,40 @@ dependencies = [
"syn", "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]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.12"
@ -736,3 +802,12 @@ name = "windows_x86_64_msvc"
version = "0.52.0" version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5"
dependencies = [
"memchr",
]

View file

@ -15,6 +15,7 @@ md5 = "0.7.0"
chrono = "0.4.33" chrono = "0.4.33"
prettytable-rs = "0.10.0" prettytable-rs = "0.10.0"
anyhow = "1.0.79" anyhow = "1.0.79"
toml = "0.8.9"
[profile.release] [profile.release]

View file

@ -1,12 +1,12 @@
# Example configuration file for rustnapshot # Example configuration file for rusnapshot
# These are all the current available options # These are all the current available options
# Snapshots directory # Snapshots directory
dest_dir = "/mnt/defvol/_snapshots/" dest_dir = "/mnt/defvol/_snapshots/"
# Directory to be snapshoted # Directory to be snapshot-ed
source_dir = "/mnt/defvol/_active/root/" source_dir = "/mnt/defvol/_active/root/"
# SQLite database file PATH, created if no exists, recommended to be in the snapshots dir # 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 # Prefix for the snapshots, they will be saved in the format prefix-$current_date
snapshot_prefix = "root" snapshot_prefix = "root"
# Snapshots identifier # Snapshots identifier

View file

@ -1,5 +1,5 @@
dest_dir = "/mnt/defvol/_snapshots/" dest_dir = "/mnt/defvol/_snapshots/"
source_dir = "/home/" source_dir = "/home/"
database_file = "/mnt/defvol/_snapshots/rustnapshot.sqlite" database_file = "/mnt/defvol/_snapshots/rusnapshot.sqlite"
snapshot_prefix = "home" snapshot_prefix = "home"
keep_only = "2" keep_only = "2"

View file

@ -1,5 +1,5 @@
dest_dir = "/mnt/defvol/_snapshots/" dest_dir = "/mnt/defvol/_snapshots/"
source_dir = "/" source_dir = "/"
database_file = "/mnt/defvol/_snapshots/rustnapshot.sqlite" database_file = "/mnt/defvol/_snapshots/rusnapshot.sqlite"
snapshot_prefix = "root" snapshot_prefix = "root"
keep_only = "2" keep_only = "2"

View file

@ -4,6 +4,7 @@ use {
clap::Parser, clap::Parser,
serde::{Deserialize, Serialize}, serde::{Deserialize, Serialize},
sqlite::Connection, sqlite::Connection,
std::collections::BTreeMap,
}; };
/// Simple and handy btrfs snapshoting tool. /// 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"])] #[clap(long = "create", conflicts_with_all = &["restore_snapshot", "delete_snapshot", "list_snapshots", "clean_snapshots"])]
pub create_snapshot: bool, pub create_snapshot: bool,
/// Enable snapshots cleaning, will keep only the last X snapshots specified with -k/--keep. /// 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, pub clean_snapshots: bool,
/// Delete a snapshot. /// Delete a snapshot.
#[clap(long = "del", requires_all = &["snapshot_id"])] #[clap(long = "del", requires_all = &["snapshot_id"])]
@ -95,3 +96,36 @@ impl Args {
Ok(()) 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<String, toml::Value> = 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(())
}
}

View file

@ -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<()> { 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)?; let snaps_data = database::return_only_x_items(&extra_args.database_connection, args)?;
for data in &snaps_data { for data in &snaps_data {
extra_args.snapshot_name = data.destination.clone() + &data.name; extra_args.snapshot_name = data.destination.clone() + &data.name;
args.snapshot_id = data.snap_id.clone(); args.snapshot_id = data.snap_id.clone();
manage_deletion(args, extra_args)?; manage_deletion(args, extra_args)?;
} }

View file

@ -8,6 +8,10 @@ use rusnapshot::{args, controller, structs::ExtraArgs};
fn try_run() -> Result<()> { fn try_run() -> Result<()> {
let mut arguments = args::Args::parse(); let mut arguments = args::Args::parse();
if arguments.config_file.is_some() {
arguments.from_config_file()?;
}
let mut extra_args = ExtraArgs { let mut extra_args = ExtraArgs {
snapshot_name: format!( snapshot_name: format!(
"{}-{}", "{}-{}",
@ -19,19 +23,19 @@ fn try_run() -> Result<()> {
arguments.init(&extra_args)?; 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 { 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() { if !Path::new(&arguments.source_dir).is_absolute() {
arguments.source_dir = std::env::current_dir()? arguments.source_dir = std::env::current_dir()?
.join(&arguments.source_dir) .join(&arguments.source_dir)