trash-util/src/main.rs

92 lines
2.5 KiB
Rust

use chrono::{prelude::*, Duration};
use clap::{Parser, Subcommand};
use std::path::PathBuf;
/// A simple trash utility
#[derive(Parser)]
#[command(name = "trash")]
#[command(about = "A simple trash utility", long_about = None)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Lists trashed files
List { path: Option<PathBuf> },
/// Put a file into the trash
Put { path: Vec<PathBuf> },
/// Restore a file by its id
Restore { id: PathBuf },
/// Prune files older than n days (defaults to 7)
Prune { age: Option<u64> },
}
fn list<P: Fn(&trash::TrashItem) -> bool>(predicate: P) -> Vec<trash::TrashItem> {
let trashed = trash::os_limited::list().unwrap();
trashed.into_iter().filter(predicate).collect()
}
fn print_list(path: Option<PathBuf>) {
let predicate: Box<dyn Fn(&trash::TrashItem) -> bool> = match &path {
Some(p) => Box::new(|item| {
let orig = match item.original_parent.canonicalize() {
Ok(p) => p,
Err(_) => item.original_parent.clone(),
};
*p.canonicalize().unwrap() == orig
}),
None => Box::new(|_| true),
};
for item in list(predicate.as_ref()) {
println!(
"{}\0{}\0{}",
item.original_path().display(),
item.id.into_string().unwrap(),
Local
.timestamp_opt(item.time_deleted, 0)
.unwrap()
.format("%Y-%m-%d %H:%M:%S"),
);
}
}
fn put(path: Vec<PathBuf>) {
trash::delete_all(path).unwrap()
}
fn restore(id: PathBuf) {
let items: Vec<trash::TrashItem> = list(|_| true).into_iter().filter(|i| i.id == id).collect();
match items.len() {
0 => panic!("No trashed file with that id"),
1 => (),
_ => panic!("Multiple trashed files with that id"),
}
trash::os_limited::restore_all(items).unwrap()
}
fn prune(max_days: Option<u64>) {
let cutoff = { Utc::now() - Duration::days(max_days.unwrap_or(7) as i64) };
let predicate =
|item: &trash::TrashItem| Utc.timestamp_opt(item.time_deleted, 0).unwrap() < cutoff;
let items: Vec<trash::TrashItem> = list(predicate).into_iter().collect();
trash::os_limited::purge_all(items).unwrap()
}
fn main() {
let args = Cli::parse();
match args.command {
Commands::List { path } => print_list(path),
Commands::Put { path } => put(path),
Commands::Restore { id } => restore(id),
Commands::Prune { age } => prune(age),
}
}