diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a3fc13..a973aea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -* `backup` CLI command * Incremental backups * Chain length describes how many incremental backups to create from the same full backup @@ -18,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Backup layers * Store multiple chains of backups in parallel, configuring each with different parameters (son-father-grandfather principle) +* CLI commands for creating, restoring & listing backups ### Changed diff --git a/src/backup/delta.rs b/src/backup/delta.rs index 34971f0..6e37e96 100644 --- a/src/backup/delta.rs +++ b/src/backup/delta.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; +use std::fmt; use std::path::PathBuf; /// Represents the changes relative to the previous backup @@ -74,3 +75,12 @@ impl Delta { } } } + +impl fmt::Display for Delta { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let added_count: usize = self.added.values().map(|s| s.len()).sum(); + let removed_count: usize = self.removed.values().map(|s| s.len()).sum(); + + write!(f, "+{}-{}", added_count, removed_count) + } +} diff --git a/src/backup/manager/meta.rs b/src/backup/manager/meta.rs index ff91310..de82576 100644 --- a/src/backup/manager/meta.rs +++ b/src/backup/manager/meta.rs @@ -127,4 +127,8 @@ where .get(layer) .map(|manager| manager.restore_backup(start_time)) } + + pub fn managers(&self) -> &HashMap> { + &self.managers + } } diff --git a/src/backup/manager/mod.rs b/src/backup/manager/mod.rs index 2b742f7..47488d9 100644 --- a/src/backup/manager/mod.rs +++ b/src/backup/manager/mod.rs @@ -181,4 +181,9 @@ where Err(other("Unknown backup.")) } + + /// Get a reference to the underlying chains + pub fn chains(&self) -> &Vec>> { + &self.chains + } } diff --git a/src/backup/mod.rs b/src/backup/mod.rs index 3bc99df..602df19 100644 --- a/src/backup/mod.rs +++ b/src/backup/mod.rs @@ -14,6 +14,7 @@ use flate2::Compression; use path::PathExt; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; +use std::fmt; use std::fs::File; use std::io; use std::path::{Path, PathBuf}; @@ -205,3 +206,20 @@ impl Backup { Ok(()) } } + +impl fmt::Display for Backup { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let letter = match self.type_ { + BackupType::Full => 'F', + BackupType::Incremental => 'I', + }; + + write!( + f, + "{} ({}, {})", + self.start_time.format(Backup::FILENAME_FORMAT), + letter, + self.delta + ) + } +} diff --git a/src/cli/backup.rs b/src/cli/backup.rs index 32d45c4..4f64882 100644 --- a/src/cli/backup.rs +++ b/src/cli/backup.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; #[derive(Subcommand)] pub enum BackupCommands { /// List all tracked backups - List, + List(BackupListArgs), /// Manually create a new backup Create(BackupCreateArgs), /// Restore a backup @@ -28,6 +28,12 @@ pub struct BackupCreateArgs { layer: String, } +#[derive(Args)] +pub struct BackupListArgs { + /// What layer to list + layer: Option, +} + #[derive(Args)] pub struct BackupRestoreArgs { /// Path to the backup inside the backup directory @@ -41,7 +47,7 @@ impl BackupArgs { pub fn run(&self, cli: &Cli) -> io::Result<()> { match &self.command { BackupCommands::Create(args) => args.run(cli), - BackupCommands::List => Ok(()), + BackupCommands::List(args) => args.run(cli), BackupCommands::Restore(args) => args.run(cli), } } @@ -120,3 +126,24 @@ impl BackupRestoreArgs { } } } + +impl BackupListArgs { + pub fn run(&self, cli: &Cli) -> io::Result<()> { + let meta = cli.meta()?; + + for (name, manager) in meta.managers().iter() { + println!("{}", name); + + for chain in manager.chains().iter().filter(|c| !c.is_empty()) { + let mut iter = chain.iter(); + println!(" {}", iter.next().unwrap()); + + for backup in iter { + println!(" {}", backup); + } + } + } + + Ok(()) + } +}