1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
mod parser;
pub mod percent_path;
pub mod trash_entry;
pub mod trash_info;
mod utils;
use std::io;
use std::path::{Path, PathBuf};
use directories::{ProjectDirs, UserDirs};
use fs_extra::dir;
use fs_extra::file;
use log::{error, warn};
use once_cell::sync::Lazy;
use snafu::{ResultExt, Snafu};
use trash_entry::{read_dir_trash_entries, TrashEntry};
use utils::{read_dir_path, remove_path};
static USER_DIRS: Lazy<UserDirs> =
Lazy::new(|| UserDirs::new().expect("Failed to determine user directories."));
static HOME_DIR: Lazy<&Path> = Lazy::new(|| &USER_DIRS.home_dir());
pub static TRASH_DIR: Lazy<PathBuf> = Lazy::new(|| HOME_DIR.join(".local/share/Trash"));
pub static TRASH_INFO_DIR: Lazy<PathBuf> = Lazy::new(|| TRASH_DIR.join("info"));
pub static TRASH_FILE_DIR: Lazy<PathBuf> = Lazy::new(|| TRASH_DIR.join("files"));
pub const TRASH_INFO_EXT: &'_ str = "trashinfo";
pub const FILE_COPY_OPT: file::CopyOptions = file::CopyOptions {
overwrite: true,
skip_exist: false,
buffer_size: 64000,
};
pub const DIR_COPY_OPT: dir::CopyOptions = dir::CopyOptions {
overwrite: true,
skip_exist: false,
buffer_size: 64000,
copy_inside: false,
content_only: false,
depth: 0,
};
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("Failed to create new trash entry struct"))]
TrashEntryNew { source: trash_entry::Error },
#[snafu(display(
"Failed to create a new trash entry by moving a file and creating a trash info file"
))]
TrashEntryCreation { source: trash_entry::Error },
#[snafu(display("Failed to read an iterator of trash entries"))]
ReadDirTrashEntry { source: trash_entry::Error },
#[snafu(display("Failed to restore trash entry"))]
TrashEntryRestore { source: trash_entry::Error },
#[snafu(display("Failed to remove trash entry"))]
TrashEntryRemove { source: trash_entry::Error },
#[snafu(context(false))]
#[snafu(display("Utils error"))]
Utils { source: utils::Error },
#[snafu(context(false))]
ReadDirPath { source: io::Error },
#[snafu(display("The stray path {} was found that could not be made into a trash entry", path.display()))]
StrayPath { path: PathBuf },
}
type Result<T, E = Error> = ::std::result::Result<T, E>;
#[macro_export]
macro_rules! ok_log {
($res:expr => $log_macro:ident!) => {
match $res {
Ok(t) => Some(t),
Err(e) => {
$log_macro!("{}", e);
None
}
}
};
($res:expr => $print_func:ident) => {
match $res {
Ok(t) => Some(t),
Err(e) => {
$print_func(format!("{}", e));
None
}
}
};
}
pub fn restore(name: impl AsRef<Path>) -> Result<()> {
Ok(TrashEntry::new(name)
.context(TrashEntryNew)?
.restore()
.context(TrashEntryRestore)?)
}
pub fn remove(name: impl AsRef<Path>) -> Result<()> {
Ok(TrashEntry::new(name)
.context(TrashEntryNew)?
.remove()
.context(TrashEntryRemove)?)
}
pub fn empty(keep_stray: bool) -> Result<()> {
read_dir_trash_entries()
.context(ReadDirTrashEntry)?
.map(|trash_entry| trash_entry.remove().context(TrashEntryRemove))
.filter_map(|res| ok_log!(res => error!))
.for_each(|_| ());
if !keep_stray {
read_dir_path(&TRASH_INFO_DIR)?
.chain(read_dir_path(&TRASH_FILE_DIR)?)
.inspect(|path| warn!("{}", StrayPath { path }.build()))
.map(|path| remove_path(path))
.filter_map(|res| ok_log!(res => error!))
.for_each(|_| ());
}
Ok(())
}
pub fn put(paths: &[impl AsRef<Path>]) -> Result<Vec<TrashEntry>> {
if paths.is_empty() {
panic!("Attempting to put empty paths");
}
let mut existing: Vec<_> = read_dir_trash_entries()
.context(ReadDirTrashEntry)?
.collect();
let old_trash_entries_end = existing.len() - 1;
for path in paths {
let trash_entry = TrashEntry::create(path, &existing).context(TrashEntryCreation)?;
existing.push(trash_entry)
}
existing.drain(..old_trash_entries_end);
Ok(existing)
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::{Context, Result};
use std::fs::File;
use std::io::Write;
use tempfile::NamedTempFile;
#[should_panic]
#[test]
fn put_test_nothing_test() {
let nothing: [&str; 0] = [];
let _ = put(¬hing);
}
#[ignore]
#[test]
fn put_test_single() -> Result<()> {
let mut tempfile = NamedTempFile::new()?;
tempfile.write_all(b"this is for the put_test_single")?;
put(&[tempfile.path()])?;
Ok(())
}
}