2 use std
::collections
::{HashMap, HashSet}
;
3 use std
::path
::PathBuf
;
5 use chrono
::{DateTime, Datelike, Local}
;
7 use super::{BackupDir, BackupInfo}
;
9 enum PruneMark { Keep, Remove }
11 fn mark_selections
<F
: Fn(DateTime
<Local
>, &BackupInfo
) -> String
> (
12 mark
: &mut HashMap
<PathBuf
, PruneMark
>,
13 list
: &Vec
<BackupInfo
>,
18 let mut include_hash
= HashSet
::new();
20 let mut already_included
= HashSet
::new();
22 let backup_id
= info
.backup_dir
.relative_path();
23 if let Some(PruneMark
::Keep
) = mark
.get(&backup_id
) {
24 let local_time
= info
.backup_dir
.backup_time().with_timezone(&Local
);
25 let sel_id
: String
= select_id(local_time
, &info
);
26 already_included
.insert(sel_id
);
31 let backup_id
= info
.backup_dir
.relative_path();
32 if let Some(_
) = mark
.get(&backup_id
) { continue; }
33 let local_time
= info
.backup_dir
.backup_time().with_timezone(&Local
);
34 let sel_id
: String
= select_id(local_time
, &info
);
36 if already_included
.contains(&sel_id
) { continue; }
38 if !include_hash
.contains(&sel_id
) {
39 if include_hash
.len() >= keep { break; }
40 include_hash
.insert(sel_id
);
41 mark
.insert(backup_id
, PruneMark
::Keep
);
43 mark
.insert(backup_id
, PruneMark
::Remove
);
48 pub fn compute_prune_info(
49 mut list
: Vec
<BackupInfo
>,
50 keep_last
: Option
<u64>,
51 keep_daily
: Option
<u64>,
52 keep_weekly
: Option
<u64>,
53 keep_monthly
: Option
<u64>,
54 keep_yearly
: Option
<u64>,
55 ) -> Result
<Vec
<(BackupInfo
, bool
)>, Error
> {
57 let mut mark
= HashMap
::new();
59 BackupInfo
::sort_list(&mut list
, false);
61 // remove inclomplete snapshots
62 let mut keep_unfinished
= true;
63 for info
in list
.iter() {
64 // backup is considered unfinished if there is no manifest
65 if info
.files
.iter().any(|name
| name
== super::MANIFEST_BLOB_NAME
) {
66 // There is a new finished backup, so there is no need
67 // to keep older unfinished backups.
68 keep_unfinished
= false;
70 let backup_id
= info
.backup_dir
.relative_path();
71 if keep_unfinished
{ // keep first unfinished
72 mark
.insert(backup_id
, PruneMark
::Keep
);
74 mark
.insert(backup_id
, PruneMark
::Remove
);
76 keep_unfinished
= false;
80 if let Some(keep_last
) = keep_last
{
81 mark_selections(&mut mark
, &list
, keep_last
as usize, |_local_time
, info
| {
82 BackupDir
::backup_time_to_string(info
.backup_dir
.backup_time())
86 if let Some(keep_daily
) = keep_daily
{
87 mark_selections(&mut mark
, &list
, keep_daily
as usize, |local_time
, _info
| {
88 format
!("{}/{}/{}", local_time
.year(), local_time
.month(), local_time
.day())
92 if let Some(keep_weekly
) = keep_weekly
{
93 mark_selections(&mut mark
, &list
, keep_weekly
as usize, |local_time
, _info
| {
94 format
!("{}/{}", local_time
.year(), local_time
.iso_week().week())
98 if let Some(keep_monthly
) = keep_monthly
{
99 mark_selections(&mut mark
, &list
, keep_monthly
as usize, |local_time
, _info
| {
100 format
!("{}/{}", local_time
.year(), local_time
.month())
104 if let Some(keep_yearly
) = keep_yearly
{
105 mark_selections(&mut mark
, &list
, keep_yearly
as usize, |local_time
, _info
| {
106 format
!("{}/{}", local_time
.year(), local_time
.year())
110 let prune_info
: Vec
<(BackupInfo
, bool
)> = list
.into_iter()
112 let backup_id
= info
.backup_dir
.relative_path();
113 let keep
= match mark
.get(&backup_id
) {
114 Some(PruneMark
::Keep
) => true,