implement trash-empty
This commit is contained in:
parent
394192ffee
commit
388c9e4452
7 changed files with 83 additions and 26 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "trash"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ $ trash restore
|
|||
[..interactive]
|
||||
|
||||
$ trash list
|
||||
|
||||
$ trash empty [days]
|
||||
```
|
||||
|
||||
About
|
||||
|
|
25
src/main.rs
25
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<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) => {
|
||||
|
|
35
src/ops.rs
35
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<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)?;
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue