Skip to content

Commit

Permalink
fix: On **windows**, delete() will now delete recursively like on t…
Browse files Browse the repository at this point in the history
…he other platforms.

Note that the current implementation may consume a lot of memory as it will traverse the
entire directory structure once while storing each path for later trashing.
  • Loading branch information
Byron committed Jul 6, 2023
2 parents a2343c2 + 41edcdf commit c1feece
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 3 deletions.
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,7 @@ where
DEFAULT_TRASH_CTX.delete_all(paths)
}

///
/// Provides information about an error.
///
#[derive(Debug)]
pub enum Error {
Unknown {
Expand Down
29 changes: 28 additions & 1 deletion src/windows.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{Error, TrashContext, TrashItem};
use std::{
ffi::{c_void, OsStr, OsString},
fs,
os::windows::{ffi::OsStrExt, prelude::*},
path::PathBuf,
};
Expand Down Expand Up @@ -42,7 +43,7 @@ impl PlatformTrashContext {
}
impl TrashContext {
/// See https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-_shfileopstructa
pub(crate) fn delete_all_canonicalized(&self, full_paths: Vec<PathBuf>) -> Result<(), Error> {
pub(crate) fn delete_specified_canonicalized(&self, full_paths: Vec<PathBuf>) -> Result<(), Error> {
ensure_com_initialized();
unsafe {
let pfo: IFileOperation = CoCreateInstance(&FileOperation as *const _, None, CLSCTX_ALL).unwrap();
Expand All @@ -67,6 +68,13 @@ impl TrashContext {
Ok(())
}
}

/// Removes all files and folder paths recursively.
pub(crate) fn delete_all_canonicalized(&self, full_paths: Vec<PathBuf>) -> Result<(), Error> {
let mut collection = Vec::new();
traverse_paths_recursively(full_paths, &mut collection)?;
self.delete_specified_canonicalized(collection)
}
}

pub fn list() -> Result<Vec<TrashItem>, Error> {
Expand Down Expand Up @@ -256,3 +264,22 @@ thread_local! {
fn ensure_com_initialized() {
CO_INITIALIZER.with(|_| {});
}

fn traverse_paths_recursively(
paths: impl IntoIterator<Item = PathBuf>,
collection: &mut Vec<PathBuf>,
) -> Result<(), Error> {
for base_path in paths {
if base_path.is_file() {
collection.push(base_path);
continue;
}

for entry in fs::read_dir(&base_path).map_err(|err| Error::Unknown { description: err.to_string() })? {
let entry = entry.map_err(|err| Error::Unknown { description: err.to_string() })?;
traverse_paths_recursively(Some(entry.path()), collection)?;
}
collection.push(base_path);
}
Ok(())
}
14 changes: 14 additions & 0 deletions tests/trash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,17 @@ fn recursive_file_deletion() {
trash::delete(parent_dir).unwrap();
assert!(!parent_dir.exists());
}

#[test]
fn recursive_file_with_content_deletion() {
let parent_dir = Path::new("remove-me-content");
let dir1 = parent_dir.join("dir1");
let dir2 = parent_dir.join("dir2");
std::fs::create_dir_all(&dir1).unwrap();
std::fs::create_dir_all(&dir2).unwrap();
File::create(dir1.join("same-name")).unwrap();
std::fs::write(dir2.join("same-name"), b"some content").unwrap();

trash::delete(parent_dir).unwrap();
assert!(!parent_dir.exists());
}

0 comments on commit c1feece

Please sign in to comment.