From 71e0c7faf3cc283b2b24e70631e5b18a7a7525cd Mon Sep 17 00:00:00 2001 From: Jiayi Zhao Date: Sun, 14 Apr 2019 17:57:50 -0400 Subject: rework error handling system rather than letting each command separately handle errors, we return a Result<(), JoshutoError> instead and allow for run.rs to handle all errors --- src/commands/change_directory.rs | 28 ++-- src/commands/cursor_move.rs | 45 +++++- src/commands/delete_files.rs | 12 +- src/commands/file_operations.rs | 330 ++++++++++++++++++--------------------- src/commands/mod.rs | 4 +- src/commands/new_directory.rs | 25 ++- src/commands/open_file.rs | 20 ++- src/commands/parent_directory.rs | 21 ++- src/commands/paste_files.rs | 222 ++++++++++++++++++++++++++ src/commands/quit.rs | 24 +-- src/commands/reload_dir.rs | 8 +- src/commands/rename_file.rs | 32 ++-- src/commands/search.rs | 29 +++- src/commands/selection.rs | 13 +- src/commands/set_mode.rs | 8 +- src/commands/show_hidden.rs | 8 +- src/commands/tab_operations.rs | 42 ++--- src/commands/tab_switch.rs | 20 ++- src/error.rs | 3 + src/main.rs | 1 + src/run.rs | 109 ++++++------- 21 files changed, 652 insertions(+), 352 deletions(-) create mode 100644 src/commands/paste_files.rs create mode 100644 src/error.rs diff --git a/src/commands/change_directory.rs b/src/commands/change_directory.rs index 8406940..0e7fd1f 100644 --- a/src/commands/change_directory.rs +++ b/src/commands/change_directory.rs @@ -1,10 +1,13 @@ use std::path; -use crate::commands::{JoshutoCommand, JoshutoRunnable}; +use crate::commands; + use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::ui; use crate::window::JoshutoView; +use commands::{JoshutoCommand, JoshutoRunnable}; #[derive(Clone, Debug)] pub struct ChangeDirectory { @@ -23,20 +26,13 @@ impl ChangeDirectory { path: &path::PathBuf, context: &mut JoshutoContext, view: &JoshutoView, - ) { - if !path.exists() { - ui::wprint_err(&view.bot_win, "Error: No such file or directory"); - return; - } + ) -> Result<(), JoshutoError> { let curr_tab = &mut context.tabs[context.curr_tab_index]; match std::env::set_current_dir(path.as_path()) { - Ok(_) => { - curr_tab.curr_path = path.clone(); - } + Ok(_) => curr_tab.curr_path = path.clone(), Err(e) => { - ui::wprint_err(&view.bot_win, e.to_string().as_str()); - return; + return Err(JoshutoError::IO(e)); } } @@ -79,6 +75,7 @@ impl ChangeDirectory { &context.username, &context.hostname, ); + Ok(()) } } @@ -91,13 +88,18 @@ impl std::fmt::Display for ChangeDirectory { } impl JoshutoRunnable for ChangeDirectory { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { - Self::change_directory(&self.path, context, view); + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { + let res = Self::change_directory(&self.path, context, view); preview::preview_file( &mut context.tabs[context.curr_tab_index], &view, &context.config_t, ); ncurses::doupdate(); + res } } diff --git a/src/commands/cursor_move.rs b/src/commands/cursor_move.rs index 57e629d..f9be9fb 100644 --- a/src/commands/cursor_move.rs +++ b/src/commands/cursor_move.rs @@ -1,5 +1,6 @@ use crate::commands::{JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::window::JoshutoView; @@ -60,7 +61,11 @@ impl std::fmt::Display for CursorMoveInc { } impl JoshutoRunnable for CursorMoveInc { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let mut movement: Option = None; { let curr_tab = context.curr_tab_mut(); @@ -69,8 +74,9 @@ impl JoshutoRunnable for CursorMoveInc { } } if let Some(s) = movement { - CursorMove::cursor_move(s, context, view); + CursorMove::cursor_move(s, context, view) } + Ok(()) } } @@ -97,7 +103,11 @@ impl std::fmt::Display for CursorMoveDec { } impl JoshutoRunnable for CursorMoveDec { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let mut movement: Option = None; { let curr_tab = context.curr_tab_mut(); @@ -114,6 +124,7 @@ impl JoshutoRunnable for CursorMoveDec { if let Some(s) = movement { CursorMove::cursor_move(s, context, view); } + Ok(()) } } @@ -138,7 +149,11 @@ impl std::fmt::Display for CursorMovePageUp { } impl JoshutoRunnable for CursorMovePageUp { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let movement: Option = { let curr_tab = context.curr_tab_mut(); if let Some(curr_list) = curr_tab.curr_list.as_ref() { @@ -154,6 +169,7 @@ impl JoshutoRunnable for CursorMovePageUp { if let Some(s) = movement { CursorMove::cursor_move(s, context, view); } + Ok(()) } } @@ -178,7 +194,11 @@ impl std::fmt::Display for CursorMovePageDown { } impl JoshutoRunnable for CursorMovePageDown { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let movement: Option = { let curr_tab = &mut context.tabs[context.curr_tab_index]; if let Some(curr_list) = curr_tab.curr_list.as_ref() { @@ -199,6 +219,7 @@ impl JoshutoRunnable for CursorMovePageDown { if let Some(s) = movement { CursorMove::cursor_move(s, context, view); } + Ok(()) } } @@ -223,7 +244,11 @@ impl std::fmt::Display for CursorMoveHome { } impl JoshutoRunnable for CursorMoveHome { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let movement: Option = { let curr_tab = context.curr_tab_mut(); if let Some(curr_list) = curr_tab.curr_list.as_ref() { @@ -240,6 +265,7 @@ impl JoshutoRunnable for CursorMoveHome { if let Some(s) = movement { CursorMove::cursor_move(s, context, view); } + Ok(()) } } @@ -264,7 +290,11 @@ impl std::fmt::Display for CursorMoveEnd { } impl JoshutoRunnable for CursorMoveEnd { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let movement: Option = { let curr_tab = context.curr_tab_mut(); if let Some(curr_list) = curr_tab.curr_list.as_ref() { @@ -278,5 +308,6 @@ impl JoshutoRunnable for CursorMoveEnd { if let Some(s) = movement { CursorMove::cursor_move(s, context, view); } + Ok(()) } } diff --git a/src/commands/delete_files.rs b/src/commands/delete_files.rs index f450e57..8f6cacb 100644 --- a/src/commands/delete_files.rs +++ b/src/commands/delete_files.rs @@ -4,6 +4,7 @@ use std::path; use crate::commands::{self, JoshutoCommand, JoshutoRunnable}; use crate::config::keymap; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::ui; use crate::window::JoshutoView; @@ -42,7 +43,11 @@ impl std::fmt::Display for DeleteFiles { } impl JoshutoRunnable for DeleteFiles { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { ui::wprint_msg(&view.bot_win, "Delete selected files? (Y/n)"); ncurses::timeout(-1); ncurses::doupdate(); @@ -54,9 +59,7 @@ impl JoshutoRunnable for DeleteFiles { match Self::remove_files(paths) { Ok(_) => ui::wprint_msg(&view.bot_win, "Deleted files"), Err(e) => { - ui::wprint_err(&view.bot_win, e.to_string().as_str()); - ncurses::doupdate(); - return; + return Err(JoshutoError::IO(e)); } } } @@ -83,5 +86,6 @@ impl JoshutoRunnable for DeleteFiles { let curr_tab = &mut context.tabs[context.curr_tab_index]; preview::preview_file(curr_tab, &view, &context.config_t); ncurses::doupdate(); + Ok(()) } } diff --git a/src/commands/file_operations.rs b/src/commands/file_operations.rs index da602e2..1e986e0 100644 --- a/src/commands/file_operations.rs +++ b/src/commands/file_operations.rs @@ -6,19 +6,31 @@ use std::time; use crate::commands::{self, JoshutoCommand, JoshutoRunnable, ProgressInfo}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::structs::JoshutoDirList; use crate::window::JoshutoView; lazy_static! { - static ref SELECTED_FILES: Mutex> = Mutex::new(vec![]); + static ref SELECTED_FILES: Mutex>> = Mutex::new(None); static ref FILE_OPERATION: Mutex = Mutex::new(FileOp::Copy); static ref TAB_SRC: atomic::AtomicUsize = atomic::AtomicUsize::new(0); } +enum FileOp { + Cut, + Copy, +} + +#[derive(Clone, Debug)] +pub struct CopyOptions { + pub overwrite: bool, + pub skip_exist: bool, +} + pub struct FileOperationThread { pub tab_src: usize, pub tab_dest: usize, - pub handle: thread::JoinHandle, + pub handle: thread::JoinHandle>, pub recv: mpsc::Receiver, } @@ -41,23 +53,14 @@ fn set_tab_src(tab_index: usize) { } fn repopulated_selected_files(dirlist: &JoshutoDirList) -> bool { - if let Some(contents) = commands::collect_selected_paths(dirlist) { - let mut data = SELECTED_FILES.lock().unwrap(); - *data = contents; - return true; + match commands::collect_selected_paths(dirlist) { + Some(s) => { + let mut data = SELECTED_FILES.lock().unwrap(); + *data = Some(s); + true + } + None => false, } - false -} - -enum FileOp { - Cut, - Copy, -} - -#[derive(Clone, Debug)] -pub struct CopyOptions { - pub overwrite: bool, - pub skip_exist: bool, } #[derive(Clone, Debug)] @@ -81,7 +84,7 @@ impl std::fmt::Display for CutFiles { } impl JoshutoRunnable for CutFiles { - fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) { + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> { let curr_tab = context.curr_tab_ref(); if let Some(s) = curr_tab.curr_list.as_ref() { if repopulated_selected_files(s) { @@ -89,6 +92,7 @@ impl JoshutoRunnable for CutFiles { set_tab_src(context.curr_tab_index); } } + Ok(()) } } @@ -113,7 +117,7 @@ impl std::fmt::Display for CopyFiles { } impl JoshutoRunnable for CopyFiles { - fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) { + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> { let curr_tab = context.curr_tab_ref(); if let Some(s) = curr_tab.curr_list.as_ref() { if repopulated_selected_files(s) { @@ -121,6 +125,7 @@ impl JoshutoRunnable for CopyFiles { set_tab_src(context.curr_tab_index); } } + Ok(()) } } @@ -128,6 +133,43 @@ pub struct PasteFiles { options: fs_extra::dir::CopyOptions, } +impl JoshutoCommand for PasteFiles {} + +impl std::fmt::Display for PasteFiles { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "{} overwrite={} skip_exist={}", + Self::command(), + self.options.overwrite, + self.options.skip_exist, + ) + } +} + +impl std::fmt::Debug for PasteFiles { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(Self::command()) + } +} + +impl JoshutoRunnable for PasteFiles { + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> { + let file_operation = FILE_OPERATION.lock().unwrap(); + + let thread = match *file_operation { + FileOp::Copy => self.copy_paste(context), + FileOp::Cut => self.cut_paste(context), + }; + + if let Ok(s) = thread { + ncurses::timeout(-1); + context.threads.push(s); + } + Ok(()) + } +} + impl PasteFiles { pub fn new(options: fs_extra::dir::CopyOptions) -> Self { PasteFiles { options } @@ -136,86 +178,58 @@ impl PasteFiles { "paste_files" } - #[cfg(target_os = "linux")] - fn cut(&self, context: &mut JoshutoContext) -> Result { - use std::os::linux::fs::MetadataExt; + fn same_fs_cut( + &self, + context: &mut JoshutoContext, + ) -> Result { + let options = self.options.clone(); - let tab_dest = context.curr_tab_index; - let tab_src_index = TAB_SRC.load(atomic::Ordering::SeqCst); + let (tx, rx) = mpsc::channel(); - let options = self.options.clone(); + let tab_dest = context.curr_tab_index; + let tab_src = TAB_SRC.load(atomic::Ordering::SeqCst); let mut destination = context.tabs[tab_dest].curr_path.clone(); - let dest_ino = destination.metadata()?.st_dev(); - let path_ino; - { - let paths = SELECTED_FILES.lock().unwrap(); - if paths.len() == 0 { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "no files selected", - )); - } - path_ino = paths[0].metadata()?.st_dev(); - } + let handle = thread::spawn(move || { + let paths: Option> = SELECTED_FILES.lock().unwrap().take(); + match paths { + None => {} + Some(s) => { + let mut progress_info = ProgressInfo { + bytes_finished: 1, + total_bytes: s.len() as u64 + 1, + }; - let (tx, rx) = mpsc::channel(); - let handle = if dest_ino == path_ino { - thread::spawn(move || { - let mut paths = SELECTED_FILES.lock().unwrap(); - let mut progress_info = ProgressInfo { - bytes_finished: 1, - total_bytes: paths.len() as u64 + 1, - }; - - for path in (*paths).iter() { - let file_name = path.file_name().unwrap().to_os_string(); - - destination.push(file_name.clone()); - - if destination.exists() { - if !options.skip_exist { - for i in 0.. { - if !destination.exists() { - break; + for path in &s { + let file_name = path.file_name().unwrap().to_os_string(); + + destination.push(file_name.clone()); + if destination.exists() { + if !options.skip_exist { + for i in 0.. { + if !destination.exists() { + break; + } + destination.pop(); + let mut file_name = file_name.clone(); + file_name.push(&format!("_{}", i)); + destination.push(file_name); } - destination.pop(); - let mut file_name = file_name.clone(); - file_name.push(&format!("_{}", i)); - destination.push(file_name); } - std::fs::rename(&path, &destination); } - } else { - std::fs::rename(&path, &destination); + std::fs::rename(&path, &destination)?; + destination.pop(); + + progress_info.bytes_finished += 1; + tx.send(progress_info.clone()).unwrap(); } - destination.pop(); - progress_info.bytes_finished += 1; - tx.send(progress_info.clone()).unwrap(); } - paths.clear(); - 0 - }) - } else { - thread::spawn(move || { - let mut paths = SELECTED_FILES.lock().unwrap(); + } + return Ok(()); + }); - let handle = |process_info: fs_extra::TransitProcess| { - let progress_info = ProgressInfo { - bytes_finished: process_info.copied_bytes, - total_bytes: process_info.total_bytes, - }; - tx.send(progress_info).unwrap(); - fs_extra::dir::TransitProcessResult::ContinueOrAbort - }; - - fs_extra::move_items_with_progress(&paths, &destination, &options, handle).unwrap(); - paths.clear(); - 0 - }) - }; let thread = FileOperationThread { - tab_src: tab_src_index, + tab_src, tab_dest, handle, recv: rx, @@ -223,75 +237,77 @@ impl PasteFiles { Ok(thread) } - #[cfg(not(target_os = "linux"))] - fn cut(&self, context: &mut JoshutoContext) -> Result { - let tab_dest = context.curr_tab_index; - let tab_src_index = TAB_SRC.load(atomic::Ordering::SeqCst); - - let mut destination = context.tabs[tab_dest].curr_path.clone(); - let options = self.options.clone(); + #[cfg(target_os = "linux")] + fn cut_paste( + &self, + context: &mut JoshutoContext, + ) -> Result { + use std::os::linux::fs::MetadataExt; + let src_ino; { let paths = SELECTED_FILES.lock().unwrap(); - if paths.len() == 0 { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "no files selected", - )); + match *paths { + Some(ref s) => { + if s.len() == 0 { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "no files selected", + )); + } + src_ino = s[0].metadata()?.st_dev(); + } + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "no files selected", + )); + } } } - let (tx, rx) = mpsc::channel(); - let handle = thread::spawn(move || { - let mut paths = SELECTED_FILES.lock().unwrap(); - - let handle = |process_info: fs_extra::TransitProcess| { - let progress_info = ProgressInfo { - bytes_finished: process_info.copied_bytes, - total_bytes: process_info.total_bytes, - }; - tx.send(progress_info).unwrap(); - fs_extra::dir::TransitProcessResult::ContinueOrAbort - }; - - fs_extra::move_items_with_progress(&paths, &destination, &options, handle).unwrap(); - paths.clear(); - 0 - }); - let thread = FileOperationThread { - tab_src: tab_src_index, - tab_dest, - handle, - recv: rx, - }; - Ok(thread) + let tab_dest = context.curr_tab_index; + let destination = &context.tabs[tab_dest].curr_path; + + let dest_ino = destination.metadata()?.st_dev(); + if dest_ino == src_ino { + self.same_fs_cut(context) + } else { + self.same_fs_cut(context) + } } - fn copy(&self, context: &mut JoshutoContext) -> Result { + fn copy_paste( + &self, + context: &mut JoshutoContext, + ) -> Result { let tab_dest = context.curr_tab_index; - let tab_src_index = TAB_SRC.load(atomic::Ordering::SeqCst); - let destination = context.tabs[tab_dest].curr_path.clone(); + + let tab_src = TAB_SRC.load(atomic::Ordering::SeqCst); let options = self.options.clone(); let (tx, rx) = mpsc::channel(); let handle = thread::spawn(move || { let paths = SELECTED_FILES.lock().unwrap(); - - let handle = |process_info: fs_extra::TransitProcess| { - let progress_info = ProgressInfo { - bytes_finished: process_info.copied_bytes, - total_bytes: process_info.total_bytes, - }; - tx.send(progress_info).unwrap(); - fs_extra::dir::TransitProcessResult::ContinueOrAbort - }; - fs_extra::copy_items_with_progress(&paths, &destination, &options, handle); - 0 + match *paths { + Some(ref s) => { + let progress_info = ProgressInfo { + bytes_finished: 0, + total_bytes: 0, + }; + match tx.send(progress_info) { + Ok(_) => {} + Err(e) => {} + } + return Ok(()); + } + None => return Ok(()), + } }); let thread = FileOperationThread { - tab_src: tab_src_index, + tab_src, tab_dest, handle, recv: rx, @@ -299,39 +315,3 @@ impl PasteFiles { Ok(thread) } } - -impl JoshutoCommand for PasteFiles {} - -impl std::fmt::Display for PasteFiles { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "{} overwrite={} skip_exist={}", - Self::command(), - self.options.overwrite, - self.options.skip_exist, - ) - } -} - -impl std::fmt::Debug for PasteFiles { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str(Self::command()) - } -} - -impl JoshutoRunnable for PasteFiles { - fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) { - let file_operation = FILE_OPERATION.lock().unwrap(); - - let thread = match *file_operation { - FileOp::Copy => self.copy(context), - FileOp::Cut => self.cut(context), - }; - - if let Ok(s) = thread { - ncurses::timeout(-1); - context.threads.push(s); - } - } -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 7cfa311..57694c5 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -41,6 +41,7 @@ use std::fmt; use std::path::PathBuf; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::structs; use crate::window::JoshutoView; @@ -51,7 +52,8 @@ pub enum CommandKeybind { } pub trait JoshutoRunnable { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView); + fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) + -> Result<(), JoshutoError>; } pub trait JoshutoCommand: JoshutoRunnable + std::fmt::Display + std::fmt::Debug {} diff --git a/src/commands/new_directory.rs b/src/commands/new_directory.rs index 243cf6a..b3d1c2d 100644 --- a/src/commands/new_directory.rs +++ b/src/commands/new_directory.rs @@ -2,6 +2,7 @@ use std::path; use crate::commands::{JoshutoCommand, JoshutoRunnable, ReloadDirList}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::textfield::JoshutoTextField; use crate::ui; use crate::window::JoshutoView; @@ -27,35 +28,33 @@ impl std::fmt::Display for NewDirectory { } impl JoshutoRunnable for NewDirectory { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let (term_rows, term_cols) = ui::getmaxyx(); const PROMPT: &str = ":mkdir "; - let user_input: Option; - - { + let user_input: Option = { let textfield = JoshutoTextField::new( 1, term_cols, (term_rows as usize - 1, 0), PROMPT.to_string(), ); - user_input = textfield.readline_with_initial("", ""); - } + textfield.readline_with_initial("", "") + }; if let Some(user_input) = user_input { let path = path::PathBuf::from(user_input); match std::fs::create_dir_all(&path) { - Ok(_) => { - ReloadDirList::reload(context, view); - } - Err(e) => { - ui::wprint_err(&view.bot_win, e.to_string().as_str()); - } + Ok(_) => ReloadDirList::reload(context, view), + Err(e) => return Err(JoshutoError::IO(e)), } } - ncurses::doupdate(); + Ok(()) } } diff --git a/src/commands/open_file.rs b/src/commands/open_file.rs index ad71b51..1d8801e 100644 --- a/src/commands/open_file.rs +++ b/src/commands/open_file.rs @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf}; use crate::commands::{self, JoshutoCommand, JoshutoRunnable}; use crate::config::mimetype; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::textfield::JoshutoTextField; use crate::ui; @@ -109,7 +110,11 @@ impl std::fmt::Display for OpenFile { } impl JoshutoRunnable for OpenFile { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let mut path: Option = None; if let Some(curr_list) = context.tabs[context.curr_tab_index].curr_list.as_ref() { if let Some(entry) = curr_list.get_curr_ref() { @@ -137,16 +142,14 @@ impl JoshutoRunnable for OpenFile { None => None, }; if let Some(paths) = paths { - if !paths.is_empty() { - Self::open_file(&paths); - } else { - ui::wprint_msg(&view.bot_win, "No files selected: 0"); - } + Self::open_file(&paths); } else { - ui::wprint_msg(&view.bot_win, "No files selected: None"); + let err = std::io::Error::new(std::io::ErrorKind::NotFound, "No files selected"); + return Err(JoshutoError::IO(err)); } } ncurses::doupdate(); + Ok(()) } } @@ -234,11 +237,12 @@ impl std::fmt::Display for OpenFileWith { } impl JoshutoRunnable for OpenFileWith { - fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) { + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> { if let Some(s) = context.tabs[context.curr_tab_index].curr_list.as_ref() { if let Some(paths) = commands::collect_selected_paths(s) { Self::open_with(&paths); } } + Ok(()) } } diff --git a/src/commands/parent_directory.rs b/src/commands/parent_directory.rs index fe219df..59a6122 100644 --- a/src/commands/parent_directory.rs +++ b/src/commands/parent_directory.rs @@ -1,5 +1,6 @@ use crate::commands::{JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::ui; use crate::window::JoshutoView; @@ -15,9 +16,12 @@ impl ParentDirectory { "parent_directory" } - pub fn parent_directory(context: &mut JoshutoContext, view: &JoshutoView) { + pub fn parent_directory( + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { if !context.curr_tab_mut().curr_path.pop() { - return; + return Ok(()); } match std::env::set_current_dir(&context.curr_tab_ref().curr_path) { @@ -54,12 +58,13 @@ impl ParentDirectory { &context.hostname, ); preview::preview_file(curr_tab, view, &context.config_t); + ncurses::doupdate(); + return Ok(()); } Err(e) => { - ui::wprint_err(&view.bot_win, e.to_string().as_str()); + return Err(JoshutoError::IO(e)); } }; - ncurses::doupdate(); } } @@ -72,7 +77,11 @@ impl std::fmt::Display for ParentDirectory { } impl JoshutoRunnable for ParentDirectory { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { - Self::parent_directory(context, view); + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { + Self::parent_directory(context, view) } } diff --git a/src/commands/paste_files.rs b/src/commands/paste_files.rs new file mode 100644 index 0000000..667023c --- /dev/null +++ b/src/commands/paste_files.rs @@ -0,0 +1,222 @@ +use lazy_static::lazy_static; +use std::path; +use std::sync::{atomic, mpsc, Mutex}; +use std::thread; +use std::time; + +use crate::commands::{self, JoshutoCommand, JoshutoRunnable, ProgressInfo}; +use crate::context::JoshutoContext; +use crate::structs::JoshutoDirList; +use crate::window::JoshutoView; + +pub struct PasteFiles { + options: fs_extra::dir::CopyOptions, +} + +impl PasteFiles { + pub fn new(options: fs_extra::dir::CopyOptions) -> Self { + PasteFiles { options } + } + pub const fn command() -> &'static str { + "paste_files" + } + + #[cfg(target_os = "linux")] + fn cut(&self, context: &mut JoshutoContext) -> Result { + use std::os::linux::fs::MetadataExt; + + let tab_dest = context.curr_tab_index; + let tab_src_index = TAB_SRC.load(atomic::Ordering::SeqCst); + + let options = self.options.clone(); + let mut destination = context.tabs[tab_dest].curr_path.clone(); + + let dest_ino = destination.metadata()?.st_dev(); + let path_ino; + { + let paths = SELECTED_FILES.lock().unwrap(); + if paths.len() == 0 { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "no files selected", + )); + } + path_ino = paths[0].metadata()?.st_dev(); + } + + let (tx, rx) = mpsc::channel(); + let handle = if dest_ino == path_ino { + thread::spawn(move || { + let mut paths = SELECTED_FILES.lock().unwrap(); + let mut progress_info = ProgressInfo { + bytes_finished: 1, + total_bytes: paths.len() as u64 + 1, + }; + + for path in (*paths).iter() { + let file_name = path.file_name().unwrap().to_os_string(); + + destination.push(file_name.clone()); + + if destination.exists() { + if !options.skip_exist { + for i in 0.. { + if !destination.exists() { + break; + } + destination.pop(); + let mut file_name = file_name.clone(); + file_name.push(&format!("_{}", i)); + destination.push(file_name); + } + std::fs::rename(&path, &destination); + } + } else { + std::fs::rename(&path, &destination); + } + destination.pop(); + progress_info.bytes_finished += 1; + tx.send(progress_info.clone()).unwrap(); + } + paths.clear(); + 0 + }) + } else { + thread::spawn(move || { + let mut paths = SELECTED_FILES.lock().unwrap(); + + let handle = |process_info: fs_extra::TransitProcess| { + let progress_info = ProgressInfo { + bytes_finished: process_info.copied_bytes, + total_bytes: process_info.total_bytes, + }; + tx.send(progress_info).unwrap(); + fs_extra::dir::TransitProcessResult::ContinueOrAbort + }; + + fs_extra::move_items_with_progress(&paths, &destination, &options, handle).unwrap(); + paths.clear(); + 0 + }) + }; + let thread = FileOperationThread { + tab_src: tab_src_index, + tab_dest, + handle, + recv: rx, + }; + Ok(thread) + } + + #[cfg(not(target_os = "linux"))] + fn cut(&self, context: &mut JoshutoContext) -> Result { + let tab_dest = context.curr_tab_index; + let tab_src_index = TAB_SRC.load(atomic::Ordering::SeqCst); + + let mut destination = context.tabs[tab_dest].curr_path.clone(); + let options = self.options.clone(); + + { + let paths = SELECTED_FILES.lock().unwrap(); + if paths.len() == 0 { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "no files selected", + )); + } + } + let (tx, rx) = mpsc::channel(); + + let handle = thread::spawn(move || { + let mut paths = SELECTED_FILES.lock().unwrap(); + + let handle = |process_info: fs_extra::TransitProcess| { + let progress_info = ProgressInfo { + bytes_finished: process_info.copied_bytes, + total_bytes: process_info.total_bytes, + }; + tx.send(progress_info).unwrap(); + fs_extra::dir::TransitProcessResult::ContinueOrAbort + }; + + fs_extra::move_items_with_progress(&paths, &destination, &options, handle).unwrap(); + paths.clear(); + 0 + }); + let thread = FileOperationThread { + tab_src: tab_src_index, + tab_dest, + handle, + recv: rx, + }; + Ok(thread) + } + + fn copy(&self, context: &mut JoshutoContext) -> Result { + let tab_dest = context.curr_tab_index; + let tab_src_index = TAB_SRC.load(atomic::Ordering::SeqCst); + + let destination = context.tabs[tab_dest].curr_path.clone(); + let options = self.options.clone(); + + let (tx, rx) = mpsc::channel(); + + let handle = thread::spawn(move || { + let paths = SELECTED_FILES.lock().unwrap(); + + let handle = |process_info: fs_extra::TransitProcess| { + let progress_info = ProgressInfo { + bytes_finished: process_info.copied_bytes, + total_bytes: process_info.total_bytes, + }; + tx.send(progress_info).unwrap(); + fs_extra::dir::TransitProcessResult::ContinueOrAbort + }; + fs_extra::copy_items_with_progress(&paths, &destination, &options, handle); + 0 + }); + let thread = FileOperationThread { + tab_src: tab_src_index, + tab_dest, + handle, + recv: rx, + }; + Ok(thread) + } +} + +impl JoshutoCommand for PasteFiles {} + +impl std::fmt::Display for PasteFiles { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "{} overwrite={} skip_exist={}", + Self::command(), + self.options.overwrite, + self.options.skip_exist, + ) + } +} + +impl std::fmt::Debug for PasteFiles { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(Self::command()) + } +} + +impl JoshutoRunnable for PasteFiles { + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) { + let file_operation = FILE_OPERATION.lock().unwrap(); + + let thread = match *file_operation { + FileOp::Copy => self.copy(context), + FileOp::Cut => self.cut(context), + }; + + if let Ok(s) = thread { + ncurses::timeout(-1); + context.threads.push(s); + } + } +} diff --git a/src/commands/quit.rs b/src/commands/quit.rs index 67c1eee..6133d31 100644 --- a/src/commands/quit.rs +++ b/src/commands/quit.rs @@ -1,6 +1,6 @@ use crate::commands::{JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; -use crate::ui; +use crate::error::JoshutoError; use crate::window::JoshutoView; #[derive(Clone, Debug)] @@ -14,16 +14,17 @@ impl Quit { "quit" } - pub fn quit(context: &mut JoshutoContext, view: &JoshutoView) { + pub fn quit(context: &mut JoshutoContext) -> Result<(), JoshutoError> { if !context.threads.is_empty() { - ui::wprint_err( - &view.bot_win, - "Error: operations running in background, use force_quit to quit", + let err = std::io::Error::new( + std::io::ErrorKind::Other, + "operations running in background, use force_quit to quit", ); - ncurses::doupdate(); - return; + Err(JoshutoError::IO(err)) + } else { + context.exit = true; + Ok(()) } - context.exit = true; } } @@ -36,8 +37,8 @@ impl std::fmt::Display for Quit { } impl JoshutoRunnable for Quit { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { - Self::quit(context, view); + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> { + Self::quit(context) } } @@ -66,7 +67,8 @@ impl std::fmt::Display for ForceQuit { } impl JoshutoRunnable for ForceQuit { - fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) { + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> Result<(), JoshutoError> { Self::force_quit(context); + Ok(()) } } diff --git a/src/commands/reload_dir.rs b/src/commands/reload_dir.rs index 7b03ded..454abf4 100644 --- a/src/commands/reload_dir.rs +++ b/src/commands/reload_dir.rs @@ -1,5 +1,6 @@ use crate::commands::{JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::window::JoshutoView; @@ -35,7 +36,11 @@ impl std::fmt::Display for ReloadDirList { } impl JoshutoRunnable for ReloadDirList { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { Self::reload(context, view); preview::preview_file( &mut context.tabs[context.curr_tab_index], @@ -43,5 +48,6 @@ impl JoshutoRunnable for ReloadDirList { &context.config_t, ); ncurses::doupdate(); + Ok(()) } } diff --git a/src/commands/rename_file.rs b/src/commands/rename_file.rs index 9a07de1..05ec9ae 100644 --- a/src/commands/rename_file.rs +++ b/src/commands/rename_file.rs @@ -3,6 +3,7 @@ use std::path; use crate::commands::{JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::textfield::JoshutoTextField; use crate::ui; @@ -34,11 +35,10 @@ impl RenameFile { context: &mut JoshutoContext, view: &JoshutoView, start_str: String, - ) { + ) -> Result<(), JoshutoError> { const PROMPT: &str = ":rename_file "; let (term_rows, term_cols) = ui::getmaxyx(); - let user_input: Option; - { + let user_input: Option = { let textfield = JoshutoTextField::new( 1, term_cols, @@ -46,7 +46,7 @@ impl RenameFile { PROMPT.to_string(), ); - user_input = match self.method { + match self.method { RenameFileMethod::Append => { if let Some(ext) = start_str.rfind('.') { textfield.readline_with_initial(&start_str[0..ext], &start_str[ext..]) @@ -56,16 +56,19 @@ impl RenameFile { } RenameFileMethod::Prepend => textfield.readline_with_initial("", &start_str), RenameFileMethod::Overwrite => textfield.readline_with_initial("", ""), - }; - } + } + }; if let Some(s) = user_input { let mut new_path = path.parent().unwrap().to_path_buf(); new_path.push(s); if new_path.exists() { - ui::wprint_err(&view.bot_win, "Error: File with name exists"); - return; + let err = std::io::Error::new( + std::io::ErrorKind::AlreadyExists, + "Filename already exists", + ); + return Err(JoshutoError::IO(err)); } match fs::rename(&path, &new_path) { Ok(_) => { @@ -76,13 +79,14 @@ impl RenameFile { curr_tab.refresh_curr(&view.mid_win, context.config_t.scroll_offset); } Err(e) => { - ui::wprint_err(&view.bot_win, e.to_string().as_str()); + return Err(JoshutoError::IO(e)); } } } else { let curr_tab = &context.tabs[context.curr_tab_index]; curr_tab.refresh_file_status(&view.bot_win); } + Ok(()) } } @@ -95,7 +99,11 @@ impl std::fmt::Display for RenameFile { } impl JoshutoRunnable for RenameFile { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let mut path: Option = None; let mut file_name: Option = None; @@ -108,14 +116,16 @@ impl JoshutoRunnable for RenameFile { if let Some(file_name) = file_name { if let Some(path) = path { - self.rename_file(&path, context, view, file_name); + let res = self.rename_file(&path, context, view, file_name); preview::preview_file( &mut context.tabs[context.curr_tab_index], view, &context.config_t, ); ncurses::doupdate(); + return res; } } + return Ok(()); } } diff --git a/src/commands/search.rs b/src/commands/search.rs index 6510726..1c456f4 100644 --- a/src/commands/search.rs +++ b/src/commands/search.rs @@ -3,6 +3,7 @@ use std::sync::Mutex; use crate::commands::{CursorMove, JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::tab::JoshutoTab; use crate::textfield::JoshutoTextField; use crate::ui; @@ -73,11 +74,14 @@ impl std::fmt::Display for Search { } impl JoshutoRunnable for Search { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { const PROMPT: &str = ":search "; let (term_rows, term_cols) = ui::getmaxyx(); - let user_input: Option; - { + let user_input: Option = { let textfield = JoshutoTextField::new( 1, term_cols, @@ -85,8 +89,8 @@ impl JoshutoRunnable for Search { PROMPT.to_string(), ); - user_input = textfield.readline_with_initial("", ""); - } + textfield.readline_with_initial("", "") + }; ncurses::doupdate(); if let Some(user_input) = user_input { @@ -100,6 +104,7 @@ impl JoshutoRunnable for Search { *data = Some(user_input); ncurses::doupdate(); } + Ok(()) } } @@ -139,8 +144,13 @@ impl std::fmt::Display for SearchNext { } impl JoshutoRunnable for SearchNext { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { search_with_func(context, view, Search::search); + Ok(()) } } @@ -165,7 +175,12 @@ impl std::fmt::Display for SearchPrev { } impl JoshutoRunnable for SearchPrev { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { search_with_func(context, view, Search::search_rev); + Ok(()) } } diff --git a/src/commands/selection.rs b/src/commands/selection.rs index fdb57b7..e4237e3 100644 --- a/src/commands/selection.rs +++ b/src/commands/selection.rs @@ -1,5 +1,6 @@ use crate::commands::{CursorMoveInc, JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::window::JoshutoView; #[derive(Debug, Clone)] @@ -32,7 +33,11 @@ impl std::fmt::Display for SelectFiles { } impl JoshutoRunnable for SelectFiles { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { if self.toggle && !self.all { let mut selected = false; @@ -41,8 +46,12 @@ impl JoshutoRunnable for SelectFiles { selected = true; } if selected { - CursorMoveInc::new(1).execute(context, view); + CursorMoveInc::new(1).execute(context, view) + } else { + Ok(()) } + } else { + Ok(()) } } } diff --git a/src/commands/set_mode.rs b/src/commands/set_mode.rs index 430d122..f39cbc3 100644 --- a/src/commands/set_mode.rs +++ b/src/commands/set_mode.rs @@ -1,5 +1,6 @@ use crate::commands::{JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::structs::JoshutoDirEntry; use crate::textfield::JoshutoTextField; use crate::ui; @@ -73,7 +74,11 @@ impl std::fmt::Display for SetMode { } impl JoshutoRunnable for SetMode { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let mut ok = false; { use std::os::unix::fs::PermissionsExt; @@ -93,5 +98,6 @@ impl JoshutoRunnable for SetMode { curr_tab.refresh_curr(&view.mid_win, context.config_t.scroll_offset); curr_tab.refresh_file_status(&view.bot_win); } + Ok(()) } } diff --git a/src/commands/show_hidden.rs b/src/commands/show_hidden.rs index be41264..10e8ada 100644 --- a/src/commands/show_hidden.rs +++ b/src/commands/show_hidden.rs @@ -1,5 +1,6 @@ use crate::commands::{JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::window::JoshutoView; @@ -33,7 +34,11 @@ impl std::fmt::Display for ToggleHiddenFiles { } impl JoshutoRunnable for ToggleHiddenFiles { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { Self::toggle_hidden(context); let curr_tab = &mut context.tabs[context.curr_tab_index]; curr_tab.reload_contents(&context.config_t.sort_option); @@ -49,5 +54,6 @@ impl JoshutoRunnable for ToggleHiddenFiles { &context.config_t, ); ncurses::doupdate(); + Ok(()) } } diff --git a/src/commands/tab_operations.rs b/src/commands/tab_operations.rs index d1a15a9..5d57560 100644 --- a/src/commands/tab_operations.rs +++ b/src/commands/tab_operations.rs @@ -3,8 +3,8 @@ use std::path; use crate::commands::{JoshutoCommand, JoshutoRunnable, Quit, TabSwitch}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::tab::JoshutoTab; -use crate::ui; use crate::window::JoshutoView; #[derive(Clone, Debug)] @@ -18,13 +18,10 @@ impl NewTab { "new_tab" } - pub fn new_tab(context: &mut JoshutoContext, view: &JoshutoView) { + pub fn new_tab(context: &mut JoshutoContext, view: &JoshutoView) -> Result<(), JoshutoError> { let curr_path: path::PathBuf = match env::current_dir() { Ok(path) => path, - Err(e) => { - ui::wprint_err(&view.bot_win, e.to_string().as_str()); - return; - } + Err(e) => return Err(JoshutoError::IO(e)), }; match JoshutoTab::new(curr_path, &context.config_t.sort_option) { @@ -32,12 +29,10 @@ impl NewTab { context.tabs.push(tab); context.curr_tab_index = context.tabs.len() - 1; - TabSwitch::tab_switch(context.tabs.len() as i32 - 1, context, view); - } - Err(e) => { - ui::wprint_err(&view.bot_win, e.to_string().as_str()); + TabSwitch::tab_switch(context.tabs.len() as i32 - 1, context, view) } - }; + Err(e) => Err(JoshutoError::IO(e)), + } } } @@ -50,9 +45,12 @@ impl std::fmt::Display for NewTab { } impl JoshutoRunnable for NewTab { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { - Self::new_tab(context, view); - ncurses::doupdate(); + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { + Self::new_tab(context, view) } } @@ -67,17 +65,16 @@ impl CloseTab { "close_tab" } - pub fn close_tab(context: &mut JoshutoContext, view: &JoshutoView) { + pub fn close_tab(context: &mut JoshutoContext, view: &JoshutoView) -> Result<(), JoshutoError> { if context.tabs.len() <= 1 { - Quit::quit(context, view); - return; + return Quit::quit(context); } let _ = context.tabs.remove(context.curr_tab_index); if context.curr_tab_index > 0 { context.curr_tab_index -= 1; } - TabSwitch::tab_switch(context.curr_tab_index as i32, context, view); + TabSwitch::tab_switch(context.curr_tab_index as i32, context, view) } } @@ -90,8 +87,11 @@ impl std::fmt::Display for CloseTab { } impl JoshutoRunnable for CloseTab { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { - Self::close_tab(context, view); - ncurses::doupdate(); + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { + Self::close_tab(context, view) } } diff --git a/src/commands/tab_switch.rs b/src/commands/tab_switch.rs index 49e3210..e8ac9ab 100644 --- a/src/commands/tab_switch.rs +++ b/src/commands/tab_switch.rs @@ -2,6 +2,7 @@ use std::env; use crate::commands::{JoshutoCommand, JoshutoRunnable}; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::ui; use crate::window::JoshutoView; @@ -19,7 +20,11 @@ impl TabSwitch { "tab_switch" } - pub fn tab_switch(new_index: i32, context: &mut JoshutoContext, view: &JoshutoView) { + pub fn tab_switch( + new_index: i32, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { context.curr_tab_index = new_index as usize; let path = &context.curr_tab_ref().curr_path; match env::set_current_dir(path) { @@ -37,8 +42,10 @@ impl TabSwitch { ui::redraw_tab_view(&view.tab_win, &context); let curr_tab = &mut context.tabs[context.curr_tab_index]; preview::preview_file(curr_tab, view, &context.config_t); + ncurses::doupdate(); + Ok(()) } - Err(e) => ui::wprint_err(&view.left_win, e.to_string().as_str()), + Err(e) => Err(JoshutoError::IO(e)), } } } @@ -52,7 +59,11 @@ impl std::fmt::Display for TabSwitch { } impl JoshutoRunnable for TabSwitch { - fn execute(&self, context: &mut JoshutoContext, view: &JoshutoView) { + fn execute( + &self, + context: &mut JoshutoContext, + view: &JoshutoView, + ) -> Result<(), JoshutoError> { let mut new_index = context.curr_tab_index as i32 + self.movement; let tab_len = context.tabs.len() as i32; while new_index < 0 { @@ -61,7 +72,6 @@ impl JoshutoRunnable for TabSwitch { while new_index >= tab_len { new_index -= tab_len; } - Self::tab_switch(new_index, context, view); - ncurses::doupdate(); + Self::tab_switch(new_index, context, view) } } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..a964132 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,3 @@ +pub enum JoshutoError { + IO(std::io::Error), +} diff --git a/src/main.rs b/src/main.rs index a4408cd..31f9679 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod commands; mod config; mod context; +mod error; mod history; mod preview; mod run; diff --git a/src/run.rs b/src/run.rs index 6ec4c16..45674f4 100644 --- a/src/run.rs +++ b/src/run.rs @@ -5,6 +5,7 @@ use crate::commands; use crate::commands::{CommandKeybind, JoshutoCommand}; use crate::config; use crate::context::JoshutoContext; +use crate::error::JoshutoError; use crate::preview; use crate::ui; use crate::window::JoshutoPanel; @@ -54,80 +55,52 @@ fn process_threads(context: &mut JoshutoContext, view: &JoshutoView) { let mut i: usize = 0; while i < context.threads.len() { match &context.threads[i].recv_timeout(&thread_wait_duration) { - Ok(progress_info) => { - if progress_info.bytes_finished == progress_info.total_bytes { - ncurses::werase(view.bot_win.win); - let thread = context.threads.swap_remove(i); - thread.handle.join().unwrap(); - let (tab_src, tab_dest) = (thread.tab_src, thread.tab_dest); - if tab_src < context.tabs.len() { - context.tabs[tab_src].reload_contents(&context.config_t.sort_option); - if tab_src == context.curr_tab_index { - context.tabs[tab_src].refresh( - view, - &context.config_t, - &context.username, - &context.hostname, - ); - } - } - if tab_dest != tab_src && tab_dest < context.tabs.len() { - context.tabs[tab_dest].reload_contents(&context.config_t.sort_option); - if tab_dest == context.curr_tab_index { - context.tabs[tab_dest].refresh( - view, - &context.config_t, - &context.username, - &context.hostname, - ); - } - } - } else { - let percent = (progress_info.bytes_finished as f64 - / progress_info.total_bytes as f64) - as f32; - ui::draw_progress_bar(&view.bot_win, percent); - ncurses::wnoutrefresh(view.bot_win.win); - i += 1; - } - ncurses::doupdate(); - } Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => { ncurses::werase(view.bot_win.win); let thread = context.threads.swap_remove(i); let (tab_src, tab_dest) = (thread.tab_src, thread.tab_dest); - thread.handle.join().unwrap(); - if tab_src < context.tabs.len() { - let dirty_tab = &mut context.tabs[tab_src]; - dirty_tab.reload_contents(&context.config_t.sort_option); - if tab_src == context.curr_tab_index { - dirty_tab.refresh( - view, - &context.config_t, - &context.username, - &context.hostname, - ); - preview::preview_file(dirty_tab, view, &context.config_t); + match thread.handle.join() { + Err(e) => { + ui::wprint_err(&view.bot_win, format!("{:?}", e).as_str()); + ncurses::doupdate(); } - } - if tab_dest != tab_src && tab_dest < context.tabs.len() { - let dirty_tab = &mut context.tabs[tab_dest]; - dirty_tab.reload_contents(&context.config_t.sort_option); - if tab_src == context.curr_tab_index { - dirty_tab.refresh( - view, - &context.config_t, - &context.username, - &context.hostname, - ); - preview::preview_file(dirty_tab, view, &context.config_t); + Ok(_) => { + if tab_src < context.tabs.len() { + let dirty_tab = &mut context.tabs[tab_src]; + dirty_tab.reload_contents(&context.config_t.sort_option); + if tab_src == context.curr_tab_index { + dirty_tab.refresh( + view, + &context.config_t, + &context.username, + &context.hostname, + ); + preview::preview_file(dirty_tab, view, &context.config_t); + } + } + if tab_dest != tab_src && tab_dest < context.tabs.len() { + let dirty_tab = &mut context.tabs[tab_dest]; + dirty_tab.reload_contents(&context.config_t.sort_option); + if tab_src == context.curr_tab_index { + dirty_tab.refresh( + view, + &context.config_t, + &context.username, + &context.hostname, + ); + preview::preview_file(dirty_tab, view, &context.config_t); + } + } + ncurses::doupdate(); } - } - ncurses::doupdate(); + }; } Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { i += 1; } + Ok(progress_info) => { + i += 1; + } } } } @@ -199,7 +172,13 @@ pub fn run(config_t: config::JoshutoConfig, keymap_t: config::JoshutoKeymap) { continue; } } - keycommand.execute(&mut context, &view); + match keycommand.execute(&mut context, &view) { + Ok(()) => {} + Err(JoshutoError::IO(e)) => { + ui::wprint_err(&view.bot_win, e.to_string().as_str()); + ncurses::doupdate(); + } + } } } ui::end_ncurses(); -- cgit v1.2.3