db: allow forcefully expiring entries with valid TTL

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ie7ca7b88cf912e8f71fb2d04481bd9996a6a6964
This commit is contained in:
raf 2026-05-24 16:33:26 +03:00
commit 3f2e34b8ea
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
2 changed files with 40 additions and 0 deletions

View file

@ -1024,6 +1024,17 @@ impl SqliteClipboardDb {
}
/// Clean up all expired entries. Returns count deleted.
pub fn expire_ttl_entries(&self) -> Result<usize, StashError> {
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<usize, StashError> {
let now = Self::now();
self

View file

@ -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(()) => {