mirror of
https://github.com/veeso/termscp.git
synced 2026-06-06 02:25:44 +02:00
* fix: replace recursive byte-counting with entry-based transfer progress Replace the expensive recursive `get_total_transfer_size` pre-calculation with a lightweight entry-based counter (`TransferProgress`) for the overall progress bar. This avoids deep `list_dir` traversals before transfers begin, which could cause FTP idle-timeout disconnections on large directory trees. The per-file byte-level progress bar (`ProgressStates`) remains unchanged. Bytes are still tracked via `TransferStates::add_bytes` for notification threshold logic. Closes #384
248 lines
7.4 KiB
Rust
248 lines
7.4 KiB
Rust
//! ## FileTransferActivity
|
|
//!
|
|
//! `filetransfer_activity` is the module which implements the Filetransfer activity, which is the main activity afterall
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use remotefs::fs::{File, Metadata};
|
|
|
|
use super::transfer::TransferPayload;
|
|
use crate::host::HostError;
|
|
use crate::ui::activities::filetransfer::{FileTransferActivity, LogLevel};
|
|
|
|
impl FileTransferActivity {
|
|
// -- reload directory --
|
|
|
|
/// Reload remote directory entries and update browser
|
|
pub(in crate::ui::activities::filetransfer) fn reload_remote_dir(&mut self) {
|
|
self.reload_dir_on(false);
|
|
}
|
|
|
|
/// Reload host_bridge directory entries and update browser
|
|
pub(in crate::ui::activities::filetransfer) fn reload_host_bridge_dir(&mut self) {
|
|
self.reload_dir_on(true);
|
|
}
|
|
|
|
/// Reload directory entries for the specified side.
|
|
fn reload_dir_on(&mut self, local: bool) {
|
|
let pane = if local {
|
|
self.browser.local_pane()
|
|
} else {
|
|
self.browser.remote_pane()
|
|
};
|
|
if !pane.connected {
|
|
return;
|
|
}
|
|
|
|
self.mount_blocking_wait("Loading directory...");
|
|
|
|
let pane = if local {
|
|
self.browser.local_pane_mut()
|
|
} else {
|
|
self.browser.remote_pane_mut()
|
|
};
|
|
let wrkdir = match pane.fs.pwd() {
|
|
Ok(wrkdir) => wrkdir,
|
|
Err(err) => {
|
|
self.log_and_alert(
|
|
LogLevel::Error,
|
|
format!("Could not scan current directory: {err}"),
|
|
);
|
|
self.umount_wait();
|
|
return;
|
|
}
|
|
};
|
|
|
|
let res = self.scan_on(local, wrkdir.as_path());
|
|
|
|
self.umount_wait();
|
|
|
|
match res {
|
|
Ok(_) => {
|
|
let explorer = if local {
|
|
self.host_bridge_mut()
|
|
} else {
|
|
self.remote_mut()
|
|
};
|
|
explorer.wrkdir = wrkdir;
|
|
}
|
|
Err(err) => {
|
|
self.log_and_alert(
|
|
LogLevel::Error,
|
|
format!("Could not scan current directory: {err}"),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Scan a directory on the specified side and update the explorer file list.
|
|
fn scan_on(&mut self, local: bool, path: &Path) -> Result<(), HostError> {
|
|
let pane = if local {
|
|
self.browser.local_pane_mut()
|
|
} else {
|
|
self.browser.remote_pane_mut()
|
|
};
|
|
let files = pane.fs.list_dir(path)?;
|
|
|
|
let explorer = if local {
|
|
self.host_bridge_mut()
|
|
} else {
|
|
self.remote_mut()
|
|
};
|
|
explorer.set_files(files);
|
|
Ok(())
|
|
}
|
|
|
|
// -- change directory --
|
|
|
|
/// Change directory on the current tab's pane (no reload).
|
|
pub(in crate::ui::activities::filetransfer) fn pane_changedir(
|
|
&mut self,
|
|
path: &Path,
|
|
push: bool,
|
|
) {
|
|
let prev_dir: PathBuf = self.browser.fs_pane().explorer.wrkdir.clone();
|
|
match self.browser.fs_pane_mut().fs.change_wrkdir(path) {
|
|
Ok(_) => {
|
|
self.log(
|
|
LogLevel::Info,
|
|
format!("Changed directory: {}", path.display()),
|
|
);
|
|
if push {
|
|
self.browser
|
|
.fs_pane_mut()
|
|
.explorer
|
|
.pushd(prev_dir.as_path());
|
|
}
|
|
}
|
|
Err(err) => {
|
|
self.log_and_alert(
|
|
LogLevel::Error,
|
|
format!("Could not change working directory: {err}"),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Change directory on the local pane and reload.
|
|
pub(in crate::ui::activities::filetransfer) fn local_changedir(
|
|
&mut self,
|
|
path: &Path,
|
|
push: bool,
|
|
) {
|
|
let prev_dir: PathBuf = self.host_bridge().wrkdir.clone();
|
|
match self.browser.local_pane_mut().fs.change_wrkdir(path) {
|
|
Ok(_) => {
|
|
self.log(
|
|
LogLevel::Info,
|
|
format!("Changed directory on host bridge: {}", path.display()),
|
|
);
|
|
self.reload_host_bridge_dir();
|
|
if push {
|
|
self.host_bridge_mut().pushd(prev_dir.as_path())
|
|
}
|
|
}
|
|
Err(err) => {
|
|
self.log_and_alert(
|
|
LogLevel::Error,
|
|
format!("Could not change working directory: {err}"),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Change directory on the remote pane and reload.
|
|
pub(in crate::ui::activities::filetransfer) fn remote_changedir(
|
|
&mut self,
|
|
path: &Path,
|
|
push: bool,
|
|
) {
|
|
let prev_dir: PathBuf = self.remote().wrkdir.clone();
|
|
match self.browser.remote_pane_mut().fs.change_wrkdir(path) {
|
|
Ok(_) => {
|
|
self.log(
|
|
LogLevel::Info,
|
|
format!("Changed directory on remote: {}", path.display()),
|
|
);
|
|
self.reload_remote_dir();
|
|
if push {
|
|
self.remote_mut().pushd(prev_dir.as_path())
|
|
}
|
|
}
|
|
Err(err) => {
|
|
self.log_and_alert(
|
|
LogLevel::Error,
|
|
format!("Could not change working directory: {err}"),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// -- temporary file download --
|
|
|
|
/// Download provided file as a temporary file
|
|
pub(in crate::ui::activities::filetransfer) fn download_file_as_temp(
|
|
&mut self,
|
|
file: &File,
|
|
) -> Result<PathBuf, String> {
|
|
let tmpfile: PathBuf = match self.cache.as_ref() {
|
|
Some(cache) => {
|
|
let mut p: PathBuf = cache.path().to_path_buf();
|
|
p.push(file.name());
|
|
p
|
|
}
|
|
None => {
|
|
return Err(String::from(
|
|
"Could not create tempfile: cache not available",
|
|
));
|
|
}
|
|
};
|
|
match self.filetransfer_recv(
|
|
TransferPayload::File(file.clone()),
|
|
tmpfile.as_path(),
|
|
Some(file.name()),
|
|
) {
|
|
Err(err) => Err(format!(
|
|
"Could not download {} to temporary file: {}",
|
|
file.path.display(),
|
|
err
|
|
)),
|
|
Ok(()) => Ok(tmpfile),
|
|
}
|
|
}
|
|
|
|
// -- file changed --
|
|
|
|
/// Check whether a file has changed on the specified side, compared to the given metadata.
|
|
pub(super) fn has_file_changed(
|
|
&mut self,
|
|
path: &Path,
|
|
other_metadata: &Metadata,
|
|
local: bool,
|
|
) -> bool {
|
|
let stat_result = if local {
|
|
self.browser.local_pane_mut().fs.stat(path)
|
|
} else {
|
|
self.browser.remote_pane_mut().fs.stat(path)
|
|
};
|
|
if let Ok(file) = stat_result {
|
|
other_metadata.modified != file.metadata().modified
|
|
|| other_metadata.size != file.metadata().size
|
|
} else {
|
|
true
|
|
}
|
|
}
|
|
|
|
// -- file exist --
|
|
|
|
/// Check whether a file exists on the specified side.
|
|
pub(crate) fn file_exists(&mut self, p: &Path, local: bool) -> bool {
|
|
let pane = if local {
|
|
self.browser.local_pane_mut()
|
|
} else {
|
|
self.browser.remote_pane_mut()
|
|
};
|
|
pane.fs.exists(p).unwrap_or_default()
|
|
}
|
|
}
|