diff options
Diffstat (limited to 'src/copy_move.rs')
-rw-r--r-- | src/copy_move.rs | 164 |
1 files changed, 93 insertions, 71 deletions
diff --git a/src/copy_move.rs b/src/copy_move.rs index 2da977b..dd49f95 100644 --- a/src/copy_move.rs +++ b/src/copy_move.rs @@ -9,57 +9,34 @@ use indicatif::{InMemoryTerm, ProgressBar, ProgressDrawTarget, ProgressState, Pr use log::info; use tuikit::prelude::{Attr, Color, Effect, Event, Term}; +use crate::constant_strings_paths::NOTIFY_EXECUTABLE; use crate::fileinfo::human_size; +use crate::log::write_log_line; use crate::opener::execute_in_child; +use crate::utils::is_program_in_path; -fn setup( - action: String, - height: usize, - width: usize, -) -> Result<(InMemoryTerm, ProgressBar, fs_extra::dir::CopyOptions)> { - let in_mem = InMemoryTerm::new(height as u16, width as u16); - let pb = ProgressBar::with_draw_target( - Some(100), - ProgressDrawTarget::term_like(Box::new(in_mem.clone())), - ); - pb.set_style( - ProgressStyle::with_template( - "{spinner} {action} [{elapsed}] [{wide_bar}] {percent}% ({eta})", - ) - .unwrap() - .with_key("eta", |state: &ProgressState, w: &mut dyn Write| { - write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap() - }) - .with_key("action", move |_: &ProgressState, w: &mut dyn Write| { - write!(w, "{}", &action).unwrap() - }) - .progress_chars("#>-"), - ); - let options = fs_extra::dir::CopyOptions::new(); - Ok((in_mem, pb, options)) -} - +/// Display the updated progress bar on the terminal. fn handle_progress_display( in_mem: &InMemoryTerm, pb: &ProgressBar, term: &Arc<Term>, process_info: fs_extra::TransitProcess, ) -> fs_extra::dir::TransitProcessResult { - pb.set_position(100 * process_info.copied_bytes / process_info.total_bytes); - let _ = term.print_with_attr( - 1, - 0, - &in_mem.to_owned().contents(), - Attr { - fg: Color::CYAN, - bg: Color::default(), - effect: Effect::REVERSE | Effect::BOLD, - }, - ); + pb.set_position(progress_bar_position(&process_info)); + let _ = term.print_with_attr(1, 1, &in_mem.contents(), CopyMove::attr()); let _ = term.present(); fs_extra::dir::TransitProcessResult::ContinueOrAbort } +/// Position of the progress bar. +/// We have to handle properly 0 bytes to avoid division by zero. +fn progress_bar_position(process_info: &fs_extra::TransitProcess) -> u64 { + if process_info.total_bytes == 0 { + return 0; + } + 100 * process_info.copied_bytes / process_info.total_bytes +} + /// Different kind of movement of files : copying or moving. #[derive(Debug)] pub enum CopyMove { @@ -68,19 +45,80 @@ pub enum CopyMove { } impl CopyMove { + fn attr() -> Attr { + Attr { + fg: Color::CYAN, + bg: Color::Default, + effect: Effect::REVERSE | Effect::BOLD, + } + } + fn verb(&self) -> &str { - match *self { + match self { CopyMove::Copy => "copy", CopyMove::Move => "move", } } fn preterit(&self) -> &str { - match *self { + match self { CopyMove::Copy => "copied", CopyMove::Move => "moved", } } + + fn copier<P, Q, F>( + &self, + ) -> for<'a, 'b> fn( + &'a [P], + Q, + &'b fs_extra::dir::CopyOptions, + F, + ) -> Result<u64, fs_extra::error::Error> + where + P: AsRef<std::path::Path>, + Q: AsRef<std::path::Path>, + F: FnMut(fs_extra::TransitProcess) -> fs_extra::dir::TransitProcessResult, + { + match self { + CopyMove::Copy => fs_extra::copy_items_with_progress, + CopyMove::Move => fs_extra::move_items_with_progress, + } + } + + fn log_and_notify(&self, hs_bytes: String) { + let message = format!("{preterit} {hs_bytes} bytes", preterit = self.preterit()); + let _ = notify(&message); + info!("{message}"); + write_log_line(message); + } + + fn setup_progress_bar( + &self, + size: (usize, usize), + ) -> Result<(InMemoryTerm, ProgressBar, fs_extra::dir::CopyOptions)> { + let (height, width) = size; + let in_mem = InMemoryTerm::new(height as u16, width as u16); + let pb = ProgressBar::with_draw_target( + Some(100), + ProgressDrawTarget::term_like(Box::new(in_mem.clone())), + ); + let action = self.verb().to_owned(); + pb.set_style( + ProgressStyle::with_template( + "{spinner} {action} [{elapsed}] [{wide_bar}] {percent}% ({eta})", + )? + .with_key("eta", |state: &ProgressState, w: &mut dyn Write| { + write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap() + }) + .with_key("action", move |_: &ProgressState, w: &mut dyn Write| { + write!(w, "{}", &action).unwrap() + }) + .progress_chars("#>-"), + ); + let options = fs_extra::dir::CopyOptions::new(); + Ok((in_mem, pb, options)) + } } /// Will copy or move a bunch of files to `dest`. @@ -94,49 +132,33 @@ pub fn copy_move( term: Arc<Term>, ) -> Result<()> { let c_term = term.clone(); - let (height, width) = term.term_size()?; - let (in_mem, pb, options) = setup(copy_or_move.verb().to_owned(), height, width)?; + let (in_mem, pb, options) = copy_or_move.setup_progress_bar(term.term_size()?)?; let handle_progress = move |process_info: fs_extra::TransitProcess| { handle_progress_display(&in_mem, &pb, &term, process_info) }; let dest = dest.to_owned(); let _ = thread::spawn(move || { - let copier_mover = match copy_or_move { - CopyMove::Copy => fs_extra::copy_items_with_progress, - CopyMove::Move => fs_extra::move_items_with_progress, - }; - 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 transfered_bytes = + match copy_or_move.copier()(&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 {}B {}", - copy_or_move.verb(), - human_size(transfered_bytes), - copy_or_move.preterit() - )); - info!( - "{} finished {}B", - copy_or_move.verb(), - human_size(transfered_bytes) - ); - info!(target: "special", - "{} finished {}B", - copy_or_move.verb(), - human_size(transfered_bytes) - ) + + copy_or_move.log_and_notify(human_size(transfered_bytes)); }); Ok(()) } /// Send a notification to the desktop. -/// Requires "notify-send" to be installed. +/// Does nothing if "notify-send" isn't installed. fn notify(text: &str) -> Result<()> { - execute_in_child("notify-send", &[text])?; + if is_program_in_path(NOTIFY_EXECUTABLE) { + execute_in_child(NOTIFY_EXECUTABLE, &[text])?; + } Ok(()) } |