diff --git a/src/tab.rs b/src/tab.rs index b3a0ede..33e3339 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -939,11 +939,24 @@ fn item_from_trash_child( metadata: fs::Metadata, sizes: IconSizes, ) -> Option { - let original_path = crate::trash::original_path_for_trash_child(&path)?; + let Some(original_path) = crate::trash::original_path_for_trash_child(&path) else { + log::warn!( + "failed to resolve original path for trash item {}, skipping entry", + path.display() + ); + return None; + }; + let Some(original_parent) = original_path.parent() else { + log::warn!( + "trash item {} has no original parent, skipping entry", + path.display() + ); + return None; + }; let entry = trash::TrashItem { id: path.as_os_str().to_os_string(), name: std::ffi::OsString::from(&name), - original_parent: original_path.parent()?.to_path_buf(), + original_parent: original_parent.to_path_buf(), time_deleted: 0, }; let size = if metadata.is_dir() { @@ -1033,6 +1046,7 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec { if !remote_scannable { match fs::read_dir(tab_path) { Ok(entries) => { + let trash = crate::trash::is_trash_path(tab_path); items = entries .filter_map(|entry_res| { let entry = entry_res @@ -1073,7 +1087,6 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec { }) .ok()?; - let trash = crate::trash::is_trash_path(tab_path); if trash { item_from_trash_child(path, name, metadata, sizes) } else { @@ -7003,7 +7016,8 @@ impl Tab { let path = path.clone(); // Acquire semaphore permit - _ = THUMB_SEMAPHORE.acquire().await; + let _permit = + THUMB_SEMAPHORE.acquire().await.unwrap(); tokio::task::spawn_blocking(move || { let start = Instant::now(); diff --git a/src/trash.rs b/src/trash.rs index d8bccca..bb9f99a 100644 --- a/src/trash.rs +++ b/src/trash.rs @@ -2,6 +2,7 @@ use cosmic::widget; use regex::Regex; use std::collections::HashSet; use std::path::{Path, PathBuf}; +use std::sync::LazyLock; use crate::config::IconSizes; use crate::tab::{Item, SearchItem}; @@ -89,7 +90,7 @@ pub fn trash_item_path(item: &trash::TrashItem) -> Option { /// - Read `~/.local/share/Trash/info/folder.trashinfo` to get the original path /// - Compute: `/sub/file.txt` pub fn original_path_for_trash_child(p: &Path) -> Option { - let files = p.ancestors().find(|a| a.ends_with("files"))?; + let files = trash_files_dir(p)?; let root = files.parent()?; let top = p.strip_prefix(files).ok()?.components().next()?; let info = @@ -103,9 +104,29 @@ pub fn original_path_for_trash_child(p: &Path) -> Option { Some(result) } +static TRASH_FOLDERS: LazyLock> = LazyLock::new(|| { + Trash::folders().unwrap_or_else(|e| { + log::warn!("failed to list trash folders: {}", e); + HashSet::new() + }) +}); + +fn is_trash_root(root: &Path) -> bool { + if TRASH_FOLDERS.is_empty() { + root.join("info").is_dir() + } else { + TRASH_FOLDERS.contains(root) + } +} + +fn trash_files_dir(path: &Path) -> Option<&Path> { + path.ancestors() + .find(|a| a.ends_with("files") && a.parent().is_some_and(is_trash_root)) +} + /// Check whether a path is inside any trash `files/` directory. pub fn is_trash_path(path: &Path) -> bool { - path.ancestors().any(|a| a.ends_with("files")) + trash_files_dir(path).is_some() } pub struct Trash;