diff options
author | Jiayi Zhao <jeff.no.zhao@gmail.com> | 2020-02-09 12:39:31 -0500 |
---|---|---|
committer | Jiayi Zhao <jeff.no.zhao@gmail.com> | 2020-02-09 13:07:31 -0500 |
commit | becbb90b2f22d58c98693d653f55ba604bb03f75 (patch) | |
tree | 085b9ac9b197a9ad3f0dd20df36848c5619adefe /src/commands/file_ops | |
parent | 656582a6c867c25667661be9b327b4cc73859d7d (diff) |
rework input thread and file operations
- no longer depend on fs_extra for copy/paste files
- in house solution preserves permissions
- ioworkers are now queued, no more parallel io tasks
- input thread now listens for ioworker threads as well
- cargo fmt
Diffstat (limited to 'src/commands/file_ops')
-rw-r--r-- | src/commands/file_ops/copy.rs | 36 | ||||
-rw-r--r-- | src/commands/file_ops/cut.rs | 36 | ||||
-rw-r--r-- | src/commands/file_ops/local_state.rs | 56 | ||||
-rw-r--r-- | src/commands/file_ops/mod.rs | 11 | ||||
-rw-r--r-- | src/commands/file_ops/name_resolution.rs | 15 | ||||
-rw-r--r-- | src/commands/file_ops/paste.rs | 55 | ||||
-rw-r--r-- | src/commands/file_ops/paste_copy.rs | 68 | ||||
-rw-r--r-- | src/commands/file_ops/paste_cut.rs | 76 |
8 files changed, 353 insertions, 0 deletions
diff --git a/src/commands/file_ops/copy.rs b/src/commands/file_ops/copy.rs new file mode 100644 index 0000000..a1a562e --- /dev/null +++ b/src/commands/file_ops/copy.rs @@ -0,0 +1,36 @@ +use crate::commands::{JoshutoCommand, JoshutoRunnable}; +use crate::context::JoshutoContext; +use crate::error::JoshutoResult; +use crate::window::JoshutoView; + +use super::local_state::{FileOp, LocalState}; + +#[derive(Clone, Debug)] +pub struct CopyFiles; + +impl CopyFiles { + pub fn new() -> Self { + CopyFiles + } + pub const fn command() -> &'static str { + "copy_files" + } +} + +impl JoshutoCommand for CopyFiles {} + +impl std::fmt::Display for CopyFiles { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(Self::command()) + } +} + +impl JoshutoRunnable for CopyFiles { + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> JoshutoResult<()> { + let curr_tab = context.curr_tab_ref(); + LocalState::repopulated_selected_files(&curr_tab.curr_list)?; + LocalState::set_file_op(FileOp::Copy); + LocalState::set_tab_src(context.curr_tab_index); + Ok(()) + } +} diff --git a/src/commands/file_ops/cut.rs b/src/commands/file_ops/cut.rs new file mode 100644 index 0000000..f466ca7 --- /dev/null +++ b/src/commands/file_ops/cut.rs @@ -0,0 +1,36 @@ +use crate::commands::{JoshutoCommand, JoshutoRunnable}; +use crate::context::JoshutoContext; +use crate::error::JoshutoResult; +use crate::window::JoshutoView; + +use super::local_state::{FileOp, LocalState}; + +#[derive(Clone, Debug)] +pub struct CutFiles; + +impl CutFiles { + pub fn new() -> Self { + CutFiles + } + pub const fn command() -> &'static str { + "cut_files" + } +} + +impl JoshutoCommand for CutFiles {} + +impl std::fmt::Display for CutFiles { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(Self::command()) + } +} + +impl JoshutoRunnable for CutFiles { + fn execute(&self, context: &mut JoshutoContext, _: &JoshutoView) -> JoshutoResult<()> { + let curr_tab = context.curr_tab_ref(); + LocalState::repopulated_selected_files(&curr_tab.curr_list)?; + LocalState::set_file_op(FileOp::Cut); + LocalState::set_tab_src(context.curr_tab_index); + Ok(()) + } +} diff --git a/src/commands/file_ops/local_state.rs b/src/commands/file_ops/local_state.rs new file mode 100644 index 0000000..2849893 --- /dev/null +++ b/src/commands/file_ops/local_state.rs @@ -0,0 +1,56 @@ +use lazy_static::lazy_static; + +use std::path; +use std::sync::{atomic, Mutex}; + +use crate::fs::JoshutoDirList; + +lazy_static! { + static ref SELECTED_FILES: Mutex<Option<Vec<path::PathBuf>>> = Mutex::new(None); + static ref FILE_OPERATION: Mutex<FileOp> = Mutex::new(FileOp::Copy); + static ref TAB_SRC: atomic::AtomicUsize = atomic::AtomicUsize::new(0); +} + +#[derive(Clone, Debug)] +pub enum FileOp { + Cut, + Copy, +} + +pub struct LocalState; + +impl LocalState { + pub fn set_file_op(operation: FileOp) { + let mut data = FILE_OPERATION.lock().unwrap(); + *data = operation; + } + + pub fn set_tab_src(tab_index: usize) { + TAB_SRC.store(tab_index, atomic::Ordering::Release); + } + + pub fn repopulated_selected_files(dirlist: &JoshutoDirList) -> std::io::Result<()> { + let selected = dirlist.get_selected_paths(); + if selected.is_empty() { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "no files selected", + )) + } else { + let selected_clone: Vec<path::PathBuf> = + selected.iter().map(|p| (*p).clone()).collect(); + let mut data = SELECTED_FILES.lock().unwrap(); + *data = Some(selected_clone); + Ok(()) + } + } + + pub fn take_selected_files() -> Option<Vec<path::PathBuf>> { + let paths = SELECTED_FILES.lock().unwrap().take(); + paths + } + + pub fn get_file_operation() -> FileOp { + (*FILE_OPERATION.lock().unwrap()).clone() + } +} diff --git a/src/commands/file_ops/mod.rs b/src/commands/file_ops/mod.rs new file mode 100644 index 0000000..e4a0da3 --- /dev/null +++ b/src/commands/file_ops/mod.rs @@ -0,0 +1,11 @@ +mod copy; +mod cut; +mod local_state; +mod name_resolution; +mod paste; +mod paste_copy; +mod paste_cut; + +pub use self::copy::CopyFiles; +pub use self::cut::CutFiles; +pub use self::paste::PasteFiles; diff --git a/src/commands/file_ops/name_resolution.rs b/src/commands/file_ops/name_resolution.rs new file mode 100644 index 0000000..a8e520b --- /dev/null +++ b/src/commands/file_ops/name_resolution.rs @@ -0,0 +1,15 @@ +use std::path; + +pub fn rename_filename_conflict(path: &mut path::PathBuf) { + let file_name = path.file_name().unwrap().to_os_string(); + for i in 0.. { + if !path.exists() { + break; + } + path.pop(); + + let mut file_name = file_name.clone(); + file_name.push(&format!("_{}", i)); + path.push(file_name); + } +} diff --git a/src/commands/file_ops/paste.rs b/src/commands/file_ops/paste.rs new file mode 100644 index 0000000..ba5dc43 --- /dev/null +++ b/src/commands/file_ops/paste.rs @@ -0,0 +1,55 @@ +use crate::commands::{JoshutoCommand, JoshutoRunnable}; +use crate::context::JoshutoContext; +use crate::error::JoshutoResult; +use crate::io::Options; +use crate::window::JoshutoView; + +use super::local_state::{FileOp, LocalState}; +use super::paste_copy::paste_copy; +use super::paste_cut::paste_cut; + +pub struct PasteFiles { + options: Options, +} + +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) -> JoshutoResult<()> { + let file_operation = LocalState::get_file_operation(); + let thread = match file_operation { + FileOp::Copy => paste_copy(context, self.options.clone()), + FileOp::Cut => paste_cut(context, self.options.clone()), + }; + let thread = thread?; + context.add_new_worker(thread); + Ok(()) + } +} + +impl PasteFiles { + pub fn new(options: Options) -> Self { + PasteFiles { options } + } + pub const fn command() -> &'static str { + "paste_files" + } +} diff --git a/src/commands/file_ops/paste_copy.rs b/src/commands/file_ops/paste_copy.rs new file mode 100644 index 0000000..26f0e5c --- /dev/null +++ b/src/commands/file_ops/paste_copy.rs @@ -0,0 +1,68 @@ +use std::fs; +use std::path::Path; +use std::sync::mpsc; +use std::thread; + +use crate::context::JoshutoContext; +use crate::io::{IOWorkerThread, Options}; +use crate::util::event::Event; + +use super::local_state::LocalState; +use super::name_resolution::rename_filename_conflict; + +pub fn recursive_copy(dest: &Path, src: &Path, options: &Options) -> std::io::Result<u64> { + let mut dest_buf = dest.to_path_buf(); + if let Some(s) = src.file_name() { + dest_buf.push(s); + } + rename_filename_conflict(&mut dest_buf); + if !src.is_dir() { + std::fs::copy(src, dest_buf) + } else { + fs::create_dir(dest_buf.as_path())?; + let mut total = 0; + for entry in fs::read_dir(src)? { + let entry = entry?; + let entry_path = entry.path(); + total += recursive_copy(dest_buf.as_path(), entry_path.as_path(), options)?; + } + Ok(total) + } +} + +pub fn paste_copy( + context: &mut JoshutoContext, + options: Options, +) -> std::io::Result<IOWorkerThread> { + let paths = LocalState::take_selected_files() + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "no files selected"))?; + if paths.is_empty() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "no files selected", + )); + } + + let tab_dest = context.curr_tab_index; + let dest = context.tabs[tab_dest].curr_path.clone(); + + let (tx_start, rx_start) = mpsc::channel(); + let (tx, rx) = mpsc::channel(); + let handle: thread::JoinHandle<std::io::Result<u64>> = thread::spawn(move || { + let mut total = 0; + rx_start.recv(); + for path in paths { + total += recursive_copy(dest.as_path(), path.as_path(), &options)?; + tx.send(Event::IOWorkerProgress(total)); + } + Ok(total) + }); + + let thread = IOWorkerThread { + handle, + tx_start, + rx, + }; + + Ok(thread) +} diff --git a/src/commands/file_ops/paste_cut.rs b/src/commands/file_ops/paste_cut.rs new file mode 100644 index 0000000..1177f12 --- /dev/null +++ b/src/commands/file_ops/paste_cut.rs @@ -0,0 +1,76 @@ +use std::fs; +use std::path::Path; +use std::sync::mpsc; +use std::thread; + +use crate::context::JoshutoContext; +use crate::io::{IOWorkerThread, Options}; +use crate::util::event::Event; + +use super::local_state::LocalState; +use super::name_resolution::rename_filename_conflict; + +pub fn recursive_cut(dest: &Path, src: &Path, options: &Options) -> std::io::Result<u64> { + let mut dest_buf = dest.to_path_buf(); + if let Some(s) = src.file_name() { + dest_buf.push(s); + } + rename_filename_conflict(&mut dest_buf); + if !src.is_dir() { + let metadata = src.metadata()?; + match std::fs::rename(src, dest_buf.as_path()) { + Err(_) => { + std::fs::copy(src, dest_buf.as_path())?; + std::fs::remove_file(src)?; + } + _ => {} + } + Ok(metadata.len()) + } else { + fs::create_dir(dest_buf.as_path())?; + let mut total = 0; + for entry in fs::read_dir(src)? { + let entry = entry?; + let entry_path = entry.path(); + total += recursive_cut(dest_buf.as_path(), entry_path.as_path(), options)?; + } + Ok(total) + } +} + +pub fn paste_cut( + context: &mut JoshutoContext, + options: Options, +) -> std::io::Result<IOWorkerThread> { + let paths = LocalState::take_selected_files() + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "no files selected"))?; + if paths.is_empty() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "no files selected", + )); + } + + let tab_dest = context.curr_tab_index; + let dest = context.tabs[tab_dest].curr_path.clone(); + + let (tx_start, rx_start) = mpsc::channel(); + let (tx, rx) = mpsc::channel(); + let handle: thread::JoinHandle<std::io::Result<u64>> = thread::spawn(move || { + let mut total = 0; + rx_start.recv(); + for path in paths { + total += recursive_cut(dest.as_path(), path.as_path(), &options)?; + tx.send(Event::IOWorkerProgress(total)); + } + Ok(total) + }); + + let thread = IOWorkerThread { + handle, + tx_start, + rx, + }; + + Ok(thread) +} |