diff --git a/Cargo.lock b/Cargo.lock index af43332..8434ce4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", diff --git a/Cargo.toml b/Cargo.toml index a507ae4..ddf96ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trash" -version = "0.1.0" +version = "0.1.1" authors = ["Michael Zhang "] edition = "2018" diff --git a/README.md b/README.md index 3bb11f8..f7f0ac1 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ $ trash restore [..interactive] $ trash list + +$ trash empty [days] ``` About diff --git a/src/main.rs b/src/main.rs index d067318..2985cbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, + }, #[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) => { diff --git a/src/ops.rs b/src/ops.rs index a28ee7f..b73378b 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -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) -> 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, 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, 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)?; diff --git a/src/trashdir.rs b/src/trashdir.rs index 7a0818f..c11a35a 100644 --- a/src/trashdir.rs +++ b/src/trashdir.rs @@ -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 { + 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>>); +pub struct TrashDirIter(PathBuf, Box>>); impl Iterator for TrashDirIter { type Item = Result; @@ -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)) } } diff --git a/src/trashinfo.rs b/src/trashinfo.rs index d04df1f..2b5dc28 100644 --- a/src/trashinfo.rs +++ b/src/trashinfo.rs @@ -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, + + /// 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) -> Result { - let path = path.as_ref(); - let original_path = path.to_path_buf(); + pub fn from_files( + info_path: impl AsRef, + deleted_path: impl AsRef, + ) -> Result { + 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, }) }