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]]
|
[[package]]
|
||||||
name = "trash"
|
name = "trash"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "trash"
|
name = "trash"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ $ trash restore
|
||||||
[..interactive]
|
[..interactive]
|
||||||
|
|
||||||
$ trash list
|
$ trash list
|
||||||
|
|
||||||
|
$ trash empty [days]
|
||||||
```
|
```
|
||||||
|
|
||||||
About
|
About
|
||||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -24,7 +24,16 @@ lazy_static! {
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
enum Command {
|
enum Command {
|
||||||
#[structopt(name = "empty")]
|
#[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")]
|
#[structopt(name = "list")]
|
||||||
List,
|
List,
|
||||||
|
@ -53,12 +62,13 @@ fn main() {
|
||||||
|
|
||||||
let cmd = Command::from_args();
|
let cmd = Command::from_args();
|
||||||
match cmd {
|
match cmd {
|
||||||
Command::Empty => {
|
Command::Empty { dry, days } => match crate::ops::empty(dry, days) {
|
||||||
println!("TODO");
|
Ok(_) => (),
|
||||||
}
|
Err(err) => error!("error: {:?}", err),
|
||||||
|
},
|
||||||
Command::List => {
|
Command::List => {
|
||||||
let home_trash = TrashDir::get_home_trash();
|
let home_trash = TrashDir::get_home_trash();
|
||||||
for info in home_trash.iter() {
|
for info in home_trash.iter().unwrap() {
|
||||||
let info = match info {
|
let info = match info {
|
||||||
Ok(info) => info,
|
Ok(info) => info,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -69,7 +79,9 @@ fn main() {
|
||||||
println!("{}\t{}", info.deletion_date, info.path.to_str().unwrap());
|
println!("{}\t{}", info.deletion_date, info.path.to_str().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Put { paths, recursive, .. } => {
|
Command::Put {
|
||||||
|
paths, recursive, ..
|
||||||
|
} => {
|
||||||
for path in paths {
|
for path in paths {
|
||||||
match crate::ops::put(path, recursive) {
|
match crate::ops::put(path, recursive) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
|
@ -81,6 +93,7 @@ fn main() {
|
||||||
let home_trash = TrashDir::get_home_trash();
|
let home_trash = TrashDir::get_home_trash();
|
||||||
let files = home_trash
|
let files = home_trash
|
||||||
.iter()
|
.iter()
|
||||||
|
.unwrap()
|
||||||
.filter_map(|entry| match entry {
|
.filter_map(|entry| match entry {
|
||||||
Ok(info) => Some(info),
|
Ok(info) => Some(info),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
35
src/ops.rs
35
src/ops.rs
|
@ -1,12 +1,44 @@
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use chrono::Local;
|
use chrono::{Duration, Local};
|
||||||
|
|
||||||
use crate::errors::Error;
|
use crate::errors::Error;
|
||||||
use crate::trashdir::TrashDir;
|
use crate::trashdir::TrashDir;
|
||||||
use crate::trashinfo::TrashInfo;
|
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> {
|
pub fn put(path: impl AsRef<Path>, recursive: bool) -> Result<(), Error> {
|
||||||
let path = path.as_ref().canonicalize()?;
|
let path = path.as_ref().canonicalize()?;
|
||||||
if path.is_dir() && !recursive {
|
if path.is_dir() && !recursive {
|
||||||
|
@ -31,6 +63,7 @@ pub fn put(path: impl AsRef<Path>, recursive: bool) -> Result<(), Error> {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
deletion_date: now,
|
deletion_date: now,
|
||||||
deleted_path: trash_file_path.clone(),
|
deleted_path: trash_file_path.clone(),
|
||||||
|
info_path: trash_info_path.clone(),
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
let trash_info_file = File::create(trash_info_path)?;
|
let trash_info_file = File::create(trash_info_path)?;
|
||||||
|
|
|
@ -31,22 +31,19 @@ impl TrashDir {
|
||||||
Ok(target)
|
Ok(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> TrashDirIter {
|
pub fn iter(&self) -> Result<TrashDirIter, Error> {
|
||||||
let iter = WalkDir::new(&self.0.join("info"))
|
let iter = WalkDir::new(&self.info_dir()?)
|
||||||
.contents_first(true)
|
.contents_first(true)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_entry(|entry| {
|
.filter_entry(|entry| match entry.path().extension() {
|
||||||
// warn!("path: {:?}", entry.path());
|
|
||||||
match entry.path().extension() {
|
|
||||||
Some(x) => x == "trashinfo",
|
Some(x) => x == "trashinfo",
|
||||||
_ => false,
|
_ => 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 {
|
impl Iterator for TrashDirIter {
|
||||||
type Item = Result<TrashInfo, Error>;
|
type Item = Result<TrashInfo, Error>;
|
||||||
|
@ -55,7 +52,7 @@ impl Iterator for TrashDirIter {
|
||||||
let entry = {
|
let entry = {
|
||||||
let mut entry;
|
let mut entry;
|
||||||
loop {
|
loop {
|
||||||
entry = match self.0.next() {
|
entry = match self.1.next() {
|
||||||
Some(Ok(entry)) => entry,
|
Some(Ok(entry)) => entry,
|
||||||
Some(Err(err)) => return Some(Err(Error::from(err))),
|
Some(Err(err)) => return Some(Err(Error::from(err))),
|
||||||
None => return None,
|
None => return None,
|
||||||
|
@ -68,6 +65,8 @@ impl Iterator for TrashDirIter {
|
||||||
entry
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct TrashInfo {
|
pub struct TrashInfo {
|
||||||
|
/// The original path where this file was located before it was deleted.
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub deletion_date: DateTime<Local>,
|
pub deletion_date: DateTime<Local>,
|
||||||
|
|
||||||
|
/// The location of the deleted file after deletion.
|
||||||
pub deleted_path: PathBuf,
|
pub deleted_path: PathBuf,
|
||||||
|
/// The location of the `info` description file.
|
||||||
|
pub info_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrashInfo {
|
impl TrashInfo {
|
||||||
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
|
pub fn from_files(
|
||||||
let path = path.as_ref();
|
info_path: impl AsRef<Path>,
|
||||||
let original_path = path.to_path_buf();
|
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 file = File::open(path)?;
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
@ -74,7 +83,8 @@ impl TrashInfo {
|
||||||
Ok(TrashInfo {
|
Ok(TrashInfo {
|
||||||
path,
|
path,
|
||||||
deletion_date,
|
deletion_date,
|
||||||
deleted_path: original_path,
|
deleted_path,
|
||||||
|
info_path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue