feat: HostBridge trait impl

This commit is contained in:
veeso
2024-10-05 18:22:56 +02:00
parent a4c9acb49f
commit 87f9369041
8 changed files with 1141 additions and 1001 deletions

65
src/host/bridge.rs Normal file
View File

@@ -0,0 +1,65 @@
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use remotefs::fs::{Metadata, UnixPex};
use remotefs::File;
use super::HostResult;
/// Trait to bridge a remote filesystem to the host filesystem
///
/// In case of `Localhost` this should be effortless, while for remote hosts this should
/// implement a real bridge when the resource is first loaded on the local
/// filesystem and then processed on the remote.
pub trait HostBridge {
/// Print working directory
fn pwd(&mut self) -> HostResult<PathBuf>;
/// Change working directory with the new provided directory
fn change_wrkdir(&mut self, new_dir: &Path) -> HostResult<PathBuf>;
/// Make a directory at path and update the file list (only if relative)
fn mkdir(&mut self, dir_name: &Path) -> HostResult<()> {
self.mkdir_ex(dir_name, false)
}
/// Extended option version of makedir.
/// ignex: don't report error if directory already exists
fn mkdir_ex(&mut self, dir_name: &Path, ignore_existing: bool) -> HostResult<()>;
/// Remove file entry
fn remove(&mut self, entry: &File) -> HostResult<()>;
/// Rename file or directory to new name
fn rename(&mut self, entry: &File, dst_path: &Path) -> HostResult<()>;
/// Copy file to destination path
fn copy(&mut self, entry: &File, dst: &Path) -> HostResult<()>;
/// Stat file and create a File
fn stat(&mut self, path: &Path) -> HostResult<File>;
/// Returns whether provided file path exists
fn exists(&mut self, path: &Path) -> HostResult<bool>;
/// Get content of a directory
fn list_dir(&mut self, path: &Path) -> HostResult<Vec<File>>;
/// Set file stat
fn setstat(&mut self, path: &Path, metadata: &Metadata) -> HostResult<()>;
/// Execute a command on localhost
fn exec(&mut self, cmd: &str) -> HostResult<String>;
/// Create a symlink from src to dst
fn symlink(&mut self, src: &Path, dst: &Path) -> HostResult<()>;
/// Change file mode to file, according to UNIX permissions
fn chmod(&mut self, path: &Path, pex: UnixPex) -> HostResult<()>;
/// Open file for reading
fn open_file(&mut self, file: &Path) -> HostResult<Box<dyn Read + Send>>;
/// Open file for writing
fn create_file(&mut self, file: &Path) -> HostResult<Box<dyn Write + Send>>;
}

1012
src/host/localhost.rs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -129,7 +129,17 @@ impl FileTransferActivity {
return;
}
},
FileExplorerTab::Remote => self.host.file_exists(path.as_path()),
FileExplorerTab::Remote => match self.host.exists(path.as_path()) {
Ok(e) => e,
Err(err) => {
error!(
"Failed to check whether {} exists on host: {}",
path.display(),
err
);
return;
}
},
_ => return,
};
let name = path

View File

