summaryrefslogtreecommitdiffstats
path: root/src/actions/undo.rs
blob: 3e003a68cb936a17a21e6c739d181f65975c6624 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::defaults;
use crate::KhResult;
use crate::utils::stdioutils;

use std::fs;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

pub fn do_undo() -> KhResult<()> {
  let backupdir = defaults::get_backupdir();

  let source_dir = get_most_recent_backup()?;

  let backup_id = source_dir.strip_prefix(backupdir)?;

  info!("Restoring {:?}", backup_id);

  let files = WalkDir::new(source_dir.clone())
    .into_iter()
    .flatten()
    .filter(|dir_entry| dir_entry.path().is_file());

  for file in files {
    restore_file_from_backup(&source_dir, &file.path())?;
  };

  Ok(())
}

fn restore_file_from_backup(source_prefix: &Path, file_path: &Path) -> KhResult<()> {
  let caldir = defaults::get_caldir();
  let path_in_cal = file_path.strip_prefix(source_prefix)?;

  let mut target_path = caldir.clone();
  target_path.push(path_in_cal);

  if target_path.exists() && !ask_overwrite(&target_path) {
    info!("ignoring {}", target_path.display());
    return Ok(());
  }
  fs::create_dir_all(&target_path.parent().ok_or_else(|| "error creating calendar directory")?)?;
  fs::copy(file_path, &target_path)?;

  info!("Restore {} to {}", file_path.display(), target_path.display());

  Ok(())
}

fn get_most_recent_backup() -> KhResult<PathBuf> {
  let backupdir = defaults::get_backupdir();
  backupdir
    .read_dir()?
    .filter_map(|result| result.ok())
    .map(|dir_entry| dir_entry.path())
    .max()
    .ok_or_else(|| "there are no backups, nothing to undo!".into())
}

fn ask_overwrite(path: &Path) -> bool {
  if cfg!(test) { return true};
  println!("File exists:\n{}", path.display());
  println!("Overwrite? y/n:");

  match stdioutils::read_single_char_from_stdin().unwrap() {
    'y' => true,
    _ => false
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  use crate::testutils::prepare_testdir;
  use assert_fs::prelude::*;
  use predicates::prelude::*;

  #[test]
  fn test_get_most_recent_backup() {
    let _testdir = prepare_testdir("testdir_with_backup");
    let result = get_most_recent_backup().unwrap();
    assert_eq!("backup_id", result.file_name().unwrap().to_str().unwrap());
  }

  #[test]
  #[should_panic]
  fn test_get_most_recent_backup_negative() {
    let _testdir = prepare_testdir("testdir");
    get_most_recent_backup().unwrap();
  }

  #[test]
  fn test_restore_file_from_backup() {
    let testdir = prepare_testdir("testdir_with_backup");
    let source_file = testdir.child(".khaleesi/backup/backup_id/my_calendar/twodaysacrossbuckets.ics");
    let source_folder = testdir.child(".khaleesi/backup/backup_id");
    let target_file = testdir.child(".khaleesi/cal/my_calendar/twodaysacrossbuckets.ics");

    restore_file_from_backup(source_folder.path(), source_file.path()).unwrap();
    target_file.assert(predicate::path::exists());
  }

  #[test]
  fn test_restore_file_from_backup_overwrite() {
    let testdir = prepare_testdir("testdir_with_backup");
    let source_file = testdir.child(".khaleesi/backup/backup_id/my_calendar/twodaysacrossbuckets.ics");
    let source_folder = testdir.child(".khaleesi/backup/backup_id");
    let target_file = testdir.child(".khaleesi/cal/my_calendar/twodaysacrossbuckets.ics");
    target_file.touch().unwrap();

    restore_file_from_backup(source_folder.path(), source_file.path()).unwrap();
    target_file.assert(predicate::path::exists());
  }
}

#[cfg(test)]
mod integration {
  use super::*;

  use crate::testutils::prepare_testdir;
  use assert_fs::prelude::*;
  use predicates::prelude::*;

  #[test]
  fn test_do_undo() {
    let testdir = prepare_testdir("testdir_with_backup");
    do_undo().unwrap();
    let target_folder = testdir.child(".khaleesi/cal/my_calendar/twodaysacrossbuckets.ics");
    target_folder.assert(predicate::path::exists());
  }
}