From e9f51a694c6bdd49493e230a7061609724d51987 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 9 Feb 2020 10:17:29 +1000 Subject: [PATCH] use our own trash folder instead of using the system trash the trash crate was invoking external commands on Macs and Linux which is slow and likely to fall over if a large number of files need to be deleted at once. --- rslib/Cargo.toml | 5 +---- rslib/src/media/files.rs | 42 ++++++++++++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/rslib/Cargo.toml b/rslib/Cargo.toml index 222105b58..b0443ae8f 100644 --- a/rslib/Cargo.toml +++ b/rslib/Cargo.toml @@ -27,8 +27,8 @@ env_logger = "0.7.1" zip = "0.5.4" log = "0.4.8" serde_tuple = "0.4.0" -trash = "1.0.0" coarsetime = "0.1.12" +utime = "0.2.1" [target.'cfg(target_vendor="apple")'.dependencies] rusqlite = { version = "0.21.0", features = ["trace"] } @@ -45,6 +45,3 @@ reqwest = { version = "0.10.1", features = ["json"] } [build-dependencies] prost-build = "0.5.0" -[dev-dependencies] -utime = "0.2.1" - diff --git a/rslib/src/media/files.rs b/rslib/src/media/files.rs index 74cdc5fe4..3223d4177 100644 --- a/rslib/src/media/files.rs +++ b/rslib/src/media/files.rs @@ -1,17 +1,17 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use crate::err::{AnkiError, Result}; +use crate::err::Result; use lazy_static::lazy_static; use log::debug; use regex::Regex; use sha1::Sha1; use std::borrow::Cow; use std::io::Read; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::{fs, io, time}; -use trash::remove_all; use unicode_normalization::{is_nfc, UnicodeNormalization}; +use utime; /// The maximum length we allow a filename to be. When combined /// with the rest of the path, the full path needs to be under ~240 chars @@ -282,12 +282,38 @@ where return Ok(()); } - let paths = files.iter().map(|f| media_folder.join(f.as_ref())); + let trash_folder = trash_folder(media_folder)?; - debug!("removing {:?}", files); - remove_all(paths).map_err(|e| AnkiError::IOError { - info: format!("removing files failed: {:?}", e), - }) + for file in files { + let src_path = media_folder.join(file.as_ref()); + let dst_path = trash_folder.join(file.as_ref()); + + // move file to trash, clobbering any existing file with the same name + fs::rename(&src_path, &dst_path)?; + + // mark it as modified, so we can expire it in the future + let secs = time::SystemTime::now() + .duration_since(time::UNIX_EPOCH) + .unwrap() + .as_secs(); + utime::set_file_times(&dst_path, secs, secs)?; + } + + Ok(()) +} + +fn trash_folder(media_folder: &Path) -> Result { + let trash_folder = media_folder.with_file_name("media.trash"); + match fs::create_dir(&trash_folder) { + Ok(()) => Ok(trash_folder), + Err(e) => { + if e.kind() == io::ErrorKind::AlreadyExists { + Ok(trash_folder) + } else { + Err(e.into()) + } + } + } } pub(super) struct AddedFile {