@@ -23,7 +23,7 @@ impl FileTransferActivity {
}
// Create file
let file_path: PathBuf = PathBuf::from(input.as_str());
if let Err(err) = self.host.open_file_write(file_path.as_path()) {
if let Err(err) = self.host.create_file(file_path.as_path()) {
self.log_and_alert(
LogLevel::Error,
format!("Could not create file \"{}\": {}", file_path.display(), err),

View File

@@ -18,7 +18,12 @@ impl FileTransferActivity {
pub(crate) fn action_walkdir_local(&mut self) -> Result<Vec<File>, WalkdirError> {
let mut acc = Vec::with_capacity(32_768);
self.walkdir(&mut acc, &self.host.pwd(), |activity, path| {
let pwd = self
.host
.pwd()
.map_err(|e| WalkdirError::Error(e.to_string()))?;
self.walkdir(&mut acc, &pwd, |activity, path| {
activity.host.list_dir(path).map_err(|e| e.to_string())
})?;

View File

@@ -32,7 +32,7 @@ use super::{Activity, Context, ExitReason};
use crate::config::themes::Theme;
use crate::explorer::{FileExplorer, FileSorting};
use crate::filetransfer::{Builder, FileTransferParams};
use crate::host::Localhost;
use crate::host::{HostBridge, Localhost};
use crate::system::config_client::ConfigClient;
use crate::system::watcher::FsWatcher;
@@ -214,7 +214,7 @@ pub struct FileTransferActivity {
/// Whether should redraw UI
redraw: bool,
/// Localhost bridge
host: Localhost,
host: Box<dyn HostBridge>,
/// Remote host client
client: Box<dyn RemoteFs>,
/// Browser
@@ -247,7 +247,7 @@ impl FileTransferActivity {
.default_input_listener(ticks),
),
redraw: true,
host,
host: Box::new(host),
client: Builder::build(params.protocol, params.params.clone(), &config_client),
browser: Browser::new(&config_client),
log_records: VecDeque::with_capacity(256), // 256 events is enough I guess

View File

@@ -3,8 +3,7 @@
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
// Locals
use std::fs::File as StdFile;
use std::io::{Read, Seek, Write};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::time::Instant;
@@ -26,8 +25,6 @@ const BUFSIZE: usize = 65535;
enum TransferErrorReason {
#[error("File transfer aborted")]
Abrupted,
#[error("Failed to seek file: {0}")]
CouldNotRewind(std::io::Error),
#[error("I/O error on localhost: {0}")]
LocalIoError(std::io::Error),
#[error("Host error: {0}")]
@@ -153,7 +150,10 @@ impl FileTransferActivity {
pub(super) fn reload_local_dir(&mut self) {
self.mount_blocking_wait("Loading local directory...");
let wrkdir: PathBuf = self.host.pwd();
let Ok(wrkdir) = self.host.pwd() else {
error!("failed to get host working directory");
return;
};
let res = self.local_scan(wrkdir.as_path());
@@ -460,13 +460,12 @@ impl FileTransferActivity {
}
// Upload file
// Try to open local file
match self.host.open_file_read(local.path.as_path()) {
Ok(fhnd) => match self.client.create(remote, &metadata) {
Ok(rhnd) => {
self.filetransfer_send_one_with_stream(local, remote, file_name, fhnd, rhnd)
}
match self.host.open_file(local.path.as_path()) {
Ok(local_read) => match self.client.create(remote, &metadata) {
Ok(rhnd) => self
.filetransfer_send_one_with_stream(local, remote, file_name, local_read, rhnd),
Err(err) if err.kind == RemoteErrorType::UnsupportedFeature => {
self.filetransfer_send_one_wno_stream(local, remote, file_name, fhnd)
self.filetransfer_send_one_wno_stream(local, remote, file_name, local_read)
}
Err(err) => Err(TransferErrorReason::FileTransferError(err)),
},
@@ -480,17 +479,18 @@ impl FileTransferActivity {
local: &File,
remote: &Path,
file_name: String,
mut reader: StdFile,
mut reader: Box<dyn Read + Send>,
mut writer: WriteStream,
) -> Result<(), TransferErrorReason> {
// Write file
let file_size: usize = reader.seek(std::io::SeekFrom::End(0)).unwrap_or(0) as usize;
let file_size = self
.host
.stat(local.path())
.map_err(TransferErrorReason::HostError)
.map(|x| x.metadata().size as usize)?;
// Init transfer
self.transfer.partial.init(file_size);
// rewind
if let Err(err) = reader.rewind() {
return Err(TransferErrorReason::CouldNotRewind(err));
}
// Write remote file
let mut total_bytes_written: usize = 0;
let mut last_progress_val: f64 = 0.0;
@@ -583,7 +583,7 @@ impl FileTransferActivity {
local: &File,
remote: &Path,
file_name: String,
mut reader: StdFile,
reader: Box<dyn Read + Send>,
) -> Result<(), TransferErrorReason> {
// Sync file size and attributes before transfer
let metadata = self
@@ -592,18 +592,19 @@ impl FileTransferActivity {
.map_err(TransferErrorReason::HostError)
.map(|x| x.metadata().clone())?;
// Write file
let file_size: usize = reader.seek(std::io::SeekFrom::End(0)).unwrap_or(0) as usize;
let file_size = self
.host
.stat(local.path())
.map_err(TransferErrorReason::HostError)
.map(|x| x.metadata().size as usize)?;
// Init transfer
self.transfer.partial.init(file_size);
// rewind
if let Err(err) = reader.rewind() {
return Err(TransferErrorReason::CouldNotRewind(err));
}
// Draw before
self.update_progress_bar(format!("Uploading \"{file_name}\""));
self.view();
// Send file
if let Err(err) = self.client.create_file(remote, &metadata, Box::new(reader)) {
if let Err(err) = self.client.create_file(remote, &metadata, reader) {
return Err(TransferErrorReason::FileTransferError(err));
}
// set stat
@@ -889,7 +890,7 @@ impl FileTransferActivity {
}
// Try to open local file
match self.host.open_file_write(local) {
match self.host.create_file(local) {
Ok(local_file) => {
// Download file from remote
match self.client.open(remote.path.as_path()) {
@@ -913,7 +914,7 @@ impl FileTransferActivity {
remote: &File,
file_name: String,
mut reader: ReadStream,
mut writer: StdFile,
mut writer: Box<dyn Write + Send>,
) -> Result<(), TransferErrorReason> {
let mut total_bytes_written: usize = 0;
// Init transfer
@@ -1020,7 +1021,7 @@ impl FileTransferActivity {
// Open local file
let reader = self
.host
.open_file_write(local)
.create_file(local)
.map_err(TransferErrorReason::HostError)
.map(Box::new)?;
// Init transfer
@@ -1207,7 +1208,7 @@ impl FileTransferActivity {
// file changed
/// Check whether provided file has changed on local disk, compared to remote file
fn has_local_file_changed(&self, local: &Path, remote: &File) -> bool {
fn has_local_file_changed(&mut self, local: &Path, remote: &File) -> bool {
// check if files are equal (in case, don't transfer)
if let Ok(local_file) = self.host.stat(local) {
local_file.metadata().modified != remote.metadata().modified
@@ -1231,10 +1232,10 @@ impl FileTransferActivity {
// -- file exist
pub(crate) fn local_file_exists(&mut self, p: &Path) -> bool {
self.host.file_exists(p)
self.host.exists(p).unwrap_or_default()
}
pub(crate) fn remote_file_exists(&mut self, p: &Path) -> bool {
self.client.stat(p).is_ok()
self.client.exists(p).unwrap_or_default()
}
}