Change the errors away from using anyhow and add --all to empty
This commit is contained in:
parent
5579e5b951
commit
2d0421f041
14 changed files with 109 additions and 76 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -9,12 +9,6 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
|
@ -194,7 +188,6 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
|||
name = "garbage"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"chrono-humanize",
|
||||
"lazy_static",
|
||||
|
|
|
@ -11,7 +11,6 @@ name = "garbage"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
chrono = "0.4"
|
||||
chrono-humanize = "0.1.1"
|
||||
lazy_static = "1.4"
|
||||
|
|
|
@ -131,6 +131,7 @@ impl Iterator for TrashDirIter {
|
|||
&name.as_bytes()[..name.len() - b".trashinfo".len()],
|
||||
))
|
||||
};
|
||||
|
||||
Some(
|
||||
TrashInfo::from_files(entry.path(), deleted_path)
|
||||
.map_err(Error::from),
|
||||
|
|
|
@ -1,15 +1,38 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
/// Result convenience type for the Error type
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
/// All errors that could happen
|
||||
#[derive(Debug, Error)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Error {
|
||||
#[error("IO error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Walkdir error: {0}")]
|
||||
WalkDir(#[from] walkdir::Error),
|
||||
#[error("Bad .trashinfo file: {0}")]
|
||||
BadTrashInfo(#[from] TrashInfoError),
|
||||
|
||||
#[error("Mount error: {0}")]
|
||||
Mount(#[from] libmount::mountinfo::ParseError),
|
||||
|
||||
#[error("Strip prefix error: {0}")]
|
||||
StripPrefix(#[from] std::path::StripPrefixError),
|
||||
|
||||
#[error("Bad .trashinfo file at {0:?}: {1}")]
|
||||
BadTrashInfo(PathBuf, TrashInfoError),
|
||||
|
||||
#[error("Date parsing error: {0}")]
|
||||
ParseDate(#[from] chrono::format::ParseError),
|
||||
|
||||
#[error("No trash directory exists at: {0:?}")]
|
||||
TrashDirDoesntExist(PathBuf),
|
||||
|
||||
#[error("No files in the trash directory: {0:?}")]
|
||||
NoFilesInThisDirectory(PathBuf),
|
||||
|
||||
#[error("Put error: {0}")]
|
||||
Put(#[from] crate::ops::put::PutError),
|
||||
}
|
||||
|
||||
/// Errors related to .trashinfo files
|
||||
|
@ -18,8 +41,10 @@ pub enum Error {
|
|||
pub enum TrashInfoError {
|
||||
#[error("Missing [TrashInfo] header")]
|
||||
MissingHeader,
|
||||
|
||||
#[error("Missing path attribute")]
|
||||
MissingPath,
|
||||
|
||||
#[error("Missing date attribute")]
|
||||
MissingDate,
|
||||
}
|
||||
|
|
11
src/info.rs
11
src/info.rs
|
@ -64,6 +64,7 @@ impl TrashInfo {
|
|||
if i == 0 {
|
||||
if line != "[Trash Info]" {
|
||||
return Err(Error::BadTrashInfo(
|
||||
info_path,
|
||||
TrashInfoError::MissingHeader,
|
||||
));
|
||||
} else {
|
||||
|
@ -94,14 +95,20 @@ impl TrashInfo {
|
|||
let path = match path {
|
||||
Some(path) => path,
|
||||
None => {
|
||||
return Err(Error::BadTrashInfo(TrashInfoError::MissingPath))
|
||||
return Err(Error::BadTrashInfo(
|
||||
info_path,
|
||||
TrashInfoError::MissingPath,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let deletion_date = match deletion_date {
|
||||
Some(deletion_date) => deletion_date,
|
||||
None => {
|
||||
return Err(Error::BadTrashInfo(TrashInfoError::MissingDate))
|
||||
return Err(Error::BadTrashInfo(
|
||||
info_path,
|
||||
TrashInfoError::MissingDate,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@ extern crate lazy_static;
|
|||
#[macro_use]
|
||||
extern crate structopt;
|
||||
#[macro_use]
|
||||
extern crate anyhow;
|
||||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
|
||||
#[macro_use]
|
||||
|
@ -26,7 +24,7 @@ use std::path::PathBuf;
|
|||
use xdg::BaseDirectories;
|
||||
|
||||
pub use crate::dir::TrashDir;
|
||||
pub use crate::errors::Error;
|
||||
pub use crate::errors::{Error, Result};
|
||||
pub use crate::info::TrashInfo;
|
||||
use crate::mounts::Mounts;
|
||||
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -1,10 +1,8 @@
|
|||
#![deny(warnings)]
|
||||
|
||||
extern crate anyhow;
|
||||
|
||||
use anyhow::Result;
|
||||
use garbage::ops::{
|
||||
self, EmptyOptions, ListOptions, PutOptions, RestoreOptions,
|
||||
use garbage::{
|
||||
ops::{self, EmptyOptions, ListOptions, PutOptions, RestoreOptions},
|
||||
Result,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
@ -46,7 +44,7 @@ fn main() {
|
|||
match run() {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
eprintln!("Error: {:?}", err);
|
||||
eprintln!("Error: {}", err);
|
||||
// for cause in err.chain() {
|
||||
// eprintln!("- {:?}", cause);
|
||||
// }
|
||||
|
|
|
@ -4,9 +4,9 @@ use std::fs::File;
|
|||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Error;
|
||||
use libmount::mountinfo::Parser;
|
||||
|
||||
use crate::errors::Error;
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::{Duration, Local};
|
||||
|
||||
use crate::errors::Result;
|
||||
use crate::TrashDir;
|
||||
|
||||
/// Options to pass to empty
|
||||
|
@ -23,11 +24,16 @@ pub struct EmptyOptions {
|
|||
/// By default, this is your home directory's trash ($XDG_DATA_HOME/Trash)
|
||||
#[structopt(long = "trash-dir", parse(from_os_str))]
|
||||
trash_dir: Option<PathBuf>,
|
||||
|
||||
/// Delete all files in the trash (by default, only files in the current
|
||||
/// directory are listed)
|
||||
#[structopt(short = "a", long = "all")]
|
||||
all: bool,
|
||||
}
|
||||
|
||||
/// Actually delete files in the trash.
|
||||
pub fn empty(options: EmptyOptions) -> Result<()> {
|
||||
let trash_dir = TrashDir::from_opt(options.trash_dir);
|
||||
let trash_dir = TrashDir::from_opt(options.trash_dir.as_ref());
|
||||
|
||||
// cutoff date
|
||||
let cutoff = if let Some(days) = options.days {
|
||||
|
@ -36,28 +42,32 @@ pub fn empty(options: EmptyOptions) -> Result<()> {
|
|||
Local::now()
|
||||
};
|
||||
|
||||
for file in trash_dir.iter()? {
|
||||
let file = file?;
|
||||
|
||||
let current_dir = env::current_dir()?;
|
||||
trash_dir
|
||||
.iter()?
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.into_iter()
|
||||
// ignore files that were deleted after the cutoff (younger)
|
||||
let ignore = file.deletion_date > cutoff;
|
||||
|
||||
if !ignore {
|
||||
.filter(|info| info.deletion_date <= cutoff)
|
||||
.filter(|info| options.all || info.path.starts_with(¤t_dir))
|
||||
.map(|info| -> Result<_> {
|
||||
if options.dry {
|
||||
println!("{:?}", file.path);
|
||||
println!("deleting {:?}", info.path);
|
||||
} else {
|
||||
fs::remove_file(file.info_path)?;
|
||||
fs::remove_file(info.info_path)?;
|
||||
|
||||
if file.deleted_path.exists() {
|
||||
if file.deleted_path.is_dir() {
|
||||
fs::remove_dir_all(file.deleted_path)?;
|
||||
if info.deleted_path.exists() {
|
||||
if info.deleted_path.is_dir() {
|
||||
fs::remove_dir_all(info.deleted_path)?;
|
||||
} else {
|
||||
fs::remove_file(file.deleted_path)?;
|
||||
fs::remove_file(info.deleted_path)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use std::path::PathBuf;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::dir::TrashDir;
|
||||
use crate::errors::Result;
|
||||
use crate::list;
|
||||
use crate::TrashDir;
|
||||
|
||||
/// Options to pass to list
|
||||
#[derive(StructOpt)]
|
||||
|
@ -25,19 +24,16 @@ pub fn list(options: ListOptions) -> Result<()> {
|
|||
let trash_dir = TrashDir::from_opt(options.trash_dir.as_ref());
|
||||
|
||||
let current_dir = env::current_dir()?;
|
||||
|
||||
let mut files = trash_dir
|
||||
.iter()
|
||||
.unwrap()
|
||||
.filter_map(|entry| match entry {
|
||||
Ok(info) => {
|
||||
if !options.all && !info.path.starts_with(¤t_dir) {
|
||||
return None;
|
||||
}
|
||||
Some(info)
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("failed to get file info: {:?}", err);
|
||||
.iter()?
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
.into_iter()
|
||||
.filter_map(|info| {
|
||||
if !options.all && !info.path.starts_with(¤t_dir) {
|
||||
None
|
||||
} else {
|
||||
Some(info)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
//! Operations that garbage can do.
|
||||
|
||||
mod empty;
|
||||
mod list;
|
||||
mod put;
|
||||
mod restore;
|
||||
pub(crate) mod empty;
|
||||
pub(crate) mod list;
|
||||
pub(crate) mod put;
|
||||
pub(crate) mod restore;
|
||||
|
||||
pub use self::empty::{empty, EmptyOptions};
|
||||
pub use self::list::{list, ListOptions};
|
||||
|
|
|
@ -4,15 +4,15 @@ use std::io::{self, Write};
|
|||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::Local;
|
||||
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::utils::{self};
|
||||
use crate::{TrashDir, TrashInfo};
|
||||
use crate::{HOME_MOUNT, MOUNTS};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
pub enum PutError {
|
||||
// #[error("Refusing to remove directory {0} without '-r' option")]
|
||||
// MissingRecursiveOption(PathBuf),
|
||||
#[error("Refusing to remove '.' or '..', skipping...")]
|
||||
|
@ -23,6 +23,9 @@ pub enum Error {
|
|||
|
||||
#[error("Invalid filename.")]
|
||||
InvalidFilename,
|
||||
|
||||
#[error("Couldn't find mount point.")]
|
||||
CouldntFindMountPoint,
|
||||
}
|
||||
|
||||
/// Options to pass to put
|
||||
|
@ -65,13 +68,14 @@ pub fn put(options: PutOptions) -> Result<()> {
|
|||
for path in options.paths.iter() {
|
||||
// don't allow deleting '.' or '..'
|
||||
let current_dir = env::current_dir()?;
|
||||
ensure!(
|
||||
!(utils::into_absolute(&path)? == current_dir.as_path()
|
||||
|| (current_dir.parent().is_some()
|
||||
&& utils::into_absolute(&path)?
|
||||
== current_dir.parent().unwrap())),
|
||||
Error::CannotTrashDotDirs
|
||||
);
|
||||
|
||||
if !(utils::into_absolute(&path)? == current_dir.as_path()
|
||||
|| (current_dir.parent().is_some()
|
||||
&& utils::into_absolute(&path)?
|
||||
== current_dir.parent().unwrap()))
|
||||
{
|
||||
return Err(Error::Put(PutError::CannotTrashDotDirs));
|
||||
}
|
||||
|
||||
// pick the best strategy for deleting this particular file
|
||||
let strategy = if let Some(ref trash_dir) = options.trash_dir {
|
||||
|
@ -112,7 +116,7 @@ impl DeletionStrategy {
|
|||
let target = target.as_ref();
|
||||
let target_mount = MOUNTS
|
||||
.get_mount_point(target)
|
||||
.ok_or_else(|| anyhow!("couldn't get mount point"))?;
|
||||
.ok_or_else(|| PutError::CouldntFindMountPoint)?;
|
||||
|
||||
// first, are we on the home mount?
|
||||
if target_mount == *HOME_MOUNT {
|
||||
|
@ -193,7 +197,7 @@ impl DeletionStrategy {
|
|||
}
|
||||
};
|
||||
if !should_continue {
|
||||
bail!(Error::CancelledByUser);
|
||||
return Err(Error::Put(PutError::CancelledByUser));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,9 +208,9 @@ impl DeletionStrategy {
|
|||
let elapsed_str = elapsed.to_string();
|
||||
let target_file = match target.file_name() {
|
||||
Some(file) => file.to_os_string(),
|
||||
None => bail!(Error::InvalidFilename),
|
||||
None => return Err(Error::Put(PutError::InvalidFilename)),
|
||||
};
|
||||
let file_name = crate::concat_os_str!(elapsed_str, ".", target_file);
|
||||
let file_name = concat_os_str!(elapsed_str, ".", target_file);
|
||||
// {
|
||||
// let buf = vec![0; elapsed_str.len() + 1 + target_file.len()];
|
||||
// unimplemented!()
|
||||
|
@ -220,7 +224,7 @@ impl DeletionStrategy {
|
|||
let trash_file_path = trash_dir.files_dir()?.join(&file_name);
|
||||
let trash_info_path = trash_dir
|
||||
.info_dir()?
|
||||
.join(crate::concat_os_str!(file_name, ".trashinfo"));
|
||||
.join(concat_os_str!(file_name, ".trashinfo"));
|
||||
|
||||
let trash_info = TrashInfo {
|
||||
path: utils::into_absolute(target)?,
|
||||
|
|
|
@ -3,8 +3,7 @@ use std::fs;
|
|||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::list;
|
||||
use crate::TrashDir;
|
||||
|
||||
|
@ -27,7 +26,7 @@ pub fn restore(options: RestoreOptions) -> Result<()> {
|
|||
let trash_dir = TrashDir::from_opt(options.trash_dir.as_ref());
|
||||
|
||||
if trash_dir.check_info_dir()?.is_none() {
|
||||
bail!("There's no trash directory here.");
|
||||
return Err(Error::TrashDirDoesntExist(trash_dir.path().to_path_buf()));
|
||||
}
|
||||
|
||||
// get list of files sorted by deletion date
|
||||
|
@ -55,7 +54,9 @@ pub fn restore(options: RestoreOptions) -> Result<()> {
|
|||
};
|
||||
|
||||
if files.len() == 0 {
|
||||
bail!("No files in this trash directory.");
|
||||
return Err(Error::NoFilesInThisDirectory(
|
||||
trash_dir.path().to_path_buf(),
|
||||
));
|
||||
}
|
||||
|
||||
list::print_files_list(files.iter(), true);
|
||||
|
|
|
@ -4,10 +4,11 @@ use std::fs;
|
|||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
|
||||
|
||||
use anyhow::Result;
|
||||
use percent_encoding::AsciiSet;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::errors::Result;
|
||||
|
||||
const MASK: &AsciiSet = percent_encoding::CONTROLS;
|
||||
|
||||
pub fn into_absolute(path: impl AsRef<Path>) -> Result<PathBuf> {
|
||||
|
|
Loading…
Reference in a new issue