mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
remotefs 0.2.0
This commit is contained in:
committed by
Christian Visintin
parent
edd0842273
commit
ec4daf8e25
@@ -28,7 +28,7 @@
|
||||
// locals
|
||||
use super::{FileExplorerTab, FileTransferActivity, LogLevel, Msg, PendingActionMsg};
|
||||
|
||||
use remotefs::Directory;
|
||||
use remotefs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Describes destination for sync browsing
|
||||
@@ -40,18 +40,18 @@ enum SyncBrowsingDestination {
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// Enter a directory on local host from entry
|
||||
pub(crate) fn action_enter_local_dir(&mut self, dir: Directory) {
|
||||
self.local_changedir(dir.path.as_path(), true);
|
||||
pub(crate) fn action_enter_local_dir(&mut self, dir: File) {
|
||||
self.local_changedir(dir.path(), true);
|
||||
if self.browser.sync_browsing && self.browser.found().is_none() {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::Path(dir.name));
|
||||
self.synchronize_browsing(SyncBrowsingDestination::Path(dir.name()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter a directory on local host from entry
|
||||
pub(crate) fn action_enter_remote_dir(&mut self, dir: Directory) {
|
||||
self.remote_changedir(dir.path.as_path(), true);
|
||||
pub(crate) fn action_enter_remote_dir(&mut self, dir: File) {
|
||||
self.remote_changedir(dir.path(), true);
|
||||
if self.browser.sync_browsing && self.browser.found().is_none() {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::Path(dir.name));
|
||||
self.synchronize_browsing(SyncBrowsingDestination::Path(dir.name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,20 +26,20 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::{FileTransferActivity, LogLevel, SelectedEntry, TransferPayload};
|
||||
use super::{FileTransferActivity, LogLevel, SelectedFile, TransferPayload};
|
||||
|
||||
use remotefs::{Entry, RemoteErrorType};
|
||||
use remotefs::{File, RemoteErrorType};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// Copy file on local
|
||||
pub(crate) fn action_local_copy(&mut self, input: String) {
|
||||
match self.get_local_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
let dest_path: PathBuf = PathBuf::from(input);
|
||||
self.local_copy_file(&entry, dest_path.as_path());
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Try to copy each file to Input/{FILE_NAME}
|
||||
let base_path: PathBuf = PathBuf::from(input);
|
||||
// Iter files
|
||||
@@ -49,18 +49,18 @@ impl FileTransferActivity {
|
||||
self.local_copy_file(entry, dest_path.as_path());
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy file on remote
|
||||
pub(crate) fn action_remote_copy(&mut self, input: String) {
|
||||
match self.get_remote_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
let dest_path: PathBuf = PathBuf::from(input);
|
||||
self.remote_copy_file(entry, dest_path.as_path());
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Try to copy each file to Input/{FILE_NAME}
|
||||
let base_path: PathBuf = PathBuf::from(input);
|
||||
// Iter files
|
||||
@@ -70,11 +70,11 @@ impl FileTransferActivity {
|
||||
self.remote_copy_file(entry, dest_path.as_path());
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn local_copy_file(&mut self, entry: &Entry, dest: &Path) {
|
||||
fn local_copy_file(&mut self, entry: &File, dest: &Path) {
|
||||
match self.host.copy(entry, dest) {
|
||||
Ok(_) => {
|
||||
self.log(
|
||||
@@ -98,7 +98,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
|
||||
fn remote_copy_file(&mut self, entry: Entry, dest: &Path) {
|
||||
fn remote_copy_file(&mut self, entry: File, dest: &Path) {
|
||||
match self.client.as_mut().copy(entry.path(), dest) {
|
||||
Ok(_) => {
|
||||
self.log(
|
||||
@@ -129,123 +129,121 @@ impl FileTransferActivity {
|
||||
}
|
||||
|
||||
/// Tricky copy will be used whenever copy command is not available on remote host
|
||||
pub(super) fn tricky_copy(&mut self, entry: Entry, dest: &Path) -> Result<(), String> {
|
||||
pub(super) fn tricky_copy(&mut self, entry: File, dest: &Path) -> Result<(), String> {
|
||||
// NOTE: VERY IMPORTANT; wait block must be umounted or something really bad will happen
|
||||
self.umount_wait();
|
||||
// match entry
|
||||
match entry {
|
||||
Entry::File(entry) => {
|
||||
// Create tempfile
|
||||
let tmpfile: tempfile::NamedTempFile = match tempfile::NamedTempFile::new() {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Copy failed: could not create temporary file: {}", err),
|
||||
);
|
||||
return Err(String::from("Could not create temporary file"));
|
||||
}
|
||||
};
|
||||
// Download file
|
||||
let name = entry.name.clone();
|
||||
let entry_path = entry.path.clone();
|
||||
if let Err(err) =
|
||||
self.filetransfer_recv(TransferPayload::File(entry), tmpfile.path(), Some(name))
|
||||
{
|
||||
if entry.is_dir() {
|
||||
let tempdir: tempfile::TempDir = match tempfile::TempDir::new() {
|
||||
Ok(d) => d,
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Copy failed: could not download to temporary file: {}", err),
|
||||
format!("Copy failed: could not create temporary directory: {}", err),
|
||||
);
|
||||
return Err(err);
|
||||
return Err(err.to_string());
|
||||
}
|
||||
// Get local fs entry
|
||||
let tmpfile_entry = match self.host.stat(tmpfile.path()) {
|
||||
Ok(e) => e.unwrap_file(),
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Copy failed: could not stat \"{}\": {}",
|
||||
tmpfile.path().display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
return Err(err.to_string());
|
||||
}
|
||||
};
|
||||
// Upload file to destination
|
||||
let wrkdir = self.remote().wrkdir.clone();
|
||||
if let Err(err) = self.filetransfer_send(
|
||||
TransferPayload::File(tmpfile_entry),
|
||||
wrkdir.as_path(),
|
||||
Some(String::from(dest.to_string_lossy())),
|
||||
) {
|
||||
};
|
||||
// Get path of dest
|
||||
let mut tempdir_path: PathBuf = tempdir.path().to_path_buf();
|
||||
tempdir_path.push(entry.name());
|
||||
// Download file
|
||||
if let Err(err) =
|
||||
self.filetransfer_recv(TransferPayload::Any(entry), tempdir.path(), None)
|
||||
{
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Copy failed: failed to download file: {}", err),
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
// Stat dir
|
||||
let tempdir_entry = match self.host.stat(tempdir_path.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Copy failed: could not write file {}: {}",
|
||||
entry_path.display(),
|
||||
"Copy failed: could not stat \"{}\": {}",
|
||||
tempdir.path().display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
return Err(err);
|
||||
return Err(err.to_string());
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
// Upload to destination
|
||||
let wrkdir: PathBuf = self.remote().wrkdir.clone();
|
||||
if let Err(err) = self.filetransfer_send(
|
||||
TransferPayload::Any(tempdir_entry),
|
||||
wrkdir.as_path(),
|
||||
Some(String::from(dest.to_string_lossy())),
|
||||
) {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Copy failed: failed to send file: {}", err),
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
Entry::Directory(_) => {
|
||||
let tempdir: tempfile::TempDir = match tempfile::TempDir::new() {
|
||||
Ok(d) => d,
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Copy failed: could not create temporary directory: {}", err),
|
||||
);
|
||||
return Err(err.to_string());
|
||||
}
|
||||
};
|
||||
// Get path of dest
|
||||
let mut tempdir_path: PathBuf = tempdir.path().to_path_buf();
|
||||
tempdir_path.push(entry.name());
|
||||
// Download file
|
||||
if let Err(err) =
|
||||
self.filetransfer_recv(TransferPayload::Any(entry), tempdir.path(), None)
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
// Create tempfile
|
||||
let tmpfile: tempfile::NamedTempFile = match tempfile::NamedTempFile::new() {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Copy failed: failed to download file: {}", err),
|
||||
format!("Copy failed: could not create temporary file: {}", err),
|
||||
);
|
||||
return Err(err);
|
||||
return Err(String::from("Could not create temporary file"));
|
||||
}
|
||||
// Stat dir
|
||||
let tempdir_entry = match self.host.stat(tempdir_path.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Copy failed: could not stat \"{}\": {}",
|
||||
tempdir.path().display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
return Err(err.to_string());
|
||||
}
|
||||
};
|
||||
// Upload to destination
|
||||
let wrkdir: PathBuf = self.remote().wrkdir.clone();
|
||||
if let Err(err) = self.filetransfer_send(
|
||||
TransferPayload::Any(tempdir_entry),
|
||||
wrkdir.as_path(),
|
||||
Some(String::from(dest.to_string_lossy())),
|
||||
) {
|
||||
};
|
||||
// Download file
|
||||
let name = entry.name();
|
||||
let entry_path = entry.path().to_path_buf();
|
||||
if let Err(err) =
|
||||
self.filetransfer_recv(TransferPayload::File(entry), tmpfile.path(), Some(name))
|
||||
{
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Copy failed: could not download to temporary file: {}", err),
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
// Get local fs entry
|
||||
let tmpfile_entry = match self.host.stat(tmpfile.path()) {
|
||||
Ok(e) if e.is_file() => e,
|
||||
Ok(_) => panic!("{} is not a file", tmpfile.path().display()),
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Copy failed: failed to send file: {}", err),
|
||||
format!(
|
||||
"Copy failed: could not stat \"{}\": {}",
|
||||
tmpfile.path().display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
return Err(err);
|
||||
return Err(err.to_string());
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
// Upload file to destination
|
||||
let wrkdir = self.remote().wrkdir.clone();
|
||||
if let Err(err) = self.filetransfer_send(
|
||||
TransferPayload::File(tmpfile_entry),
|
||||
wrkdir.as_path(),
|
||||
Some(String::from(dest.to_string_lossy())),
|
||||
) {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Copy failed: could not write file {}: {}",
|
||||
entry_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,46 +26,46 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::{FileTransferActivity, LogLevel, SelectedEntry};
|
||||
use super::{FileTransferActivity, LogLevel, SelectedFile};
|
||||
|
||||
use remotefs::Entry;
|
||||
use remotefs::File;
|
||||
|
||||
impl FileTransferActivity {
|
||||
pub(crate) fn action_local_delete(&mut self) {
|
||||
match self.get_local_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
// Delete file
|
||||
self.local_remove_file(&entry);
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Iter files
|
||||
for entry in entries.iter() {
|
||||
// Delete file
|
||||
self.local_remove_file(entry);
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn action_remote_delete(&mut self) {
|
||||
match self.get_remote_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
// Delete file
|
||||
self.remote_remove_file(&entry);
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Iter files
|
||||
for entry in entries.iter() {
|
||||
// Delete file
|
||||
self.remote_remove_file(entry);
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn local_remove_file(&mut self, entry: &Entry) {
|
||||
pub(crate) fn local_remove_file(&mut self, entry: &File) {
|
||||
match self.host.remove(entry) {
|
||||
Ok(_) => {
|
||||
// Log
|
||||
@@ -87,7 +87,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remote_remove_file(&mut self, entry: &Entry) {
|
||||
pub(crate) fn remote_remove_file(&mut self, entry: &File) {
|
||||
match self.client.remove_dir_all(entry.path()) {
|
||||
Ok(_) => {
|
||||
self.log(
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::{FileTransferActivity, LogLevel, SelectedEntry, TransferPayload};
|
||||
use super::{FileTransferActivity, LogLevel, SelectedFile, TransferPayload};
|
||||
|
||||
// ext
|
||||
use remotefs::{Entry, File};
|
||||
use remotefs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -37,10 +37,10 @@ use std::time::SystemTime;
|
||||
|
||||
impl FileTransferActivity {
|
||||
pub(crate) fn action_edit_local_file(&mut self) {
|
||||
let entries: Vec<Entry> = match self.get_local_selected_entries() {
|
||||
SelectedEntry::One(entry) => vec![entry],
|
||||
SelectedEntry::Many(entries) => entries,
|
||||
SelectedEntry::None => vec![],
|
||||
let entries: Vec<File> = match self.get_local_selected_entries() {
|
||||
SelectedFile::One(entry) => vec![entry],
|
||||
SelectedFile::Many(entries) => entries,
|
||||
SelectedFile::None => vec![],
|
||||
};
|
||||
// Edit all entries
|
||||
for entry in entries.iter() {
|
||||
@@ -59,21 +59,21 @@ impl FileTransferActivity {
|
||||
}
|
||||
|
||||
pub(crate) fn action_edit_remote_file(&mut self) {
|
||||
let entries: Vec<Entry> = match self.get_remote_selected_entries() {
|
||||
SelectedEntry::One(entry) => vec![entry],
|
||||
SelectedEntry::Many(entries) => entries,
|
||||
SelectedEntry::None => vec![],
|
||||
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
||||
SelectedFile::One(entry) => vec![entry],
|
||||
SelectedFile::Many(entries) => entries,
|
||||
SelectedFile::None => vec![],
|
||||
};
|
||||
// Edit all entries
|
||||
for entry in entries.into_iter() {
|
||||
// Check if file
|
||||
if let Entry::File(file) = entry {
|
||||
if entry.is_file() {
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!("Opening file \"{}\"…", file.path.display()),
|
||||
format!("Opening file \"{}\"…", entry.path().display()),
|
||||
);
|
||||
// Edit file
|
||||
if let Err(err) = self.edit_remote_file(file) {
|
||||
if let Err(err) = self.edit_remote_file(entry) {
|
||||
self.log_and_alert(LogLevel::Error, err);
|
||||
}
|
||||
}
|
||||
@@ -149,8 +149,8 @@ impl FileTransferActivity {
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
// Download file
|
||||
let file_name = file.name.clone();
|
||||
let file_path = file.path.clone();
|
||||
let file_name = file.name();
|
||||
let file_path = file.path().to_path_buf();
|
||||
if let Err(err) = self.filetransfer_recv(
|
||||
TransferPayload::File(file),
|
||||
tmpfile.as_path(),
|
||||
@@ -160,7 +160,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
// Get current file modification time
|
||||
let prev_mtime: SystemTime = match self.host.stat(tmpfile.as_path()) {
|
||||
Ok(e) => e.metadata().mtime,
|
||||
Ok(e) => e.metadata().modified.unwrap_or(std::time::UNIX_EPOCH),
|
||||
Err(err) => {
|
||||
return Err(format!(
|
||||
"Could not stat \"{}\": {}",
|
||||
@@ -174,7 +174,7 @@ impl FileTransferActivity {
|
||||
return Err(err);
|
||||
}
|
||||
// Get local fs entry
|
||||
let tmpfile_entry: Entry = match self.host.stat(tmpfile.as_path()) {
|
||||
let tmpfile_entry: File = match self.host.stat(tmpfile.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
return Err(format!(
|
||||
@@ -185,7 +185,12 @@ impl FileTransferActivity {
|
||||
}
|
||||
};
|
||||
// Check if file has changed
|
||||
match prev_mtime != tmpfile_entry.metadata().mtime {
|
||||
match prev_mtime
|
||||
!= tmpfile_entry
|
||||
.metadata()
|
||||
.modified
|
||||
.unwrap_or(std::time::UNIX_EPOCH)
|
||||
{
|
||||
true => {
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
@@ -196,7 +201,7 @@ impl FileTransferActivity {
|
||||
);
|
||||
// Get local fs entry
|
||||
let tmpfile_entry = match self.host.stat(tmpfile.as_path()) {
|
||||
Ok(e) => e.unwrap_file(),
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
return Err(format!(
|
||||
"Could not stat \"{}\": {}",
|
||||
|
||||
@@ -27,19 +27,19 @@
|
||||
*/
|
||||
// locals
|
||||
use super::super::browser::FileExplorerTab;
|
||||
use super::{Entry, FileTransferActivity, LogLevel, SelectedEntry, TransferOpts, TransferPayload};
|
||||
use super::{File, FileTransferActivity, LogLevel, SelectedFile, TransferOpts, TransferPayload};
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
impl FileTransferActivity {
|
||||
pub(crate) fn action_local_find(&mut self, input: String) -> Result<Vec<Entry>, String> {
|
||||
pub(crate) fn action_local_find(&mut self, input: String) -> Result<Vec<File>, String> {
|
||||
match self.host.find(input.as_str()) {
|
||||
Ok(entries) => Ok(entries),
|
||||
Err(err) => Err(format!("Could not search for files: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn action_remote_find(&mut self, input: String) -> Result<Vec<Entry>, String> {
|
||||
pub(crate) fn action_remote_find(&mut self, input: String) -> Result<Vec<File>, String> {
|
||||
match self.client.as_mut().find(input.as_str()) {
|
||||
Ok(entries) => Ok(entries),
|
||||
Err(err) => Err(format!("Could not search for files: {}", err)),
|
||||
@@ -48,14 +48,15 @@ impl FileTransferActivity {
|
||||
|
||||
pub(crate) fn action_find_changedir(&mut self) {
|
||||
// Match entry
|
||||
if let SelectedEntry::One(entry) = self.get_found_selected_entries() {
|
||||
if let SelectedFile::One(entry) = self.get_found_selected_entries() {
|
||||
// Get path: if a directory, use directory path; if it is a File, get parent path
|
||||
let path: PathBuf = match entry {
|
||||
Entry::Directory(dir) => dir.path,
|
||||
Entry::File(file) => match file.path.parent() {
|
||||
let path = if entry.is_dir() {
|
||||
entry.path().to_path_buf()
|
||||
} else {
|
||||
match entry.path().parent() {
|
||||
None => PathBuf::from("."),
|
||||
Some(p) => p.to_path_buf(),
|
||||
},
|
||||
}
|
||||
};
|
||||
// Change directory
|
||||
match self.browser.tab() {
|
||||
@@ -75,13 +76,13 @@ impl FileTransferActivity {
|
||||
FileExplorerTab::FindRemote | FileExplorerTab::Remote => self.local().wrkdir.clone(),
|
||||
};
|
||||
match self.get_found_selected_entries() {
|
||||
SelectedEntry::One(entry) => match self.browser.tab() {
|
||||
SelectedFile::One(entry) => match self.browser.tab() {
|
||||
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
|
||||
let file_to_check = Self::file_to_check(&entry, opts.save_as.as_ref());
|
||||
if self.config().get_prompt_on_file_replace()
|
||||
&& self.remote_file_exists(file_to_check.as_path())
|
||||
&& !self.should_replace_file(
|
||||
opts.save_as.as_deref().unwrap_or_else(|| entry.name()),
|
||||
opts.save_as.clone().unwrap_or_else(|| entry.name()),
|
||||
)
|
||||
{
|
||||
// Do not replace
|
||||
@@ -103,7 +104,7 @@ impl FileTransferActivity {
|
||||
if self.config().get_prompt_on_file_replace()
|
||||
&& self.local_file_exists(file_to_check.as_path())
|
||||
&& !self.should_replace_file(
|
||||
opts.save_as.as_deref().unwrap_or_else(|| entry.name()),
|
||||
opts.save_as.clone().unwrap_or_else(|| entry.name()),
|
||||
)
|
||||
{
|
||||
// Do not replace
|
||||
@@ -121,7 +122,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
},
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// In case of selection: save multiple files in wrkdir/input
|
||||
let mut dest_path: PathBuf = wrkdir;
|
||||
if let Some(save_as) = opts.save_as {
|
||||
@@ -132,7 +133,7 @@ impl FileTransferActivity {
|
||||
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
|
||||
if self.config().get_prompt_on_file_replace() {
|
||||
// Check which file would be replaced
|
||||
let existing_files: Vec<&Entry> = entries
|
||||
let existing_files: Vec<&File> = entries
|
||||
.iter()
|
||||
.filter(|x| {
|
||||
self.remote_file_exists(
|
||||
@@ -163,7 +164,7 @@ impl FileTransferActivity {
|
||||
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
|
||||
if self.config().get_prompt_on_file_replace() {
|
||||
// Check which file would be replaced
|
||||
let existing_files: Vec<&Entry> = entries
|
||||
let existing_files: Vec<&File> = entries
|
||||
.iter()
|
||||
.filter(|x| {
|
||||
self.local_file_exists(
|
||||
@@ -191,28 +192,28 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn action_find_delete(&mut self) {
|
||||
match self.get_found_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
// Delete file
|
||||
self.remove_found_file(&entry);
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Iter files
|
||||
for entry in entries.iter() {
|
||||
// Delete file
|
||||
self.remove_found_file(entry);
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_found_file(&mut self, entry: &Entry) {
|
||||
fn remove_found_file(&mut self, entry: &File) {
|
||||
match self.browser.tab() {
|
||||
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
|
||||
self.local_remove_file(entry);
|
||||
@@ -225,39 +226,39 @@ impl FileTransferActivity {
|
||||
|
||||
pub(crate) fn action_find_open(&mut self) {
|
||||
match self.get_found_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
// Open file
|
||||
self.open_found_file(&entry, None);
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Iter files
|
||||
for entry in entries.iter() {
|
||||
// Open file
|
||||
self.open_found_file(entry, None);
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn action_find_open_with(&mut self, with: &str) {
|
||||
match self.get_found_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
// Open file
|
||||
self.open_found_file(&entry, Some(with));
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Iter files
|
||||
for entry in entries.iter() {
|
||||
// Open file
|
||||
self.open_found_file(entry, Some(with));
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_found_file(&mut self, entry: &Entry, with: Option<&str>) {
|
||||
fn open_found_file(&mut self, entry: &File, with: Option<&str>) {
|
||||
match self.browser.tab() {
|
||||
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
|
||||
self.action_open_local_file(entry, with);
|
||||
|
||||
@@ -29,7 +29,7 @@ pub(self) use super::{
|
||||
browser::FileExplorerTab, FileTransferActivity, Id, LogLevel, Msg, PendingActionMsg,
|
||||
TransferOpts, TransferPayload,
|
||||
};
|
||||
pub(self) use remotefs::Entry;
|
||||
pub(self) use remotefs::File;
|
||||
use tuirealm::{State, StateValue};
|
||||
|
||||
// actions
|
||||
@@ -49,100 +49,100 @@ pub(crate) mod submit;
|
||||
pub(crate) mod symlink;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum SelectedEntry {
|
||||
One(Entry),
|
||||
Many(Vec<Entry>),
|
||||
pub(crate) enum SelectedFile {
|
||||
One(File),
|
||||
Many(Vec<File>),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SelectedEntryIndex {
|
||||
enum SelectedFileIndex {
|
||||
One(usize),
|
||||
Many(Vec<usize>),
|
||||
None,
|
||||
}
|
||||
|
||||
impl From<Option<&Entry>> for SelectedEntry {
|
||||
fn from(opt: Option<&Entry>) -> Self {
|
||||
impl From<Option<&File>> for SelectedFile {
|
||||
fn from(opt: Option<&File>) -> Self {
|
||||
match opt {
|
||||
Some(e) => SelectedEntry::One(e.clone()),
|
||||
None => SelectedEntry::None,
|
||||
Some(e) => SelectedFile::One(e.clone()),
|
||||
None => SelectedFile::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<&Entry>> for SelectedEntry {
|
||||
fn from(files: Vec<&Entry>) -> Self {
|
||||
SelectedEntry::Many(files.into_iter().cloned().collect())
|
||||
impl From<Vec<&File>> for SelectedFile {
|
||||
fn from(files: Vec<&File>) -> Self {
|
||||
SelectedFile::Many(files.into_iter().cloned().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// Get local file entry
|
||||
pub(crate) fn get_local_selected_entries(&self) -> SelectedEntry {
|
||||
pub(crate) fn get_local_selected_entries(&self) -> SelectedFile {
|
||||
match self.get_selected_index(&Id::ExplorerLocal) {
|
||||
SelectedEntryIndex::One(idx) => SelectedEntry::from(self.local().get(idx)),
|
||||
SelectedEntryIndex::Many(files) => {
|
||||
let files: Vec<&Entry> = files
|
||||
SelectedFileIndex::One(idx) => SelectedFile::from(self.local().get(idx)),
|
||||
SelectedFileIndex::Many(files) => {
|
||||
let files: Vec<&File> = files
|
||||
.iter()
|
||||
.map(|x| self.local().get(*x)) // Usize to Option<Entry>
|
||||
.map(|x| self.local().get(*x)) // Usize to Option<File>
|
||||
.flatten()
|
||||
.collect();
|
||||
SelectedEntry::from(files)
|
||||
SelectedFile::from(files)
|
||||
}
|
||||
SelectedEntryIndex::None => SelectedEntry::None,
|
||||
SelectedFileIndex::None => SelectedFile::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get remote file entry
|
||||
pub(crate) fn get_remote_selected_entries(&self) -> SelectedEntry {
|
||||
pub(crate) fn get_remote_selected_entries(&self) -> SelectedFile {
|
||||
match self.get_selected_index(&Id::ExplorerRemote) {
|
||||
SelectedEntryIndex::One(idx) => SelectedEntry::from(self.remote().get(idx)),
|
||||
SelectedEntryIndex::Many(files) => {
|
||||
let files: Vec<&Entry> = files
|
||||
SelectedFileIndex::One(idx) => SelectedFile::from(self.remote().get(idx)),
|
||||
SelectedFileIndex::Many(files) => {
|
||||
let files: Vec<&File> = files
|
||||
.iter()
|
||||
.map(|x| self.remote().get(*x)) // Usize to Option<Entry>
|
||||
.map(|x| self.remote().get(*x)) // Usize to Option<File>
|
||||
.flatten()
|
||||
.collect();
|
||||
SelectedEntry::from(files)
|
||||
SelectedFile::from(files)
|
||||
}
|
||||
SelectedEntryIndex::None => SelectedEntry::None,
|
||||
SelectedFileIndex::None => SelectedFile::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether only one entry is selected on local host
|
||||
pub(crate) fn is_local_selected_one(&self) -> bool {
|
||||
matches!(self.get_local_selected_entries(), SelectedEntry::One(_))
|
||||
matches!(self.get_local_selected_entries(), SelectedFile::One(_))
|
||||
}
|
||||
|
||||
/// Returns whether only one entry is selected on remote host
|
||||
pub(crate) fn is_remote_selected_one(&self) -> bool {
|
||||
matches!(self.get_remote_selected_entries(), SelectedEntry::One(_))
|
||||
matches!(self.get_remote_selected_entries(), SelectedFile::One(_))
|
||||
}
|
||||
|
||||
/// Get remote file entry
|
||||
pub(crate) fn get_found_selected_entries(&self) -> SelectedEntry {
|
||||
pub(crate) fn get_found_selected_entries(&self) -> SelectedFile {
|
||||
match self.get_selected_index(&Id::ExplorerFind) {
|
||||
SelectedEntryIndex::One(idx) => {
|
||||
SelectedEntry::from(self.found().as_ref().unwrap().get(idx))
|
||||
SelectedFileIndex::One(idx) => {
|
||||
SelectedFile::from(self.found().as_ref().unwrap().get(idx))
|
||||
}
|
||||
SelectedEntryIndex::Many(files) => {
|
||||
let files: Vec<&Entry> = files
|
||||
SelectedFileIndex::Many(files) => {
|
||||
let files: Vec<&File> = files
|
||||
.iter()
|
||||
.map(|x| self.found().as_ref().unwrap().get(*x)) // Usize to Option<Entry>
|
||||
.map(|x| self.found().as_ref().unwrap().get(*x)) // Usize to Option<File>
|
||||
.flatten()
|
||||
.collect();
|
||||
SelectedEntry::from(files)
|
||||
SelectedFile::from(files)
|
||||
}
|
||||
SelectedEntryIndex::None => SelectedEntry::None,
|
||||
SelectedFileIndex::None => SelectedFile::None,
|
||||
}
|
||||
}
|
||||
|
||||
// -- private
|
||||
|
||||
fn get_selected_index(&self, id: &Id) -> SelectedEntryIndex {
|
||||
fn get_selected_index(&self, id: &Id) -> SelectedFileIndex {
|
||||
match self.app.state(id) {
|
||||
Ok(State::One(StateValue::Usize(idx))) => SelectedEntryIndex::One(idx),
|
||||
Ok(State::One(StateValue::Usize(idx))) => SelectedFileIndex::One(idx),
|
||||
Ok(State::Vec(files)) => {
|
||||
let list: Vec<usize> = files
|
||||
.iter()
|
||||
@@ -151,9 +151,9 @@ impl FileTransferActivity {
|
||||
_ => 0,
|
||||
})
|
||||
.collect();
|
||||
SelectedEntryIndex::Many(list)
|
||||
SelectedFileIndex::Many(list)
|
||||
}
|
||||
_ => SelectedEntryIndex::None,
|
||||
_ => SelectedFileIndex::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::{Entry, FileTransferActivity, LogLevel};
|
||||
use std::fs::File;
|
||||
use super::{File, FileTransferActivity, LogLevel};
|
||||
use std::fs::File as StdFile;
|
||||
use std::path::PathBuf;
|
||||
|
||||
impl FileTransferActivity {
|
||||
@@ -86,7 +86,7 @@ impl FileTransferActivity {
|
||||
),
|
||||
Ok(tfile) => {
|
||||
// Stat tempfile
|
||||
let local_file: Entry = match self.host.stat(tfile.path()) {
|
||||
let local_file: File = match self.host.stat(tfile.path()) {
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
@@ -96,9 +96,9 @@ impl FileTransferActivity {
|
||||
}
|
||||
Ok(f) => f,
|
||||
};
|
||||
if let Entry::File(local_file) = local_file {
|
||||
if local_file.is_file() {
|
||||
// Create file
|
||||
let reader = Box::new(match File::open(tfile.path()) {
|
||||
let reader = Box::new(match StdFile::open(tfile.path()) {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
|
||||
@@ -26,17 +26,17 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::{Entry, FileTransferActivity, LogLevel, SelectedEntry, TransferPayload};
|
||||
use super::{File, FileTransferActivity, LogLevel, SelectedFile, TransferPayload};
|
||||
// ext
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// Open local file
|
||||
pub(crate) fn action_open_local(&mut self) {
|
||||
let entries: Vec<Entry> = match self.get_local_selected_entries() {
|
||||
SelectedEntry::One(entry) => vec![entry],
|
||||
SelectedEntry::Many(entries) => entries,
|
||||
SelectedEntry::None => vec![],
|
||||
let entries: Vec<File> = match self.get_local_selected_entries() {
|
||||
SelectedFile::One(entry) => vec![entry],
|
||||
SelectedFile::Many(entries) => entries,
|
||||
SelectedFile::None => vec![],
|
||||
};
|
||||
entries
|
||||
.iter()
|
||||
@@ -45,10 +45,10 @@ impl FileTransferActivity {
|
||||
|
||||
/// Open local file
|
||||
pub(crate) fn action_open_remote(&mut self) {
|
||||
let entries: Vec<Entry> = match self.get_remote_selected_entries() {
|
||||
SelectedEntry::One(entry) => vec![entry],
|
||||
SelectedEntry::Many(entries) => entries,
|
||||
SelectedEntry::None => vec![],
|
||||
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
||||
SelectedFile::One(entry) => vec![entry],
|
||||
SelectedFile::Many(entries) => entries,
|
||||
SelectedFile::None => vec![],
|
||||
};
|
||||
entries
|
||||
.iter()
|
||||
@@ -56,20 +56,21 @@ impl FileTransferActivity {
|
||||
}
|
||||
|
||||
/// Perform open lopcal file
|
||||
pub(crate) fn action_open_local_file(&mut self, entry: &Entry, open_with: Option<&str>) {
|
||||
pub(crate) fn action_open_local_file(&mut self, entry: &File, open_with: Option<&str>) {
|
||||
self.open_path_with(entry.path(), open_with);
|
||||
}
|
||||
|
||||
/// Open remote file. The file is first downloaded to a temporary directory on localhost
|
||||
pub(crate) fn action_open_remote_file(&mut self, entry: &Entry, open_with: Option<&str>) {
|
||||
pub(crate) 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()) {
|
||||
None => {
|
||||
self.log(LogLevel::Error, String::from("Could not create tempdir"));
|
||||
return;
|
||||
}
|
||||
Some(p) => p,
|
||||
};
|
||||
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"));
|
||||
@@ -101,10 +102,10 @@ impl FileTransferActivity {
|
||||
|
||||
/// Open selected file with provided application
|
||||
pub(crate) fn action_local_open_with(&mut self, with: &str) {
|
||||
let entries: Vec<Entry> = match self.get_local_selected_entries() {
|
||||
SelectedEntry::One(entry) => vec![entry],
|
||||
SelectedEntry::Many(entries) => entries,
|
||||
SelectedEntry::None => vec![],
|
||||
let entries: Vec<File> = match self.get_local_selected_entries() {
|
||||
SelectedFile::One(entry) => vec![entry],
|
||||
SelectedFile::Many(entries) => entries,
|
||||
SelectedFile::None => vec![],
|
||||
};
|
||||
// Open all entries
|
||||
entries
|
||||
@@ -114,10 +115,10 @@ impl FileTransferActivity {
|
||||
|
||||
/// Open selected file with provided application
|
||||
pub(crate) fn action_remote_open_with(&mut self, with: &str) {
|
||||
let entries: Vec<Entry> = match self.get_remote_selected_entries() {
|
||||
SelectedEntry::One(entry) => vec![entry],
|
||||
SelectedEntry::Many(entries) => entries,
|
||||
SelectedEntry::None => vec![],
|
||||
let entries: Vec<File> = match self.get_remote_selected_entries() {
|
||||
SelectedFile::One(entry) => vec![entry],
|
||||
SelectedFile::Many(entries) => entries,
|
||||
SelectedFile::None => vec![],
|
||||
};
|
||||
// Open all entries
|
||||
entries
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::{Entry, FileTransferActivity, LogLevel, SelectedEntry};
|
||||
use super::{File, FileTransferActivity, LogLevel, SelectedFile};
|
||||
|
||||
use remotefs::RemoteErrorType;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -34,11 +34,11 @@ use std::path::{Path, PathBuf};
|
||||
impl FileTransferActivity {
|
||||
pub(crate) fn action_local_rename(&mut self, input: String) {
|
||||
match self.get_local_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
let dest_path: PathBuf = PathBuf::from(input);
|
||||
self.local_rename_file(&entry, dest_path.as_path());
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Try to copy each file to Input/{FILE_NAME}
|
||||
let base_path: PathBuf = PathBuf::from(input);
|
||||
// Iter files
|
||||
@@ -48,17 +48,17 @@ impl FileTransferActivity {
|
||||
self.local_rename_file(entry, dest_path.as_path());
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn action_remote_rename(&mut self, input: String) {
|
||||
match self.get_remote_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
let dest_path: PathBuf = PathBuf::from(input);
|
||||
self.remote_rename_file(&entry, dest_path.as_path());
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// Try to copy each file to Input/{FILE_NAME}
|
||||
let base_path: PathBuf = PathBuf::from(input);
|
||||
// Iter files
|
||||
@@ -68,11 +68,11 @@ impl FileTransferActivity {
|
||||
self.remote_rename_file(entry, dest_path.as_path());
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn local_rename_file(&mut self, entry: &Entry, dest: &Path) {
|
||||
fn local_rename_file(&mut self, entry: &File, dest: &Path) {
|
||||
match self.host.rename(entry, dest) {
|
||||
Ok(_) => {
|
||||
self.log(
|
||||
@@ -96,7 +96,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
|
||||
fn remote_rename_file(&mut self, entry: &Entry, dest: &Path) {
|
||||
fn remote_rename_file(&mut self, entry: &File, dest: &Path) {
|
||||
match self.client.as_mut().mov(entry.path(), dest) {
|
||||
Ok(_) => {
|
||||
self.log(
|
||||
@@ -125,7 +125,7 @@ impl FileTransferActivity {
|
||||
|
||||
/// Tricky move will be used whenever copy command is not available on remote host.
|
||||
/// It basically uses the tricky_copy function, then it just deletes the previous entry (`entry`)
|
||||
fn tricky_move(&mut self, entry: &Entry, dest: &Path) {
|
||||
fn tricky_move(&mut self, entry: &File, dest: &Path) {
|
||||
debug!(
|
||||
"Using tricky-move to move entry {} to {}",
|
||||
entry.path().display(),
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
*/
|
||||
// locals
|
||||
use super::{
|
||||
Entry, FileTransferActivity, LogLevel, Msg, PendingActionMsg, SelectedEntry, TransferOpts,
|
||||
File, FileTransferActivity, LogLevel, Msg, PendingActionMsg, SelectedFile, TransferOpts,
|
||||
TransferPayload,
|
||||
};
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -52,19 +52,18 @@ impl FileTransferActivity {
|
||||
fn local_send_file(&mut self, opts: TransferOpts) {
|
||||
let wrkdir: PathBuf = self.remote().wrkdir.clone();
|
||||
match self.get_local_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
let file_to_check = Self::file_to_check(&entry, opts.save_as.as_ref());
|
||||
if self.config().get_prompt_on_file_replace()
|
||||
&& self.remote_file_exists(file_to_check.as_path())
|
||||
&& !self.should_replace_file(
|
||||
opts.save_as.as_deref().unwrap_or_else(|| entry.name()),
|
||||
)
|
||||
&& !self
|
||||
.should_replace_file(opts.save_as.clone().unwrap_or_else(|| entry.name()))
|
||||
{
|
||||
// Do not replace
|
||||
return;
|
||||
}
|
||||
if let Err(err) = self.filetransfer_send(
|
||||
TransferPayload::Any(entry.clone()),
|
||||
TransferPayload::Any(entry),
|
||||
wrkdir.as_path(),
|
||||
opts.save_as,
|
||||
) {
|
||||
@@ -76,7 +75,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// In case of selection: save multiple files in wrkdir/input
|
||||
let mut dest_path: PathBuf = wrkdir;
|
||||
if let Some(save_as) = opts.save_as {
|
||||
@@ -85,7 +84,7 @@ impl FileTransferActivity {
|
||||
// Iter files
|
||||
if self.config().get_prompt_on_file_replace() {
|
||||
// Check which file would be replaced
|
||||
let existing_files: Vec<&Entry> = entries
|
||||
let existing_files: Vec<&File> = entries
|
||||
.iter()
|
||||
.filter(|x| {
|
||||
self.remote_file_exists(
|
||||
@@ -111,25 +110,24 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remote_recv_file(&mut self, opts: TransferOpts) {
|
||||
let wrkdir: PathBuf = self.local().wrkdir.clone();
|
||||
match self.get_remote_selected_entries() {
|
||||
SelectedEntry::One(entry) => {
|
||||
SelectedFile::One(entry) => {
|
||||
let file_to_check = Self::file_to_check(&entry, opts.save_as.as_ref());
|
||||
if self.config().get_prompt_on_file_replace()
|
||||
&& self.local_file_exists(file_to_check.as_path())
|
||||
&& !self.should_replace_file(
|
||||
opts.save_as.as_deref().unwrap_or_else(|| entry.name()),
|
||||
)
|
||||
&& !self
|
||||
.should_replace_file(opts.save_as.clone().unwrap_or_else(|| entry.name()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if let Err(err) = self.filetransfer_recv(
|
||||
TransferPayload::Any(entry.clone()),
|
||||
TransferPayload::Any(entry),
|
||||
wrkdir.as_path(),
|
||||
opts.save_as,
|
||||
) {
|
||||
@@ -141,7 +139,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectedEntry::Many(entries) => {
|
||||
SelectedFile::Many(entries) => {
|
||||
// In case of selection: save multiple files in wrkdir/input
|
||||
let mut dest_path: PathBuf = wrkdir;
|
||||
if let Some(save_as) = opts.save_as {
|
||||
@@ -150,7 +148,7 @@ impl FileTransferActivity {
|
||||
// Iter files
|
||||
if self.config().get_prompt_on_file_replace() {
|
||||
// Check which file would be replaced
|
||||
let existing_files: Vec<&Entry> = entries
|
||||
let existing_files: Vec<&File> = entries
|
||||
.iter()
|
||||
.filter(|x| {
|
||||
self.local_file_exists(
|
||||
@@ -176,13 +174,13 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectedEntry::None => {}
|
||||
SelectedFile::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set pending transfer into storage
|
||||
pub(crate) fn should_replace_file(&mut self, file_name: &str) -> bool {
|
||||
self.mount_radio_replace(file_name);
|
||||
pub(crate) fn should_replace_file(&mut self, file_name: String) -> bool {
|
||||
self.mount_radio_replace(&file_name);
|
||||
// Wait for answer
|
||||
trace!("Asking user whether he wants to replace file {}", file_name);
|
||||
if self.wait_for_pending_msg(&[
|
||||
@@ -201,8 +199,8 @@ impl FileTransferActivity {
|
||||
}
|
||||
|
||||
/// Set pending transfer for many files into storage and mount radio
|
||||
pub(crate) fn should_replace_files(&mut self, files: Vec<&Entry>) -> bool {
|
||||
let file_names: Vec<&str> = files.iter().map(|x| x.name()).collect();
|
||||
pub(crate) fn should_replace_files(&mut self, files: Vec<&File>) -> bool {
|
||||
let file_names: Vec<String> = files.iter().map(|x| x.name()).collect();
|
||||
self.mount_radio_replace_many(file_names.as_slice());
|
||||
// Wait for answer
|
||||
trace!(
|
||||
@@ -225,14 +223,14 @@ impl FileTransferActivity {
|
||||
}
|
||||
|
||||
/// Get file to check for path
|
||||
pub(crate) fn file_to_check(e: &Entry, alt: Option<&String>) -> PathBuf {
|
||||
pub(crate) fn file_to_check(e: &File, alt: Option<&String>) -> PathBuf {
|
||||
match alt {
|
||||
Some(s) => PathBuf::from(s),
|
||||
None => PathBuf::from(e.name()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn file_to_check_many(e: &Entry, wrkdir: &Path) -> PathBuf {
|
||||
pub(crate) fn file_to_check_many(e: &File, wrkdir: &Path) -> PathBuf {
|
||||
let mut p = wrkdir.to_path_buf();
|
||||
p.push(e.name());
|
||||
p
|
||||
|
||||
@@ -26,9 +26,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::{Entry, FileTransferActivity};
|
||||
|
||||
use remotefs::fs::{File, Metadata};
|
||||
use super::{File, FileTransferActivity};
|
||||
|
||||
enum SubmitAction {
|
||||
ChangeDir,
|
||||
@@ -38,73 +36,59 @@ enum SubmitAction {
|
||||
impl FileTransferActivity {
|
||||
/// Decides which action to perform on submit for local explorer
|
||||
/// Return true whether the directory changed
|
||||
pub(crate) fn action_submit_local(&mut self, entry: Entry) {
|
||||
let (action, entry) = match &entry {
|
||||
Entry::Directory(_) => (SubmitAction::ChangeDir, entry),
|
||||
Entry::File(File {
|
||||
path,
|
||||
metadata:
|
||||
Metadata {
|
||||
symlink: Some(symlink),
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => {
|
||||
// Stat file
|
||||
let stat_file = match self.host.stat(symlink.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
"Could not stat file pointed by {} ({}): {}",
|
||||
path.display(),
|
||||
symlink.display(),
|
||||
err
|
||||
);
|
||||
entry
|
||||
}
|
||||
};
|
||||
(SubmitAction::ChangeDir, stat_file)
|
||||
}
|
||||
Entry::File(_) => (SubmitAction::None, entry),
|
||||
pub(crate) fn action_submit_local(&mut self, entry: File) {
|
||||
let (action, entry) = if entry.is_dir() {
|
||||
(SubmitAction::ChangeDir, entry)
|
||||
} else if entry.metadata().symlink.is_some() {
|
||||
// Stat file
|
||||
let symlink = entry.metadata().symlink.as_ref().unwrap();
|
||||
let stat_file = match self.host.stat(symlink.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
"Could not stat file pointed by {} ({}): {}",
|
||||
entry.path().display(),
|
||||
symlink.display(),
|
||||
err
|
||||
);
|
||||
entry
|
||||
}
|
||||
};
|
||||
(SubmitAction::ChangeDir, stat_file)
|
||||
} else {
|
||||
(SubmitAction::None, entry)
|
||||
};
|
||||
if let (SubmitAction::ChangeDir, Entry::Directory(dir)) = (action, entry) {
|
||||
self.action_enter_local_dir(dir)
|
||||
if let (SubmitAction::ChangeDir, entry) = (action, entry) {
|
||||
self.action_enter_local_dir(entry)
|
||||
}
|
||||
}
|
||||
|
||||
/// Decides which action to perform on submit for remote explorer
|
||||
/// Return true whether the directory changed
|
||||
pub(crate) fn action_submit_remote(&mut self, entry: Entry) {
|
||||
let (action, entry) = match &entry {
|
||||
Entry::Directory(_) => (SubmitAction::ChangeDir, entry),
|
||||
Entry::File(File {
|
||||
path,
|
||||
metadata:
|
||||
Metadata {
|
||||
symlink: Some(symlink),
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => {
|
||||
// Stat file
|
||||
let stat_file = match self.client.stat(symlink.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
"Could not stat file pointed by {} ({}): {}",
|
||||
path.display(),
|
||||
symlink.display(),
|
||||
err
|
||||
);
|
||||
entry
|
||||
}
|
||||
};
|
||||
(SubmitAction::ChangeDir, stat_file)
|
||||
}
|
||||
Entry::File(_) => (SubmitAction::None, entry),
|
||||
pub(crate) fn action_submit_remote(&mut self, entry: File) {
|
||||
let (action, entry) = if entry.is_dir() {
|
||||
(SubmitAction::ChangeDir, entry)
|
||||
} else if entry.metadata().symlink.is_some() {
|
||||
// Stat file
|
||||
let symlink = entry.metadata().symlink.as_ref().unwrap();
|
||||
let stat_file = match self.client.stat(symlink.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
"Could not stat file pointed by {} ({}): {}",
|
||||
entry.path().display(),
|
||||
symlink.display(),
|
||||
err
|
||||
);
|
||||
entry
|
||||
}
|
||||
};
|
||||
(SubmitAction::ChangeDir, stat_file)
|
||||
} else {
|
||||
(SubmitAction::None, entry)
|
||||
};
|
||||
if let (SubmitAction::ChangeDir, Entry::Directory(dir)) = (action, entry) {
|
||||
self.action_enter_remote_dir(dir)
|
||||
if let (SubmitAction::ChangeDir, entry) = (action, entry) {
|
||||
self.action_enter_remote_dir(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::{FileTransferActivity, LogLevel, SelectedEntry};
|
||||
use super::{FileTransferActivity, LogLevel, SelectedFile};
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -34,7 +34,7 @@ impl FileTransferActivity {
|
||||
/// Create symlink on localhost
|
||||
#[cfg(target_family = "unix")]
|
||||
pub(crate) fn action_local_symlink(&mut self, name: String) {
|
||||
if let SelectedEntry::One(entry) = self.get_local_selected_entries() {
|
||||
if let SelectedFile::One(entry) = self.get_local_selected_entries() {
|
||||
match self
|
||||
.host
|
||||
.symlink(PathBuf::from(name.as_str()).as_path(), entry.path())
|
||||
@@ -66,7 +66,7 @@ impl FileTransferActivity {
|
||||
|
||||
/// Copy file on remote
|
||||
pub(crate) fn action_remote_symlink(&mut self, name: String) {
|
||||
if let SelectedEntry::One(entry) = self.get_remote_selected_entries() {
|
||||
if let SelectedFile::One(entry) = self.get_remote_selected_entries() {
|
||||
match self
|
||||
.client
|
||||
.symlink(PathBuf::from(name.as_str()).as_path(), entry.path())
|
||||
|
||||
@@ -31,7 +31,8 @@ use crate::explorer::FileSorting;
|
||||
use crate::utils::fmt::fmt_time;
|
||||
|
||||
use bytesize::ByteSize;
|
||||
use remotefs::Entry;
|
||||
use remotefs::File;
|
||||
use std::time::UNIX_EPOCH;
|
||||
|
||||
use tui_realm_stdlib::{Input, List, Paragraph, ProgressBar, Radio, Span};
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
@@ -399,7 +400,7 @@ pub struct FileInfoPopup {
|
||||
}
|
||||
|
||||
impl FileInfoPopup {
|
||||
pub fn new(file: &Entry) -> Self {
|
||||
pub fn new(file: &File) -> Self {
|
||||
let mut texts: TableBuilder = TableBuilder::default();
|
||||
// Abs path
|
||||
let real_path = file.metadata().symlink.as_deref();
|
||||
@@ -422,9 +423,18 @@ impl FileInfoPopup {
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("Size: "))
|
||||
.add_col(TextSpan::new(format!("{} ({})", bsize, size).as_str()).fg(Color::Cyan));
|
||||
let atime: String = fmt_time(file.metadata().atime, "%b %d %Y %H:%M:%S");
|
||||
let ctime: String = fmt_time(file.metadata().ctime, "%b %d %Y %H:%M:%S");
|
||||
let mtime: String = fmt_time(file.metadata().mtime, "%b %d %Y %H:%M:%S");
|
||||
let atime: String = fmt_time(
|
||||
file.metadata().accessed.unwrap_or(UNIX_EPOCH),
|
||||
"%b %d %Y %H:%M:%S",
|
||||
);
|
||||
let ctime: String = fmt_time(
|
||||
file.metadata().created.unwrap_or(UNIX_EPOCH),
|
||||
"%b %d %Y %H:%M:%S",
|
||||
);
|
||||
let mtime: String = fmt_time(
|
||||
file.metadata().modified.unwrap_or(UNIX_EPOCH),
|
||||
"%b %d %Y %H:%M:%S",
|
||||
);
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("Creation time: "))
|
||||
@@ -1373,7 +1383,7 @@ pub struct ReplacingFilesListPopup {
|
||||
}
|
||||
|
||||
impl ReplacingFilesListPopup {
|
||||
pub fn new(files: &[&str], color: Color) -> Self {
|
||||
pub fn new(files: &[String], color: Color) -> Self {
|
||||
Self {
|
||||
component: List::default()
|
||||
.borders(
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
use crate::explorer::{builder::FileExplorerBuilder, FileExplorer, FileSorting, GroupDirs};
|
||||
use crate::system::config_client::ConfigClient;
|
||||
|
||||
use remotefs::Entry;
|
||||
use remotefs::File;
|
||||
use std::path::Path;
|
||||
|
||||
/// File explorer tab
|
||||
@@ -92,7 +92,7 @@ impl Browser {
|
||||
self.found.as_mut().map(|x| &mut x.1)
|
||||
}
|
||||
|
||||
pub fn set_found(&mut self, tab: FoundExplorerTab, files: Vec<Entry>, wrkdir: &Path) {
|
||||
pub fn set_found(&mut self, tab: FoundExplorerTab, files: Vec<File>, wrkdir: &Path) {
|
||||
let mut explorer = Self::build_found_explorer(wrkdir);
|
||||
explorer.set_files(files);
|
||||
self.found = Some((tab, explorer));
|
||||
|
||||
@@ -191,7 +191,8 @@ impl FileTransferActivity {
|
||||
TransferPayload::File(file) => {
|
||||
format!(
|
||||
"File \"{}\" has been successfully transferred ({})",
|
||||
file.name, transfer_stats
|
||||
file.name(),
|
||||
transfer_stats
|
||||
)
|
||||
}
|
||||
TransferPayload::Any(entry) => {
|
||||
|
||||
@@ -32,7 +32,7 @@ use crate::utils::fmt::fmt_millis;
|
||||
|
||||
// Ext
|
||||
use bytesize::ByteSize;
|
||||
use remotefs::fs::{Entry, File, UnixPex, Welcome};
|
||||
use remotefs::fs::{File, ReadStream, UnixPex, Welcome, WriteStream};
|
||||
use remotefs::{RemoteError, RemoteErrorType};
|
||||
use std::fs::File as StdFile;
|
||||
use std::io::{Read, Seek, Write};
|
||||
@@ -59,13 +59,13 @@ enum TransferErrorReason {
|
||||
|
||||
/// Represents the entity to send or receive during a transfer.
|
||||
/// - File: describes an individual `File` to send
|
||||
/// - Any: Can be any kind of `Entry`, but just one
|
||||
/// - Many: a list of `Entry`
|
||||
/// - Any: Can be any kind of `File`, but just one
|
||||
/// - Many: a list of `File`
|
||||
#[derive(Debug)]
|
||||
pub(super) enum TransferPayload {
|
||||
File(File),
|
||||
Any(Entry),
|
||||
Many(Vec<Entry>),
|
||||
Any(File),
|
||||
Many(Vec<File>),
|
||||
}
|
||||
|
||||
impl FileTransferActivity {
|
||||
@@ -224,7 +224,7 @@ impl FileTransferActivity {
|
||||
// Mount progress bar
|
||||
self.mount_progress_bar(format!("Uploading {}…", file.path.display()));
|
||||
// Get remote path
|
||||
let file_name: String = file.name.clone();
|
||||
let file_name: String = file.name();
|
||||
let mut remote_path: PathBuf = PathBuf::from(curr_remote_path);
|
||||
let remote_file_name: PathBuf = match dst_name {
|
||||
Some(s) => PathBuf::from(s.as_str()),
|
||||
@@ -242,7 +242,7 @@ impl FileTransferActivity {
|
||||
/// Send a `TransferPayload` of type `Any`
|
||||
fn filetransfer_send_any(
|
||||
&mut self,
|
||||
entry: &Entry,
|
||||
entry: &File,
|
||||
curr_remote_path: &Path,
|
||||
dst_name: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
@@ -263,7 +263,7 @@ impl FileTransferActivity {
|
||||
/// Send many entries to remote
|
||||
fn filetransfer_send_many(
|
||||
&mut self,
|
||||
entries: &[Entry],
|
||||
entries: &[File],
|
||||
curr_remote_path: &Path,
|
||||
) -> Result<(), String> {
|
||||
// Reset states
|
||||
@@ -289,15 +289,12 @@ impl FileTransferActivity {
|
||||
|
||||
fn filetransfer_send_recurse(
|
||||
&mut self,
|
||||
entry: &Entry,
|
||||
entry: &File,
|
||||
curr_remote_path: &Path,
|
||||
dst_name: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
// Write popup
|
||||
let file_name: String = match entry {
|
||||
Entry::Directory(dir) => dir.name.clone(),
|
||||
Entry::File(file) => file.name.clone(),
|
||||
};
|
||||
let file_name = entry.name();
|
||||
// Get remote path
|
||||
let mut remote_path: PathBuf = PathBuf::from(curr_remote_path);
|
||||
let remote_file_name: PathBuf = match dst_name {
|
||||
@@ -306,107 +303,104 @@ impl FileTransferActivity {
|
||||
};
|
||||
remote_path.push(remote_file_name);
|
||||
// Match entry
|
||||
let result: Result<(), String> = match entry {
|
||||
Entry::File(file) => {
|
||||
match self.filetransfer_send_one(file, remote_path.as_path(), file_name) {
|
||||
Err(err) => {
|
||||
// If transfer was abrupted or there was an IO error on remote, remove file
|
||||
if matches!(
|
||||
err,
|
||||
TransferErrorReason::Abrupted | TransferErrorReason::RemoteIoError(_)
|
||||
) {
|
||||
// Stat file on remote and remove it if exists
|
||||
match self.client.stat(remote_path.as_path()) {
|
||||
Err(err) => self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not remove created file {}: {}",
|
||||
remote_path.display(),
|
||||
err
|
||||
),
|
||||
let result: Result<(), String> = if entry.is_dir() {
|
||||
// Create directory on remote first
|
||||
match self
|
||||
.client
|
||||
.create_dir(remote_path.as_path(), UnixPex::from(0o755))
|
||||
{
|
||||
Ok(_) => {
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!("Created directory \"{}\"", remote_path.display()),
|
||||
);
|
||||
}
|
||||
Err(err) if err.kind == RemoteErrorType::DirectoryAlreadyExists => {
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!(
|
||||
"Directory \"{}\" already exists on remote",
|
||||
remote_path.display()
|
||||
),
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Failed to create directory \"{}\": {}",
|
||||
remote_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
return Err(err.to_string());
|
||||
}
|
||||
}
|
||||
// Get files in dir
|
||||
match self.host.scan_dir(entry.path()) {
|
||||
Ok(entries) => {
|
||||
// Iterate over files
|
||||
for entry in entries.iter() {
|
||||
// If aborted; break
|
||||
if self.transfer.aborted() {
|
||||
break;
|
||||
}
|
||||
// Send entry; name is always None after first call
|
||||
if let Err(err) =
|
||||
self.filetransfer_send_recurse(entry, remote_path.as_path(), None)
|
||||
{
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not scan directory \"{}\": {}",
|
||||
entry.path().display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
Err(err.to_string())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match self.filetransfer_send_one(entry, remote_path.as_path(), file_name) {
|
||||
Err(err) => {
|
||||
// If transfer was abrupted or there was an IO error on remote, remove file
|
||||
if matches!(
|
||||
err,
|
||||
TransferErrorReason::Abrupted | TransferErrorReason::RemoteIoError(_)
|
||||
) {
|
||||
// Stat file on remote and remove it if exists
|
||||
match self.client.stat(remote_path.as_path()) {
|
||||
Err(err) => self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not remove created file {}: {}",
|
||||
remote_path.display(),
|
||||
err
|
||||
),
|
||||
Ok(entry) => {
|
||||
if let Err(err) = self.client.remove_file(entry.path()) {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not remove created file {}: {}",
|
||||
remote_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
Ok(entry) => {
|
||||
if let Err(err) = self.client.remove_file(entry.path()) {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not remove created file {}: {}",
|
||||
remote_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err.to_string())
|
||||
}
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
Entry::Directory(dir) => {
|
||||
// Create directory on remote first
|
||||
match self
|
||||
.client
|
||||
.create_dir(remote_path.as_path(), UnixPex::from(0o755))
|
||||
{
|
||||
Ok(_) => {
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!("Created directory \"{}\"", remote_path.display()),
|
||||
);
|
||||
}
|
||||
Err(err) if err.kind == RemoteErrorType::DirectoryAlreadyExists => {
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!(
|
||||
"Directory \"{}\" already exists on remote",
|
||||
remote_path.display()
|
||||
),
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Failed to create directory \"{}\": {}",
|
||||
remote_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
return Err(err.to_string());
|
||||
}
|
||||
}
|
||||
// Get files in dir
|
||||
match self.host.scan_dir(dir.path.as_path()) {
|
||||
Ok(entries) => {
|
||||
// Iterate over files
|
||||
for entry in entries.iter() {
|
||||
// If aborted; break
|
||||
if self.transfer.aborted() {
|
||||
break;
|
||||
}
|
||||
// Send entry; name is always None after first call
|
||||
if let Err(err) =
|
||||
self.filetransfer_send_recurse(entry, remote_path.as_path(), None)
|
||||
{
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not scan directory \"{}\": {}",
|
||||
dir.path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
Err(err.to_string())
|
||||
}
|
||||
Err(err.to_string())
|
||||
}
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
};
|
||||
// Scan dir on remote
|
||||
@@ -458,7 +452,7 @@ impl FileTransferActivity {
|
||||
remote: &Path,
|
||||
file_name: String,
|
||||
mut reader: StdFile,
|
||||
mut writer: Box<dyn Write>,
|
||||
mut writer: WriteStream,
|
||||
) -> Result<(), TransferErrorReason> {
|
||||
// Write file
|
||||
let file_size: usize = reader.seek(std::io::SeekFrom::End(0)).unwrap_or(0) as usize;
|
||||
@@ -632,7 +626,7 @@ impl FileTransferActivity {
|
||||
/// If entry is a directory, this applies to directory only
|
||||
fn filetransfer_recv_any(
|
||||
&mut self,
|
||||
entry: &Entry,
|
||||
entry: &File,
|
||||
local_path: &Path,
|
||||
dst_name: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
@@ -660,7 +654,7 @@ impl FileTransferActivity {
|
||||
// Mount progress bar
|
||||
self.mount_progress_bar(format!("Downloading {}…", entry.path.display()));
|
||||
// Receive
|
||||
let result = self.filetransfer_recv_one(local_path, entry, entry.name.clone());
|
||||
let result = self.filetransfer_recv_one(local_path, entry, entry.name());
|
||||
// Umount progress bar
|
||||
self.umount_progress_bar();
|
||||
// Return result
|
||||
@@ -670,7 +664,7 @@ impl FileTransferActivity {
|
||||
/// Send many entries to remote
|
||||
fn filetransfer_recv_many(
|
||||
&mut self,
|
||||
entries: &[Entry],
|
||||
entries: &[File],
|
||||
curr_remote_path: &Path,
|
||||
) -> Result<(), String> {
|
||||
// Reset states
|
||||
@@ -696,142 +690,132 @@ impl FileTransferActivity {
|
||||
|
||||
fn filetransfer_recv_recurse(
|
||||
&mut self,
|
||||
entry: &Entry,
|
||||
entry: &File,
|
||||
local_path: &Path,
|
||||
dst_name: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
// Write popup
|
||||
let file_name: String = match entry {
|
||||
Entry::Directory(dir) => dir.name.clone(),
|
||||
Entry::File(file) => file.name.clone(),
|
||||
};
|
||||
let file_name = entry.name();
|
||||
// Match entry
|
||||
let result: Result<(), String> = match entry {
|
||||
Entry::File(file) => {
|
||||
// Get local file
|
||||
let mut local_file_path: PathBuf = PathBuf::from(local_path);
|
||||
let local_file_name: String = match dst_name {
|
||||
Some(n) => n,
|
||||
None => file.name.clone(),
|
||||
};
|
||||
local_file_path.push(local_file_name.as_str());
|
||||
// Download file
|
||||
if let Err(err) =
|
||||
self.filetransfer_recv_one(local_file_path.as_path(), file, file_name)
|
||||
{
|
||||
// If transfer was abrupted or there was an IO error on remote, remove file
|
||||
if matches!(
|
||||
err,
|
||||
TransferErrorReason::Abrupted | TransferErrorReason::LocalIoError(_)
|
||||
) {
|
||||
// Stat file
|
||||
match self.host.stat(local_file_path.as_path()) {
|
||||
Err(err) => self.log(
|
||||
let result: Result<(), String> = if entry.is_dir() {
|
||||
// Get dir name
|
||||
let mut local_dir_path: PathBuf = PathBuf::from(local_path);
|
||||
match dst_name {
|
||||
Some(name) => local_dir_path.push(name),
|
||||
None => local_dir_path.push(entry.name()),
|
||||
}
|
||||
// Create directory on local
|
||||
match self.host.mkdir_ex(local_dir_path.as_path(), true) {
|
||||
Ok(_) => {
|
||||
// Apply file mode to directory
|
||||
#[cfg(any(target_family = "unix", target_os = "macos", target_os = "linux"))]
|
||||
if let Some(mode) = entry.metadata().mode {
|
||||
if let Err(err) = self.host.chmod(local_dir_path.as_path(), mode) {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not remove created file {}: {}",
|
||||
local_file_path.display(),
|
||||
"Could not apply file mode {:o} to \"{}\": {}",
|
||||
u32::from(mode),
|
||||
local_dir_path.display(),
|
||||
err
|
||||
),
|
||||
),
|
||||
Ok(entry) => {
|
||||
if let Err(err) = self.host.remove(&entry) {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not remove created file {}: {}",
|
||||
local_file_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!("Created directory \"{}\"", local_dir_path.display()),
|
||||
);
|
||||
// Get files in dir
|
||||
match self.client.list_dir(entry.path()) {
|
||||
Ok(entries) => {
|
||||
// Iterate over files
|
||||
for entry in entries.iter() {
|
||||
// If transfer has been aborted; break
|
||||
if self.transfer.aborted() {
|
||||
break;
|
||||
}
|
||||
// Receive entry; name is always None after first call
|
||||
// Local path becomes local_dir_path
|
||||
if let Err(err) = self.filetransfer_recv_recurse(
|
||||
entry,
|
||||
local_dir_path.as_path(),
|
||||
None,
|
||||
) {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not scan directory \"{}\": {}",
|
||||
entry.path().display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
Err(err.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Failed to create directory \"{}\": {}",
|
||||
local_dir_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
Err(err.to_string())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Entry::Directory(dir) => {
|
||||
// Get dir name
|
||||
let mut local_dir_path: PathBuf = PathBuf::from(local_path);
|
||||
match dst_name {
|
||||
Some(name) => local_dir_path.push(name),
|
||||
None => local_dir_path.push(dir.name.as_str()),
|
||||
}
|
||||
// Create directory on local
|
||||
match self.host.mkdir_ex(local_dir_path.as_path(), true) {
|
||||
Ok(_) => {
|
||||
// Apply file mode to directory
|
||||
#[cfg(any(
|
||||
target_family = "unix",
|
||||
target_os = "macos",
|
||||
target_os = "linux"
|
||||
))]
|
||||
if let Some(mode) = dir.metadata.mode {
|
||||
if let Err(err) = self.host.chmod(local_dir_path.as_path(), mode) {
|
||||
} else {
|
||||
// Get local file
|
||||
let mut local_file_path: PathBuf = PathBuf::from(local_path);
|
||||
let local_file_name: String = match dst_name {
|
||||
Some(n) => n,
|
||||
None => entry.name(),
|
||||
};
|
||||
local_file_path.push(local_file_name.as_str());
|
||||
// Download file
|
||||
if let Err(err) =
|
||||
self.filetransfer_recv_one(local_file_path.as_path(), entry, file_name)
|
||||
{
|
||||
// If transfer was abrupted or there was an IO error on remote, remove file
|
||||
if matches!(
|
||||
err,
|
||||
TransferErrorReason::Abrupted | TransferErrorReason::LocalIoError(_)
|
||||
) {
|
||||
// Stat file
|
||||
match self.host.stat(local_file_path.as_path()) {
|
||||
Err(err) => self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not remove created file {}: {}",
|
||||
local_file_path.display(),
|
||||
err
|
||||
),
|
||||
),
|
||||
Ok(entry) => {
|
||||
if let Err(err) = self.host.remove(&entry) {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not apply file mode {:o} to \"{}\": {}",
|
||||
u32::from(mode),
|
||||
local_dir_path.display(),
|
||||
"Could not remove created file {}: {}",
|
||||
local_file_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.log(
|
||||
LogLevel::Info,
|
||||
format!("Created directory \"{}\"", local_dir_path.display()),
|
||||
);
|
||||
// Get files in dir
|
||||
match self.client.list_dir(dir.path.as_path()) {
|
||||
Ok(entries) => {
|
||||
// Iterate over files
|
||||
for entry in entries.iter() {
|
||||
// If transfer has been aborted; break
|
||||
if self.transfer.aborted() {
|
||||
break;
|
||||
}
|
||||
// Receive entry; name is always None after first call
|
||||
// Local path becomes local_dir_path
|
||||
if let Err(err) = self.filetransfer_recv_recurse(
|
||||
entry,
|
||||
local_dir_path.as_path(),
|
||||
None,
|
||||
) {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not scan directory \"{}\": {}",
|
||||
dir.path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
Err(err.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Failed to create directory \"{}\": {}",
|
||||
local_dir_path.display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
Err(err.to_string())
|
||||
}
|
||||
}
|
||||
Err(err.to_string())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
// Reload directory on local
|
||||
@@ -872,13 +856,13 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive an `Entry` from remote using stream
|
||||
/// Receive an `File` from remote using stream
|
||||
fn filetransfer_recv_one_with_stream(
|
||||
&mut self,
|
||||
local: &Path,
|
||||
remote: &File,
|
||||
file_name: String,
|
||||
mut reader: Box<dyn Read>,
|
||||
mut reader: ReadStream,
|
||||
mut writer: StdFile,
|
||||
) -> Result<(), TransferErrorReason> {
|
||||
let mut total_bytes_written: usize = 0;
|
||||
@@ -979,7 +963,7 @@ impl FileTransferActivity {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive an `Entry` from remote without using stream
|
||||
/// Receive an `File` from remote without using stream
|
||||
fn filetransfer_recv_one_wno_stream(
|
||||
&mut self,
|
||||
local: &Path,
|
||||
@@ -1098,7 +1082,7 @@ impl FileTransferActivity {
|
||||
let tmpfile: PathBuf = match self.cache.as_ref() {
|
||||
Some(cache) => {
|
||||
let mut p: PathBuf = cache.path().to_path_buf();
|
||||
p.push(file.name.as_str());
|
||||
p.push(file.name());
|
||||
p
|
||||
}
|
||||
None => {
|
||||
@@ -1111,7 +1095,7 @@ impl FileTransferActivity {
|
||||
match self.filetransfer_recv(
|
||||
TransferPayload::File(file.clone()),
|
||||
tmpfile.as_path(),
|
||||
Some(file.name.clone()),
|
||||
Some(file.name()),
|
||||
) {
|
||||
Err(err) => Err(format!(
|
||||
"Could not download {} to temporary file: {}",
|
||||
@@ -1125,48 +1109,54 @@ impl FileTransferActivity {
|
||||
// -- transfer sizes
|
||||
|
||||
/// Get total size of transfer for localhost
|
||||
fn get_total_transfer_size_local(&mut self, entry: &Entry) -> usize {
|
||||
match entry {
|
||||
Entry::File(file) => file.metadata.size as usize,
|
||||
Entry::Directory(dir) => {
|
||||
// List dir
|
||||
match self.host.scan_dir(dir.path.as_path()) {
|
||||
Ok(files) => files
|
||||
.iter()
|
||||
.map(|x| self.get_total_transfer_size_local(x))
|
||||
.sum(),
|
||||
Err(err) => {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!("Could not list directory {}: {}", dir.path.display(), err),
|
||||
);
|
||||
0
|
||||
}
|
||||
fn get_total_transfer_size_local(&mut self, entry: &File) -> usize {
|
||||
if entry.is_dir() {
|
||||
// List dir
|
||||
match self.host.scan_dir(entry.path()) {
|
||||
Ok(files) => files
|
||||
.iter()
|
||||
.map(|x| self.get_total_transfer_size_local(x))
|
||||
.sum(),
|
||||
Err(err) => {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not list directory {}: {}",
|
||||
entry.path().display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entry.metadata.size as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Get total size of transfer for remote host
|
||||
fn get_total_transfer_size_remote(&mut self, entry: &Entry) -> usize {
|
||||
match entry {
|
||||
Entry::File(file) => file.metadata.size as usize,
|
||||
Entry::Directory(dir) => {
|
||||
// List directory
|
||||
match self.client.list_dir(dir.path.as_path()) {
|
||||
Ok(files) => files
|
||||
.iter()
|
||||
.map(|x| self.get_total_transfer_size_remote(x))
|
||||
.sum(),
|
||||
Err(err) => {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!("Could not list directory {}: {}", dir.path.display(), err),
|
||||
);
|
||||
0
|
||||
}
|
||||
fn get_total_transfer_size_remote(&mut self, entry: &File) -> usize {
|
||||
if entry.is_dir() {
|
||||
// List directory
|
||||
match self.client.list_dir(entry.path()) {
|
||||
Ok(files) => files
|
||||
.iter()
|
||||
.map(|x| self.get_total_transfer_size_remote(x))
|
||||
.sum(),
|
||||
Err(err) => {
|
||||
self.log(
|
||||
LogLevel::Error,
|
||||
format!(
|
||||
"Could not list directory {}: {}",
|
||||
entry.path().display(),
|
||||
err
|
||||
),
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entry.metadata.size as usize
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
*/
|
||||
// locals
|
||||
use super::{
|
||||
actions::SelectedEntry,
|
||||
actions::SelectedFile,
|
||||
browser::{FileExplorerTab, FoundExplorerTab},
|
||||
ExitReason, FileTransferActivity, Id, Msg, TransferMsg, TransferOpts, UiMsg,
|
||||
};
|
||||
// externals
|
||||
use remotefs::fs::Entry;
|
||||
use remotefs::fs::File;
|
||||
use tuirealm::{
|
||||
props::{AttrValue, Attribute},
|
||||
State, StateValue, Update,
|
||||
@@ -121,7 +121,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
TransferMsg::EnterDirectory if self.browser.tab() == FileExplorerTab::Local => {
|
||||
if let SelectedEntry::One(entry) = self.get_local_selected_entries() {
|
||||
if let SelectedFile::One(entry) = self.get_local_selected_entries() {
|
||||
self.action_submit_local(entry);
|
||||
// Update file list if sync
|
||||
if self.browser.sync_browsing && self.browser.found().is_none() {
|
||||
@@ -131,7 +131,7 @@ impl FileTransferActivity {
|
||||
}
|
||||
}
|
||||
TransferMsg::EnterDirectory if self.browser.tab() == FileExplorerTab::Remote => {
|
||||
if let SelectedEntry::One(entry) = self.get_remote_selected_entries() {
|
||||
if let SelectedFile::One(entry) = self.get_remote_selected_entries() {
|
||||
self.action_submit_remote(entry);
|
||||
// Update file list if sync
|
||||
if self.browser.sync_browsing && self.browser.found().is_none() {
|
||||
@@ -296,7 +296,7 @@ impl FileTransferActivity {
|
||||
// Mount wait
|
||||
self.mount_blocking_wait(format!(r#"Searching for "{}"…"#, search).as_str());
|
||||
// Find
|
||||
let res: Result<Vec<Entry>, String> = match self.browser.tab() {
|
||||
let res: Result<Vec<File>, String> = match self.browser.tab() {
|
||||
FileExplorerTab::Local => self.action_local_find(search.clone()),
|
||||
FileExplorerTab::Remote => self.action_remote_find(search.clone()),
|
||||
_ => panic!("Trying to search for files, while already in a find result"),
|
||||
@@ -449,17 +449,17 @@ impl FileTransferActivity {
|
||||
UiMsg::ShowDisconnectPopup => self.mount_disconnect(),
|
||||
UiMsg::ShowExecPopup => self.mount_exec(),
|
||||
UiMsg::ShowFileInfoPopup if self.browser.tab() == FileExplorerTab::Local => {
|
||||
if let SelectedEntry::One(file) = self.get_local_selected_entries() {
|
||||
if let SelectedFile::One(file) = self.get_local_selected_entries() {
|
||||
self.mount_file_info(&file);
|
||||
}
|
||||
}
|
||||
UiMsg::ShowFileInfoPopup if self.browser.tab() == FileExplorerTab::Remote => {
|
||||
if let SelectedEntry::One(file) = self.get_remote_selected_entries() {
|
||||
if let SelectedFile::One(file) = self.get_remote_selected_entries() {
|
||||
self.mount_file_info(&file);
|
||||
}
|
||||
}
|
||||
UiMsg::ShowFileInfoPopup => {
|
||||
if let SelectedEntry::One(file) = self.get_found_selected_entries() {
|
||||
if let SelectedFile::One(file) = self.get_found_selected_entries() {
|
||||
self.mount_file_info(&file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ use super::{
|
||||
use crate::explorer::FileSorting;
|
||||
use crate::utils::ui::draw_area_in;
|
||||
// Ext
|
||||
use remotefs::fs::Entry;
|
||||
use remotefs::fs::File;
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::tui::layout::{Constraint, Direction, Layout};
|
||||
use tuirealm::tui::widgets::Clear;
|
||||
@@ -719,7 +719,7 @@ impl FileTransferActivity {
|
||||
assert!(self.app.active(&Id::ReplacePopup).is_ok());
|
||||
}
|
||||
|
||||
pub(super) fn mount_radio_replace_many(&mut self, files: &[&str]) {
|
||||
pub(super) fn mount_radio_replace_many(&mut self, files: &[String]) {
|
||||
let warn_color = self.theme().misc_warn_dialog;
|
||||
assert!(self
|
||||
.app
|
||||
@@ -750,7 +750,7 @@ impl FileTransferActivity {
|
||||
let _ = self.app.umount(&Id::ReplacingFilesListPopup); // NOTE: replace anyway
|
||||
}
|
||||
|
||||
pub(super) fn mount_file_info(&mut self, file: &Entry) {
|
||||
pub(super) fn mount_file_info(&mut self, file: &File) {
|
||||
assert!(self
|
||||
.app
|
||||
.remount(
|
||||
|
||||
Reference in New Issue
Block a user