1use cosmic::widget;
2use regex::Regex;
3use std::collections::HashSet;
4use std::path::PathBuf;
5
6use crate::config::IconSizes;
7use crate::tab::{Item, SearchItem};
8
9pub trait TrashExt {
10 fn is_empty() -> bool {
11 true
12 }
13
14 fn entries() -> usize {
15 0
16 }
17
18 fn folders() -> Result<HashSet<PathBuf>, trash::Error> {
19 Err(trash::Error::Unknown {
20 description: "reading trash folders not supported on this platform".into(),
21 })
22 }
23
24 fn scan(_sizes: IconSizes) -> Vec<Item> {
25 log::warn!("viewing trash not supported on this platform");
26 Vec::new()
27 }
28
29 fn scan_search<F: Fn(SearchItem) -> bool + Sync>(_callback: F, _regex: &Regex) {}
30
31 fn icon(icon_size: u16) -> widget::icon::Handle {
32 widget::icon::from_name(if Self::is_empty() {
33 "user-trash"
34 } else {
35 "user-trash-full"
36 })
37 .size(icon_size)
38 .handle()
39 }
40
41 fn icon_symbolic(icon_size: u16) -> widget::icon::Handle {
42 widget::icon::from_name(if Self::is_empty() {
43 "user-trash-symbolic"
44 } else {
45 "user-trash-full-symbolic"
46 })
47 .size(icon_size)
48 .handle()
49 }
50}
51
52pub struct Trash;
53
54#[cfg(any(
56 target_os = "windows",
57 all(
58 unix,
59 not(target_os = "macos"),
60 not(target_os = "ios"),
61 not(target_os = "android")
62 )
63))]
64impl TrashExt for Trash {
65 fn is_empty() -> bool {
66 trash::os_limited::is_empty().unwrap_or(true)
67 }
68
69 fn entries() -> usize {
70 match trash::os_limited::list() {
71 Ok(entries) => entries.len(),
72 Err(_err) => 0,
73 }
74 }
75
76 #[cfg(not(target_os = "windows"))]
78 fn folders() -> Result<HashSet<PathBuf>, trash::Error> {
79 trash::os_limited::trash_folders()
80 }
81
82 fn scan(sizes: IconSizes) -> Vec<Item> {
83 use crate::localize::LANGUAGE_SORTER;
84 use crate::tab::item_from_trash_entry;
85 use std::cmp::Ordering;
86
87 let entries = match trash::os_limited::list() {
88 Ok(entry) => entry,
89 Err(err) => {
90 log::warn!("failed to read trash items: {err}");
91 return Vec::new();
92 }
93 };
94 let mut items: Vec<_> = entries
95 .into_iter()
96 .filter_map(|entry| {
97 let metadata = trash::os_limited::metadata(&entry)
98 .inspect_err(|err| {
99 log::warn!("failed to get metadata for trash item {entry:?}: {err}")
100 })
101 .ok()?;
102 Some(item_from_trash_entry(entry, metadata, sizes))
103 })
104 .collect();
105 items.sort_by(|a, b| match (a.metadata.is_dir(), b.metadata.is_dir()) {
106 (true, false) => Ordering::Less,
107 (false, true) => Ordering::Greater,
108 _ => LANGUAGE_SORTER.compare(&a.display_name, &b.display_name),
109 });
110 items
111 }
112
113 fn scan_search<F: Fn(SearchItem) -> bool + Sync>(callback: F, regex: &Regex) {
114 let entries = match trash::os_limited::list() {
115 Ok(entries) => entries,
116 Err(err) => {
117 log::warn!("failed to read trash items: {err}");
118 return;
119 }
120 };
121
122 for entry in entries {
123 if let Ok(metadata) = trash::os_limited::metadata(&entry).inspect_err(|err| {
124 log::warn!("failed to get metadata for trash item {entry:?}: {err}")
125 }) {
126 let name = entry.name.to_string_lossy();
127 if regex.is_match(&name) && !callback(SearchItem::Trash(entry, metadata)) {
128 break;
129 }
130 }
131 }
132 }
133}
134
135#[cfg(not(any(
137 target_os = "windows",
138 all(
139 unix,
140 not(target_os = "macos"),
141 not(target_os = "ios"),
142 not(target_os = "android")
143 )
144)))]
145impl TrashExt for Trash {}