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, KeepPartial, 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 fn remove_incomplete_snapshots(
49 mark
: &mut HashMap
<PathBuf
, PruneMark
>,
50 list
: &Vec
<BackupInfo
>,
53 let mut keep_unfinished
= true;
54 for info
in list
.iter() {
55 // backup is considered unfinished if there is no manifest
56 if info
.files
.iter().any(|name
| name
== super::MANIFEST_BLOB_NAME
) {
57 // There is a new finished backup, so there is no need
58 // to keep older unfinished backups.
59 keep_unfinished
= false;
61 let backup_id
= info
.backup_dir
.relative_path();
62 if keep_unfinished
{ // keep first unfinished
63 mark
.insert(backup_id
, PruneMark
::KeepPartial
);
65 mark
.insert(backup_id
, PruneMark
::Remove
);
67 keep_unfinished
= false;
72 pub struct PruneOptions
{
73 pub keep_last
: Option
<u64>,
74 pub keep_daily
: Option
<u64>,
75 pub keep_weekly
: Option
<u64>,
76 pub keep_monthly
: Option
<u64>,
77 pub keep_yearly
: Option
<u64>,
82 pub fn new() -> Self {
92 pub fn keep_last(mut self, value
: Option
<u64>) -> Self {
93 self.keep_last
= value
;
97 pub fn keep_daily(mut self, value
: Option
<u64>) -> Self {
98 self.keep_daily
= value
;
102 pub fn keep_weekly(mut self, value
: Option
<u64>) -> Self {
103 self.keep_weekly
= value
;
107 pub fn keep_monthly(mut self, value
: Option
<u64>) -> Self {
108 self.keep_monthly
= value
;
112 pub fn keep_yearly(mut self, value
: Option
<u64>) -> Self {
113 self.keep_yearly
= value
;
118 pub fn compute_prune_info(
119 mut list
: Vec
<BackupInfo
>,
120 options
: &PruneOptions
,
121 ) -> Result
<Vec
<(BackupInfo
, bool
)>, Error
> {
123 let mut mark
= HashMap
::new();
125 BackupInfo
::sort_list(&mut list
, false);
127 remove_incomplete_snapshots(&mut mark
, &list
);
129 if let Some(keep_last
) = options
.keep_last
{
130 mark_selections(&mut mark
, &list
, keep_last
as usize, |_local_time
, info
| {
131 BackupDir
::backup_time_to_string(info
.backup_dir
.backup_time())
135 if let Some(keep_daily
) = options
.keep_daily
{
136 mark_selections(&mut mark
, &list
, keep_daily
as usize, |local_time
, _info
| {
137 format
!("{}/{}/{}", local_time
.year(), local_time
.month(), local_time
.day())
141 if let Some(keep_weekly
) = options
.keep_weekly
{
142 mark_selections(&mut mark
, &list
, keep_weekly
as usize, |local_time
, _info
| {
143 format
!("{}/{}", local_time
.year(), local_time
.iso_week().week())
147 if let Some(keep_monthly
) = options
.keep_monthly
{
148 mark_selections(&mut mark
, &list
, keep_monthly
as usize, |local_time
, _info
| {
149 format
!("{}/{}", local_time
.year(), local_time
.month())
153 if let Some(keep_yearly
) = options
.keep_yearly
{
154 mark_selections(&mut mark
, &list
, keep_yearly
as usize, |local_time
, _info
| {
155 format
!("{}/{}", local_time
.year(), local_time
.year())
159 let prune_info
: Vec
<(BackupInfo
, bool
)> = list
.into_iter()
161 let backup_id
= info
.backup_dir
.relative_path();
162 let keep
= match mark
.get(&backup_id
) {
163 Some(PruneMark
::Keep
) => true,
164 Some(PruneMark
::KeepPartial
) => true,