92 lines
2.5 KiB
Rust
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),
|
|
}
|
|
}
|