diff options
-rw-r--r-- | src/constant_strings_paths.rs | 10 | ||||
-rw-r--r-- | src/copy_move.rs | 17 | ||||
-rw-r--r-- | src/event_exec.rs | 24 | ||||
-rw-r--r-- | src/status.rs | 4 | ||||
-rw-r--r-- | src/term_manager.rs | 55 | ||||
-rw-r--r-- | src/trash.rs | 438 | ||||
-rw-r--r-- | src/utils.rs | 2 |
7 files changed, 294 insertions, 256 deletions
diff --git a/src/constant_strings_paths.rs b/src/constant_strings_paths.rs index 2deecfb..81f1c22 100644 --- a/src/constant_strings_paths.rs +++ b/src/constant_strings_paths.rs @@ -51,9 +51,7 @@ pub static HARDCODED_SHORTCUTS: [&str; 9] = [ "/tmp", "/usr", ]; -/// Path to the trash folder -pub static TRASH_FOLDER: &str = "~/.config/fm/trash/"; -/// Path the to the file holding original places of trashed_files -pub static TRASH_FILE: &str = "~/.config/fm/trash_file"; -/// Path toe the temporary file used to write the trash file. -pub static TRASH_FILE_TEMP: &str = "~/tmp/trash_file_temp"; +/// Path to the trash folder files +pub static TRASH_FOLDER_FILES: &str = "~/.local/share/Trash/files"; +/// Path to the trash folder info file +pub static TRASH_FOLDER_INFO: &str = "~/.local/share/Trash/info"; diff --git a/src/copy_move.rs b/src/copy_move.rs index 7baae80..bd20650 100644 --- a/src/copy_move.rs +++ b/src/copy_move.rs @@ -5,6 +5,7 @@ use std::thread; use fs_extra; use indicatif::{InMemoryTerm, ProgressBar, ProgressDrawTarget, ProgressState, ProgressStyle}; +use log::info; use notify_rust::Notification; use tuikit::prelude::{Attr, Color, Effect, Event, Term}; @@ -60,6 +61,7 @@ fn handle_progress_display( } /// Different kind of movement of files : copying or moving. +#[derive(Debug)] pub enum CopyMove { Copy, Move, @@ -102,8 +104,14 @@ pub fn copy_move( CopyMove::Copy => fs_extra::copy_items_with_progress, CopyMove::Move => fs_extra::move_items_with_progress, }; - let transfered_bytes = - copier_mover(&sources, &dest, &options, handle_progress).unwrap_or_default(); + let transfered_bytes = match copier_mover(&sources, &dest, &options, handle_progress) { + Ok(transfered_bytes) => transfered_bytes, + Err(e) => { + info!("copy move couldn't copy: {:?}", e); + 0 + } + }; + let _ = c_term.send_event(Event::User(())); let _ = notify( &format!("fm: {} finished", copy_or_move.verb()), @@ -113,6 +121,11 @@ pub fn copy_move( copy_or_move.preterit() ), ); + info!( + "{} finished {}B", + copy_or_move.verb(), + human_size(transfered_bytes) + ) }); Ok(()) } diff --git a/src/event_exec.rs b/src/event_exec.rs index 6864406..593512a 100644 --- a/src/event_exec.rs +++ b/src/event_exec.rs @@ -3,6 +3,10 @@ use std::cmp::min; use std::fs; use std::path; +use copypasta::{ClipboardContext, ClipboardProvider}; +use log::info; +use sysinfo::SystemExt; + use crate::bulkrename::Bulkrename; use crate::completion::InputCompleted; use crate::constant_strings_paths::DEFAULT_DRAGNDROP; @@ -20,9 +24,7 @@ use crate::selectable_content::SelectableContent; use crate::status::Status; use crate::tab::Tab; use crate::term_manager::MIN_WIDTH_FOR_DUAL_PANE; - -use copypasta::{ClipboardContext, ClipboardProvider}; -use log::info; +use crate::utils::disk_used_by_path; /// Every kind of mutation of the application is defined here. /// It mutates `Status` or its children `Tab`. @@ -1270,17 +1272,27 @@ impl EventExec { } pub fn event_trash_move_file(status: &mut Status) -> FmResult<()> { + let trash_mount_point = disk_used_by_path( + status.system_info.disks(), + &std::path::PathBuf::from(&status.trash.trash_folder_files), + ); + for flagged in status.flagged.content.iter() { + let origin_mount_point = disk_used_by_path(status.disks(), flagged); + if trash_mount_point != origin_mount_point { + continue; + } status.trash.trash(flagged.to_owned())?; } + status.flagged.clear(); + status.selected().refresh_view()?; Ok(()) } pub fn event_trash_restore_file(status: &mut Status) -> FmResult<()> { - let (origin, _) = status.trash.selected().unwrap(); - status.trash.restore(origin.to_owned())?; - status.selected().refresh_view()?; + status.trash.restore()?; status.selected().mode = Mode::Normal; + status.selected().refresh_view()?; Ok(()) } diff --git a/src/status.rs b/src/status.rs index c549ac9..6b0175a 100644 --- a/src/status.rs +++ b/src/status.rs @@ -47,7 +47,7 @@ pub struct Status { skimer: Skimer, /// do we display one or two tabs ? pub dual_pane: bool, - system_info: System, + pub system_info: System, /// do we display all info or only the filenames ? pub display_full: bool, /// The opener used by the application. @@ -78,7 +78,7 @@ impl Status { let mut tab = Tab::new(args, height)?; tab.shortcut .extend_with_mount_points(&Self::disks_mounts(sys.disks())); - let trash = Trash::parse_trash_file(term.clone())?; + let trash = Trash::parse_info_trashs()?; Ok(Self { tabs: [tab.clone(), tab], diff --git a/src/term_manager.rs b/src/term_manager.rs index 6351bdc..8123825 100644 --- a/src/term_manager.rs +++ b/src/term_manager.rs @@ -21,7 +21,7 @@ use crate::preview::{Preview, TextKind, Window}; use crate::selectable_content::SelectableContent; use crate::status::Status; use crate::tab::Tab; -use crate::trash::PathPair; +use crate::trash::TrashInfo; /// At least 100 chars width to display 2 tabs. pub const MIN_WIDTH_FOR_DUAL_PANE: usize = 100; @@ -293,18 +293,21 @@ impl<'a> WinTab<'a> { fn trash( &self, canvas: &mut dyn Canvas, - selectable: &impl SelectableContent<PathPair>, + selectable: &impl SelectableContent<TrashInfo>, ) -> FmResult<()> { - canvas.print(0, 1, "Restore the selected file")?; - for (row, (origin, _dest)) in selectable.content().iter().enumerate() { + canvas.print(1, 0, "Restore the selected file")?; + for (row, trashinfo) in selectable.content().iter().enumerate() { let mut attr = Attr::default(); if row == selectable.index() { attr.effect |= Effect::REVERSE; } - let s = origin - .to_str() - .ok_or_else(|| FmError::custom("display", "Unreadable filename"))?; - let _ = canvas.print_with_attr(row + ContentWindow::WINDOW_MARGIN_TOP, 4, s, attr); + + let _ = canvas.print_with_attr( + row + ContentWindow::WINDOW_MARGIN_TOP, + 4, + &format!("{}", trashinfo), + attr, + ); } Ok(()) } @@ -331,17 +334,31 @@ impl<'a> WinTab<'a> { confirmed_mode: NeedConfirmation, canvas: &mut dyn Canvas, ) -> FmResult<()> { - for (row, path) in status.flagged.content.iter().enumerate() { - canvas.print_with_attr( - row + ContentWindow::WINDOW_MARGIN_TOP + 2, - 4, - path.to_str() - .ok_or_else(|| FmError::custom("display", "Unreadable filename"))?, - Attr::default(), - )?; - } info!("confirmed action: {:?}", confirmed_mode); - let content = match confirmed_mode { + match confirmed_mode { + NeedConfirmation::EmptyTrash => { + for (row, trashinfo) in status.trash.content.iter().enumerate() { + canvas.print_with_attr( + row + ContentWindow::WINDOW_MARGIN_TOP + 2, + 4, + &format!("{}", trashinfo), + Attr::default(), + )?; + } + } + NeedConfirmation::Copy | NeedConfirmation::Delete | NeedConfirmation::Move => { + for (row, path) in status.flagged.content.iter().enumerate() { + canvas.print_with_attr( + row + ContentWindow::WINDOW_MARGIN_TOP + 2, + 4, + path.to_str() + .ok_or_else(|| FmError::custom("display", "Unreadable filename"))?, + Attr::default(), + )?; + } + } + } + let confirmation_string = match confirmed_mode { NeedConfirmation::Copy => { format!( "Files will be copied to {}", @@ -354,7 +371,7 @@ impl<'a> WinTab<'a> { } NeedConfirmation::EmptyTrash => "Trash will be emptied".to_owned(), }; - canvas.print_with_attr(2, 3, &content, Self::ATTR_YELLOW_BOLD)?; + canvas.print_with_attr(2, 3, &confirmation_string, Self::ATTR_YELLOW_BOLD)?; Ok(()) } diff --git a/src/trash.rs b/src/trash.rs index 3eeed9f..9b283a0 100644 --- a/src/trash.rs +++ b/src/trash.rs @@ -1,31 +1,34 @@ -use std::fs::{create_dir, remove_dir_all, rename, File, OpenOptions}; -use std::io::{prelude::*, BufRead, BufReader, BufWriter}; +use std::fs::{create_dir, read_dir, remove_dir_all}; +use std::io::prelude::*; use std::path::{Path, PathBuf}; -use std::sync::Arc; use chrono::{Local, NaiveDateTime}; use log::info; -use tuikit::term::Term; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; -use crate::constant_strings_paths::{TRASH_FILE, TRASH_FILE_TEMP, TRASH_FOLDER}; -use crate::copy_move::{copy_move, CopyMove}; +use crate::constant_strings_paths::{TRASH_FOLDER_FILES, TRASH_FOLDER_INFO}; use crate::fm_error::{FmError, FmResult}; use crate::impl_selectable_content; use crate::utils::read_lines; -static DATETIME_FORMAT: &str = "%Y-%m-%dT%H:%M:%S"; +static TRASHINFO_DATETIME_FORMAT: &str = "%Y-%m-%dT%H:%M:%S"; + #[derive(Debug, Clone)] pub struct TrashInfo { - path: PathBuf, + origin: PathBuf, + dest_name: String, deletion_date: String, } impl TrashInfo { - pub fn new(origin: &Path) -> Self { + pub fn new(origin: &Path, dest_name: &str) -> Self { let date = Local::now(); - let deletion_date = format!("{}", date.format(DATETIME_FORMAT)); + let deletion_date = format!("{}", date.format(TRASHINFO_DATETIME_FORMAT)); + let dest_name = dest_name.to_owned(); Self { - path: PathBuf::from(origin), + origin: PathBuf::from(origin), + dest_name, deletion_date, } } @@ -36,261 +39,270 @@ impl TrashInfo { Path={} DeletionDate={} ", - path_to_string(&self.path)?, + path_to_string(&self.origin)?, self.deletion_date )) } pub fn write_trash_info(&self, dest: &Path) -> FmResult<()> { - let mut file = OpenOptions::new().write(true).open(dest)?; - if let Err(e) = writeln!(file, "{}", self.to_string()?) { + info!("writing trash_info {} for {:?}", self, dest); + + // let mut file = OpenOptions::new().write(true).open(dest)?; + let mut file = std::fs::File::create(dest)?; + if let Err(e) = write!(file, "{}", self.to_string()?) { info!("Couldn't write to trash file: {}", e) } Ok(()) } - pub fn from_trash_info_file(&self, trash_info_file: &Path) -> FmResult<Self> { - let mut p: Option<PathBuf> = None; - let mut d: Option<String> = None; + pub fn from_trash_info_file(trash_info_file: &Path) -> FmResult<Self> { + let mut option_path: Option<PathBuf> = None; + let mut option_deleted_time: Option<String> = None; let mut found_trash_info_line: bool = false; - if let Ok(lines) = read_lines(trash_info_file) { - for (index, line_result) in lines.enumerate() { - if let Ok(line) = line_result.as_ref() { - if line.starts_with("[TrashInfo]") { - if index == 0 { - found_trash_info_line = true; - continue; - } else { - return Err(FmError::custom( - "from_trash_info_file", - "[TrashInfo] was found after first line", - )); - } - } - if line.starts_with("Path=") && p.is_none() { - if !found_trash_info_line { - return Err(FmError::custom( - "from_trash_info_file", - "Found Path line before TrashInfo", - )); - } - let path_str = &line[6..]; - p = Some(PathBuf::from(path_str)); - } else if line.starts_with("DeletionDate=") && d.is_none() { - if !found_trash_info_line { - return Err(FmError::custom( - "from_trash_info_file", - "Found DeletionDate line before TrashInfo", - )); + if let Some(dest_name) = trash_info_file.file_name() { + let dest_name = Self::remove_extension(dest_name.to_str().unwrap().to_owned())?; + if let Ok(lines) = read_lines(trash_info_file) { + for (index, line_result) in lines.enumerate() { + if let Ok(line) = line_result.as_ref() { + if line.starts_with("[TrashInfo]") { + if index == 0 { + found_trash_info_line = true; + continue; + } else { + return trashinfo_error("[TrashInfo] was found after first line"); + } } - let deletion_date_str = &line[13..]; - match parsed_date_from_path_info(&deletion_date_str) { - Ok(()) => (), - Err(e) => return Err(e), + if line.starts_with("Path=") && option_path.is_none() { + if !found_trash_info_line { + return trashinfo_error("Found Path line before TrashInfo"); + } + let path_str = &line[6..]; + option_path = Some(PathBuf::from(path_str)); + } else if line.starts_with("DeletionDate=") && option_deleted_time.is_none() + { + if !found_trash_info_line { + return trashinfo_error("Found DeletionDate line before TrashInfo"); + } + let deletion_date_str = &line[13..]; + match parsed_date_from_path_info(deletion_date_str) { + Ok(()) => (), + Err(e) => return Err(e), + } + option_deleted_time = Some(deletion_date_str.to_owned()) } - d = Some(deletion_date_str.to_owned()) } } } + match (option_path, option_deleted_time) { + (Some(origin), Some(deletion_date)) => Ok(Self { + dest_name, + deletion_date, + origin, + }), + _ => trashinfo_error("Couldn't parse the trash info file"), + } + } else { + trashinfo_error("Couldn't parse the trash info filename") } + } - match (p, d) { - (Some(path), Some(deletion_date)) => Ok(Self { - deletion_date, - path, - }), - _ => Err(FmError::custom( - "from_trash_info_file", - "Couldn't parse the trash info file", - )), + fn remove_extension(mut destname: String) -> FmResult<String> { + if destname.ends_with(".trashinfo") { + destname.truncate(destname.len() - 10); + Ok(destname) + } else { + Err(FmError::custom( + "trahsinfo", + "filename doesn't contain .trashfino", + )) } } } +impl std::fmt::Display for TrashInfo { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "{} - trashed on {}", + path_to_string(&self.origin).unwrap_or_default(), + self.deletion_date + ) + } +} + #[derive(Clone)] pub struct Trash { - pub content: Vec<(PathBuf, PathBuf)>, - term: Arc<Term>, + pub content: Vec<TrashInfo>, index: usize, - trash_folder: String, - trash_file: String, - trash_file_temp: String, + pub trash_folder_files: String, + trash_folder_info: String, } impl Trash { - 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(self.trash_folder.clone()); + fn pick_dest_name(&self, origin: &Path) -> FmResult<String> { if let Some(file_name) = origin.file_name() { - 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())?; + let mut dest = file_name + .to_str() + .ok_or_else(|| { + FmError::custom( + "pick_dest_name", + "Couldn't parse the origin filename into a string", + ) + })? + .to_owned(); + let mut dest_path = PathBuf::from(&self.trash_folder_files); + dest_path.push(&dest); + while dest_path.exists() { + dest.push_str(&rand_string()); + dest_path = PathBuf::from(&self.trash_folder_files); + dest_path.push(&dest); } - info!("moved to trash {:?} -> {:?}", origin, dest); - Ok(dest) - } else { - Err(FmError::custom( - "trash", - &format!("couldn't trash {:?} - wrong filename", origin), - )) + return Ok(dest); } + Err(FmError::custom( + "pick_dest_name", + "Couldn't extract the filename", + )) } - fn contains(&self, origin: &Path) -> bool { - self.find_dest(origin).is_some() - } + pub fn parse_info_trashs() -> FmResult<Self> { + let trash_folder_files = shellexpand::tilde(TRASH_FOLDER_FILES).to_string(); + let trash_folder_info = shellexpand::tilde(TRASH_FOLDER_INFO).to_string(); + let index = 0; + + match read_dir(&trash_folder_info) { + Ok(read_dir) => { + let content: Vec<TrashInfo> = read_dir + .filter_map(|res_direntry| res_direntry.ok()) + .filter(|direntry| direntry.path().extension().is_some()) + .filter(|direntry| { + direntry.path().extension().unwrap().to_str().unwrap() == "trashinfo" + }) + .map(|direntry| TrashInfo::from_trash_info_file(&direntry.path())) + .filter_map(|trashinfo_res| trashinfo_res.ok()) + .collect(); - fn find_dest(&self, origin: &Path) -> Option<&Path> { - for (o, d) in self.content.iter() { - if o == origin { - return Some(d); + Ok(Self { + content, + index, + trash_folder_files, + trash_folder_info, + }) + } + Err(error) => { + info!("Couldn't read path {} - {}", trash_folder_files, error); + Err(FmError::from(error)) } } - None } - fn write_to_trash_file(&self, origin: PathBuf, dest: PathBuf) -> FmResult<()> { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .open(self.trash_file.clone())?; - if let Err(e) = writeln!( - file, - "{}:{}", - origin - .to_str() - .ok_or_else(|| FmError::custom("write_to_trash_file", "couldn't read origin"))?, - dest.to_str() - .ok_or_else(|| FmError::custom("write_to_trash_file", "couldn't read dest"))?, - ) { - info!("Couldn't write to trash file: {}", e) + pub fn trash(&mut self, origin: PathBuf) -> FmResult<()> { + if origin.is_relative() { + return Err(FmError::custom("trash", "origin path shoudl be absolute")); } - Ok(()) - } - 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.to_owned()], - 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", - &format!("Couldn't find {} in trash file", origin_str), - )); - } - Ok(()) - } else { - Err(FmError::custom( - "restore", - &format!("Couldn't restore {}", origin_str), - )) - } - } + let dest_file_name = self.pick_dest_name(&origin)?; + let trash_info = TrashInfo::new(&origin, &dest_file_name); + let mut trashfile_filename = PathBuf::from(&self.trash_folder_files); + trashfile_filename.push(&dest_file_name); - fn found_in_trash_file(&self, origin_str: &str) -> Option<usize> { - 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(); - if sp.is_empty() { - continue; - } - let origin_line = sp[0]; - if origin_line.starts_with(origin_str) { - return Some(index); - } - } - } - } - None - } + let mut dest_trashinfo_name = dest_file_name.clone(); + dest_trashinfo_name.push_str(".trashinfo"); + let mut trashinfo_filename = PathBuf::from(&self.trash_folder_info); + trashinfo_filename.push(dest_trashinfo_name); + info!("moving to trash ... {:?} -> {:?}", origin, dest_file_name); + trash_info.write_trash_info(&trashinfo_filename)?; - fn remove_line_from_trash_file(&mut self, index: usize) -> FmResult<()> { - { - let file: File = File::open(self.trash_file.clone())?; - let out_file: File = File::create(self.trash_file_temp.clone())?; + self.content.push(trash_info); - let reader = BufReader::new(&file); - let mut writer = BufWriter::new(&out_file); + std::fs::rename(&origin, &trashfile_filename)?; - for (i, line) in reader.lines().enumerate() { - let line = line.as_ref()?; - if i != index { - writeln!(writer, "{}", line)?; - } - } - } - rename(self.trash_file_temp.clone(), self.trash_file.clone())?; + info!("moved to trash {:?} -> {:?}", origin, dest_file_name); Ok(()) } pub fn empty_trash(&mut self) -> FmResult<()> { - remove_dir_all(self.trash_folder.clone())?; - create_dir(self.trash_folder.clone())?; + remove_dir_all(&self.trash_folder_files)?; + create_dir(&self.trash_folder_files)?; + + remove_dir_all(&self.trash_folder_info)?; + create_dir(&self.trash_folder_info)?; 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(()) } + + pub fn restore(&mut self) -> FmResult<()> { + let trashinfo = self.content[self.index].to_owned(); + + let origin = trashinfo.origin; + let dest_name = trashinfo.dest_name; + let parent = find_parent(&origin)?; + + let mut info_name = dest_name.clone(); + info_name.push_str(".trashinfo"); + + let mut trashed_file_content = PathBuf::from(&self.trash_folder_files); + trashed_file_content.push(&dest_name); + + let mut trashed_file_info = PathBuf::from(&self.trash_folder_info); + trashed_file_info.push(&info_name); + + if !trashed_file_content.exists() { + return Err(FmError::custom( + "trash restore", + "Couldn't find the trashed file", + )); + } + + if !trashed_file_info.exists() { + return Err(FmError::custom( + "trash restore", + "Couldn't find the trashed file info", + )); + } + + if !parent.exists() { + std::fs::create_dir_all(&parent)? + } + + std::fs::rename(&trashed_file_content, &origin)?; + self.content.remove(self.index); + std::fs::remove_file(&trashed_file_info)?; + info!("trash: restored {:?} <- {:?}", origin, trashed_file_content); + Ok(()) + } } -pub type PathPair = (PathBuf, PathBuf); +impl_selectable_content!(TrashInfo, Trash); -impl_selectable_content!(PathPair, Trash); +fn path_to_string(path: &Path) -> FmResult<&str> { + path.to_str() + .ok_or_else(|| FmError::custom("path_to_string", "couldn't parse origin into string")) +} + +fn parsed_date_from_path_info(ds: &str) -> FmResult<()> { + NaiveDateTime::parse_from_str(ds, TRASHINFO_DATETIME_FORMAT)?; + Ok(()) +} -fn find_parent_as_string(path: &Path) -> FmResult<String> { +fn rand_string() -> String { + thread_rng() + .sample_iter(&Alphanumeric) + .take(2) + .map(char::from) + .collect() +} + +fn trashinfo_error(msg: &str) -> FmResult<TrashInfo> { + Err(FmError::custom("trash", msg)) +} + +fn find_parent(path: &Path) -> FmResult<PathBuf> { Ok(path .parent() .ok_or_else(|| { @@ -299,19 +311,5 @@ fn find_parent_as_string(path: &Path) -> FmResult<String> { &format!("Couldn't find parent of {:?}", path), ) })? - .to_str() - .ok_or_else(|| { - FmError::custom("find_parent_as_string", "couldn't parse parent into string") - })? .to_owned()) } - -fn path_to_string(path: &Path) -> FmResult<&str> { - path.to_str() - .ok_or_else(|| FmError::custom("path_to_string", "couldn't parse origin into string")) -} - -fn parsed_date_from_path_info(ds: &str) -> FmResult<()> { - NaiveDateTime::parse_from_str(ds, DATETIME_FORMAT)?; - Ok(()) -} diff --git a/src/utils.rs b/src/utils.rs index 12a20af..db145ac 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -18,7 +18,7 @@ pub fn init_term() -> FmResult<Term> { Ok(term) } -fn disk_used_by_path<'a>(disks: &'a [Disk], path: &Path) -> Option<&'a Disk> { +pub fn disk_used_by_path<'a>(disks: &'a [Disk], path: &Path) -> Option<&'a Disk> { let mut disks: Vec<&Disk> = disks.iter().collect(); disks.sort_by_key(|disk| disk.mount_point().as_os_str().len()); disks.reverse(); |