Change the errors away from using anyhow and add --all to empty

This commit is contained in:
Michael Zhang 2020-11-03 21:57:37 -06:00
parent 5579e5b951
commit 2d0421f041
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
14 changed files with 109 additions and 76 deletions

7
Cargo.lock generated
View file

@ -9,12 +9,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "anyhow"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.6" version = "0.3.6"
@ -194,7 +188,6 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
name = "garbage" name = "garbage"
version = "0.2.2" version = "0.2.2"
dependencies = [ dependencies = [
"anyhow",
"chrono", "chrono",
"chrono-humanize", "chrono-humanize",
"lazy_static", "lazy_static",

View file

@ -11,7 +11,6 @@ name = "garbage"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
anyhow = "1.0"
chrono = "0.4" chrono = "0.4"
chrono-humanize = "0.1.1" chrono-humanize = "0.1.1"
lazy_static = "1.4" lazy_static = "1.4"

View file

@ -131,6 +131,7 @@ impl Iterator for TrashDirIter {
&name.as_bytes()[..name.len() - b".trashinfo".len()], &name.as_bytes()[..name.len() - b".trashinfo".len()],
)) ))
}; };
Some( Some(
TrashInfo::from_files(entry.path(), deleted_path) TrashInfo::from_files(entry.path(), deleted_path)
.map_err(Error::from), .map_err(Error::from),

View file

@ -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 /// All errors that could happen
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum Error { pub enum Error {
#[error("IO error: {0}")] #[error("IO error: {0}")]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
#[error("Walkdir error: {0}")] #[error("Walkdir error: {0}")]
WalkDir(#[from] walkdir::Error), 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}")] #[error("Date parsing error: {0}")]
ParseDate(#[from] chrono::format::ParseError), 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 /// Errors related to .trashinfo files
@ -18,8 +41,10 @@ pub enum Error {
pub enum TrashInfoError { pub enum TrashInfoError {
#[error("Missing [TrashInfo] header")] #[error("Missing [TrashInfo] header")]
MissingHeader, MissingHeader,
#[error("Missing path attribute")] #[error("Missing path attribute")]
MissingPath, MissingPath,
#[error("Missing date attribute")] #[error("Missing date attribute")]
MissingDate, MissingDate,
} }

View file

@ -64,6 +64,7 @@ impl TrashInfo {
if i == 0 { if i == 0 {
if line != "[Trash Info]" { if line != "[Trash Info]" {
return Err(Error::BadTrashInfo( return Err(Error::BadTrashInfo(
info_path,
TrashInfoError::MissingHeader, TrashInfoError::MissingHeader,
)); ));
} else { } else {
@ -94,14 +95,20 @@ impl TrashInfo {
let path = match path { let path = match path {
Some(path) => path, Some(path) => path,
None => { None => {
return Err(Error::BadTrashInfo(TrashInfoError::MissingPath)) return Err(Error::BadTrashInfo(
info_path,
TrashInfoError::MissingPath,
))
} }
}; };
let deletion_date = match deletion_date { let deletion_date = match deletion_date {
Some(deletion_date) => deletion_date, Some(deletion_date) => deletion_date,
None => { None => {
return Err(Error::BadTrashInfo(TrashInfoError::MissingDate)) return Err(Error::BadTrashInfo(
info_path,
TrashInfoError::MissingDate,
))
} }
}; };

View file

@ -7,8 +7,6 @@ extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate structopt; extern crate structopt;
#[macro_use] #[macro_use]
extern crate anyhow;
#[macro_use]
extern crate thiserror; extern crate thiserror;
#[macro_use] #[macro_use]
@ -26,7 +24,7 @@ use std::path::PathBuf;
use xdg::BaseDirectories; use xdg::BaseDirectories;
pub use crate::dir::TrashDir; pub use crate::dir::TrashDir;
pub use crate::errors::Error; pub use crate::errors::{Error, Result};
pub use crate::info::TrashInfo; pub use crate::info::TrashInfo;
use crate::mounts::Mounts; use crate::mounts::Mounts;

View file

@ -1,10 +1,8 @@
#![deny(warnings)] #![deny(warnings)]
extern crate anyhow; use garbage::{
ops::{self, EmptyOptions, ListOptions, PutOptions, RestoreOptions},
use anyhow::Result; Result,
use garbage::ops::{
self, EmptyOptions, ListOptions, PutOptions, RestoreOptions,
}; };
use structopt::StructOpt; use structopt::StructOpt;
@ -46,7 +44,7 @@ fn main() {
match run() { match run() {
Ok(_) => (), Ok(_) => (),
Err(err) => { Err(err) => {
eprintln!("Error: {:?}", err); eprintln!("Error: {}", err);
// for cause in err.chain() { // for cause in err.chain() {
// eprintln!("- {:?}", cause); // eprintln!("- {:?}", cause);
// } // }

View file

@ -4,9 +4,9 @@ use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use anyhow::Error;
use libmount::mountinfo::Parser; use libmount::mountinfo::Parser;
use crate::errors::Error;
use crate::utils; use crate::utils;
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,9 +1,10 @@
use std::env;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::Result;
use chrono::{Duration, Local}; use chrono::{Duration, Local};
use crate::errors::Result;
use crate::TrashDir; use crate::TrashDir;
/// Options to pass to empty /// 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) /// By default, this is your home directory's trash ($XDG_DATA_HOME/Trash)
#[structopt(long = "trash-dir", parse(from_os_str))] #[structopt(long = "trash-dir", parse(from_os_str))]
trash_dir: Option<PathBuf>, 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. /// Actually delete files in the trash.
pub fn empty(options: EmptyOptions) -> Result<()> { 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 // cutoff date
let cutoff = if let Some(days) = options.days { let cutoff = if let Some(days) = options.days {
@ -36,28 +42,32 @@ pub fn empty(options: EmptyOptions) -> Result<()> {
Local::now() Local::now()
}; };
for file in trash_dir.iter()? { let current_dir = env::current_dir()?;
let file = file?; trash_dir
.iter()?
.collect::<Result<Vec<_>>>()?
.into_iter()
// ignore files that were deleted after the cutoff (younger) // ignore files that were deleted after the cutoff (younger)
let ignore = file.deletion_date > cutoff; .filter(|info| info.deletion_date <= cutoff)
.filter(|info| options.all || info.path.starts_with(&current_dir))
if !ignore { .map(|info| -> Result<_> {
if options.dry { if options.dry {
println!("{:?}", file.path); println!("deleting {:?}", info.path);
} else { } else {
fs::remove_file(file.info_path)?; fs::remove_file(info.info_path)?;
if file.deleted_path.exists() { if info.deleted_path.exists() {
if file.deleted_path.is_dir() { if info.deleted_path.is_dir() {
fs::remove_dir_all(file.deleted_path)?; fs::remove_dir_all(info.deleted_path)?;
} else { } else {
fs::remove_file(file.deleted_path)?; fs::remove_file(info.deleted_path)?;
} }
} }
} }
}
} Ok(())
})
.collect::<Result<_>>()?;
Ok(()) Ok(())
} }

View file

@ -1,10 +1,9 @@
use std::path::PathBuf;
use std::env; use std::env;
use std::path::PathBuf;
use anyhow::Result; use crate::dir::TrashDir;
use crate::errors::Result;
use crate::list; use crate::list;
use crate::TrashDir;
/// Options to pass to list /// Options to pass to list
#[derive(StructOpt)] #[derive(StructOpt)]
@ -25,19 +24,16 @@ pub fn list(options: ListOptions) -> Result<()> {
let trash_dir = TrashDir::from_opt(options.trash_dir.as_ref()); let trash_dir = TrashDir::from_opt(options.trash_dir.as_ref());
let current_dir = env::current_dir()?; let current_dir = env::current_dir()?;
let mut files = trash_dir let mut files = trash_dir
.iter() .iter()?
.unwrap() .collect::<Result<Vec<_>>>()?
.filter_map(|entry| match entry { .into_iter()
Ok(info) => { .filter_map(|info| {
if !options.all && !info.path.starts_with(&current_dir) { if !options.all && !info.path.starts_with(&current_dir) {
return None;
}
Some(info)
}
Err(err) => {
eprintln!("failed to get file info: {:?}", err);
None None
} else {
Some(info)
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View file

@ -1,9 +1,9 @@
//! Operations that garbage can do. //! Operations that garbage can do.
mod empty; pub(crate) mod empty;
mod list; pub(crate) mod list;
mod put; pub(crate) mod put;
mod restore; pub(crate) mod restore;
pub use self::empty::{empty, EmptyOptions}; pub use self::empty::{empty, EmptyOptions};
pub use self::list::{list, ListOptions}; pub use self::list::{list, ListOptions};

View file

@ -4,15 +4,15 @@ use std::io::{self, Write};
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use anyhow::Result;
use chrono::Local; use chrono::Local;
use crate::errors::{Error, Result};
use crate::utils::{self}; use crate::utils::{self};
use crate::{TrashDir, TrashInfo}; use crate::{TrashDir, TrashInfo};
use crate::{HOME_MOUNT, MOUNTS}; use crate::{HOME_MOUNT, MOUNTS};
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum Error { pub enum PutError {
// #[error("Refusing to remove directory {0} without '-r' option")] // #[error("Refusing to remove directory {0} without '-r' option")]
// MissingRecursiveOption(PathBuf), // MissingRecursiveOption(PathBuf),
#[error("Refusing to remove '.' or '..', skipping...")] #[error("Refusing to remove '.' or '..', skipping...")]
@ -23,6 +23,9 @@ pub enum Error {
#[error("Invalid filename.")] #[error("Invalid filename.")]
InvalidFilename, InvalidFilename,
#[error("Couldn't find mount point.")]
CouldntFindMountPoint,
} }
/// Options to pass to put /// Options to pass to put
@ -65,13 +68,14 @@ pub fn put(options: PutOptions) -> Result<()> {
for path in options.paths.iter() { for path in options.paths.iter() {
// don't allow deleting '.' or '..' // don't allow deleting '.' or '..'
let current_dir = env::current_dir()?; let current_dir = env::current_dir()?;
ensure!(
!(utils::into_absolute(&path)? == current_dir.as_path() if !(utils::into_absolute(&path)? == current_dir.as_path()
|| (current_dir.parent().is_some() || (current_dir.parent().is_some()
&& utils::into_absolute(&path)? && utils::into_absolute(&path)?
== current_dir.parent().unwrap())), == current_dir.parent().unwrap()))
Error::CannotTrashDotDirs {
); return Err(Error::Put(PutError::CannotTrashDotDirs));
}
// pick the best strategy for deleting this particular file // pick the best strategy for deleting this particular file
let strategy = if let Some(ref trash_dir) = options.trash_dir { let strategy = if let Some(ref trash_dir) = options.trash_dir {
@ -112,7 +116,7 @@ impl DeletionStrategy {
let target = target.as_ref(); let target = target.as_ref();
let target_mount = MOUNTS let target_mount = MOUNTS
.get_mount_point(target) .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? // first, are we on the home mount?
if target_mount == *HOME_MOUNT { if target_mount == *HOME_MOUNT {
@ -193,7 +197,7 @@ impl DeletionStrategy {
} }
}; };
if !should_continue { 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 elapsed_str = elapsed.to_string();
let target_file = match target.file_name() { let target_file = match target.file_name() {
Some(file) => file.to_os_string(), 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()]; // let buf = vec![0; elapsed_str.len() + 1 + target_file.len()];
// unimplemented!() // unimplemented!()
@ -220,7 +224,7 @@ impl DeletionStrategy {
let trash_file_path = trash_dir.files_dir()?.join(&file_name); let trash_file_path = trash_dir.files_dir()?.join(&file_name);
let trash_info_path = trash_dir let trash_info_path = trash_dir
.info_dir()? .info_dir()?
.join(crate::concat_os_str!(file_name, ".trashinfo")); .join(concat_os_str!(file_name, ".trashinfo"));
let trash_info = TrashInfo { let trash_info = TrashInfo {
path: utils::into_absolute(target)?, path: utils::into_absolute(target)?,

View file

@ -3,8 +3,7 @@ use std::fs;
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::Result; use crate::errors::{Error, Result};
use crate::list; use crate::list;
use crate::TrashDir; use crate::TrashDir;
@ -27,7 +26,7 @@ pub fn restore(options: RestoreOptions) -> Result<()> {
let trash_dir = TrashDir::from_opt(options.trash_dir.as_ref()); let trash_dir = TrashDir::from_opt(options.trash_dir.as_ref());
if trash_dir.check_info_dir()?.is_none() { 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 // get list of files sorted by deletion date
@ -55,7 +54,9 @@ pub fn restore(options: RestoreOptions) -> Result<()> {
}; };
if files.len() == 0 { 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); list::print_files_list(files.iter(), true);

View file

@ -4,10 +4,11 @@ use std::fs;
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR}; use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
use anyhow::Result;
use percent_encoding::AsciiSet; use percent_encoding::AsciiSet;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::errors::Result;
const MASK: &AsciiSet = percent_encoding::CONTROLS; const MASK: &AsciiSet = percent_encoding::CONTROLS;
pub fn into_absolute(path: impl AsRef<Path>) -> Result<PathBuf> { pub fn into_absolute(path: impl AsRef<Path>) -> Result<PathBuf> {