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(())
}
}