implement trash-empty

This commit is contained in:
Michael Zhang 2019-07-23 02:02:05 -05:00
parent 394192ffee
commit 388c9e4452
No known key found for this signature in database
GPG key ID: 5BAEFE5D04F0CE6C
7 changed files with 83 additions and 26 deletions

2
Cargo.lock generated
View file

@ -283,7 +283,7 @@ dependencies = [
[[package]]
name = "trash"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -1,6 +1,6 @@
[package]
name = "trash"
version = "0.1.0"
version = "0.1.1"
authors = ["Michael Zhang <iptq@protonmail.com>"]
edition = "2018"

View file

@ -15,6 +15,8 @@ $ trash restore
[..interactive]
$ trash list
$ trash empty [days]
```
About

View file

@ -24,7 +24,16 @@ lazy_static! {
#[derive(StructOpt)]
enum Command {
#[structopt(name = "empty")]
Empty,
Empty {
/// Only list the files that are to be deleted, without
/// actually deleting anything.
#[structopt(long = "dry")]
dry: bool,
/// Delete all files older than (this number) of days.
/// Removes everything if this option is not specified
days: Option<u32>,
},
#[structopt(name = "list")]
List,
@ -53,12 +62,13 @@ fn main() {
let cmd = Command::from_args();
match cmd {
Command::Empty => {
println!("TODO");
}
Command::Empty { dry, days } => match crate::ops::empty(dry, days) {
Ok(_) => (),
Err(err) => error!("error: {:?}", err),
},
Command::List => {
let home_trash = TrashDir::get_home_trash();
for info in home_trash.iter() {
for info in home_trash.iter().unwrap() {
let info = match info {
Ok(info) => info,
Err(err) => {
@ -69,7 +79,9 @@ fn main() {
println!("{}\t{}", info.deletion_date, info.path.to_str().unwrap());
}
}
Command::Put { paths, recursive, .. } => {
Command::Put {
paths, recursive, ..
} => {
for path in paths {
match crate::ops::put(path, recursive) {
Ok(_) => (),
@ -81,6 +93,7 @@ fn main() {
let home_trash = TrashDir::get_home_trash();
let files = home_trash
.iter()
.unwrap()
.filter_map(|entry| match entry {
Ok(info) => Some(info),
Err(err) => {

View file

@ -1,12 +1,44 @@
use std::fs::{self, File};
use std::path::Path;
use chrono::Local;
use chrono::{Duration, Local};
use crate::errors::Error;
use crate::trashdir::TrashDir;
use crate::trashinfo::TrashInfo;
pub fn empty(dry: bool, days: Option<u32>) -> Result<(), Error> {
let home_trash = TrashDir::get_home_trash();
let files_dir = home_trash.files_dir()?;
let info_dir = home_trash.info_dir()?;
let cutoff = if let Some(days) = days {
Local::now() - Duration::days(days.into())
} else {
Local::now()
};
for file in home_trash.iter()? {
let file = file?;
let mut ignore = false;
// ignore files that were deleted after the cutoff (younger)
if file.deletion_date > cutoff {
ignore = true;
}
if !ignore {
if dry {
println!("{:?}", file.path);
} else {
fs::remove_file(file.info_path)?;
fs::remove_file(file.deleted_path)?;
}
}
}
Ok(())
}
pub fn put(path: impl AsRef<Path>, recursive: bool) -> Result<(), Error> {
let path = path.as_ref().canonicalize()?;
if path.is_dir() && !recursive {
@ -31,6 +63,7 @@ pub fn put(path: impl AsRef<Path>, recursive: bool) -> Result<(), Error> {
path: path.clone(),
deletion_date: now,
deleted_path: trash_file_path.clone(),
info_path: trash_info_path.clone(),
};
{
let trash_info_file = File::create(trash_info_path)?;

View file

@ -31,22 +31,19 @@ impl TrashDir {
Ok(target)
}
pub fn iter(&self) -> TrashDirIter {
let iter = WalkDir::new(&self.0.join("info"))
.contents_first(true)
pub fn iter(&self) -> Result<TrashDirIter, Error> {
let iter = WalkDir::new(&self.info_dir()?)
.contents_first(true)
.into_iter()
.filter_entry(|entry| {
// warn!("path: {:?}", entry.path());
match entry.path().extension() {
Some(x) => x == "trashinfo",
_ => false,
}
.filter_entry(|entry| match entry.path().extension() {
Some(x) => x == "trashinfo",
_ => false,
});
TrashDirIter(Box::new(iter))
Ok(TrashDirIter(self.0.clone(), Box::new(iter)))
}
}
pub struct TrashDirIter(Box<Iterator<Item = walkdir::Result<DirEntry>>>);
pub struct TrashDirIter(PathBuf, Box<Iterator<Item = walkdir::Result<DirEntry>>>);
impl Iterator for TrashDirIter {
type Item = Result<TrashInfo, Error>;
@ -55,7 +52,7 @@ impl Iterator for TrashDirIter {
let entry = {
let mut entry;
loop {
entry = match self.0.next() {
entry = match self.1.next() {
Some(Ok(entry)) => entry,
Some(Err(err)) => return Some(Err(Error::from(err))),
None => return None,
@ -68,6 +65,8 @@ impl Iterator for TrashDirIter {
entry
};
Some(TrashInfo::from_file(entry.path()).map_err(Error::from))
let name = entry.path().file_name().unwrap();
let deleted_path = self.0.join("files").join(name);
Some(TrashInfo::from_files(entry.path(), deleted_path).map_err(Error::from))
}
}

View file

@ -15,15 +15,24 @@ const DATE_FORMAT: &str = "%Y-%m-%dT%H:%M:%S";
#[derive(Debug)]
pub struct TrashInfo {
/// The original path where this file was located before it was deleted.
pub path: PathBuf,
pub deletion_date: DateTime<Local>,
/// The location of the deleted file after deletion.
pub deleted_path: PathBuf,
/// The location of the `info` description file.
pub info_path: PathBuf,
}
impl TrashInfo {
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
let path = path.as_ref();
let original_path = path.to_path_buf();
pub fn from_files(
info_path: impl AsRef<Path>,
deleted_path: impl AsRef<Path>,
) -> Result<Self, Error> {
let path = info_path.as_ref();
let info_path = path.to_path_buf();
let deleted_path = deleted_path.as_ref().to_path_buf();
let file = File::open(path)?;
let reader = BufReader::new(file);
@ -74,7 +83,8 @@ impl TrashInfo {
Ok(TrashInfo {
path,
deletion_date,
deleted_path: original_path,
deleted_path,
info_path,
})
}