1#[cfg(feature = "native")]
8use std::ffi::OsString;
9use std::path::{Path, PathBuf};
10
11use async_trait::async_trait;
12
13#[derive(Debug, thiserror::Error)]
15pub enum TrashError {
16 #[error("{0}")]
17 Backend(String),
18 #[error("task join failed: {0}")]
19 Join(String),
20}
21
22pub struct TrashId(pub(crate) TrashIdInner);
26
27pub(crate) enum TrashIdInner {
28 #[cfg(feature = "native")]
30 System(OsString),
31 #[cfg(not(feature = "native"))]
33 _Unavailable,
34}
35
36impl TrashId {
37 #[cfg(feature = "native")]
39 pub(crate) fn system(id: OsString) -> Self {
40 Self(TrashIdInner::System(id))
41 }
42}
43
44impl std::fmt::Debug for TrashId {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match &self.0 {
47 #[cfg(feature = "native")]
48 TrashIdInner::System(_) => f.write_str("TrashId::System(..)"),
49 #[cfg(not(feature = "native"))]
50 TrashIdInner::_Unavailable => f.write_str("TrashId::Unavailable"),
51 }
52 }
53}
54
55#[derive(Debug)]
57pub struct TrashEntry {
58 pub id: TrashId,
59 pub name: String,
60 pub original_path: PathBuf,
61 pub deleted_at: i64,
63}
64
65pub fn find_restore_match<T>(items: Vec<(String, T)>, target: &str) -> Result<Vec<T>, String> {
70 let mut exact = Vec::new();
71 let mut substring = Vec::new();
72 let mut substring_names = Vec::new();
73
74 for (name, item) in items {
75 if name == target {
76 exact.push(item);
77 } else if name.contains(target) {
78 substring_names.push(name);
79 substring.push(item);
80 }
81 }
82
83 if exact.len() == 1 {
84 return Ok(exact);
85 }
86
87 let mut all_names: Vec<String> = Vec::new();
89 if !exact.is_empty() {
90 all_names.extend(std::iter::repeat_n(target.to_string(), exact.len()));
91 }
92 all_names.extend(substring_names);
93
94 let mut all: Vec<T> = exact;
95 all.extend(substring);
96
97 if all.is_empty() {
98 return Err(format!("'{}' not found in trash", target));
99 }
100 if all.len() > 1 {
101 return Err(format!(
102 "multiple matches for '{}': {}. Be more specific.",
103 target,
104 all_names.join(", ")
105 ));
106 }
107 Ok(all)
108}
109
110#[async_trait]
112pub trait TrashBackend: Send + Sync {
113 async fn trash(&self, path: &Path) -> Result<(), TrashError>;
115
116 async fn list(&self, filter: Option<&str>) -> Result<Vec<TrashEntry>, TrashError>;
118
119 async fn find_by_name(&self, name: &str) -> Result<Vec<TrashEntry>, TrashError>;
123
124 async fn restore(&self, entries: Vec<TrashEntry>) -> Result<(), TrashError>;
126
127 async fn purge_all(&self) -> Result<usize, TrashError>;
129}