From 6e2fbce3c01e1d877de5ef595b0d1cab88b02808 Mon Sep 17 00:00:00 2001 From: qkzk Date: Mon, 26 Dec 2022 00:39:05 +0100 Subject: trash: first working draft, very basic api --- src/trash.rs | 154 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 47 deletions(-) (limited to 'src/trash.rs') 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, + pub content: Vec<(PathBuf, PathBuf)>, term: Arc, + index: usize, + trash_folder: String, + trash_file: String, + trash_file_temp: String, } impl Trash { - pub fn new(term: Arc) -> Self { - Self { - content: BTreeMap::new(), - term, + pub fn parse_trash_file(term: Arc) -> FmResult { + 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 { - 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 { + 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 { - 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) -> FmResult { - Ok(Self::new(term)) - } +pub type PathPair = (PathBuf, PathBuf); + +impl_selectable_content!(PathPair, Trash); + +fn find_parent_as_string(path: &Path) -> FmResult { + 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

(filename: P) -> io::Result>> -where - P: AsRef, -{ - 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")) } -- cgit v1.2.3