1use anyhow::Result;
6use chrono::{Duration, Utc};
7use serde::Serialize;
8use tracing::{info, warn};
9
10pub const DEFAULT_CLEANUP_PERIOD_DAYS: u32 = 30;
12
13#[derive(Debug, Clone, Default, Serialize)]
15pub struct CleanupStats {
16 pub sessions: usize,
18 pub summaries: usize,
20 pub errors: usize,
22 pub directories: usize,
24}
25
26impl CleanupStats {
27 pub fn has_changes(&self) -> bool {
29 self.sessions > 0 || self.summaries > 0
30 }
31}
32
33pub fn get_cutoff_date(period_days: u32) -> chrono::DateTime<Utc> {
38 Utc::now() - Duration::days(period_days as i64)
39}
40
41pub fn cleanup_summaries(period_days: u32) -> Result<usize> {
49 crate::session::resume::cleanup_old_summaries(period_days)
50}
51
52pub fn cleanup_expired_data(period_days: u32) -> CleanupStats {
60 let mut stats = CleanupStats::default();
61
62 match cleanup_summaries(period_days) {
64 Ok(count) => {
65 stats.summaries = count;
66 if count > 0 {
67 stats.directories += 1;
68 }
69 }
70 Err(e) => {
71 warn!("Failed to cleanup summaries: {}", e);
72 stats.errors += 1;
73 }
74 }
75
76 stats
77}
78
79pub fn schedule_cleanup(period_days: u32) {
87 tokio::spawn(async move {
88 tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
90
91 let stats = cleanup_expired_data(period_days);
92
93 if stats.has_changes() {
94 info!("Cleanup complete: {} summaries removed", stats.summaries);
95 }
96
97 if stats.errors > 0 {
98 warn!("Cleanup encountered {} errors", stats.errors);
99 }
100 });
101}
102
103pub fn force_cleanup(period_days: u32) -> CleanupStats {
111 let stats = cleanup_expired_data(period_days);
112
113 info!(
114 "Force cleanup complete: {} summaries removed",
115 stats.summaries
116 );
117
118 if stats.errors > 0 {
119 warn!("Cleanup encountered {} errors", stats.errors);
120 }
121
122 stats
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_get_cutoff_date() {
131 let cutoff = get_cutoff_date(30);
132 let now = Utc::now();
133
134 let diff = now - cutoff;
136 assert!(diff.num_days() >= 29 && diff.num_days() <= 31);
137 }
138
139 #[test]
140 fn test_cleanup_stats_has_changes() {
141 let empty = CleanupStats::default();
142 assert!(!empty.has_changes());
143
144 let with_sessions = CleanupStats {
145 sessions: 1,
146 ..Default::default()
147 };
148 assert!(with_sessions.has_changes());
149
150 let with_summaries = CleanupStats {
151 summaries: 1,
152 ..Default::default()
153 };
154 assert!(with_summaries.has_changes());
155 }
156}