From bc3939c9bc09bee4e5b0c943e6ecde3f652aa947 Mon Sep 17 00:00:00 2001 From: qwjyh Date: Fri, 8 Nov 2024 06:09:04 +0900 Subject: [PATCH] (WIP): implementing multi device backup search --- src/backups.rs | 17 ++++++++-------- src/cmd_backup.rs | 15 ++------------ src/cmd_status.rs | 52 +++++++++++++++++++++++++++++++++++------------ src/util.rs | 12 +++++++++++ 4 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/backups.rs b/src/backups.rs index 896fcd9..5b39e61 100644 --- a/src/backups.rs +++ b/src/backups.rs @@ -27,7 +27,7 @@ pub fn backups_file(device: &Device) -> PathBuf { } /// Targets for backup source or destination. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct BackupTarget { /// `name()` of [`crate::storages::Storage`]. /// Use `String` for serialization/deserialization. @@ -47,14 +47,13 @@ impl BackupTarget { /// Get full path of the [`BackupTarget`]. pub fn path(&self, storages: &Storages, device: &Device) -> Option { let parent = storages.get(&self.storage).unwrap(); - let parent_path = parent - .mount_path(device)?; + let parent_path = parent.mount_path(device)?; Some(parent_path.join(self.path.clone())) } } /// Type of backup commands. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum BackupCommand { ExternallyInvoked(ExternallyInvoked), } @@ -81,7 +80,7 @@ impl BackupCommandExt for BackupCommand { /// Backup commands which is not invoked from xdbm itself. /// Call xdbm externally to record backup datetime and status. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExternallyInvoked { name: String, pub note: String, @@ -104,7 +103,7 @@ impl BackupCommandExt for ExternallyInvoked { } /// Backup execution log. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct BackupLog { pub datetime: DateTime, status: BackupResult, @@ -124,7 +123,7 @@ impl BackupLog { } /// Result of backup. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum BackupResult { Success, Failure, @@ -141,7 +140,7 @@ impl BackupResult { } /// Backup source, destination, command and logs. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Backup { /// must be unique name: String, @@ -202,7 +201,7 @@ impl Backup { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Backups { pub list: BTreeMap, } diff --git a/src/cmd_backup.rs b/src/cmd_backup.rs index df6d13d..9c58378 100644 --- a/src/cmd_backup.rs +++ b/src/cmd_backup.rs @@ -5,7 +5,7 @@ use std::{ }; use anyhow::{anyhow, Context, Ok, Result}; -use chrono::{Local, TimeDelta}; +use chrono::Local; use console::Style; use dunce::canonicalize; use git2::Repository; @@ -154,17 +154,6 @@ pub fn cmd_backup_list( Ok(()) } -fn duration_style(time: TimeDelta) -> Style { - match time { - x if x < TimeDelta::days(7) => Style::new().green(), - x if x < TimeDelta::days(14) => Style::new().yellow(), - x if x < TimeDelta::days(28) => Style::new().magenta(), - x if x < TimeDelta::days(28 * 3) => Style::new().red(), - x if x < TimeDelta::days(180) => Style::new().red().bold(), - _ => Style::new().on_red().black(), - } -} - /// TODO: status printing fn write_backups_list( mut writer: impl io::Write, @@ -222,7 +211,7 @@ fn write_backups_list( Some(log) => { let time = Local::now() - log.datetime; let s = util::format_summarized_duration(time); - let style = duration_style(time); + let style = util::duration_style(time); (style.apply_to(s), style) } None => { diff --git a/src/cmd_status.rs b/src/cmd_status.rs index cd90860..ba1ad87 100644 --- a/src/cmd_status.rs +++ b/src/cmd_status.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result}; -use console::Style; +use chrono::Local; use std::{ env, path::{self, Path, PathBuf}, @@ -40,7 +40,11 @@ pub(crate) fn cmd_status( if show_backup { let devices = devices::get_devices(config_dir)?; let storages = storages::Storages::read(config_dir)?; - let backups = Backups::read(config_dir, &currrent_device)?; + let backups = devices.iter().map(|device| { + Backups::read(config_dir, device) + .context("Backups were not found") + .unwrap() + }); let (target_storage, target_diff_from_storage) = util::min_parent_storage(&path, &storages, &currrent_device) @@ -48,13 +52,27 @@ pub(crate) fn cmd_status( let covering_backup: Vec<_> = devices .iter() - .map(|device| { + .zip(backups) + .map(|(device, backups)| { + debug!( + "dev {}, storage {:?}", + device.name(), + backups + .list + .iter() + .map(|(backup_name, backup)| format!( + "{} {}", + backup_name, + backup.source().storage + )) + .collect::>() + ); ( device, parent_backups( &target_diff_from_storage, target_storage, - &backups, + backups, &storages, device, ), @@ -78,9 +96,14 @@ pub(crate) fn cmd_status( for (backup_device, covering_backups) in covering_backup { println!("Device: {}", backup_device.name()); for (backup, path_from_backup) in covering_backups { + let last_backup = match backup.last_backup() { + Some(log) => util::format_summarized_duration(Local::now() - log.datetime), + None => "---".to_string(), + }; println!( - " {:( target_path_from_storage: &'a Path, target_storage: &'a Storage, - backups: &'a Backups, + backups: Backups, storages: &'a Storages, device: &'a Device, -) -> Vec<(&'a Backup, PathBuf)> { +) -> Vec<(Backup, PathBuf)> { trace!("Dev {:?}", device.name()); let target_path = match target_storage.mount_path(device) { Some(target_path) => target_path.join(target_path_from_storage), @@ -106,10 +129,12 @@ fn parent_backups<'a>( trace!("Path on the device {:?}", target_path); backups .list - .iter() + .into_iter() .filter_map(|(_k, backup)| { let backup_path = backup.source().path(storages, device)?; - let diff = pathdiff::diff_paths(&target_path, backup_path)?; + trace!("{:?}", backup_path.components()); + let diff = pathdiff::diff_paths(&target_path, backup_path.clone())?; + trace!("Backup: {:?}, Diff: {:?}", backup_path, diff); if diff.components().any(|c| c == path::Component::ParentDir) { None } else { @@ -218,7 +243,7 @@ mod test { let covering_backups_1 = parent_backups( &target_path_from_storage1, target_storage1, - &backups, + backups.clone(), &storages, &device1, ); @@ -231,7 +256,7 @@ mod test { let covering_backups_2 = parent_backups( &target_path_from_storage2, target_storage2, - &backups, + backups.clone(), &storages, &device2, ); @@ -244,12 +269,13 @@ mod test { let covering_backups_3 = parent_backups( &target_path_from_storage3, target_storage3, - &backups, + backups, &storages, &device2, ); assert_eq!(covering_backups_3.len(), 1); - let mut covering_backup_names_3 = covering_backups_3.iter().map(|(backup, _)| backup.name()); + let mut covering_backup_names_3 = + covering_backups_3.iter().map(|(backup, _)| backup.name()); assert_eq!(covering_backup_names_3.next().unwrap(), "backup_2"); assert!(covering_backup_names_3.next().is_none()); } diff --git a/src/util.rs b/src/util.rs index 8f27bc4..5730dc7 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,7 @@ use std::path::{self, PathBuf}; use anyhow::{Context, Result}; +use chrono::TimeDelta; use console::Style; use crate::{ @@ -59,6 +60,17 @@ pub fn format_summarized_duration(dt: chrono::Duration) -> String { } } +pub fn duration_style(time: TimeDelta) -> Style { + match time { + x if x < TimeDelta::days(7) => Style::new().green(), + x if x < TimeDelta::days(14) => Style::new().yellow(), + x if x < TimeDelta::days(28) => Style::new().magenta(), + x if x < TimeDelta::days(28 * 3) => Style::new().red(), + x if x < TimeDelta::days(180) => Style::new().red().bold(), + _ => Style::new().on_red().black(), + } +} + #[cfg(test)] mod test { use anyhow::Result;