//! ## FileTransferActivity //! //! `filetransfer_activity` is the module which implements the Filetransfer activity, which is the main activity afterall // locals // ext use std::path::{Path, PathBuf}; use tuirealm::terminal::TerminalAdapter; use super::{File, FileTransferActivity, LogLevel, TransferPayload}; impl FileTransferActivity { /// Open selected file(s) with default application pub(crate) fn action_open(&mut self) { let entries = self.get_selected_entries().get_files(); entries.iter().for_each(|x| self.open_file(x, None)); // clear queue self.browser.explorer_mut().clear_queue(); self.reload_browser_file_list(); } /// Open selected file(s) with provided application pub(crate) fn action_open_with(&mut self, with: &str) { let entries = self.get_selected_entries().get_files(); entries.iter().for_each(|x| self.open_file(x, Some(with))); // clear queue self.browser.explorer_mut().clear_queue(); self.reload_browser_file_list(); } /// Open a file, dispatching based on whether the active pane is localhost. pub(crate) fn open_file(&mut self, entry: &File, open_with: Option<&str>) { if self.browser.fs_pane().fs.is_localhost() { // Direct open from local path self.open_path_with(entry.path(), open_with); } else if self.is_local_tab() { // Non-localhost host bridge: download via HostBridge API self.open_bridged_file(entry, open_with); } else { // Remote: download via filetransfer_recv self.action_open_remote_file(entry, open_with); } } /// Open remote file. The file is first downloaded to a temporary directory on localhost fn action_open_remote_file(&mut self, entry: &File, open_with: Option<&str>) { // Download file let tmpfile: String = match self.get_cache_tmp_name(&entry.name(), entry.extension().as_deref()) { None => { self.log(LogLevel::Error, String::from("Could not create tempdir")); return; } Some(p) => p, }; let cache: PathBuf = match self.cache.as_ref() { None => { self.log(LogLevel::Error, String::from("Could not create tempdir")); return; } Some(p) => p.path().to_path_buf(), }; match self.filetransfer_recv( TransferPayload::Any(entry.clone()), cache.as_path(), Some(tmpfile.clone()), ) { Ok(_) => { // Make file and open if file exists let mut tmp: PathBuf = cache; tmp.push(tmpfile.as_str()); if tmp.exists() { self.open_path_with(tmp.as_path(), open_with); } } Err(err) => { self.log( LogLevel::Error, format!("Failed to download remote entry: {err}"), ); } } } fn open_bridged_file(&mut self, entry: &File, open_with: Option<&str>) { // Download file let tmpfile: String = match self.get_cache_tmp_name(&entry.name(), entry.extension().as_deref()) { None => { self.log(LogLevel::Error, String::from("Could not create tempdir")); return; } Some(p) => p, }; let cache: PathBuf = match self.cache.as_ref() { None => { self.log(LogLevel::Error, String::from("Could not create tempdir")); return; } Some(p) => p.path().to_path_buf(), }; let tmpfile = cache.join(tmpfile); // open from host bridge let mut reader = match self.browser.local_pane_mut().fs.open_file(entry.path()) { Ok(reader) => reader, Err(err) => { self.log( LogLevel::Error, format!("Failed to open bridged entry: {err}"), ); return; } }; // write to file let mut writer = match std::fs::File::create(tmpfile.as_path()) { Ok(writer) => writer, Err(err) => { self.log(LogLevel::Error, format!("Failed to create file: {err}")); return; } }; if let Err(err) = std::io::copy(&mut reader, &mut writer) { self.log(LogLevel::Error, format!("Failed to write file: {err}")); return; } if tmpfile.exists() { self.open_path_with(tmpfile.as_path(), open_with); } } /// Common function which opens a path with default or specified program. fn open_path_with(&mut self, p: &Path, with: Option<&str>) { // Open file let result = match with { None => open::that(p), Some(with) => open::with(p, with), }; // Log result match result { Ok(_) => self.log(LogLevel::Info, format!("Opened file `{}`", p.display())), Err(err) => self.log( LogLevel::Error, format!("Failed to open file `{}`: {}", p.display(), err), ), } // NOTE: clear screen in order to prevent crap on stderr if let Some(ctx) = self.context.as_mut() { // Clear screen if let Err(err) = ctx.terminal().clear_screen() { error!("Could not clear screen screen: {}", err); } } } }