diff --git a/src/db/mod.rs b/src/db/mod.rs index 7af285b..41aec9f 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1024,6 +1024,17 @@ impl SqliteClipboardDb { } /// Clean up all expired entries. Returns count deleted. + pub fn expire_ttl_entries(&self) -> Result { + self + .conn + .execute( + "UPDATE clipboard SET is_expired = 1 WHERE expires_at IS NOT NULL AND \ + (is_expired IS NULL OR is_expired = 0)", + [], + ) + .map_err(|e| StashError::Trim(e.to_string().into())) + } + pub fn cleanup_expired(&self) -> Result { let now = Self::now(); self diff --git a/src/main.rs b/src/main.rs index f006d36..a711c8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -169,6 +169,13 @@ enum DbAction { ask: bool, }, + /// Immediately expire all entries with a TTL + Expire { + /// Ask for confirmation before expiring + #[arg(long)] + ask: bool, + }, + /// Optimize database using VACUUM Vacuum, @@ -406,6 +413,28 @@ fn main() -> eyre::Result<()> { } } }, + DbAction::Expire { ask } => { + let should_proceed = !ask + || confirm( + "Are you sure you want to immediately expire all entries with \ + a TTL?", + ); + if should_proceed { + match db.expire_ttl_entries() { + Ok(0) => { + println!("no entries with a TTL to expire"); + }, + Ok(count) => { + println!("marked {count} entries as expired"); + }, + Err(e) => { + log::error!("failed to expire entries: {e}"); + }, + } + } else { + log::info!("db expire command aborted by user."); + } + }, DbAction::Vacuum => { match db.vacuum() { Ok(()) => {