From 5d44766a45664f949bc9ae7f44cd38f2e411d0be Mon Sep 17 00:00:00 2001 From: Josh Megnauth Date: Tue, 8 Oct 2024 01:30:04 -0400 Subject: [PATCH] feat: Short circuiting check for empty trash `is_empty()` is a short circuiting function that checks if the trash is empty on Freedesktop compatible systems and Windows. The main purpose of `is_empty()` is to avoid evaluating the entire trash context when the caller is only interested in whether the trash is empty or not. This is especially useful for full trashes with many items. --- src/freedesktop.rs | 26 ++++++++++++++++++++++++++ src/lib.rs | 18 ++++++++++++++++++ src/windows.rs | 15 +++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/src/freedesktop.rs b/src/freedesktop.rs index 9db12fa..625c8e4 100644 --- a/src/freedesktop.rs +++ b/src/freedesktop.rs @@ -197,6 +197,32 @@ pub fn list() -> Result, Error> { Ok(result) } +pub fn is_empty() -> Result { + let trash_folders = trash_folders()?; + + if trash_folders.is_empty() { + return Ok(true); + } + + for folder in trash_folders { + // We're only concerned if the trash contains any files + // Therefore, we only need to check if the bin itself is empty + let bin = folder.join("files"); + match bin.read_dir() { + Ok(mut entries) => { + if let Some(Ok(_)) = entries.next() { + return Ok(false); + } + } + Err(e) => { + warn!("The trash files folder {:?} could not be read. Error was {:?}", bin, e); + } + } + } + + Ok(true) +} + pub fn trash_folders() -> Result, Error> { let EvaluatedTrashFolders { trash_folders, home_error, .. } = eval_trash_folders()?; diff --git a/src/lib.rs b/src/lib.rs index cf624cc..0fd60b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -374,6 +374,24 @@ pub mod os_limited { platform::list() } + /// Returns whether the trash is empty or has at least one item. + /// + /// Unlike calling [`list`], this function short circuits without evaluating every item. + /// + /// # Example + /// + /// ``` + /// use trash::os_limited::is_empty; + /// if is_empty().unwrap_or(true) { + /// println!("Trash is empty"); + /// } else { + /// println!("Trash contains at least one item"); + /// } + /// ``` + pub fn is_empty() -> Result { + platform::is_empty() + } + /// Returns all valid trash bins on supported Unix platforms. /// /// Valid trash folders include the user's personal "home trash" as well as designated trash diff --git a/src/windows.rs b/src/windows.rs index 52aebaf..2d5e729 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -126,6 +126,21 @@ pub fn list() -> Result, Error> { } } +pub fn is_empty() -> Result { + ensure_com_initialized(); + unsafe { + let recycle_bin: IShellItem = + SHGetKnownFolderItem(&FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, Handle::default())?; + let pesi: IEnumShellItems = recycle_bin.BindToHandler(None, &BHID_EnumItems)?; + + let mut count = 0u32; + let mut items = [None]; + pesi.Next(&mut items, Some(&mut count as *mut u32))?; + + Ok(count == 0) + } +} + pub fn metadata(item: &TrashItem) -> Result { ensure_com_initialized(); let id_as_wide = to_wide_path(&item.id);