summaryrefslogtreecommitdiffstats
path: root/src/trash.rs
diff options
context:
space:
mode:
authorqkzk <qu3nt1n@gmail.com>2022-12-26 00:39:05 +0100
committerqkzk <qu3nt1n@gmail.com>2022-12-26 00:39:05 +0100
commit6e2fbce3c01e1d877de5ef595b0d1cab88b02808 (patch)
treefc31a2550f54a4283abd7b7d468584d41f0a0e93 /src/trash.rs
parenteab76cb61ee82e9433f568e56e2c65299d0aa415 (diff)
trash: first working draft, very basic api
Diffstat (limited to 'src/trash.rs')
-rw-r--r--src/trash.rs154
1 files changed, 107 insertions, 47 deletions
diff --git a/src/trash.rs b/src/trash.rs
index 5c5f84f..0ef84b7 100644
--- a/src/trash.rs
+++ b/src/trash.rs
@@ -1,47 +1,72 @@
-use std::collections::BTreeMap;
-use std::fs::{rename, File, OpenOptions};
-use std::io::{self, prelude::*, BufRead, BufReader, BufWriter};
+use std::fs::{create_dir, remove_dir_all, rename, File, OpenOptions};
+use std::io::{prelude::*, BufRead, BufReader, BufWriter};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use log::info;
use tuikit::term::Term;
+use crate::constant_strings_paths::{TRASH_FILE, TRASH_FILE_TEMP, TRASH_FOLDER};
use crate::copy_move::{copy_move, CopyMove};
use crate::fm_error::{FmError, FmResult};
+use crate::impl_selectable_content;
+use crate::utils::read_lines;
-// TODO! make it tilde expand machin
-pub static TRASH_PATH: &str = "/home/quentin/.config/fm/trash/";
-pub static TRASH_FILE: &str = "/home/quentin/.config/fm/trash_file";
-pub static TRASH_FILE_TEMP: &str = "/tmp/trash_file_temp";
-
-// TODO! make it a navigable content
#[derive(Clone)]
pub struct Trash {
- pub content: BTreeMap<PathBuf, PathBuf>,
+ pub content: Vec<(PathBuf, PathBuf)>,
term: Arc<Term>,
+ index: usize,
+ trash_folder: String,
+ trash_file: String,
+ trash_file_temp: String,
}
impl Trash {
- pub fn new(term: Arc<Term>) -> Self {
- Self {
- content: BTreeMap::new(),
- term,
+ pub fn parse_trash_file(term: Arc<Term>) -> FmResult<Self> {
+ let trash_path = shellexpand::tilde(TRASH_FOLDER).to_string();
+ let trash_file = shellexpand::tilde(TRASH_FILE).to_string();
+ let trash_file_temp = shellexpand::tilde(TRASH_FILE_TEMP).to_string();
+ let mut content = vec![];
+ if let Ok(lines) = read_lines(&trash_file) {
+ for line_result in lines {
+ if let Ok(line) = line_result.as_ref() {
+ let sp: Vec<&str> = line.split(':').collect();
+ if sp.is_empty() || sp.len() < 2 {
+ continue;
+ }
+ let origin = PathBuf::from(sp[0]);
+ let dest = PathBuf::from(sp[1]);
+ content.push((origin, dest));
+ }
+ }
}
+ Ok(Self {
+ content,
+ term,
+ index: 0,
+ trash_folder: trash_path,
+ trash_file,
+ trash_file_temp,
+ })
}
pub fn trash(&mut self, origin: PathBuf) -> FmResult<PathBuf> {
- let mut dest = PathBuf::from(TRASH_PATH);
+ let mut dest = PathBuf::from(self.trash_folder.clone());
if let Some(file_name) = origin.file_name() {
- dest.push(file_name);
- copy_move(
- CopyMove::Move,
- vec![origin.clone()],
- TRASH_PATH.to_owned(),
- self.term.clone(),
- )?;
- self.content.insert(origin.clone(), dest.clone());
- self.write_to_trash_file(origin.clone(), dest.clone())?;
+ if !self.contains(&origin) {
+ dest.push(file_name);
+ copy_move(
+ CopyMove::Move,
+ vec![origin.clone()],
+ self.trash_folder.to_owned(),
+ self.term.clone(),
+ )?;
+
+ self.content.push((origin.clone(), dest.clone()));
+ self.write_to_trash_file(origin.clone(), dest.clone())?;
+ }
+ info!("moved to trash {:?} -> {:?}", origin, dest);
Ok(dest)
} else {
Err(FmError::custom(
@@ -51,11 +76,29 @@ impl Trash {
}
}
+ fn contains(&self, origin: &Path) -> bool {
+ for (o, _) in self.content.iter() {
+ if o == origin {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn find_dest(&self, origin: &Path) -> Option<PathBuf> {
+ for (o, d) in self.content.iter() {
+ if o == origin {
+ return Some(d.to_owned());
+ }
+ }
+ None
+ }
+
fn write_to_trash_file(&self, origin: PathBuf, dest: PathBuf) -> FmResult<()> {
let mut file = OpenOptions::new()
.write(true)
.append(true)
- .open(TRASH_FILE)?;
+ .open(self.trash_file.clone())?;
if let Err(e) = writeln!(
file,
"{}:{}",
@@ -70,17 +113,23 @@ impl Trash {
Ok(())
}
- pub fn restore(&mut self, origin_str: String) -> FmResult<()> {
- let origin = PathBuf::from(origin_str.clone());
- if let Some(dest) = self.content.get(&origin) {
+ pub fn restore(&mut self, origin: PathBuf) -> FmResult<()> {
+ let origin_str = path_to_string(&origin)?.to_owned();
+ if let Some(dest) = self.find_dest(&origin) {
+ let parent = find_parent_as_string(&origin)?;
if let Some(index) = self.found_in_trash_file(&origin_str) {
copy_move(
CopyMove::Move,
vec![dest.clone()],
- origin_str,
+ parent,
self.term.clone(),
)?;
+ info!(
+ "trash: restoring {:?} <- {:?} - index {}",
+ origin, dest, index
+ );
self.remove_line_from_trash_file(index)?;
+ self.content.remove(index);
} else {
return Err(FmError::custom(
"restore",
@@ -97,10 +146,10 @@ impl Trash {
}
fn found_in_trash_file(&self, origin_str: &str) -> Option<usize> {
- if let Ok(lines) = read_lines(TRASH_FILE) {
+ if let Ok(lines) = read_lines(self.trash_file.clone()) {
for (index, line_result) in lines.enumerate() {
if let Ok(line) = line_result.as_ref() {
- let sp: Vec<&str> = line.split(":").collect();
+ let sp: Vec<&str> = line.split(':').collect();
if sp.is_empty() {
continue;
}
@@ -116,8 +165,8 @@ impl Trash {
fn remove_line_from_trash_file(&mut self, index: usize) -> FmResult<()> {
{
- let file: File = File::open(TRASH_FILE)?;
- let out_file: File = File::open(TRASH_FILE_TEMP)?;
+ let file: File = File::open(self.trash_file.clone())?;
+ let out_file: File = File::create(self.trash_file_temp.clone())?;
let reader = BufReader::new(&file);
let mut writer = BufWriter::new(&out_file);
@@ -129,27 +178,38 @@ impl Trash {
}
}
}
- rename(TRASH_FILE_TEMP, TRASH_FILE)?;
+ rename(self.trash_file_temp.clone(), self.trash_file.clone())?;
Ok(())
}
- // TODO!
pub fn empty_trash(&mut self) -> FmResult<()> {
+ remove_dir_all(self.trash_folder.clone())?;
+ create_dir(self.trash_folder.clone())?;
+ let number_of_elements = self.content.len();
+
+ self.content = vec![];
+
+ File::create(self.trash_file.clone())?;
+ info!("Emptied the trash: {} elements removed", number_of_elements);
+
Ok(())
}
+}
- // TODO!
- pub fn parse_trash_file(term: Arc<Term>) -> FmResult<Self> {
- Ok(Self::new(term))
- }
+pub type PathPair = (PathBuf, PathBuf);
+
+impl_selectable_content!(PathPair, Trash);
+
+fn find_parent_as_string(path: &Path) -> FmResult<String> {
+ Ok(path
+ .parent()
+ .ok_or_else(|| FmError::custom("restore", &format!("Couldn't find parent of {:?}", path)))?
+ .to_str()
+ .ok_or_else(|| FmError::custom("restore", "couldn't parse parent into string"))?
+ .to_owned())
}
-// The output is wrapped in a Result to allow matching on errors
-// Returns an Iterator to the Reader of the lines of the file.
-fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
-where
- P: AsRef<Path>,
-{
- let file = File::open(filename)?;
- Ok(io::BufReader::new(file).lines())
+fn path_to_string(path: &Path) -> FmResult<&str> {
+ path.to_str()
+ .ok_or_else(|| FmError::custom("restore", "couldn't parse origin into string"))
}