mirror of
https://github.com/qwjyh/xdbm
synced 2024-11-24 07:31:05 +09:00
add backup done
This commit is contained in:
parent
4f0d725b52
commit
b1b174b4b3
7 changed files with 117 additions and 12 deletions
|
@ -20,8 +20,8 @@
|
|||
- [ ] backup add
|
||||
- [ ] test for backup add
|
||||
- [ ] backup list
|
||||
- [ ] status printing
|
||||
- [ ] backup done
|
||||
- [x] status printing
|
||||
- [x] backup done
|
||||
- [ ] fancy display
|
||||
- [ ] no commit option
|
||||
|
||||
|
|
|
@ -101,11 +101,23 @@ impl BackupCommandExt for ExternallyInvoked {
|
|||
/// Backup execution log.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct BackupLog {
|
||||
datetime: DateTime<Local>,
|
||||
pub datetime: DateTime<Local>,
|
||||
status: BackupResult,
|
||||
log: String,
|
||||
}
|
||||
|
||||
impl BackupLog {
|
||||
pub fn new_with_current_time(status: BackupResult, log: String) -> BackupLog {
|
||||
let timestamp = Local::now();
|
||||
trace!("Generating timestamp: {:?}", timestamp);
|
||||
BackupLog {
|
||||
datetime: timestamp,
|
||||
status,
|
||||
log,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of backup.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum BackupResult {
|
||||
|
@ -113,6 +125,16 @@ pub enum BackupResult {
|
|||
Failure,
|
||||
}
|
||||
|
||||
impl BackupResult {
|
||||
pub fn from_exit_code(code: u64) -> Self {
|
||||
if code == 0 {
|
||||
Self::Success
|
||||
} else {
|
||||
Self::Failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Backup source, destination, command and logs.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Backup {
|
||||
|
@ -164,6 +186,15 @@ impl Backup {
|
|||
pub fn command(&self) -> &BackupCommand {
|
||||
&self.command
|
||||
}
|
||||
|
||||
pub fn add_log(&mut self, newlog: BackupLog) -> () {
|
||||
self.logs.push(newlog)
|
||||
}
|
||||
|
||||
/// Get the last backup.
|
||||
pub fn last_backup(&self) -> Option<&BackupLog> {
|
||||
self.logs.iter().max_by_key(|log| log.datetime)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -183,6 +214,10 @@ impl Backups {
|
|||
self.list.get(name)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, name: &String) -> Option<&mut Backup> {
|
||||
self.list.get_mut(name)
|
||||
}
|
||||
|
||||
/// Add new [`Backup`].
|
||||
/// New `backup` must has new unique name.
|
||||
pub fn add(&mut self, backup: Backup) -> Result<()> {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
io::{self, stdout, Write},
|
||||
path::{PathBuf, self},
|
||||
path::{self, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result, Ok};
|
||||
use anyhow::{anyhow, Context, Ok, Result};
|
||||
use chrono::Local;
|
||||
use dunce::canonicalize;
|
||||
use git2::Repository;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
@ -235,6 +236,7 @@ fn write_backups_list(
|
|||
let mut src_storage_width = 0;
|
||||
let mut dest_width = 0;
|
||||
let mut dest_storage_width = 0;
|
||||
let mut cmd_name_width = 0;
|
||||
// get widths
|
||||
for ((dev, _name), backup) in &backups {
|
||||
let device = backup.device(devices).context(format!(
|
||||
|
@ -250,6 +252,7 @@ fn write_backups_list(
|
|||
dest_width = dest_width.max(format!("{}", dest.display()).width());
|
||||
dest_storage_width = dest_storage_width.max(backup.destination().storage.width());
|
||||
let cmd_name = backup.command().name();
|
||||
cmd_name_width = cmd_name_width.max(cmd_name.width());
|
||||
}
|
||||
// main printing
|
||||
for ((dev, _name), backup) in &backups {
|
||||
|
@ -260,9 +263,18 @@ fn write_backups_list(
|
|||
let src = backup.source().path(&storages, &device)?;
|
||||
let dest = backup.destination().path(&storages, &device)?;
|
||||
let cmd_name = backup.command().name();
|
||||
let last_backup_elapsed = match backup.last_backup() {
|
||||
Some(log) => {
|
||||
let time = Local::now() - log.datetime;
|
||||
util::format_summarized_duration(time)
|
||||
}
|
||||
None => {
|
||||
format!("---")
|
||||
}
|
||||
};
|
||||
writeln!(
|
||||
writer,
|
||||
"{name:<name_width$} [{dev:<dev_width$}] {src:<src_storage_width$} → {dest:<dest_storage_width$} {cmd_name}",
|
||||
"{name:<name_width$} [{dev:<dev_width$}] {src:<src_storage_width$} → {dest:<dest_storage_width$} {last_backup_elapsed}",
|
||||
name = backup.name(),
|
||||
src = backup.source().storage,
|
||||
dest = backup.destination().storage,
|
||||
|
@ -275,9 +287,41 @@ fn write_backups_list(
|
|||
" dest: {dest:<dest_width$}",
|
||||
dest = dest.display()
|
||||
)?;
|
||||
writeln!(writer, " {note}", note = cmd_note,)?;
|
||||
writeln!(
|
||||
writer,
|
||||
" {cmd_name:<cmd_name_width$}({note})",
|
||||
note = cmd_note,
|
||||
)?;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_backup_done(
|
||||
name: String,
|
||||
exit_status: u64,
|
||||
log: Option<String>,
|
||||
repo: Repository,
|
||||
config_dir: &PathBuf,
|
||||
) -> Result<()> {
|
||||
let device = devices::get_device(&config_dir)?;
|
||||
let mut backups = Backups::read(&config_dir, &device)?;
|
||||
let backup = backups
|
||||
.get_mut(&name)
|
||||
.context(format!("Failed to get backup with name {}", name))?;
|
||||
trace!("Got backup: {:?}", backup);
|
||||
let backup_name = backup.name().clone();
|
||||
let status = BackupResult::from_exit_code(exit_status);
|
||||
let new_log = BackupLog::new_with_current_time(status, log.unwrap_or("".to_string()));
|
||||
trace!("New backup log: {:?}", new_log);
|
||||
backup.add_log(new_log);
|
||||
trace!("Added");
|
||||
backups.write(&config_dir, &device)?;
|
||||
add_and_commit(
|
||||
&repo,
|
||||
&backups::backups_file(&device),
|
||||
&format!("Done backup: {}", backup_name),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub(crate) fn cmd_storage_add(
|
|||
} else {
|
||||
manually_construct_physical_drive_partition(
|
||||
name,
|
||||
canonicalize(util::expand_tilde(path.unwrap()))?,
|
||||
canonicalize(util::expand_tilde(path.unwrap())?)?,
|
||||
&device,
|
||||
)?
|
||||
};
|
||||
|
@ -77,7 +77,7 @@ pub(crate) fn cmd_storage_add(
|
|||
trace!("SubDirectory arguments: path: {:?}", path);
|
||||
// Nightly feature std::path::absolute
|
||||
trace!("Canonicalize path: {:?}", path);
|
||||
let path = canonicalize(util::expand_tilde(path))?;
|
||||
let path = canonicalize(util::expand_tilde(path)?)?;
|
||||
trace!("canonicalized: path: {:?}", path);
|
||||
|
||||
let storage = directory::Directory::try_from_device_path(
|
||||
|
@ -99,7 +99,7 @@ pub(crate) fn cmd_storage_add(
|
|||
));
|
||||
}
|
||||
trace!("Canonicalize path: {:?}", path);
|
||||
let path = canonicalize(util::expand_tilde(path))?;
|
||||
let path = canonicalize(util::expand_tilde(path)?)?;
|
||||
let storage = storages::online_storage::OnlineStorage::new(
|
||||
name, provider, capacity, alias, path, &device,
|
||||
);
|
||||
|
|
|
@ -120,7 +120,7 @@ fn main() -> Result<()> {
|
|||
name,
|
||||
exit_status,
|
||||
log,
|
||||
} => todo!(),
|
||||
} => cmd_backup::cmd_backup_done(name, exit_status, log, repo, &config_dir)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ impl PhysicalDrivePartition {
|
|||
let fs = disk.file_system();
|
||||
trace!("fs: {:?}", fs);
|
||||
let fs = std::str::from_utf8(fs)?;
|
||||
let local_info = LocalInfo::new(alias, disk.mount_point().to_path_buf().canonicalize()?);
|
||||
let local_info = LocalInfo::new(alias, disk.mount_point().to_path_buf());
|
||||
Ok(PhysicalDrivePartition {
|
||||
name: name,
|
||||
kind: format!("{:?}", disk.kind()),
|
||||
|
|
26
src/util.rs
26
src/util.rs
|
@ -1,5 +1,8 @@
|
|||
use std::path::{self, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use chrono::format;
|
||||
|
||||
use crate::{
|
||||
devices::Device,
|
||||
storages::{Storage, StorageExt, Storages},
|
||||
|
@ -35,3 +38,26 @@ pub fn min_parent_storage<'a>(
|
|||
let storage = storages.get(name)?;
|
||||
Some((storage, pathdiff))
|
||||
}
|
||||
|
||||
/// Expand first `~` in path as `home_dir`.
|
||||
pub fn expand_tilde(path: PathBuf) -> Result<PathBuf> {
|
||||
if path.components().next() == Some(path::Component::Normal("~".as_ref())) {
|
||||
let mut expanded_path = dirs::home_dir().context("Failed to expand home directory.")?;
|
||||
for c in path.components().skip(1) {
|
||||
expanded_path.push(c)
|
||||
}
|
||||
Ok(expanded_path)
|
||||
} else {
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_summarized_duration(dt: chrono::Duration) -> String {
|
||||
if dt.num_days() > 0 {
|
||||
format!("{}d", dt.num_days())
|
||||
} else if dt.num_hours() > 0 {
|
||||
format!("{}h", dt.num_hours())
|
||||
} else {
|
||||
format!("{}min", dt.num_minutes())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue