summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiayi Zhao <jeff.no.zhao@gmail.com>2020-12-26 21:45:46 -0500
committerJiayi Zhao <jeff.no.zhao@gmail.com>2020-12-26 21:45:46 -0500
commit2b2672123d31bdcfd579916fa3fdc6cd961008a3 (patch)
tree3537f86ff7e4eb7b0851db7974138edc3e105556
parent883a20d31a05ca0b3eac09ee1952904ec0b2131a (diff)
add support for trashing files instead of permanently deleting
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml1
-rw-r--r--config/joshuto.toml1
-rw-r--r--src/commands/delete_files.rs44
-rw-r--r--src/config/config.rs5
5 files changed, 56 insertions, 5 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 16703cf..90d7f54 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -195,6 +195,7 @@ dependencies = [
"structopt",
"termion",
"toml",
+ "trash",
"tui",
"unicode-width",
"users",
@@ -592,6 +593,15 @@ dependencies = [
]
[[package]]
+name = "trash"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbf511f5673142be74cd6a46cfb7e3da8d2b95bc08d2d46c072798e6ce8b5b9f"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
name = "tui"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index abfcad1..12e12c2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,7 @@ serde_derive = "^1"
shell-words = "^1"
structopt = "^0"
termion = "^1"
+trash = "^1"
toml = "^0"
tui = "^0"
unicode-width = "^0"
diff --git a/config/joshuto.toml b/config/joshuto.toml
index b47d547..c44ff26 100644
--- a/config/joshuto.toml
+++ b/config/joshuto.toml
@@ -8,6 +8,7 @@ scroll_offset = 6
show_preview = true
xdg_open = false
+use_trash = true
max_preview_size = 2097152 # 2MB
# lexical, mtime, natural
diff --git a/src/commands/delete_files.rs b/src/commands/delete_files.rs
index ed73e18..443875e 100644
--- a/src/commands/delete_files.rs
+++ b/src/commands/delete_files.rs
@@ -11,9 +11,26 @@ use crate::util::load_child::LoadChild;
use super::reload;
-pub fn remove_files<'a, I>(paths: I) -> std::io::Result<()>
+fn trash_error_to_io_error(err: trash::Error) -> std::io::Error {
+ match err {
+ trash::Error::Unknown => std::io::Error::new(std::io::ErrorKind::Other, "Unknown Error"),
+ trash::Error::TargetedRoot => {
+ std::io::Error::new(std::io::ErrorKind::Other, "Targeted Root")
+ }
+ trash::Error::CanonicalizePath { code: _ } => {
+ std::io::Error::new(std::io::ErrorKind::NotFound, "Not found")
+ }
+ trash::Error::Remove { code: Some(1) } => std::io::Error::new(
+ std::io::ErrorKind::InvalidData,
+ "Cannot move files to trash from mounted system",
+ ),
+ _ => std::io::Error::new(std::io::ErrorKind::Other, "Unknown Error"),
+ }
+}
+
+pub fn remove_files<P>(paths: &[P]) -> std::io::Result<()>
where
- I: Iterator<Item = &'a path::Path>,
+ P: AsRef<path::Path>,
{
for path in paths {
if let Ok(metadata) = fs::symlink_metadata(path) {
@@ -27,6 +44,18 @@ where
Ok(())
}
+pub fn trash_files<P>(paths: &[P]) -> std::io::Result<()>
+where
+ P: AsRef<path::Path>,
+{
+ for path in paths {
+ if let Err(e) = trash::delete(path) {
+ return Err(trash_error_to_io_error(e));
+ }
+ }
+ Ok(())
+}
+
fn delete_files(context: &mut JoshutoContext, backend: &mut TuiBackend) -> std::io::Result<()> {
let tab_index = context.tab_context_ref().get_index();
let paths = match context.tab_context_ref().curr_tab_ref().curr_list_ref() {
@@ -34,7 +63,6 @@ fn delete_files(context: &mut JoshutoContext, backend: &mut TuiBackend) -> std::
None => vec![],
};
let paths_len = paths.len();
-
if paths_len == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
@@ -42,6 +70,12 @@ fn delete_files(context: &mut JoshutoContext, backend: &mut TuiBackend) -> std::
));
}
+ let delete_func = if context.config_ref().use_trash {
+ trash_files
+ } else {
+ remove_files
+ };
+
let ch = {
let prompt_str = format!("Delete {} files? (Y/n)", paths_len);
let mut prompt = TuiPrompt::new(&prompt_str);
@@ -56,13 +90,13 @@ fn delete_files(context: &mut JoshutoContext, backend: &mut TuiBackend) -> std::
prompt.get_key(backend, context)
};
if ch == Key::Char('y') {
- remove_files(paths.iter().map(|p| p.as_path()))?;
+ delete_func(&paths)?;
reload::reload(context, tab_index)?;
let msg = format!("Deleted {} files", paths_len);
context.push_msg(msg);
}
} else {
- remove_files(paths.iter().map(|p| p.as_path()))?;
+ delete_func(&paths)?;
reload::reload(context, tab_index)?;
let msg = format!("Deleted {} files", paths_len);
context.push_msg(msg);
diff --git a/src/config/config.rs b/src/config/config.rs
index c4a3fcc..0604123 100644
--- a/src/config/config.rs
+++ b/src/config/config.rs
@@ -69,6 +69,8 @@ pub struct JoshutoRawConfig {
collapse_preview: bool,
#[serde(default)]
xdg_open: bool,
+ #[serde(default = "default_true")]
+ use_trash: bool,
#[serde(default = "default_max_preview_size")]
max_preview_size: u64,
column_ratio: Option<[usize; 3]>,
@@ -99,6 +101,7 @@ impl Flattenable<JoshutoConfig> for JoshutoRawConfig {
show_preview: self.show_preview,
collapse_preview: self.collapse_preview,
xdg_open: self.xdg_open,
+ use_trash: self.use_trash,
max_preview_size: self.max_preview_size,
column_ratio,
sort_option,
@@ -113,6 +116,7 @@ pub struct JoshutoConfig {
pub show_preview: bool,
pub collapse_preview: bool,
pub xdg_open: bool,
+ pub use_trash: bool,
pub max_preview_size: u64,
pub sort_option: sort::SortOption,
pub column_ratio: (usize, usize, usize),
@@ -135,6 +139,7 @@ impl std::default::Default for JoshutoConfig {
show_preview: true,
collapse_preview: true,
xdg_open: false,
+ use_trash: true,
max_preview_size: default_max_preview_size(),
sort_option,
column_ratio: default_column_ratio(),