1use anyhow::Result;
10
11use crate::config::{Config, MaintenanceConfig};
12use crate::db::SearchFilter;
13use crate::{backup, config, confirm, db, list, prune};
14
15const PREVIEW_SAMPLES: usize = 5;
17
18fn cutoff(cfg: &MaintenanceConfig) -> Option<String> {
21 let secs = prune::parse_duration(&cfg.auto_prune_after).ok()?;
22 Some(prune::cutoff_timestamp(secs))
23}
24
25pub fn run_status() -> Result<()> {
27 let cfg = Config::load()?;
28 let m = &cfg.maintenance;
29
30 println!("Maintenance :");
31 println!(
32 " Nettoyage automatique : {}",
33 if m.auto_prune_enabled {
34 "activé"
35 } else {
36 "désactivé"
37 }
38 );
39 println!(" Ancienneté : {}", m.auto_prune_after);
40 println!(
41 " Sauvegarde avant purge: {}",
42 if m.auto_backup_before_prune {
43 "oui"
44 } else {
45 "non"
46 }
47 );
48
49 match cutoff(m) {
50 Some(cutoff) => {
51 let conn = db::open(&config::db_path()?)?;
52 let n = db::count_older_than(&conn, &cutoff, &SearchFilter::default())?;
53 println!(" Coupe : {cutoff}");
54 println!(" Entrées concernées : {n}");
55 }
56 None => {
57 println!(
58 " [ERREUR] auto_prune_after invalide : {:?} (ex : 180d, 6m, 1y)",
59 m.auto_prune_after
60 );
61 }
62 }
63 Ok(())
64}
65
66pub fn run(dry_run: bool, assume_yes: bool) -> Result<()> {
73 let cfg = Config::load()?;
74 let m = &cfg.maintenance;
75
76 if !m.auto_prune_enabled {
77 println!(
78 "Nettoyage automatique désactivé ([maintenance] auto_prune_enabled = false). \
79 Rien à faire."
80 );
81 return Ok(());
82 }
83
84 let cutoff = match cutoff(m) {
85 Some(c) => c,
86 None => {
87 anyhow::bail!(
88 "maintenance.auto_prune_after invalide : {:?} (ex : 180d, 6m, 1y)",
89 m.auto_prune_after
90 );
91 }
92 };
93
94 let conn = db::open(&config::db_path()?)?;
95 let filter = SearchFilter::default();
96 let total = db::count_older_than(&conn, &cutoff, &filter)?;
97
98 if total == 0 {
99 println!("Aucune commande antérieure à {cutoff}. Rien à nettoyer.");
100 return Ok(());
101 }
102
103 println!("{total} commande(s) antérieure(s) à {cutoff} seront supprimées.");
104 let samples = db::fetch_older_than(&conn, &cutoff, &filter, PREVIEW_SAMPLES)?;
105 if !samples.is_empty() {
106 println!("Exemples :");
107 for r in &samples {
108 println!("{}", list::short_line(r));
109 }
110 }
111
112 if dry_run {
113 println!("\n[dry-run] Aucune suppression effectuée.");
114 return Ok(());
115 }
116
117 if !confirm::confirm(&format!("Supprimer ces {total} commande(s) ?"), assume_yes)? {
118 println!("Nettoyage annulé.");
119 return Ok(());
120 }
121
122 if m.auto_backup_before_prune {
123 let safety = backup::create_backup(None)?;
124 println!("Sauvegarde automatique : {}", safety.path.display());
125 }
126
127 let n = db::delete_older_than(&conn, &cutoff, &filter)?;
128 println!("{n} commande(s) supprimée(s).");
129 Ok(())
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn cutoff_none_si_duree_invalide() {
138 let m = MaintenanceConfig {
139 auto_prune_enabled: true,
140 auto_prune_after: "pas-une-duree".to_string(),
141 auto_backup_before_prune: true,
142 };
143 assert!(cutoff(&m).is_none());
144 }
145
146 #[test]
147 fn cutoff_some_si_duree_valide() {
148 let m = MaintenanceConfig {
149 auto_prune_enabled: true,
150 auto_prune_after: "180d".to_string(),
151 auto_backup_before_prune: true,
152 };
153 assert!(cutoff(&m).is_some());
154 }
155}