Merge branch '0.5.1' into 0.6.0

This commit is contained in:
veeso
2021-06-21 18:22:19 +02:00
55 changed files with 2384 additions and 1639 deletions
+125 -5
View File
@@ -27,8 +27,9 @@
*/
extern crate tempfile;
// locals
use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry};
use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry, TransferPayload};
use crate::filetransfer::FileTransferErrorType;
use crate::fs::FsFile;
use std::path::{Path, PathBuf};
impl FileTransferActivity {
@@ -66,7 +67,7 @@ impl FileTransferActivity {
match self.get_remote_selected_entries() {
SelectedEntry::One(entry) => {
let dest_path: PathBuf = PathBuf::from(input);
self.remote_copy_file(&entry, dest_path.as_path());
self.remote_copy_file(entry, dest_path.as_path());
// Reload entries
self.reload_remote_dir();
}
@@ -74,7 +75,7 @@ impl FileTransferActivity {
// Try to copy each file to Input/{FILE_NAME}
let base_path: PathBuf = PathBuf::from(input);
// Iter files
for entry in entries.iter() {
for entry in entries.into_iter() {
let mut dest_path: PathBuf = base_path.clone();
dest_path.push(entry.get_name());
self.remote_copy_file(entry, dest_path.as_path());
@@ -110,8 +111,8 @@ impl FileTransferActivity {
}
}
fn remote_copy_file(&mut self, entry: &FsEntry, dest: &Path) {
match self.client.as_mut().copy(entry, dest) {
fn remote_copy_file(&mut self, entry: FsEntry, dest: &Path) {
match self.client.as_mut().copy(&entry, dest) {
Ok(_) => {
self.log(
LogLevel::Info,
@@ -139,4 +140,123 @@ impl FileTransferActivity {
},
}
}
/// ### tricky_copy
///
/// Tricky copy will be used whenever copy command is not available on remote host
fn tricky_copy(&mut self, entry: FsEntry, dest: &Path) {
// match entry
match entry {
FsEntry::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;
}
};
// Download file
let name = entry.name.clone();
let entry_path = entry.abs_path.clone();
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;
}
// Get local fs entry
let tmpfile_entry: FsFile = 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;
}
};
// 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;
}
}
FsEntry::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;
}
};
// Get path of dest
let mut tempdir_path: PathBuf = tempdir.path().to_path_buf();
tempdir_path.push(entry.get_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;
}
// Stat dir
let tempdir_entry: FsEntry = 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;
}
};
// 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;
}
}
}
}
}
+157 -4
View File
@@ -26,7 +26,14 @@
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry};
use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry, TransferPayload};
use crate::fs::FsFile;
// ext
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use std::fs::OpenOptions;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
impl FileTransferActivity {
pub(crate) fn action_edit_local_file(&mut self) {
@@ -60,15 +67,15 @@ impl FileTransferActivity {
SelectedEntry::None => vec![],
};
// Edit all entries
for entry in entries.iter() {
for entry in entries.into_iter() {
// Check if file
if let FsEntry::File(file) = entry {
self.log(
LogLevel::Info,
format!("Opening file \"{}\"...", entry.get_abs_path().display()),
format!("Opening file \"{}\"...", file.abs_path.display()),
);
// Edit file
if let Err(err) = self.edit_remote_file(&file) {
if let Err(err) = self.edit_remote_file(file) {
self.log_and_alert(LogLevel::Error, err);
}
}
@@ -76,4 +83,150 @@ impl FileTransferActivity {
// Reload entries
self.reload_remote_dir();
}
/// ### edit_local_file
///
/// Edit a file on localhost
fn edit_local_file(&mut self, path: &Path) -> Result<(), String> {
// Read first 2048 bytes or less from file to check if it is textual
match OpenOptions::new().read(true).open(path) {
Ok(mut f) => {
// Read
let mut buff: [u8; 2048] = [0; 2048];
match f.read(&mut buff) {
Ok(size) => {
if content_inspector::inspect(&buff[0..size]).is_binary() {
return Err("Could not open file in editor: file is binary".to_string());
}
}
Err(err) => {
return Err(format!("Could not read file: {}", err));
}
}
}
Err(err) => {
return Err(format!("Could not read file: {}", err));
}
}
// Put input mode back to normal
if let Err(err) = disable_raw_mode() {
error!("Failed to disable raw mode: {}", err);
}
// Leave alternate mode
#[cfg(not(target_os = "windows"))]
if let Some(ctx) = self.context.as_mut() {
ctx.leave_alternate_screen();
}
// Open editor
match edit::edit_file(path) {
Ok(_) => self.log(
LogLevel::Info,
format!(
"Changes performed through editor saved to \"{}\"!",
path.display()
),
),
Err(err) => return Err(format!("Could not open editor: {}", err)),
}
#[cfg(not(target_os = "windows"))]
if let Some(ctx) = self.context.as_mut() {
// Clear screen
ctx.clear_screen();
// Enter alternate mode
ctx.enter_alternate_screen();
}
// Re-enable raw mode
let _ = enable_raw_mode();
Ok(())
}
/// ### edit_remote_file
///
/// Edit file on remote host
fn edit_remote_file(&mut self, file: FsFile) -> Result<(), String> {
// Create temp file
let tmpfile: PathBuf = match self.download_file_as_temp(&file) {
Ok(p) => p,
Err(err) => return Err(err),
};
// Download file
let file_name = file.name.clone();
let file_path = file.abs_path.clone();
if let Err(err) = self.filetransfer_recv(
TransferPayload::File(file),
tmpfile.as_path(),
Some(file_name.clone()),
) {
return Err(format!("Could not open file {}: {}", file_name, err));
}
// Get current file modification time
let prev_mtime: SystemTime = match self.host.stat(tmpfile.as_path()) {
Ok(e) => e.get_last_change_time(),
Err(err) => {
return Err(format!(
"Could not stat \"{}\": {}",
tmpfile.as_path().display(),
err
))
}
};
// Edit file
if let Err(err) = self.edit_local_file(tmpfile.as_path()) {
return Err(err);
}
// Get local fs entry
let tmpfile_entry: FsEntry = match self.host.stat(tmpfile.as_path()) {
Ok(e) => e,
Err(err) => {
return Err(format!(
"Could not stat \"{}\": {}",
tmpfile.as_path().display(),
err
))
}
};
// Check if file has changed
match prev_mtime != tmpfile_entry.get_last_change_time() {
true => {
self.log(
LogLevel::Info,
format!(
"File \"{}\" has changed; writing changes to remote",
file_path.display()
),
);
// Get local fs entry
let tmpfile_entry: FsFile = match self.host.stat(tmpfile.as_path()) {
Ok(e) => e.unwrap_file(),
Err(err) => {
return Err(format!(
"Could not stat \"{}\": {}",
tmpfile.as_path().display(),
err
))
}
};
// Send file
let wrkdir = self.remote().wrkdir.clone();
if let Err(err) = self.filetransfer_send(
TransferPayload::File(tmpfile_entry),
wrkdir.as_path(),
Some(file_name),
) {
return Err(format!(
"Could not write file {}: {}",
file_path.display(),
err
));
}
}
false => {
self.log(
LogLevel::Info,
format!("File \"{}\" hasn't changed", file_path.display()),
);
}
}
Ok(())
}
}
+49 -16
View File
@@ -27,7 +27,7 @@
*/
// locals
use super::super::browser::FileExplorerTab;
use super::{FileTransferActivity, FsEntry, SelectedEntry};
use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry, TransferPayload};
use std::path::PathBuf;
@@ -77,10 +77,30 @@ impl FileTransferActivity {
match self.get_found_selected_entries() {
SelectedEntry::One(entry) => match self.browser.tab() {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
self.filetransfer_send(&entry.get_realfile(), wrkdir.as_path(), save_as);
if let Err(err) = self.filetransfer_send(
TransferPayload::Any(entry.get_realfile()),
wrkdir.as_path(),
save_as,
) {
self.log_and_alert(
LogLevel::Error,
format!("Could not upload file: {}", err),
);
return;
}
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
self.filetransfer_recv(&entry.get_realfile(), wrkdir.as_path(), save_as);
if let Err(err) = self.filetransfer_recv(
TransferPayload::Any(entry.get_realfile()),
wrkdir.as_path(),
save_as,
) {
self.log_and_alert(
LogLevel::Error,
format!("Could not download file: {}", err),
);
return;
}
}
},
SelectedEntry::Many(entries) => {
@@ -90,21 +110,34 @@ impl FileTransferActivity {
dest_path.push(save_as);
}
// Iter files
for entry in entries.iter() {
match self.browser.tab() {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
self.filetransfer_send(
&entry.get_realfile(),
dest_path.as_path(),
None,
);
let entries = entries.iter().map(|x| x.get_realfile()).collect();
match self.browser.tab() {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
if let Err(err) = self.filetransfer_send(
TransferPayload::Many(entries),
dest_path.as_path(),
None,
) {
{
self.log_and_alert(
LogLevel::Error,
format!("Could not upload file: {}", err),
);
return;
}
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
self.filetransfer_recv(
&entry.get_realfile(),
dest_path.as_path(),
None,
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
if let Err(err) = self.filetransfer_recv(
TransferPayload::Many(entries),
dest_path.as_path(),
None,
) {
self.log_and_alert(
LogLevel::Error,
format!("Could not download file: {}", err),
);
return;
}
}
}
@@ -25,7 +25,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub(self) use super::{FileTransferActivity, FsEntry, LogLevel};
pub(self) use super::{FileTransferActivity, FsEntry, LogLevel, TransferPayload};
use tuirealm::{Payload, Value};
// actions
@@ -82,8 +82,7 @@ impl FileTransferActivity {
let files: Vec<&FsEntry> = files
.iter()
.map(|x| self.local().get(*x)) // Usize to Option<FsEntry>
.filter(|x| x.is_some()) // Get only some values
.map(|x| x.unwrap()) // Option to FsEntry
.flatten()
.collect();
SelectedEntry::from(files)
}
@@ -101,8 +100,7 @@ impl FileTransferActivity {
let files: Vec<&FsEntry> = files
.iter()
.map(|x| self.remote().get(*x)) // Usize to Option<FsEntry>
.filter(|x| x.is_some()) // Get only some values
.map(|x| x.unwrap()) // Option to FsEntry
.flatten()
.collect();
SelectedEntry::from(files)
}
@@ -122,8 +120,7 @@ impl FileTransferActivity {
let files: Vec<&FsEntry> = files
.iter()
.map(|x| self.found().as_ref().unwrap().get(*x)) // Usize to Option<FsEntry>
.filter(|x| x.is_some()) // Get only some values
.map(|x| x.unwrap()) // Option to FsEntry
.flatten()
.collect();
SelectedEntry::from(files)
}
+20 -7
View File
@@ -28,7 +28,7 @@
// deps
extern crate open;
// locals
use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry};
use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry, TransferPayload};
// ext
use std::path::{Path, PathBuf};
@@ -90,12 +90,25 @@ impl FileTransferActivity {
}
Some(p) => p.path().to_path_buf(),
};
self.filetransfer_recv(&entry, cache.as_path(), Some(tmpfile.clone()));
// 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);
match self.filetransfer_recv(
TransferPayload::Any(entry),
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),
);
}
}
}
+53 -7
View File
@@ -26,7 +26,7 @@
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, SelectedEntry};
use super::{FileTransferActivity, LogLevel, SelectedEntry, TransferPayload};
use std::path::PathBuf;
impl FileTransferActivity {
@@ -50,7 +50,19 @@ impl FileTransferActivity {
let wrkdir: PathBuf = self.remote().wrkdir.clone();
match self.get_local_selected_entries() {
SelectedEntry::One(entry) => {
self.filetransfer_send(&entry.get_realfile(), wrkdir.as_path(), save_as);
if let Err(err) = self.filetransfer_send(
TransferPayload::Any(entry.get_realfile()),
wrkdir.as_path(),
save_as,
) {
{
self.log_and_alert(
LogLevel::Error,
format!("Could not upload file: {}", err),
);
return;
}
}
}
SelectedEntry::Many(entries) => {
// In case of selection: save multiple files in wrkdir/input
@@ -59,8 +71,19 @@ impl FileTransferActivity {
dest_path.push(save_as);
}
// Iter files
for entry in entries.iter() {
self.filetransfer_send(&entry.get_realfile(), dest_path.as_path(), None);
let entries = entries.iter().map(|x| x.get_realfile()).collect();
if let Err(err) = self.filetransfer_send(
TransferPayload::Many(entries),
dest_path.as_path(),
None,
) {
{
self.log_and_alert(
LogLevel::Error,
format!("Could not upload file: {}", err),
);
return;
}
}
}
SelectedEntry::None => {}
@@ -71,7 +94,19 @@ impl FileTransferActivity {
let wrkdir: PathBuf = self.local().wrkdir.clone();
match self.get_remote_selected_entries() {
SelectedEntry::One(entry) => {
self.filetransfer_recv(&entry.get_realfile(), wrkdir.as_path(), save_as);
if let Err(err) = self.filetransfer_recv(
TransferPayload::Any(entry.get_realfile()),
wrkdir.as_path(),
save_as,
) {
{
self.log_and_alert(
LogLevel::Error,
format!("Could not download file: {}", err),
);
return;
}
}
}
SelectedEntry::Many(entries) => {
// In case of selection: save multiple files in wrkdir/input
@@ -80,8 +115,19 @@ impl FileTransferActivity {
dest_path.push(save_as);
}
// Iter files
for entry in entries.iter() {
self.filetransfer_recv(&entry.get_realfile(), dest_path.as_path(), None);
let entries = entries.iter().map(|x| x.get_realfile()).collect();
if let Err(err) = self.filetransfer_recv(
TransferPayload::Many(entries),
dest_path.as_path(),
None,
) {
{
self.log_and_alert(
LogLevel::Error,
format!("Could not download file: {}", err),
);
return;
}
}
}
SelectedEntry::None => {}