Skip to main content

kaish_kernel/
trash_system.rs

1//! System trash backend wrapping the `trash` crate.
2//!
3//! This is the default implementation — identical to the previous inline behavior,
4//! just behind the `TrashBackend` trait.
5
6use std::path::Path;
7
8use async_trait::async_trait;
9
10use crate::trash::{TrashBackend, TrashEntry, TrashError, TrashId, find_restore_match};
11
12/// System trash backend using the freedesktop.org / platform trash via the `trash` crate.
13pub struct SystemTrash;
14
15impl SystemTrash {
16    /// Convert a `trash::TrashItem` to our `TrashEntry`.
17    fn to_entry(item: &trash::TrashItem) -> TrashEntry {
18        TrashEntry {
19            id: TrashId::system(item.id.clone()),
20            name: item.name.to_string_lossy().to_string(),
21            original_path: item.original_parent.join(&item.name),
22            deleted_at: item.time_deleted,
23        }
24    }
25}
26
27/// Run a blocking trash operation, flattening JoinError/trash::Error.
28async fn spawn_trash<F, T>(op: F) -> Result<T, TrashError>
29where
30    F: FnOnce() -> Result<T, trash::Error> + Send + 'static,
31    T: Send + 'static,
32{
33    match tokio::task::spawn_blocking(op).await {
34        Ok(Ok(v)) => Ok(v),
35        Ok(Err(e)) => Err(TrashError::Backend(e.to_string())),
36        Err(e) => Err(TrashError::Join(e.to_string())),
37    }
38}
39
40#[async_trait]
41impl TrashBackend for SystemTrash {
42    async fn trash(&self, path: &Path) -> Result<(), TrashError> {
43        let p = path.to_owned();
44        spawn_trash(move || trash::delete(&p)).await
45    }
46
47    async fn list(&self, filter: Option<&str>) -> Result<Vec<TrashEntry>, TrashError> {
48        let items = spawn_trash(trash::os_limited::list).await?;
49        let filter_owned = filter.map(|s| s.to_owned());
50
51        let entries: Vec<TrashEntry> = items
52            .iter()
53            .filter(|item| {
54                if let Some(ref f) = filter_owned {
55                    item.name.to_string_lossy().contains(f.as_str())
56                } else {
57                    true
58                }
59            })
60            .map(Self::to_entry)
61            .collect();
62
63        Ok(entries)
64    }
65
66    async fn find_by_name(&self, name: &str) -> Result<Vec<TrashEntry>, TrashError> {
67        let items = spawn_trash(trash::os_limited::list).await?;
68
69        let named_items: Vec<(String, &trash::TrashItem)> = items
70            .iter()
71            .map(|item| (item.name.to_string_lossy().to_string(), item))
72            .collect();
73
74        find_restore_match(named_items, name)
75            .map(|matched| matched.into_iter().map(Self::to_entry).collect())
76            .map_err(TrashError::Backend)
77    }
78
79    async fn restore(&self, entries: Vec<TrashEntry>) -> Result<(), TrashError> {
80        // Reconstruct trash::TrashItem from our TrashEntry
81        let items: Vec<trash::TrashItem> = entries
82            .into_iter()
83            .map(|entry| {
84                let crate::trash::TrashIdInner::System(id) = entry.id.0;
85                trash::TrashItem {
86                    id,
87                    name: entry.name.into(),
88                    original_parent: entry
89                        .original_path
90                        .parent()
91                        .unwrap_or(Path::new("/"))
92                        .to_path_buf(),
93                    time_deleted: entry.deleted_at,
94                }
95            })
96            .collect();
97
98        spawn_trash(move || trash::os_limited::restore_all(items)).await
99    }
100
101    async fn purge_all(&self) -> Result<usize, TrashError> {
102        let items = spawn_trash(trash::os_limited::list).await?;
103        if items.is_empty() {
104            return Ok(0);
105        }
106        let count = items.len();
107        spawn_trash(move || trash::os_limited::purge_all(items)).await?;
108        Ok(count)
109    }
110}