summaryrefslogtreecommitdiffstats
path: root/src/copy_move.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/copy_move.rs')
-rw-r--r--src/copy_move.rs164
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(())
}