Merge pull request #31 from veeso/mini-refactoring-file-transfer-activity

Mini refactoring file transfer activity
This commit is contained in:
Christian Visintin
2021-05-08 19:05:37 +02:00
committed by GitHub
22 changed files with 1484 additions and 1155 deletions
+13 -7
View File
@@ -56,6 +56,7 @@ pub enum NextActivity {
pub struct ActivityManager { pub struct ActivityManager {
context: Option<Context>, context: Option<Context>,
interval: Duration, interval: Duration,
local_dir: PathBuf,
} }
impl ActivityManager { impl ActivityManager {
@@ -64,19 +65,16 @@ impl ActivityManager {
/// Initializes a new Activity Manager /// Initializes a new Activity Manager
pub fn new(local_dir: &Path, interval: Duration) -> Result<ActivityManager, HostError> { pub fn new(local_dir: &Path, interval: Duration) -> Result<ActivityManager, HostError> {
// Prepare Context // Prepare Context
let host: Localhost = match Localhost::new(local_dir.to_path_buf()) {
Ok(h) => h,
Err(e) => return Err(e),
};
// Initialize configuration client // Initialize configuration client
let (config_client, error): (Option<ConfigClient>, Option<String>) = let (config_client, error): (Option<ConfigClient>, Option<String>) =
match Self::init_config_client() { match Self::init_config_client() {
Ok(cli) => (Some(cli), None), Ok(cli) => (Some(cli), None),
Err(err) => (None, Some(err)), Err(err) => (None, Some(err)),
}; };
let ctx: Context = Context::new(host, config_client, error); let ctx: Context = Context::new(config_client, error);
Ok(ActivityManager { Ok(ActivityManager {
context: Some(ctx), context: Some(ctx),
local_dir: local_dir.to_path_buf(),
interval, interval,
}) })
} }
@@ -182,7 +180,7 @@ impl ActivityManager {
/// Returns the next activity to run /// Returns the next activity to run
fn run_filetransfer(&mut self) -> Option<NextActivity> { fn run_filetransfer(&mut self) -> Option<NextActivity> {
// Get context // Get context
let ctx: Context = match self.context.take() { let mut ctx: Context = match self.context.take() {
Some(ctx) => ctx, Some(ctx) => ctx,
None => return None, None => return None,
}; };
@@ -193,7 +191,15 @@ impl ActivityManager {
}; };
// Prepare activity // Prepare activity
let protocol: FileTransferProtocol = ft_params.protocol; let protocol: FileTransferProtocol = ft_params.protocol;
let mut activity: FileTransferActivity = FileTransferActivity::new(protocol); let host: Localhost = match Localhost::new(self.local_dir.clone()) {
Ok(host) => host,
Err(err) => {
// Set error in context
ctx.set_error(format!("Could not initialize localhost: {}", err));
return None;
}
};
let mut activity: FileTransferActivity = FileTransferActivity::new(host, protocol);
// Prepare result // Prepare result
let result: Option<NextActivity>; let result: Option<NextActivity>;
// Create activity // Create activity
+4 -5
View File
@@ -185,9 +185,8 @@ impl FtpFileTransfer {
return Err(()); return Err(());
} }
// Get symlink // Get symlink
let symlink: Option<Box<FsEntry>> = match symlink_path { let symlink: Option<Box<FsEntry>> = symlink_path.map(|p| {
None => None, Box::new(match p.to_string_lossy().ends_with('/') {
Some(p) => Some(Box::new(match p.to_string_lossy().ends_with('/') {
true => { true => {
// NOTE: is_dir becomes true // NOTE: is_dir becomes true
is_dir = true; is_dir = true;
@@ -226,8 +225,8 @@ impl FtpFileTransfer {
group: gid, group: gid,
unix_pex: Some(unix_pex), unix_pex: Some(unix_pex),
}), }),
})), })
}; });
let mut abs_path: PathBuf = PathBuf::from(path); let mut abs_path: PathBuf = PathBuf::from(path);
abs_path.push(file_name.as_str()); abs_path.push(file_name.as_str());
let abs_path: PathBuf = Self::resolve(abs_path.as_path()); let abs_path: PathBuf = Self::resolve(abs_path.as_path());
@@ -1,760 +0,0 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileExplorerTab, FileTransferActivity, FsEntry, LogLevel};
use tuirealm::{Payload, Value};
// externals
use std::path::PathBuf;
impl FileTransferActivity {
/// ### action_enter_local_dir
///
/// Enter a directory on local host from entry
/// Return true whether the directory changed
pub(super) fn action_enter_local_dir(&mut self, entry: FsEntry, block_sync: bool) -> bool {
match entry {
FsEntry::Directory(dir) => {
self.local_changedir(dir.abs_path.as_path(), true);
if self.browser.sync_browsing && !block_sync {
self.action_change_remote_dir(dir.name, true);
}
true
}
FsEntry::File(file) => {
match &file.symlink {
Some(symlink_entry) => {
// If symlink and is directory, point to symlink
match &**symlink_entry {
FsEntry::Directory(dir) => {
self.local_changedir(dir.abs_path.as_path(), true);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_change_remote_dir(dir.name.clone(), true);
}
true
}
_ => false,
}
}
None => false,
}
}
}
}
/// ### action_enter_remote_dir
///
/// Enter a directory on local host from entry
/// Return true whether the directory changed
pub(super) fn action_enter_remote_dir(&mut self, entry: FsEntry, block_sync: bool) -> bool {
match entry {
FsEntry::Directory(dir) => {
self.remote_changedir(dir.abs_path.as_path(), true);
if self.browser.sync_browsing && !block_sync {
self.action_change_local_dir(dir.name, true);
}
true
}
FsEntry::File(file) => {
match &file.symlink {
Some(symlink_entry) => {
// If symlink and is directory, point to symlink
match &**symlink_entry {
FsEntry::Directory(dir) => {
self.remote_changedir(dir.abs_path.as_path(), true);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_change_local_dir(dir.name.clone(), true);
}
true
}
_ => false,
}
}
None => false,
}
}
}
}
/// ### action_change_local_dir
///
/// Change local directory reading value from input
pub(super) fn action_change_local_dir(&mut self, input: String, block_sync: bool) {
let dir_path: PathBuf = self.local_to_abs_path(PathBuf::from(input.as_str()).as_path());
self.local_changedir(dir_path.as_path(), true);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_change_remote_dir(input, true);
}
}
/// ### action_change_remote_dir
///
/// Change remote directory reading value from input
pub(super) fn action_change_remote_dir(&mut self, input: String, block_sync: bool) {
let dir_path: PathBuf = self.remote_to_abs_path(PathBuf::from(input.as_str()).as_path());
self.remote_changedir(dir_path.as_path(), true);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_change_local_dir(input, true);
}
}
/// ### action_go_to_previous_local_dir
///
/// Go to previous directory from localhost
pub(super) fn action_go_to_previous_local_dir(&mut self, block_sync: bool) {
if let Some(d) = self.local.popd() {
self.local_changedir(d.as_path(), false);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_go_to_previous_remote_dir(true);
}
}
}
/// ### action_go_to_previous_remote_dir
///
/// Go to previous directory from remote host
pub(super) fn action_go_to_previous_remote_dir(&mut self, block_sync: bool) {
if let Some(d) = self.remote.popd() {
self.remote_changedir(d.as_path(), false);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_go_to_previous_local_dir(true);
}
}
}
/// ### action_go_to_local_upper_dir
///
/// Go to upper directory on local host
pub(super) fn action_go_to_local_upper_dir(&mut self, block_sync: bool) {
// Get pwd
let path: PathBuf = self.local.wrkdir.clone();
// Go to parent directory
if let Some(parent) = path.as_path().parent() {
self.local_changedir(parent, true);
// If sync is enabled update remote too
if self.browser.sync_browsing && !block_sync {
self.action_go_to_remote_upper_dir(true);
}
}
}
/// #### action_go_to_remote_upper_dir
///
/// Go to upper directory on remote host
pub(super) fn action_go_to_remote_upper_dir(&mut self, block_sync: bool) {
// Get pwd
let path: PathBuf = self.remote.wrkdir.clone();
// Go to parent directory
if let Some(parent) = path.as_path().parent() {
self.remote_changedir(parent, true);
// If sync is enabled update local too
if self.browser.sync_browsing && !block_sync {
self.action_go_to_local_upper_dir(true);
}
}
}
/// ### action_local_copy
///
/// Copy file on local
pub(super) fn action_local_copy(&mut self, input: String) {
if let Some(idx) = self.get_local_file_idx() {
let dest_path: PathBuf = PathBuf::from(input);
let entry: FsEntry = self.local.get(idx).unwrap().clone();
if let Some(ctx) = self.context.as_mut() {
match ctx.local.copy(&entry, dest_path.as_path()) {
Ok(_) => {
self.log(
LogLevel::Info,
format!(
"Copied \"{}\" to \"{}\"",
entry.get_abs_path().display(),
dest_path.display()
)
.as_str(),
);
// Reload entries
let wrkdir: PathBuf = self.local.wrkdir.clone();
self.local_scan(wrkdir.as_path());
}
Err(err) => self.log_and_alert(
LogLevel::Error,
format!(
"Could not copy \"{}\" to \"{}\": {}",
entry.get_abs_path().display(),
dest_path.display(),
err
),
),
}
}
}
}
/// ### action_remote_copy
///
/// Copy file on remote
pub(super) fn action_remote_copy(&mut self, input: String) {
if let Some(idx) = self.get_remote_file_idx() {
let dest_path: PathBuf = PathBuf::from(input);
let entry: FsEntry = self.remote.get(idx).unwrap().clone();
match self.client.as_mut().copy(&entry, dest_path.as_path()) {
Ok(_) => {
self.log(
LogLevel::Info,
format!(
"Copied \"{}\" to \"{}\"",
entry.get_abs_path().display(),
dest_path.display()
)
.as_str(),
);
self.reload_remote_dir();
}
Err(err) => self.log_and_alert(
LogLevel::Error,
format!(
"Could not copy \"{}\" to \"{}\": {}",
entry.get_abs_path().display(),
dest_path.display(),
err
),
),
}
}
}
pub(super) fn action_local_mkdir(&mut self, input: String) {
match self
.context
.as_mut()
.unwrap()
.local
.mkdir(PathBuf::from(input.as_str()).as_path())
{
Ok(_) => {
// Reload files
self.log(
LogLevel::Info,
format!("Created directory \"{}\"", input).as_ref(),
);
let wrkdir: PathBuf = self.local.wrkdir.clone();
self.local_scan(wrkdir.as_path());
}
Err(err) => {
// Report err
self.log_and_alert(
LogLevel::Error,
format!("Could not create directory \"{}\": {}", input, err),
);
}
}
}
pub(super) fn action_remote_mkdir(&mut self, input: String) {
match self
.client
.as_mut()
.mkdir(PathBuf::from(input.as_str()).as_path())
{
Ok(_) => {
// Reload files
self.log(
LogLevel::Info,
format!("Created directory \"{}\"", input).as_ref(),
);
self.reload_remote_dir();
}
Err(err) => {
// Report err
self.log_and_alert(
LogLevel::Error,
format!("Could not create directory \"{}\": {}", input, err),
);
}
}
}
pub(super) fn action_local_rename(&mut self, input: String) {
let entry: Option<FsEntry> = self.get_local_file_entry().cloned();
if let Some(entry) = entry {
let mut dst_path: PathBuf = PathBuf::from(input);
// Check if path is relative
if dst_path.as_path().is_relative() {
let mut wrkdir: PathBuf = self.local.wrkdir.clone();
wrkdir.push(dst_path);
dst_path = wrkdir;
}
let full_path: PathBuf = entry.get_abs_path();
// Rename file or directory and report status as popup
match self
.context
.as_mut()
.unwrap()
.local
.rename(&entry, dst_path.as_path())
{
Ok(_) => {
// Reload files
let path: PathBuf = self.local.wrkdir.clone();
self.local_scan(path.as_path());
// Log
self.log(
LogLevel::Info,
format!(
"Renamed file \"{}\" to \"{}\"",
full_path.display(),
dst_path.display()
)
.as_ref(),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not rename file \"{}\": {}", full_path.display(), err),
);
}
}
}
}
pub(super) fn action_remote_rename(&mut self, input: String) {
if let Some(idx) = self.get_remote_file_idx() {
if let Some(entry) = self.remote.get(idx) {
let dst_path: PathBuf = PathBuf::from(input);
let full_path: PathBuf = entry.get_abs_path();
// Rename file or directory and report status as popup
match self.client.as_mut().rename(entry, dst_path.as_path()) {
Ok(_) => {
// Reload files
let path: PathBuf = self.remote.wrkdir.clone();
self.remote_scan(path.as_path());
// Log
self.log(
LogLevel::Info,
format!(
"Renamed file \"{}\" to \"{}\"",
full_path.display(),
dst_path.display()
)
.as_ref(),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not rename file \"{}\": {}", full_path.display(), err),
);
}
}
}
}
}
pub(super) fn action_local_delete(&mut self) {
let entry: Option<FsEntry> = self.get_local_file_entry().cloned();
if let Some(entry) = entry {
let full_path: PathBuf = entry.get_abs_path();
// Delete file or directory and report status as popup
match self.context.as_mut().unwrap().local.remove(&entry) {
Ok(_) => {
// Reload files
let p: PathBuf = self.local.wrkdir.clone();
self.local_scan(p.as_path());
// Log
self.log(
LogLevel::Info,
format!("Removed file \"{}\"", full_path.display()).as_ref(),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not delete file \"{}\": {}", full_path.display(), err),
);
}
}
}
}
pub(super) fn action_remote_delete(&mut self) {
if let Some(idx) = self.get_remote_file_idx() {
// Check if file entry exists
if let Some(entry) = self.remote.get(idx) {
let full_path: PathBuf = entry.get_abs_path();
// Delete file
match self.client.remove(entry) {
Ok(_) => {
self.reload_remote_dir();
self.log(
LogLevel::Info,
format!("Removed file \"{}\"", full_path.display()).as_ref(),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not delete file \"{}\": {}", full_path.display(), err),
);
}
}
}
}
}
pub(super) fn action_local_saveas(&mut self, input: String) {
if let Some(idx) = self.get_local_file_idx() {
// Get pwd
let wrkdir: PathBuf = self.remote.wrkdir.clone();
if self.local.get(idx).is_some() {
let file: FsEntry = self.local.get(idx).unwrap().clone();
// Call upload; pass realfile, keep link name
self.filetransfer_send(&file.get_realfile(), wrkdir.as_path(), Some(input));
}
}
}
pub(super) fn action_remote_saveas(&mut self, input: String) {
if let Some(idx) = self.get_remote_file_idx() {
// Get pwd
let wrkdir: PathBuf = self.local.wrkdir.clone();
if self.remote.get(idx).is_some() {
let file: FsEntry = self.remote.get(idx).unwrap().clone();
// Call upload; pass realfile, keep link name
self.filetransfer_recv(&file.get_realfile(), wrkdir.as_path(), Some(input));
}
}
}
pub(super) fn action_local_newfile(&mut self, input: String) {
// Check if file exists
let mut file_exists: bool = false;
for file in self.local.iter_files_all() {
if input == file.get_name() {
file_exists = true;
}
}
if file_exists {
self.log_and_alert(
LogLevel::Warn,
format!("File \"{}\" already exists", input,),
);
return;
}
// Create file
let file_path: PathBuf = PathBuf::from(input.as_str());
if let Some(ctx) = self.context.as_mut() {
if let Err(err) = ctx.local.open_file_write(file_path.as_path()) {
self.log_and_alert(
LogLevel::Error,
format!("Could not create file \"{}\": {}", file_path.display(), err),
);
} else {
self.log(
LogLevel::Info,
format!("Created file \"{}\"", file_path.display()).as_str(),
);
}
// Reload files
let path: PathBuf = self.local.wrkdir.clone();
self.local_scan(path.as_path());
}
}
pub(super) fn action_remote_newfile(&mut self, input: String) {
// Check if file exists
let mut file_exists: bool = false;
for file in self.remote.iter_files_all() {
if input == file.get_name() {
file_exists = true;
}
}
if file_exists {
self.log_and_alert(
LogLevel::Warn,
format!("File \"{}\" already exists", input,),
);
return;
}
// Get path on remote
let file_path: PathBuf = PathBuf::from(input.as_str());
// Create file (on local)
match tempfile::NamedTempFile::new() {
Err(err) => self.log_and_alert(
LogLevel::Error,
format!("Could not create tempfile: {}", err),
),
Ok(tfile) => {
// Stat tempfile
if let Some(ctx) = self.context.as_mut() {
let local_file: FsEntry = match ctx.local.stat(tfile.path()) {
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not stat tempfile: {}", err),
);
return;
}
Ok(f) => f,
};
if let FsEntry::File(local_file) = local_file {
// Create file
match self.client.send_file(&local_file, file_path.as_path()) {
Err(err) => self.log_and_alert(
LogLevel::Error,
format!(
"Could not create file \"{}\": {}",
file_path.display(),
err
),
),
Ok(writer) => {
// Finalize write
if let Err(err) = self.client.on_sent(writer) {
self.log_and_alert(
LogLevel::Warn,
format!("Could not finalize file: {}", err),
);
} else {
self.log(
LogLevel::Info,
format!("Created file \"{}\"", file_path.display())
.as_str(),
);
}
// Reload files
let path: PathBuf = self.remote.wrkdir.clone();
self.remote_scan(path.as_path());
}
}
}
}
}
}
}
pub(super) fn action_local_exec(&mut self, input: String) {
match self.context.as_mut().unwrap().local.exec(input.as_str()) {
Ok(output) => {
// Reload files
self.log(
LogLevel::Info,
format!("\"{}\": {}", input, output).as_ref(),
);
let wrkdir: PathBuf = self.local.wrkdir.clone();
self.local_scan(wrkdir.as_path());
}
Err(err) => {
// Report err
self.log_and_alert(
LogLevel::Error,
format!("Could not execute command \"{}\": {}", input, err),
);
}
}
}
pub(super) fn action_remote_exec(&mut self, input: String) {
match self.client.as_mut().exec(input.as_str()) {
Ok(output) => {
// Reload files
self.log(
LogLevel::Info,
format!("\"{}\": {}", input, output).as_ref(),
);
self.reload_remote_dir();
}
Err(err) => {
// Report err
self.log_and_alert(
LogLevel::Error,
format!("Could not execute command \"{}\": {}", input, err),
);
}
}
}
pub(super) fn action_local_find(&mut self, input: String) -> Result<Vec<FsEntry>, String> {
match self.context.as_mut().unwrap().local.find(input.as_str()) {
Ok(entries) => Ok(entries),
Err(err) => Err(format!("Could not search for files: {}", err)),
}
}
pub(super) fn action_remote_find(&mut self, input: String) -> Result<Vec<FsEntry>, String> {
match self.client.as_mut().find(input.as_str()) {
Ok(entries) => Ok(entries),
Err(err) => Err(format!("Could not search for files: {}", err)),
}
}
pub(super) fn action_find_changedir(&mut self, idx: usize) {
// Match entry
if let Some(entry) = self.found.as_ref().unwrap().get(idx) {
// Get path: if a directory, use directory path; if it is a File, get parent path
let path: PathBuf = match entry {
FsEntry::Directory(dir) => dir.abs_path.clone(),
FsEntry::File(file) => match file.abs_path.parent() {
None => PathBuf::from("."),
Some(p) => p.to_path_buf(),
},
};
// Change directory
match self.tab {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
self.local_changedir(path.as_path(), true)
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
self.remote_changedir(path.as_path(), true)
}
}
}
}
pub(super) fn action_find_transfer(&mut self, idx: usize, name: Option<String>) {
let entry: Option<FsEntry> = self.found.as_ref().unwrap().get(idx).cloned();
if let Some(entry) = entry {
// Download file
match self.tab {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
let wrkdir: PathBuf = self.remote.wrkdir.clone();
self.filetransfer_send(&entry.get_realfile(), wrkdir.as_path(), name);
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
let wrkdir: PathBuf = self.local.wrkdir.clone();
self.filetransfer_recv(&entry.get_realfile(), wrkdir.as_path(), name);
}
}
}
}
pub(super) fn action_find_delete(&mut self, idx: usize) {
let entry: Option<FsEntry> = self.found.as_ref().unwrap().get(idx).cloned();
if let Some(entry) = entry {
// Download file
match self.tab {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
let full_path: PathBuf = entry.get_abs_path();
// Delete file or directory and report status as popup
match self.context.as_mut().unwrap().local.remove(&entry) {
Ok(_) => {
// Reload files
let p: PathBuf = self.local.wrkdir.clone();
self.local_scan(p.as_path());
// Log
self.log(
LogLevel::Info,
format!("Removed file \"{}\"", full_path.display()).as_ref(),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!(
"Could not delete file \"{}\": {}",
full_path.display(),
err
),
);
}
}
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
let full_path: PathBuf = entry.get_abs_path();
// Delete file
match self.client.remove(&entry) {
Ok(_) => {
self.reload_remote_dir();
self.log(
LogLevel::Info,
format!("Removed file \"{}\"", full_path.display()).as_ref(),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!(
"Could not delete file \"{}\": {}",
full_path.display(),
err
),
);
}
}
}
}
}
}
/// ### get_local_file_entry
///
/// Get local file entry
pub(super) fn get_local_file_entry(&self) -> Option<&FsEntry> {
match self.get_local_file_idx() {
None => None,
Some(idx) => self.local.get(idx),
}
}
/// ### get_remote_file_entry
///
/// Get remote file entry
pub(super) fn get_remote_file_entry(&self) -> Option<&FsEntry> {
match self.get_remote_file_idx() {
None => None,
Some(idx) => self.remote.get(idx),
}
}
// -- private
/// ### get_local_file_idx
///
/// Get index of selected file in the local tab
fn get_local_file_idx(&self) -> Option<usize> {
match self.view.get_state(super::COMPONENT_EXPLORER_LOCAL) {
Some(Payload::One(Value::Usize(idx))) => Some(idx),
_ => None,
}
}
/// ### get_remote_file_idx
///
/// Get index of selected file in the remote file
fn get_remote_file_idx(&self) -> Option<usize> {
match self.view.get_state(super::COMPONENT_EXPLORER_REMOTE) {
Some(Payload::One(Value::Usize(idx))) => Some(idx),
_ => None,
}
}
}
@@ -0,0 +1,184 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, FsEntry};
use std::path::PathBuf;
impl FileTransferActivity {
/// ### action_enter_local_dir
///
/// Enter a directory on local host from entry
/// Return true whether the directory changed
pub(crate) fn action_enter_local_dir(&mut self, entry: FsEntry, block_sync: bool) -> bool {
match entry {
FsEntry::Directory(dir) => {
self.local_changedir(dir.abs_path.as_path(), true);
if self.browser.sync_browsing && !block_sync {
self.action_change_remote_dir(dir.name, true);
}
true
}
FsEntry::File(file) => {
match &file.symlink {
Some(symlink_entry) => {
// If symlink and is directory, point to symlink
match &**symlink_entry {
FsEntry::Directory(dir) => {
self.local_changedir(dir.abs_path.as_path(), true);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_change_remote_dir(dir.name.clone(), true);
}
true
}
_ => false,
}
}
None => false,
}
}
}
}
/// ### action_enter_remote_dir
///
/// Enter a directory on local host from entry
/// Return true whether the directory changed
pub(crate) fn action_enter_remote_dir(&mut self, entry: FsEntry, block_sync: bool) -> bool {
match entry {
FsEntry::Directory(dir) => {
self.remote_changedir(dir.abs_path.as_path(), true);
if self.browser.sync_browsing && !block_sync {
self.action_change_local_dir(dir.name, true);
}
true
}
FsEntry::File(file) => {
match &file.symlink {
Some(symlink_entry) => {
// If symlink and is directory, point to symlink
match &**symlink_entry {
FsEntry::Directory(dir) => {
self.remote_changedir(dir.abs_path.as_path(), true);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_change_local_dir(dir.name.clone(), true);
}
true
}
_ => false,
}
}
None => false,
}
}
}
}
/// ### action_change_local_dir
///
/// Change local directory reading value from input
pub(crate) fn action_change_local_dir(&mut self, input: String, block_sync: bool) {
let dir_path: PathBuf = self.local_to_abs_path(PathBuf::from(input.as_str()).as_path());
self.local_changedir(dir_path.as_path(), true);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_change_remote_dir(input, true);
}
}
/// ### action_change_remote_dir
///
/// Change remote directory reading value from input
pub(crate) fn action_change_remote_dir(&mut self, input: String, block_sync: bool) {
let dir_path: PathBuf = self.remote_to_abs_path(PathBuf::from(input.as_str()).as_path());
self.remote_changedir(dir_path.as_path(), true);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_change_local_dir(input, true);
}
}
/// ### action_go_to_previous_local_dir
///
/// Go to previous directory from localhost
pub(crate) fn action_go_to_previous_local_dir(&mut self, block_sync: bool) {
if let Some(d) = self.local_mut().popd() {
self.local_changedir(d.as_path(), false);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_go_to_previous_remote_dir(true);
}
}
}
/// ### action_go_to_previous_remote_dir
///
/// Go to previous directory from remote host
pub(crate) fn action_go_to_previous_remote_dir(&mut self, block_sync: bool) {
if let Some(d) = self.remote_mut().popd() {
self.remote_changedir(d.as_path(), false);
// Check whether to sync
if self.browser.sync_browsing && !block_sync {
self.action_go_to_previous_local_dir(true);
}
}
}
/// ### action_go_to_local_upper_dir
///
/// Go to upper directory on local host
pub(crate) fn action_go_to_local_upper_dir(&mut self, block_sync: bool) {
// Get pwd
let path: PathBuf = self.local().wrkdir.clone();
// Go to parent directory
if let Some(parent) = path.as_path().parent() {
self.local_changedir(parent, true);
// If sync is enabled update remote too
if self.browser.sync_browsing && !block_sync {
self.action_go_to_remote_upper_dir(true);
}
}
}
/// #### action_go_to_remote_upper_dir
///
/// Go to upper directory on remote host
pub(crate) fn action_go_to_remote_upper_dir(&mut self, block_sync: bool) {
// Get pwd
let path: PathBuf = self.remote().wrkdir.clone();
// Go to parent directory
if let Some(parent) = path.as_path().parent() {
self.remote_changedir(parent, true);
// If sync is enabled update local too
if self.browser.sync_browsing && !block_sync {
self.action_go_to_local_upper_dir(true);
}
}
}
}
@@ -0,0 +1,98 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, FsEntry, LogLevel};
use std::path::PathBuf;
impl FileTransferActivity {
/// ### action_local_copy
///
/// Copy file on local
pub(crate) fn action_local_copy(&mut self, input: String) {
if let Some(idx) = self.get_local_file_idx() {
let dest_path: PathBuf = PathBuf::from(input);
let entry: FsEntry = self.local().get(idx).unwrap().clone();
match self.host.copy(&entry, dest_path.as_path()) {
Ok(_) => {
self.log(
LogLevel::Info,
format!(
"Copied \"{}\" to \"{}\"",
entry.get_abs_path().display(),
dest_path.display()
),
);
// Reload entries
let wrkdir: PathBuf = self.local().wrkdir.clone();
self.local_scan(wrkdir.as_path());
}
Err(err) => self.log_and_alert(
LogLevel::Error,
format!(
"Could not copy \"{}\" to \"{}\": {}",
entry.get_abs_path().display(),
dest_path.display(),
err
),
),
}
}
}
/// ### action_remote_copy
///
/// Copy file on remote
pub(crate) fn action_remote_copy(&mut self, input: String) {
if let Some(idx) = self.get_remote_file_idx() {
let dest_path: PathBuf = PathBuf::from(input);
let entry: FsEntry = self.remote().get(idx).unwrap().clone();
match self.client.as_mut().copy(&entry, dest_path.as_path()) {
Ok(_) => {
self.log(
LogLevel::Info,
format!(
"Copied \"{}\" to \"{}\"",
entry.get_abs_path().display(),
dest_path.display()
),
);
self.reload_remote_dir();
}
Err(err) => self.log_and_alert(
LogLevel::Error,
format!(
"Could not copy \"{}\" to \"{}\": {}",
entry.get_abs_path().display(),
dest_path.display(),
err
),
),
}
}
}
}
@@ -0,0 +1,84 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, FsEntry, LogLevel};
use std::path::PathBuf;
impl FileTransferActivity {
pub(crate) fn action_local_delete(&mut self) {
let entry: Option<FsEntry> = self.get_local_file_entry().cloned();
if let Some(entry) = entry {
let full_path: PathBuf = entry.get_abs_path();
// Delete file or directory and report status as popup
match self.host.remove(&entry) {
Ok(_) => {
// Reload files
let p: PathBuf = self.local().wrkdir.clone();
self.local_scan(p.as_path());
// Log
self.log(
LogLevel::Info,
format!("Removed file \"{}\"", full_path.display()),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not delete file \"{}\": {}", full_path.display(), err),
);
}
}
}
}
pub(crate) fn action_remote_delete(&mut self) {
if let Some(idx) = self.get_remote_file_idx() {
// Check if file entry exists
let entry = self.remote().get(idx).cloned();
if let Some(entry) = entry {
let full_path: PathBuf = entry.get_abs_path();
// Delete file
match self.client.remove(&entry) {
Ok(_) => {
self.reload_remote_dir();
self.log(
LogLevel::Info,
format!("Removed file \"{}\"", full_path.display()),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not delete file \"{}\": {}", full_path.display(), err),
);
}
}
}
}
}
}
@@ -0,0 +1,76 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, FsEntry, LogLevel};
use std::path::PathBuf;
impl FileTransferActivity {
pub(crate) fn action_edit_local_file(&mut self) {
if self.get_local_file_entry().is_some() {
let fsentry: FsEntry = self.get_local_file_entry().unwrap().clone();
// Check if file
if fsentry.is_file() {
self.log(
LogLevel::Info,
format!("Opening file \"{}\"...", fsentry.get_abs_path().display()),
);
// Edit file
match self.edit_local_file(fsentry.get_abs_path().as_path()) {
Ok(_) => {
// Reload directory
let pwd: PathBuf = self.local().wrkdir.clone();
self.local_scan(pwd.as_path());
}
Err(err) => self.log_and_alert(LogLevel::Error, err),
}
}
}
}
pub(crate) fn action_edit_remote_file(&mut self) {
if self.get_remote_file_entry().is_some() {
let fsentry: FsEntry = self.get_remote_file_entry().unwrap().clone();
// Check if file
if let FsEntry::File(file) = fsentry.clone() {
self.log(
LogLevel::Info,
format!("Opening file \"{}\"...", fsentry.get_abs_path().display()),
);
// Edit file
match self.edit_remote_file(&file) {
Ok(_) => {
// Reload directory
let pwd: PathBuf = self.remote().wrkdir.clone();
self.remote_scan(pwd.as_path());
}
Err(err) => self.log_and_alert(LogLevel::Error, err),
}
}
}
}
}
@@ -0,0 +1,67 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, LogLevel};
use std::path::PathBuf;
impl FileTransferActivity {
pub(crate) fn action_local_exec(&mut self, input: String) {
match self.host.exec(input.as_str()) {
Ok(output) => {
// Reload files
self.log(LogLevel::Info, format!("\"{}\": {}", input, output));
let wrkdir: PathBuf = self.local().wrkdir.clone();
self.local_scan(wrkdir.as_path());
}
Err(err) => {
// Report err
self.log_and_alert(
LogLevel::Error,
format!("Could not execute command \"{}\": {}", input, err),
);
}
}
}
pub(crate) fn action_remote_exec(&mut self, input: String) {
match self.client.as_mut().exec(input.as_str()) {
Ok(output) => {
// Reload files
self.log(LogLevel::Info, format!("\"{}\": {}", input, output));
self.reload_remote_dir();
}
Err(err) => {
// Report err
self.log_and_alert(
LogLevel::Error,
format!("Could not execute command \"{}\": {}", input, err),
);
}
}
}
}
@@ -0,0 +1,146 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::super::browser::FileExplorerTab;
use super::{FileTransferActivity, FsEntry, LogLevel};
use std::path::PathBuf;
impl FileTransferActivity {
pub(crate) fn action_local_find(&mut self, input: String) -> Result<Vec<FsEntry>, 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<FsEntry>, String> {
match self.client.as_mut().find(input.as_str()) {
Ok(entries) => Ok(entries),
Err(err) => Err(format!("Could not search for files: {}", err)),
}
}
pub(crate) fn action_find_changedir(&mut self, idx: usize) {
// Match entry
if let Some(entry) = self.found().as_ref().unwrap().get(idx) {
// Get path: if a directory, use directory path; if it is a File, get parent path
let path: PathBuf = match entry {
FsEntry::Directory(dir) => dir.abs_path.clone(),
FsEntry::File(file) => match file.abs_path.parent() {
None => PathBuf::from("."),
Some(p) => p.to_path_buf(),
},
};
// Change directory
match self.browser.tab() {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
self.local_changedir(path.as_path(), true)
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
self.remote_changedir(path.as_path(), true)
}
}
}
}
pub(crate) fn action_find_transfer(&mut self, idx: usize, name: Option<String>) {
let entry: Option<FsEntry> = self.found().as_ref().unwrap().get(idx).cloned();
if let Some(entry) = entry {
// Download file
match self.browser.tab() {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
let wrkdir: PathBuf = self.remote().wrkdir.clone();
self.filetransfer_send(&entry.get_realfile(), wrkdir.as_path(), name);
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
let wrkdir: PathBuf = self.local().wrkdir.clone();
self.filetransfer_recv(&entry.get_realfile(), wrkdir.as_path(), name);
}
}
}
}
pub(crate) fn action_find_delete(&mut self, idx: usize) {
let entry: Option<FsEntry> = self.found().as_ref().unwrap().get(idx).cloned();
if let Some(entry) = entry {
// Download file
match self.browser.tab() {
FileExplorerTab::FindLocal | FileExplorerTab::Local => {
let full_path: PathBuf = entry.get_abs_path();
// Delete file or directory and report status as popup
match self.host.remove(&entry) {
Ok(_) => {
// Reload files
let p: PathBuf = self.local().wrkdir.clone();
self.local_scan(p.as_path());
// Log
self.log(
LogLevel::Info,
format!("Removed file \"{}\"", full_path.display()),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!(
"Could not delete file \"{}\": {}",
full_path.display(),
err
),
);
}
}
}
FileExplorerTab::FindRemote | FileExplorerTab::Remote => {
let full_path: PathBuf = entry.get_abs_path();
// Delete file
match self.client.remove(&entry) {
Ok(_) => {
self.reload_remote_dir();
self.log(
LogLevel::Info,
format!("Removed file \"{}\"", full_path.display()),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!(
"Could not delete file \"{}\": {}",
full_path.display(),
err
),
);
}
}
}
}
}
}
}
@@ -0,0 +1,70 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, LogLevel};
use std::path::PathBuf;
impl FileTransferActivity {
pub(crate) fn action_local_mkdir(&mut self, input: String) {
match self.host.mkdir(PathBuf::from(input.as_str()).as_path()) {
Ok(_) => {
// Reload files
self.log(LogLevel::Info, format!("Created directory \"{}\"", input));
let wrkdir: PathBuf = self.local().wrkdir.clone();
self.local_scan(wrkdir.as_path());
}
Err(err) => {
// Report err
self.log_and_alert(
LogLevel::Error,
format!("Could not create directory \"{}\": {}", input, err),
);
}
}
}
pub(crate) fn action_remote_mkdir(&mut self, input: String) {
match self
.client
.as_mut()
.mkdir(PathBuf::from(input.as_str()).as_path())
{
Ok(_) => {
// Reload files
self.log(LogLevel::Info, format!("Created directory \"{}\"", input));
self.reload_remote_dir();
}
Err(err) => {
// Report err
self.log_and_alert(
LogLevel::Error,
format!("Could not create directory \"{}\": {}", input, err),
);
}
}
}
}
@@ -0,0 +1,85 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pub(self) use super::{FileTransferActivity, FsEntry, LogLevel};
use tuirealm::{Payload, Value};
// actions
pub(crate) mod change_dir;
pub(crate) mod copy;
pub(crate) mod delete;
pub(crate) mod edit;
pub(crate) mod exec;
pub(crate) mod find;
pub(crate) mod mkdir;
pub(crate) mod newfile;
pub(crate) mod rename;
pub(crate) mod save;
impl FileTransferActivity {
/// ### get_local_file_entry
///
/// Get local file entry
pub(crate) fn get_local_file_entry(&self) -> Option<&FsEntry> {
match self.get_local_file_idx() {
None => None,
Some(idx) => self.local().get(idx),
}
}
/// ### get_remote_file_entry
///
/// Get remote file entry
pub(crate) fn get_remote_file_entry(&self) -> Option<&FsEntry> {
match self.get_remote_file_idx() {
None => None,
Some(idx) => self.remote().get(idx),
}
}
// -- private
/// ### get_local_file_idx
///
/// Get index of selected file in the local tab
fn get_local_file_idx(&self) -> Option<usize> {
match self.view.get_state(super::COMPONENT_EXPLORER_LOCAL) {
Some(Payload::One(Value::Usize(idx))) => Some(idx),
_ => None,
}
}
/// ### get_remote_file_idx
///
/// Get index of selected file in the remote file
fn get_remote_file_idx(&self) -> Option<usize> {
match self.view.get_state(super::COMPONENT_EXPLORER_REMOTE) {
Some(Payload::One(Value::Usize(idx))) => Some(idx),
_ => None,
}
}
}
@@ -0,0 +1,130 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, FsEntry, LogLevel};
use std::path::PathBuf;
impl FileTransferActivity {
pub(crate) fn action_local_newfile(&mut self, input: String) {
// Check if file exists
let mut file_exists: bool = false;
for file in self.local().iter_files_all() {
if input == file.get_name() {
file_exists = true;
}
}
if file_exists {
self.log_and_alert(
LogLevel::Warn,
format!("File \"{}\" already exists", input,),
);
return;
}
// Create file
let file_path: PathBuf = PathBuf::from(input.as_str());
if let Err(err) = self.host.open_file_write(file_path.as_path()) {
self.log_and_alert(
LogLevel::Error,
format!("Could not create file \"{}\": {}", file_path.display(), err),
);
} else {
self.log(
LogLevel::Info,
format!("Created file \"{}\"", file_path.display()),
);
}
// Reload files
let path: PathBuf = self.local().wrkdir.clone();
self.local_scan(path.as_path());
}
pub(crate) fn action_remote_newfile(&mut self, input: String) {
// Check if file exists
let mut file_exists: bool = false;
for file in self.remote().iter_files_all() {
if input == file.get_name() {
file_exists = true;
}
}
if file_exists {
self.log_and_alert(
LogLevel::Warn,
format!("File \"{}\" already exists", input,),
);
return;
}
// Get path on remote
let file_path: PathBuf = PathBuf::from(input.as_str());
// Create file (on local)
match tempfile::NamedTempFile::new() {
Err(err) => self.log_and_alert(
LogLevel::Error,
format!("Could not create tempfile: {}", err),
),
Ok(tfile) => {
// Stat tempfile
let local_file: FsEntry = match self.host.stat(tfile.path()) {
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not stat tempfile: {}", err),
);
return;
}
Ok(f) => f,
};
if let FsEntry::File(local_file) = local_file {
// Create file
match self.client.send_file(&local_file, file_path.as_path()) {
Err(err) => self.log_and_alert(
LogLevel::Error,
format!("Could not create file \"{}\": {}", file_path.display(), err),
),
Ok(writer) => {
// Finalize write
if let Err(err) = self.client.on_sent(writer) {
self.log_and_alert(
LogLevel::Warn,
format!("Could not finalize file: {}", err),
);
} else {
self.log(
LogLevel::Info,
format!("Created file \"{}\"", file_path.display()),
);
}
// Reload files
let path: PathBuf = self.remote().wrkdir.clone();
self.remote_scan(path.as_path());
}
}
}
}
}
}
}
@@ -0,0 +1,102 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, FsEntry, LogLevel};
use std::path::PathBuf;
impl FileTransferActivity {
pub(crate) fn action_local_rename(&mut self, input: String) {
let entry: Option<FsEntry> = self.get_local_file_entry().cloned();
if let Some(entry) = entry {
let mut dst_path: PathBuf = PathBuf::from(input);
// Check if path is relative
if dst_path.as_path().is_relative() {
let mut wrkdir: PathBuf = self.local().wrkdir.clone();
wrkdir.push(dst_path);
dst_path = wrkdir;
}
let full_path: PathBuf = entry.get_abs_path();
// Rename file or directory and report status as popup
match self.host.rename(&entry, dst_path.as_path()) {
Ok(_) => {
// Reload files
let path: PathBuf = self.local().wrkdir.clone();
self.local_scan(path.as_path());
// Log
self.log(
LogLevel::Info,
format!(
"Renamed file \"{}\" to \"{}\"",
full_path.display(),
dst_path.display()
),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not rename file \"{}\": {}", full_path.display(), err),
);
}
}
}
}
pub(crate) fn action_remote_rename(&mut self, input: String) {
if let Some(idx) = self.get_remote_file_idx() {
let entry = self.remote().get(idx).cloned();
if let Some(entry) = entry {
let dst_path: PathBuf = PathBuf::from(input);
let full_path: PathBuf = entry.get_abs_path();
// Rename file or directory and report status as popup
match self.client.as_mut().rename(&entry, dst_path.as_path()) {
Ok(_) => {
// Reload files
let path: PathBuf = self.remote().wrkdir.clone();
self.remote_scan(path.as_path());
// Log
self.log(
LogLevel::Info,
format!(
"Renamed file \"{}\" to \"{}\"",
full_path.display(),
dst_path.display()
),
);
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not rename file \"{}\": {}", full_path.display(), err),
);
}
}
}
}
}
}
@@ -0,0 +1,56 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{FileTransferActivity, FsEntry};
use std::path::PathBuf;
impl FileTransferActivity {
pub(crate) fn action_local_saveas(&mut self, input: String) {
if let Some(idx) = self.get_local_file_idx() {
// Get pwd
let wrkdir: PathBuf = self.remote().wrkdir.clone();
if self.local().get(idx).is_some() {
let file: FsEntry = self.local().get(idx).unwrap().clone();
// Call upload; pass realfile, keep link name
self.filetransfer_send(&file.get_realfile(), wrkdir.as_path(), Some(input));
}
}
}
pub(crate) fn action_remote_saveas(&mut self, input: String) {
if let Some(idx) = self.get_remote_file_idx() {
// Get pwd
let wrkdir: PathBuf = self.local().wrkdir.clone();
if self.remote().get(idx).is_some() {
let file: FsEntry = self.remote().get(idx).unwrap().clone();
// Call upload; pass realfile, keep link name
self.filetransfer_recv(&file.get_realfile(), wrkdir.as_path(), Some(input));
}
}
}
}
@@ -0,0 +1,177 @@
//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::fs::explorer::{builder::FileExplorerBuilder, FileExplorer, FileSorting, GroupDirs};
use crate::fs::FsEntry;
use crate::system::config_client::ConfigClient;
/// ## FileExplorerTab
///
/// File explorer tab
#[derive(Clone, Copy)]
pub(super) enum FileExplorerTab {
Local,
Remote,
FindLocal, // Find result tab
FindRemote, // Find result tab
}
/// ## Browser
///
/// Browser contains the browser options
pub(super) struct Browser {
local: FileExplorer, // Local File explorer state
remote: FileExplorer, // Remote File explorer state
found: Option<FileExplorer>, // File explorer for find result
tab: FileExplorerTab, // Current selected tab
pub sync_browsing: bool,
}
impl Browser {
/// ### new
///
/// Build a new `Browser` struct
pub(super) fn new(cli: Option<&ConfigClient>) -> Self {
Self {
local: Self::build_local_explorer(cli),
remote: Self::build_remote_explorer(cli),
found: None,
tab: FileExplorerTab::Local,
sync_browsing: false,
}
}
pub fn local(&self) -> &FileExplorer {
&self.local
}
pub fn local_mut(&mut self) -> &mut FileExplorer {
&mut self.local
}
pub fn remote(&self) -> &FileExplorer {
&self.remote
}
pub fn remote_mut(&mut self) -> &mut FileExplorer {
&mut self.remote
}
pub fn found(&self) -> Option<&FileExplorer> {
self.found.as_ref()
}
pub fn found_mut(&mut self) -> Option<&mut FileExplorer> {
self.found.as_mut()
}
pub fn set_found(&mut self, files: Vec<FsEntry>) {
let mut explorer = Self::build_found_explorer();
explorer.set_files(files);
self.found = Some(explorer);
}
pub fn del_found(&mut self) {
self.found = None;
}
pub(super) fn tab(&self) -> FileExplorerTab {
self.tab
}
/// ### change_tab
///
/// Update tab value
pub(super) fn change_tab(&mut self, tab: FileExplorerTab) {
self.tab = tab;
}
/// ### toggle_sync_browsing
///
/// Invert the current state for the sync browsing
pub fn toggle_sync_browsing(&mut self) {
self.sync_browsing = !self.sync_browsing;
}
/// ### build_local_explorer
///
/// Build a file explorer with local host setup
pub fn build_local_explorer(cli: Option<&ConfigClient>) -> FileExplorer {
let mut builder = Self::build_explorer(cli);
if let Some(cli) = cli {
builder.with_formatter(cli.get_local_file_fmt().as_deref());
}
builder.build()
}
/// ### build_remote_explorer
///
/// Build a file explorer with remote host setup
pub fn build_remote_explorer(cli: Option<&ConfigClient>) -> FileExplorer {
let mut builder = Self::build_explorer(cli);
if let Some(cli) = cli {
builder.with_formatter(cli.get_remote_file_fmt().as_deref());
}
builder.build()
}
/// ### build_explorer
///
/// Build explorer reading configuration from `ConfigClient`
fn build_explorer(cli: Option<&ConfigClient>) -> FileExplorerBuilder {
let mut builder: FileExplorerBuilder = FileExplorerBuilder::new();
// Set common keys
builder
.with_file_sorting(FileSorting::ByName)
.with_stack_size(16);
match &cli {
Some(cli) => {
builder // Build according to current configuration
.with_group_dirs(cli.get_group_dirs())
.with_hidden_files(cli.get_show_hidden_files());
}
None => {
builder // Build default
.with_group_dirs(Some(GroupDirs::First));
}
};
builder
}
/// ### build_found_explorer
///
/// Build explorer reading from `ConfigClient`, for found result (has some differences)
fn build_found_explorer() -> FileExplorer {
FileExplorerBuilder::new()
.with_file_sorting(FileSorting::ByName)
.with_group_dirs(Some(GroupDirs::First))
.with_hidden_files(true)
.with_stack_size(0)
.with_formatter(Some("{NAME} {SYMLINK}"))
.build()
}
}
@@ -23,22 +23,23 @@
*/ */
// Locals // Locals
use super::{ConfigClient, FileTransferActivity, LogLevel, LogRecord}; use super::{ConfigClient, FileTransferActivity, LogLevel, LogRecord};
use crate::fs::explorer::{builder::FileExplorerBuilder, FileExplorer, FileSorting, GroupDirs};
use crate::system::environment; use crate::system::environment;
use crate::system::sshkey_storage::SshKeyStorage; use crate::system::sshkey_storage::SshKeyStorage;
// Ext // Ext
use std::env; use std::env;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
const LOG_CAPACITY: usize = 256;
impl FileTransferActivity { impl FileTransferActivity {
/// ### log /// ### log
/// ///
/// Add message to log events /// Add message to log events
pub(super) fn log(&mut self, level: LogLevel, msg: &str) { pub(super) fn log(&mut self, level: LogLevel, msg: String) {
// Create log record // Create log record
let record: LogRecord = LogRecord::new(level, msg); let record: LogRecord = LogRecord::new(level, msg);
//Check if history overflows the size //Check if history overflows the size
if self.log_records.len() + 1 > self.log_size { if self.log_records.len() + 1 > LOG_CAPACITY {
self.log_records.pop_back(); // Start cleaning events from back self.log_records.pop_back(); // Start cleaning events from back
} }
// Eventually push front the new record // Eventually push front the new record
@@ -52,8 +53,8 @@ impl FileTransferActivity {
/// ///
/// Add message to log events and also display it as an alert /// Add message to log events and also display it as an alert
pub(super) fn log_and_alert(&mut self, level: LogLevel, msg: String) { pub(super) fn log_and_alert(&mut self, level: LogLevel, msg: String) {
self.log(level, msg.as_str());
self.mount_error(msg.as_str()); self.mount_error(msg.as_str());
self.log(level, msg);
// Update log // Update log
let msg = self.update_logbox(); let msg = self.update_logbox();
self.update(msg); self.update(msg);
@@ -91,64 +92,6 @@ impl FileTransferActivity {
} }
} }
/// ### build_explorer
///
/// Build explorer reading configuration from `ConfigClient`
fn build_explorer(cli: Option<&ConfigClient>) -> FileExplorerBuilder {
let mut builder: FileExplorerBuilder = FileExplorerBuilder::new();
// Set common keys
builder
.with_file_sorting(FileSorting::ByName)
.with_stack_size(16);
match &cli {
Some(cli) => {
builder // Build according to current configuration
.with_group_dirs(cli.get_group_dirs())
.with_hidden_files(cli.get_show_hidden_files());
}
None => {
builder // Build default
.with_group_dirs(Some(GroupDirs::First));
}
};
builder
}
/// ### build_local_explorer
///
/// Build a file explorer with local host setup
pub(super) fn build_local_explorer(cli: Option<&ConfigClient>) -> FileExplorer {
let mut builder = Self::build_explorer(cli);
if let Some(cli) = cli {
builder.with_formatter(cli.get_local_file_fmt().as_deref());
}
builder.build()
}
/// ### build_remote_explorer
///
/// Build a file explorer with remote host setup
pub(super) fn build_remote_explorer(cli: Option<&ConfigClient>) -> FileExplorer {
let mut builder = Self::build_explorer(cli);
if let Some(cli) = cli {
builder.with_formatter(cli.get_remote_file_fmt().as_deref());
}
builder.build()
}
/// ### build_found_explorer
///
/// Build explorer reading from `ConfigClient`, for found result (has some differences)
pub(super) fn build_found_explorer() -> FileExplorer {
FileExplorerBuilder::new()
.with_file_sorting(FileSorting::ByName)
.with_group_dirs(Some(GroupDirs::First))
.with_hidden_files(true)
.with_stack_size(0)
.with_formatter(Some("{NAME} {SYMLINK}"))
.build()
}
/// ### setup_text_editor /// ### setup_text_editor
/// ///
/// Set text editor to use /// Set text editor to use
@@ -182,7 +125,7 @@ impl FileTransferActivity {
pub(super) fn local_to_abs_path(&self, path: &Path) -> PathBuf { pub(super) fn local_to_abs_path(&self, path: &Path) -> PathBuf {
match path.is_relative() { match path.is_relative() {
true => { true => {
let mut d: PathBuf = self.local.wrkdir.clone(); let mut d: PathBuf = self.local().wrkdir.clone();
d.push(path); d.push(path);
d d
} }
@@ -196,7 +139,7 @@ impl FileTransferActivity {
pub(super) fn remote_to_abs_path(&self, path: &Path) -> PathBuf { pub(super) fn remote_to_abs_path(&self, path: &Path) -> PathBuf {
match path.is_relative() { match path.is_relative() {
true => { true => {
let mut wrkdir: PathBuf = self.remote.wrkdir.clone(); let mut wrkdir: PathBuf = self.remote().wrkdir.clone();
wrkdir.push(path); wrkdir.push(path);
wrkdir wrkdir
} }
+41 -57
View File
@@ -26,11 +26,12 @@
* SOFTWARE. * SOFTWARE.
*/ */
// This module is split into files, cause it's just too big // This module is split into files, cause it's just too big
mod actions; pub(self) mod actions;
mod misc; pub(self) mod browser;
mod session; pub(self) mod misc;
mod update; pub(self) mod session;
mod view; pub(self) mod update;
pub(self) mod view;
// Dependencies // Dependencies
extern crate chrono; extern crate chrono;
@@ -46,7 +47,9 @@ use crate::filetransfer::sftp_transfer::SftpFileTransfer;
use crate::filetransfer::{FileTransfer, FileTransferProtocol}; use crate::filetransfer::{FileTransfer, FileTransferProtocol};
use crate::fs::explorer::FileExplorer; use crate::fs::explorer::FileExplorer;
use crate::fs::FsEntry; use crate::fs::FsEntry;
use crate::host::Localhost;
use crate::system::config_client::ConfigClient; use crate::system::config_client::ConfigClient;
use browser::Browser;
// Includes // Includes
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
@@ -59,7 +62,6 @@ use tuirealm::View;
// -- Storage keys // -- Storage keys
const STORAGE_EXPLORER_WIDTH: &str = "FILETRANSFER_EXPLORER_WIDTH"; const STORAGE_EXPLORER_WIDTH: &str = "FILETRANSFER_EXPLORER_WIDTH";
const STORAGE_LOGBOX_WIDTH: &str = "LOGBOX_WIDTH";
// -- components // -- components
@@ -87,16 +89,6 @@ const COMPONENT_RADIO_SORTING: &str = "RADIO_SORTING";
const COMPONENT_SPAN_STATUS_BAR: &str = "STATUS_BAR"; const COMPONENT_SPAN_STATUS_BAR: &str = "STATUS_BAR";
const COMPONENT_LIST_FILEINFO: &str = "LIST_FILEINFO"; const COMPONENT_LIST_FILEINFO: &str = "LIST_FILEINFO";
/// ## FileExplorerTab
///
/// File explorer tab
enum FileExplorerTab {
Local,
Remote,
FindLocal, // Find result tab
FindRemote, // Find result tab
}
/// ## LogLevel /// ## LogLevel
/// ///
/// Log level type /// Log level type
@@ -119,11 +111,11 @@ impl LogRecord {
/// ### new /// ### new
/// ///
/// Instantiates a new LogRecord /// Instantiates a new LogRecord
pub fn new(level: LogLevel, msg: &str) -> LogRecord { pub fn new(level: LogLevel, msg: String) -> LogRecord {
LogRecord { LogRecord {
time: Local::now(), time: Local::now(),
level, level,
msg: String::from(msg), msg,
} }
} }
} }
@@ -203,30 +195,6 @@ impl Default for TransferStates {
} }
} }
/// ## Browser
///
/// Browser contains the browser options
struct Browser {
pub sync_browsing: bool,
}
impl Default for Browser {
fn default() -> Self {
Self {
sync_browsing: false,
}
}
}
impl Browser {
/// ### toggle_sync_browsing
///
/// Invert the current state for the sync browsing
pub fn toggle_sync_browsing(&mut self) {
self.sync_browsing = !self.sync_browsing;
}
}
/// ## FileTransferActivity /// ## FileTransferActivity
/// ///
/// FileTransferActivity is the data holder for the file transfer activity /// FileTransferActivity is the data holder for the file transfer activity
@@ -234,28 +202,25 @@ pub struct FileTransferActivity {
exit_reason: Option<ExitReason>, // Exit reason exit_reason: Option<ExitReason>, // Exit reason
context: Option<Context>, // Context holder context: Option<Context>, // Context holder
view: View, // View view: View, // View
host: Localhost, // Localhost
client: Box<dyn FileTransfer>, // File transfer client client: Box<dyn FileTransfer>, // File transfer client
local: FileExplorer, // Local File explorer state browser: Browser, // Browser
remote: FileExplorer, // Remote File explorer state
found: Option<FileExplorer>, // File explorer for find result
tab: FileExplorerTab, // Current selected tab
log_records: VecDeque<LogRecord>, // Log records log_records: VecDeque<LogRecord>, // Log records
log_size: usize, // Log records size (max)
transfer: TransferStates, // Transfer states transfer: TransferStates, // Transfer states
browser: Browser, // Browser states
} }
impl FileTransferActivity { impl FileTransferActivity {
/// ### new /// ### new
/// ///
/// Instantiates a new FileTransferActivity /// Instantiates a new FileTransferActivity
pub fn new(protocol: FileTransferProtocol) -> FileTransferActivity { pub fn new(host: Localhost, protocol: FileTransferProtocol) -> FileTransferActivity {
// Get config client // Get config client
let config_client: Option<ConfigClient> = Self::init_config_client(); let config_client: Option<ConfigClient> = Self::init_config_client();
FileTransferActivity { FileTransferActivity {
exit_reason: None, exit_reason: None,
context: None, context: None,
view: View::init(), view: View::init(),
host,
client: match protocol { client: match protocol {
FileTransferProtocol::Sftp => Box::new(SftpFileTransfer::new( FileTransferProtocol::Sftp => Box::new(SftpFileTransfer::new(
Self::make_ssh_storage(config_client.as_ref()), Self::make_ssh_storage(config_client.as_ref()),
@@ -265,16 +230,35 @@ impl FileTransferActivity {
Self::make_ssh_storage(config_client.as_ref()), Self::make_ssh_storage(config_client.as_ref()),
)), )),
}, },
local: Self::build_local_explorer(config_client.as_ref()), browser: Browser::new(config_client.as_ref()),
remote: Self::build_remote_explorer(config_client.as_ref()),
found: None,
tab: FileExplorerTab::Local,
log_records: VecDeque::with_capacity(256), // 256 events is enough I guess log_records: VecDeque::with_capacity(256), // 256 events is enough I guess
log_size: 256, // Must match with capacity
transfer: TransferStates::default(), transfer: TransferStates::default(),
browser: Browser::default(),
} }
} }
pub(crate) fn local(&self) -> &FileExplorer {
self.browser.local()
}
pub(crate) fn local_mut(&mut self) -> &mut FileExplorer {
self.browser.local_mut()
}
pub(crate) fn remote(&self) -> &FileExplorer {
self.browser.remote()
}
pub(crate) fn remote_mut(&mut self) -> &mut FileExplorer {
self.browser.remote_mut()
}
pub(crate) fn found(&self) -> Option<&FileExplorer> {
self.browser.found()
}
pub(crate) fn found_mut(&mut self) -> Option<&mut FileExplorer> {
self.browser.found_mut()
}
} }
/** /**
@@ -296,10 +280,10 @@ impl Activity for FileTransferActivity {
// Put raw mode on enabled // Put raw mode on enabled
let _ = enable_raw_mode(); let _ = enable_raw_mode();
// Set working directory // Set working directory
let pwd: PathBuf = self.context.as_ref().unwrap().local.pwd(); let pwd: PathBuf = self.host.pwd();
// Get files at current wd // Get files at current wd
self.local_scan(pwd.as_path()); self.local_scan(pwd.as_path());
self.local.wrkdir = pwd; self.local_mut().wrkdir = pwd;
// Configure text editor // Configure text editor
self.setup_text_editor(); self.setup_text_editor();
// init view // init view
@@ -86,7 +86,7 @@ impl FileTransferActivity {
// Log welcome // Log welcome
self.log( self.log(
LogLevel::Info, LogLevel::Info,
format!("Established connection with '{}': \"{}\"", addr, banner).as_ref(), format!("Established connection with '{}': \"{}\"", addr, banner),
); );
} }
// Try to change directory to entry directory // Try to change directory to entry directory
@@ -141,7 +141,7 @@ impl FileTransferActivity {
if let Ok(pwd) = self.client.pwd() { if let Ok(pwd) = self.client.pwd() {
self.remote_scan(pwd.as_path()); self.remote_scan(pwd.as_path());
// Set wrkdir // Set wrkdir
self.remote.wrkdir = pwd; self.remote_mut().wrkdir = pwd;
} }
} }
@@ -192,8 +192,7 @@ impl FileTransferActivity {
"Could not remove created file {}: {}", "Could not remove created file {}: {}",
remote_path.display(), remote_path.display(),
err err
) ),
.as_str(),
), ),
Ok(entry) => { Ok(entry) => {
if let Err(err) = self.client.remove(&entry) { if let Err(err) = self.client.remove(&entry) {
@@ -203,8 +202,7 @@ impl FileTransferActivity {
"Could not remove created file {}: {}", "Could not remove created file {}: {}",
remote_path.display(), remote_path.display(),
err err
) ),
.as_str(),
); );
} }
} }
@@ -218,16 +216,10 @@ impl FileTransferActivity {
Ok(_) => { Ok(_) => {
self.log( self.log(
LogLevel::Info, LogLevel::Info,
format!("Created directory \"{}\"", remote_path.display()).as_ref(), format!("Created directory \"{}\"", remote_path.display()),
); );
// Get files in dir // Get files in dir
match self match self.host.scan_dir(dir.abs_path.as_path()) {
.context
.as_ref()
.unwrap()
.local
.scan_dir(dir.abs_path.as_path())
{
Ok(entries) => { Ok(entries) => {
// Iterate over files // Iterate over files
for entry in entries.iter() { for entry in entries.iter() {
@@ -265,7 +257,7 @@ impl FileTransferActivity {
} }
} }
// Scan dir on remote // Scan dir on remote
let path: PathBuf = self.remote.wrkdir.clone(); let path: PathBuf = self.remote().wrkdir.clone();
self.remote_scan(path.as_path()); self.remote_scan(path.as_path());
// If aborted; show popup // If aborted; show popup
if self.transfer.aborted { if self.transfer.aborted {
@@ -322,28 +314,25 @@ impl FileTransferActivity {
err, err,
TransferErrorReason::Abrupted | TransferErrorReason::LocalIoError(_) TransferErrorReason::Abrupted | TransferErrorReason::LocalIoError(_)
) { ) {
let local = &mut self.context.as_mut().unwrap().local;
// Stat file // Stat file
match local.stat(local_file_path.as_path()) { match self.host.stat(local_file_path.as_path()) {
Err(err) => self.log( Err(err) => self.log(
LogLevel::Error, LogLevel::Error,
format!( format!(
"Could not remove created file {}: {}", "Could not remove created file {}: {}",
local_file_path.display(), local_file_path.display(),
err err
) ),
.as_str(),
), ),
Ok(entry) => { Ok(entry) => {
if let Err(err) = local.remove(&entry) { if let Err(err) = self.host.remove(&entry) {
self.log( self.log(
LogLevel::Error, LogLevel::Error,
format!( format!(
"Could not remove created file {}: {}", "Could not remove created file {}: {}",
local_file_path.display(), local_file_path.display(),
err err
) ),
.as_str(),
); );
} }
} }
@@ -359,24 +348,12 @@ impl FileTransferActivity {
None => local_dir_path.push(dir.name.as_str()), None => local_dir_path.push(dir.name.as_str()),
} }
// Create directory on local // Create directory on local
match self match self.host.mkdir_ex(local_dir_path.as_path(), true) {
.context
.as_mut()
.unwrap()
.local
.mkdir_ex(local_dir_path.as_path(), true)
{
Ok(_) => { Ok(_) => {
// Apply file mode to directory // Apply file mode to directory
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
if let Some(pex) = dir.unix_pex { if let Some(pex) = dir.unix_pex {
if let Err(err) = self if let Err(err) = self.host.chmod(local_dir_path.as_path(), pex) {
.context
.as_ref()
.unwrap()
.local
.chmod(local_dir_path.as_path(), pex)
{
self.log( self.log(
LogLevel::Error, LogLevel::Error,
format!( format!(
@@ -384,14 +361,13 @@ impl FileTransferActivity {
pex, pex,
local_dir_path.display(), local_dir_path.display(),
err err
) ),
.as_ref(),
); );
} }
} }
self.log( self.log(
LogLevel::Info, LogLevel::Info,
format!("Created directory \"{}\"", local_dir_path.display()).as_ref(), format!("Created directory \"{}\"", local_dir_path.display()),
); );
// Get files in dir // Get files in dir
match self.client.list_dir(dir.abs_path.as_path()) { match self.client.list_dir(dir.abs_path.as_path()) {
@@ -426,8 +402,7 @@ impl FileTransferActivity {
"Failed to create directory \"{}\": {}", "Failed to create directory \"{}\": {}",
local_dir_path.display(), local_dir_path.display(),
err err
) ),
.as_ref(),
); );
} }
} }
@@ -464,13 +439,7 @@ impl FileTransferActivity {
) -> Result<(), TransferErrorReason> { ) -> Result<(), TransferErrorReason> {
// Upload file // Upload file
// Try to open local file // Try to open local file
match self match self.host.open_file_read(local.abs_path.as_path()) {
.context
.as_ref()
.unwrap()
.local
.open_file_read(local.abs_path.as_path())
{
Ok(mut fhnd) => match self.client.send_file(local, remote) { Ok(mut fhnd) => match self.client.send_file(local, remote) {
Ok(mut rhnd) => { Ok(mut rhnd) => {
// Write file // Write file
@@ -544,7 +513,7 @@ impl FileTransferActivity {
if let Err(err) = self.client.on_sent(rhnd) { if let Err(err) = self.client.on_sent(rhnd) {
self.log( self.log(
LogLevel::Warn, LogLevel::Warn,
format!("Could not finalize remote stream: \"{}\"", err).as_str(), format!("Could not finalize remote stream: \"{}\"", err),
); );
} }
// if upload was abrupted, return error // if upload was abrupted, return error
@@ -559,8 +528,7 @@ impl FileTransferActivity {
remote.display(), remote.display(),
fmt_millis(self.transfer.started.elapsed()), fmt_millis(self.transfer.started.elapsed()),
ByteSize(self.transfer.bytes_per_second()), ByteSize(self.transfer.bytes_per_second()),
) ),
.as_ref(),
); );
} }
Err(err) => return Err(TransferErrorReason::FileTransferError(err)), Err(err) => return Err(TransferErrorReason::FileTransferError(err)),
@@ -580,7 +548,7 @@ impl FileTransferActivity {
file_name: String, file_name: String,
) -> Result<(), TransferErrorReason> { ) -> Result<(), TransferErrorReason> {
// Try to open local file // Try to open local file
match self.context.as_ref().unwrap().local.open_file_write(local) { match self.host.open_file_write(local) {
Ok(mut local_file) => { Ok(mut local_file) => {
// Download file from remote // Download file from remote
match self.client.recv_file(remote) { match self.client.recv_file(remote) {
@@ -647,7 +615,7 @@ impl FileTransferActivity {
if let Err(err) = self.client.on_recv(rhnd) { if let Err(err) = self.client.on_recv(rhnd) {
self.log( self.log(
LogLevel::Warn, LogLevel::Warn,
format!("Could not finalize remote stream: \"{}\"", err).as_str(), format!("Could not finalize remote stream: \"{}\"", err),
); );
} }
// If download was abrupted, return Error // If download was abrupted, return Error
@@ -657,8 +625,7 @@ impl FileTransferActivity {
// Apply file mode to file // Apply file mode to file
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
if let Some(pex) = remote.unix_pex { if let Some(pex) = remote.unix_pex {
if let Err(err) = self.context.as_ref().unwrap().local.chmod(local, pex) if let Err(err) = self.host.chmod(local, pex) {
{
self.log( self.log(
LogLevel::Error, LogLevel::Error,
format!( format!(
@@ -666,8 +633,7 @@ impl FileTransferActivity {
pex, pex,
local.display(), local.display(),
err err
) ),
.as_ref(),
); );
} }
} }
@@ -680,8 +646,7 @@ impl FileTransferActivity {
local.display(), local.display(),
fmt_millis(self.transfer.started.elapsed()), fmt_millis(self.transfer.started.elapsed()),
ByteSize(self.transfer.bytes_per_second()), ByteSize(self.transfer.bytes_per_second()),
) ),
.as_ref(),
); );
} }
Err(err) => return Err(TransferErrorReason::FileTransferError(err)), Err(err) => return Err(TransferErrorReason::FileTransferError(err)),
@@ -696,10 +661,10 @@ impl FileTransferActivity {
/// ///
/// Scan current local directory /// Scan current local directory
pub(super) fn local_scan(&mut self, path: &Path) { pub(super) fn local_scan(&mut self, path: &Path) {
match self.context.as_ref().unwrap().local.scan_dir(path) { match self.host.scan_dir(path) {
Ok(files) => { Ok(files) => {
// Set files and sort (sorting is implicit) // Set files and sort (sorting is implicit)
self.local.set_files(files); self.local_mut().set_files(files);
} }
Err(err) => { Err(err) => {
self.log_and_alert( self.log_and_alert(
@@ -717,7 +682,7 @@ impl FileTransferActivity {
match self.client.list_dir(path) { match self.client.list_dir(path) {
Ok(files) => { Ok(files) => {
// Set files and sort (sorting is implicit) // Set files and sort (sorting is implicit)
self.remote.set_files(files); self.remote_mut().set_files(files);
} }
Err(err) => { Err(err) => {
self.log_and_alert( self.log_and_alert(
@@ -733,21 +698,21 @@ impl FileTransferActivity {
/// Change directory for local /// Change directory for local
pub(super) fn local_changedir(&mut self, path: &Path, push: bool) { pub(super) fn local_changedir(&mut self, path: &Path, push: bool) {
// Get current directory // Get current directory
let prev_dir: PathBuf = self.local.wrkdir.clone(); let prev_dir: PathBuf = self.local().wrkdir.clone();
// Change directory // Change directory
match self.context.as_mut().unwrap().local.change_wrkdir(path) { match self.host.change_wrkdir(path) {
Ok(_) => { Ok(_) => {
self.log( self.log(
LogLevel::Info, LogLevel::Info,
format!("Changed directory on local: {}", path.display()).as_str(), format!("Changed directory on local: {}", path.display()),
); );
// Reload files // Reload files
self.local_scan(path); self.local_scan(path);
// Set wrkdir // Set wrkdir
self.local.wrkdir = PathBuf::from(path); self.local_mut().wrkdir = PathBuf::from(path);
// Push prev_dir to stack // Push prev_dir to stack
if push { if push {
self.local.pushd(prev_dir.as_path()) self.local_mut().pushd(prev_dir.as_path())
} }
} }
Err(err) => { Err(err) => {
@@ -762,21 +727,21 @@ impl FileTransferActivity {
pub(super) fn remote_changedir(&mut self, path: &Path, push: bool) { pub(super) fn remote_changedir(&mut self, path: &Path, push: bool) {
// Get current directory // Get current directory
let prev_dir: PathBuf = self.remote.wrkdir.clone(); let prev_dir: PathBuf = self.remote().wrkdir.clone();
// Change directory // Change directory
match self.client.as_mut().change_dir(path) { match self.client.as_mut().change_dir(path) {
Ok(_) => { Ok(_) => {
self.log( self.log(
LogLevel::Info, LogLevel::Info,
format!("Changed directory on remote: {}", path.display()).as_str(), format!("Changed directory on remote: {}", path.display()),
); );
// Update files // Update files
self.remote_scan(path); self.remote_scan(path);
// Set wrkdir // Set wrkdir
self.remote.wrkdir = PathBuf::from(path); self.remote_mut().wrkdir = PathBuf::from(path);
// Push prev_dir to stack // Push prev_dir to stack
if push { if push {
self.remote.pushd(prev_dir.as_path()) self.remote_mut().pushd(prev_dir.as_path())
} }
} }
Err(err) => { Err(err) => {
@@ -826,8 +791,7 @@ impl FileTransferActivity {
format!( format!(
"Changes performed through editor saved to \"{}\"!", "Changes performed through editor saved to \"{}\"!",
path.display() path.display()
) ),
.as_str(),
), ),
Err(err) => return Err(format!("Could not open editor: {}", err)), Err(err) => return Err(format!("Could not open editor: {}", err)),
} }
@@ -858,8 +822,7 @@ impl FileTransferActivity {
return Err(format!("Could not open file {}: {}", file.name, err)); return Err(format!("Could not open file {}: {}", file.name, err));
} }
// Get current file modification time // Get current file modification time
let prev_mtime: SystemTime = match self.context.as_ref().unwrap().local.stat(tmpfile.path()) let prev_mtime: SystemTime = match self.host.stat(tmpfile.path()) {
{
Ok(e) => e.get_last_change_time(), Ok(e) => e.get_last_change_time(),
Err(err) => { Err(err) => {
return Err(format!( return Err(format!(
@@ -874,8 +837,7 @@ impl FileTransferActivity {
return Err(err); return Err(err);
} }
// Get local fs entry // Get local fs entry
let tmpfile_entry: FsEntry = match self.context.as_ref().unwrap().local.stat(tmpfile.path()) let tmpfile_entry: FsEntry = match self.host.stat(tmpfile.path()) {
{
Ok(e) => e, Ok(e) => e,
Err(err) => { Err(err) => {
return Err(format!( return Err(format!(
@@ -893,21 +855,19 @@ impl FileTransferActivity {
format!( format!(
"File \"{}\" has changed; writing changes to remote", "File \"{}\" has changed; writing changes to remote",
file.abs_path.display() file.abs_path.display()
) ),
.as_ref(),
); );
// Get local fs entry // Get local fs entry
let tmpfile_entry: FsEntry = let tmpfile_entry: FsEntry = match self.host.stat(tmpfile.path()) {
match self.context.as_ref().unwrap().local.stat(tmpfile.path()) { Ok(e) => e,
Ok(e) => e, Err(err) => {
Err(err) => { return Err(format!(
return Err(format!( "Could not stat \"{}\": {}",
"Could not stat \"{}\": {}", tmpfile.path().display(),
tmpfile.path().display(), err
err ))
)) }
} };
};
// Write file // Write file
let tmpfile_entry: &FsFile = match &tmpfile_entry { let tmpfile_entry: &FsFile = match &tmpfile_entry {
FsEntry::Directory(_) => panic!("tempfile is a directory for some reason"), FsEntry::Directory(_) => panic!("tempfile is a directory for some reason"),
@@ -929,7 +889,7 @@ impl FileTransferActivity {
false => { false => {
self.log( self.log(
LogLevel::Info, LogLevel::Info,
format!("File \"{}\" hasn't changed", file.abs_path.display()).as_ref(), format!("File \"{}\" hasn't changed", file.abs_path.display()),
); );
} }
} }
+76 -141
View File
@@ -29,7 +29,7 @@
extern crate bytesize; extern crate bytesize;
// locals // locals
use super::{ use super::{
FileExplorerTab, FileTransferActivity, LogLevel, COMPONENT_EXPLORER_FIND, browser::FileExplorerTab, FileTransferActivity, LogLevel, COMPONENT_EXPLORER_FIND,
COMPONENT_EXPLORER_LOCAL, COMPONENT_EXPLORER_REMOTE, COMPONENT_INPUT_COPY, COMPONENT_EXPLORER_LOCAL, COMPONENT_EXPLORER_REMOTE, COMPONENT_INPUT_COPY,
COMPONENT_INPUT_EXEC, COMPONENT_INPUT_FIND, COMPONENT_INPUT_GOTO, COMPONENT_INPUT_MKDIR, COMPONENT_INPUT_EXEC, COMPONENT_INPUT_FIND, COMPONENT_INPUT_GOTO, COMPONENT_INPUT_MKDIR,
COMPONENT_INPUT_NEWFILE, COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS, COMPONENT_INPUT_NEWFILE, COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS,
@@ -68,7 +68,7 @@ impl FileTransferActivity {
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_RIGHT) => { (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_RIGHT) => {
// Change tab // Change tab
self.view.active(COMPONENT_EXPLORER_REMOTE); self.view.active(COMPONENT_EXPLORER_REMOTE);
self.tab = FileExplorerTab::Remote; self.browser.change_tab(FileExplorerTab::Remote);
None None
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_BACKSPACE) => { (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_BACKSPACE) => {
@@ -83,7 +83,7 @@ impl FileTransferActivity {
(COMPONENT_EXPLORER_LOCAL, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => { (COMPONENT_EXPLORER_LOCAL, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
// Match selected file // Match selected file
let mut entry: Option<FsEntry> = None; let mut entry: Option<FsEntry> = None;
if let Some(e) = self.local.get(*idx) { if let Some(e) = self.local().get(*idx) {
entry = Some(e.clone()); entry = Some(e.clone());
} }
if let Some(entry) = entry { if let Some(entry) = entry {
@@ -102,7 +102,7 @@ impl FileTransferActivity {
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_SPACE) => { (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_SPACE) => {
// Get pwd // Get pwd
let wrkdir: PathBuf = self.remote.wrkdir.clone(); let wrkdir: PathBuf = self.remote().wrkdir.clone();
// Get file and clone (due to mutable / immutable stuff...) // Get file and clone (due to mutable / immutable stuff...)
if self.get_local_file_entry().is_some() { if self.get_local_file_entry().is_some() {
let file: FsEntry = self.get_local_file_entry().unwrap().clone(); let file: FsEntry = self.get_local_file_entry().unwrap().clone();
@@ -116,7 +116,7 @@ impl FileTransferActivity {
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_A) => { (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_A) => {
// Toggle hidden files // Toggle hidden files
self.local.toggle_hidden_files(); self.local_mut().toggle_hidden_files();
// Reload file list component // Reload file list component
self.update_local_filelist() self.update_local_filelist()
} }
@@ -129,33 +129,13 @@ impl FileTransferActivity {
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_L) => { (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_L) => {
// Reload directory // Reload directory
let pwd: PathBuf = self.local.wrkdir.clone(); let pwd: PathBuf = self.local().wrkdir.clone();
self.local_scan(pwd.as_path()); self.local_scan(pwd.as_path());
// Reload file list component // Reload file list component
self.update_local_filelist() self.update_local_filelist()
} }
(COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_O) => { (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_O) => {
// Clone entry due to mutable stuff... self.action_edit_local_file();
if self.get_local_file_entry().is_some() {
let fsentry: FsEntry = self.get_local_file_entry().unwrap().clone();
// Check if file
if fsentry.is_file() {
self.log(
LogLevel::Info,
format!("Opening file \"{}\"...", fsentry.get_abs_path().display())
.as_str(),
);
// Edit file
match self.edit_local_file(fsentry.get_abs_path().as_path()) {
Ok(_) => {
// Reload directory
let pwd: PathBuf = self.local.wrkdir.clone();
self.local_scan(pwd.as_path());
}
Err(err) => self.log_and_alert(LogLevel::Error, err),
}
}
}
// Reload file list component // Reload file list component
self.update_local_filelist() self.update_local_filelist()
} }
@@ -171,13 +151,13 @@ impl FileTransferActivity {
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_LEFT) => { (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_LEFT) => {
// Change tab // Change tab
self.view.active(COMPONENT_EXPLORER_LOCAL); self.view.active(COMPONENT_EXPLORER_LOCAL);
self.tab = FileExplorerTab::Local; self.browser.change_tab(FileExplorerTab::Local);
None None
} }
(COMPONENT_EXPLORER_REMOTE, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => { (COMPONENT_EXPLORER_REMOTE, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
// Match selected file // Match selected file
let mut entry: Option<FsEntry> = None; let mut entry: Option<FsEntry> = None;
if let Some(e) = self.remote.get(*idx) { if let Some(e) = self.remote().get(*idx) {
entry = Some(e.clone()); entry = Some(e.clone());
} }
if let Some(entry) = entry { if let Some(entry) = entry {
@@ -200,7 +180,7 @@ impl FileTransferActivity {
let file: FsEntry = self.get_remote_file_entry().unwrap().clone(); let file: FsEntry = self.get_remote_file_entry().unwrap().clone();
let name: String = file.get_name().to_string(); let name: String = file.get_name().to_string();
// Call upload; pass realfile, keep link name // Call upload; pass realfile, keep link name
let wrkdir: PathBuf = self.local.wrkdir.clone(); let wrkdir: PathBuf = self.local().wrkdir.clone();
self.filetransfer_recv(&file.get_realfile(), wrkdir.as_path(), Some(name)); self.filetransfer_recv(&file.get_realfile(), wrkdir.as_path(), Some(name));
self.update_local_filelist() self.update_local_filelist()
} else { } else {
@@ -219,7 +199,7 @@ impl FileTransferActivity {
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_A) => { (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_A) => {
// Toggle hidden files // Toggle hidden files
self.remote.toggle_hidden_files(); self.remote_mut().toggle_hidden_files();
// Reload file list component // Reload file list component
self.update_remote_filelist() self.update_remote_filelist()
} }
@@ -232,33 +212,14 @@ impl FileTransferActivity {
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_L) => { (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_L) => {
// Reload directory // Reload directory
let pwd: PathBuf = self.remote.wrkdir.clone(); let pwd: PathBuf = self.remote().wrkdir.clone();
self.remote_scan(pwd.as_path()); self.remote_scan(pwd.as_path());
// Reload file list component // Reload file list component
self.update_remote_filelist() self.update_remote_filelist()
} }
(COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_O) => { (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_O) => {
// Clone entry due to mutable stuff... // Edit file
if self.get_remote_file_entry().is_some() { self.action_edit_remote_file();
let fsentry: FsEntry = self.get_remote_file_entry().unwrap().clone();
// Check if file
if let FsEntry::File(file) = fsentry.clone() {
self.log(
LogLevel::Info,
format!("Opening file \"{}\"...", fsentry.get_abs_path().display())
.as_str(),
);
// Edit file
match self.edit_remote_file(&file) {
Ok(_) => {
// Reload directory
let pwd: PathBuf = self.remote.wrkdir.clone();
self.remote_scan(pwd.as_path());
}
Err(err) => self.log_and_alert(LogLevel::Error, err),
}
}
}
// Reload file list component // Reload file list component
self.update_remote_filelist() self.update_remote_filelist()
} }
@@ -371,7 +332,7 @@ impl FileTransferActivity {
// Finalize find // Finalize find
self.finalize_find(); self.finalize_find();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None, _ => None,
@@ -383,7 +344,7 @@ impl FileTransferActivity {
Some(Payload::One(Value::Usize(idx))) => { Some(Payload::One(Value::Usize(idx))) => {
self.action_find_transfer(idx, None); self.action_find_transfer(idx, None);
// Reload files // Reload files
match self.tab { match self.browser.tab() {
// NOTE: swapped by purpose // NOTE: swapped by purpose
FileExplorerTab::FindLocal => self.update_remote_filelist(), FileExplorerTab::FindLocal => self.update_remote_filelist(),
FileExplorerTab::FindRemote => self.update_local_filelist(), FileExplorerTab::FindRemote => self.update_local_filelist(),
@@ -411,14 +372,14 @@ impl FileTransferActivity {
} }
(COMPONENT_INPUT_COPY, Msg::OnSubmit(Payload::One(Value::Str(input)))) => { (COMPONENT_INPUT_COPY, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
// Copy file // Copy file
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.action_local_copy(input.to_string()), FileExplorerTab::Local => self.action_local_copy(input.to_string()),
FileExplorerTab::Remote => self.action_remote_copy(input.to_string()), FileExplorerTab::Remote => self.action_remote_copy(input.to_string()),
_ => panic!("Found tab doesn't support COPY"), _ => panic!("Found tab doesn't support COPY"),
} }
self.umount_copy(); self.umount_copy();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None, _ => None,
@@ -431,14 +392,14 @@ impl FileTransferActivity {
} }
(COMPONENT_INPUT_EXEC, Msg::OnSubmit(Payload::One(Value::Str(input)))) => { (COMPONENT_INPUT_EXEC, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
// Exex command // Exex command
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.action_local_exec(input.to_string()), FileExplorerTab::Local => self.action_local_exec(input.to_string()),
FileExplorerTab::Remote => self.action_remote_exec(input.to_string()), FileExplorerTab::Remote => self.action_remote_exec(input.to_string()),
_ => panic!("Found tab doesn't support EXEC"), _ => panic!("Found tab doesn't support EXEC"),
} }
self.umount_exec(); self.umount_exec();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None, _ => None,
@@ -452,7 +413,7 @@ impl FileTransferActivity {
(COMPONENT_INPUT_FIND, Msg::OnSubmit(Payload::One(Value::Str(input)))) => { (COMPONENT_INPUT_FIND, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
self.umount_find_input(); self.umount_find_input();
// Find // Find
let res: Result<Vec<FsEntry>, String> = match self.tab { let res: Result<Vec<FsEntry>, String> = match self.browser.tab() {
FileExplorerTab::Local => self.action_local_find(input.to_string()), FileExplorerTab::Local => self.action_local_find(input.to_string()),
FileExplorerTab::Remote => self.action_remote_find(input.to_string()), FileExplorerTab::Remote => self.action_remote_find(input.to_string()),
_ => panic!("Trying to search for files, while already in a find result"), _ => panic!("Trying to search for files, while already in a find result"),
@@ -465,18 +426,16 @@ impl FileTransferActivity {
} }
Ok(files) => { Ok(files) => {
// Create explorer and load files // Create explorer and load files
let mut explorer = Self::build_found_explorer(); self.browser.set_found(files);
explorer.set_files(files);
self.found = Some(explorer);
// Mount result widget // Mount result widget
self.mount_find(input); self.mount_find(input);
self.update_find_list(); self.update_find_list();
// Initialize tab // Initialize tab
self.tab = match self.tab { self.browser.change_tab(match self.browser.tab() {
FileExplorerTab::Local => FileExplorerTab::FindLocal, FileExplorerTab::Local => FileExplorerTab::FindLocal,
FileExplorerTab::Remote => FileExplorerTab::FindRemote, FileExplorerTab::Remote => FileExplorerTab::FindRemote,
_ => FileExplorerTab::FindLocal, _ => FileExplorerTab::FindLocal,
}; });
} }
} }
None None
@@ -487,7 +446,7 @@ impl FileTransferActivity {
None None
} }
(COMPONENT_INPUT_GOTO, Msg::OnSubmit(Payload::One(Value::Str(input)))) => { (COMPONENT_INPUT_GOTO, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => { FileExplorerTab::Local => {
self.action_change_local_dir(input.to_string(), false) self.action_change_local_dir(input.to_string(), false)
} }
@@ -500,14 +459,14 @@ impl FileTransferActivity {
self.umount_goto(); self.umount_goto();
// Reload files if sync // Reload files if sync
if self.browser.sync_browsing { if self.browser.sync_browsing {
match self.tab { match self.browser.tab() {
FileExplorerTab::Remote => self.update_local_filelist(), FileExplorerTab::Remote => self.update_local_filelist(),
FileExplorerTab::Local => self.update_remote_filelist(), FileExplorerTab::Local => self.update_remote_filelist(),
_ => None, _ => None,
}; };
} }
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None, _ => None,
@@ -519,14 +478,14 @@ impl FileTransferActivity {
None None
} }
(COMPONENT_INPUT_MKDIR, Msg::OnSubmit(Payload::One(Value::Str(input)))) => { (COMPONENT_INPUT_MKDIR, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.action_local_mkdir(input.to_string()), FileExplorerTab::Local => self.action_local_mkdir(input.to_string()),
FileExplorerTab::Remote => self.action_remote_mkdir(input.to_string()), FileExplorerTab::Remote => self.action_remote_mkdir(input.to_string()),
_ => panic!("Found tab doesn't support MKDIR"), _ => panic!("Found tab doesn't support MKDIR"),
} }
self.umount_mkdir(); self.umount_mkdir();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None, _ => None,
@@ -538,14 +497,14 @@ impl FileTransferActivity {
None None
} }
(COMPONENT_INPUT_NEWFILE, Msg::OnSubmit(Payload::One(Value::Str(input)))) => { (COMPONENT_INPUT_NEWFILE, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.action_local_newfile(input.to_string()), FileExplorerTab::Local => self.action_local_newfile(input.to_string()),
FileExplorerTab::Remote => self.action_remote_newfile(input.to_string()), FileExplorerTab::Remote => self.action_remote_newfile(input.to_string()),
_ => panic!("Found tab doesn't support NEWFILE"), _ => panic!("Found tab doesn't support NEWFILE"),
} }
self.umount_newfile(); self.umount_newfile();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None, _ => None,
@@ -557,14 +516,14 @@ impl FileTransferActivity {
None None
} }
(COMPONENT_INPUT_RENAME, Msg::OnSubmit(Payload::One(Value::Str(input)))) => { (COMPONENT_INPUT_RENAME, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.action_local_rename(input.to_string()), FileExplorerTab::Local => self.action_local_rename(input.to_string()),
FileExplorerTab::Remote => self.action_remote_rename(input.to_string()), FileExplorerTab::Remote => self.action_remote_rename(input.to_string()),
_ => panic!("Found tab doesn't support RENAME"), _ => panic!("Found tab doesn't support RENAME"),
} }
self.umount_rename(); self.umount_rename();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None, _ => None,
@@ -576,7 +535,7 @@ impl FileTransferActivity {
None None
} }
(COMPONENT_INPUT_SAVEAS, Msg::OnSubmit(Payload::One(Value::Str(input)))) => { (COMPONENT_INPUT_SAVEAS, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.action_local_saveas(input.to_string()), FileExplorerTab::Local => self.action_local_saveas(input.to_string()),
FileExplorerTab::Remote => self.action_remote_saveas(input.to_string()), FileExplorerTab::Remote => self.action_remote_saveas(input.to_string()),
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => { FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => {
@@ -590,7 +549,7 @@ impl FileTransferActivity {
} }
self.umount_saveas(); self.umount_saveas();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
// NOTE: Swapped is intentional // NOTE: Swapped is intentional
FileExplorerTab::Local => self.update_remote_filelist(), FileExplorerTab::Local => self.update_remote_filelist(),
FileExplorerTab::Remote => self.update_local_filelist(), FileExplorerTab::Remote => self.update_local_filelist(),
@@ -612,7 +571,7 @@ impl FileTransferActivity {
} }
(COMPONENT_RADIO_DELETE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => { (COMPONENT_RADIO_DELETE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Choice is 'YES' // Choice is 'YES'
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.action_local_delete(), FileExplorerTab::Local => self.action_local_delete(),
FileExplorerTab::Remote => self.action_remote_delete(), FileExplorerTab::Remote => self.action_remote_delete(),
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => { FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => {
@@ -622,14 +581,14 @@ impl FileTransferActivity {
{ {
self.action_find_delete(idx); self.action_find_delete(idx);
// Reload entries // Reload entries
self.found.as_mut().unwrap().del_entry(idx); self.found_mut().unwrap().del_entry(idx);
self.update_find_list(); self.update_find_list();
} }
} }
} }
self.umount_radio_delete(); self.umount_radio_delete();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
FileExplorerTab::FindLocal => self.update_local_filelist(), FileExplorerTab::FindLocal => self.update_local_filelist(),
@@ -672,15 +631,15 @@ impl FileTransferActivity {
3 => FileSorting::BySize, 3 => FileSorting::BySize,
_ => FileSorting::ByName, _ => FileSorting::ByName,
}; };
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.local.sort_by(sorting), FileExplorerTab::Local => self.local_mut().sort_by(sorting),
FileExplorerTab::Remote => self.remote.sort_by(sorting), FileExplorerTab::Remote => self.remote_mut().sort_by(sorting),
_ => panic!("Found result doesn't support SORTING"), _ => panic!("Found result doesn't support SORTING"),
} }
// Update status bar // Update status bar
self.refresh_status_bar(); self.refresh_status_bar();
// Reload files // Reload files
match self.tab { match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(), FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(), FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None, _ => None,
@@ -739,16 +698,16 @@ impl FileTransferActivity {
"{}:{} ", "{}:{} ",
hostname, hostname,
FileTransferActivity::elide_wrkdir_path( FileTransferActivity::elide_wrkdir_path(
self.local.wrkdir.as_path(), self.local().wrkdir.as_path(),
hostname.as_str(), hostname.as_str(),
width width
) )
.display() .display()
); );
let files: Vec<String> = self let files: Vec<String> = self
.local .local()
.iter_files() .iter_files()
.map(|x: &FsEntry| self.local.fmt_file(x)) .map(|x: &FsEntry| self.local().fmt_file(x))
.collect(); .collect();
// Update // Update
let props = FileListPropsBuilder::from(props) let props = FileListPropsBuilder::from(props)
@@ -780,16 +739,16 @@ impl FileTransferActivity {
"{}:{} ", "{}:{} ",
params.address, params.address,
FileTransferActivity::elide_wrkdir_path( FileTransferActivity::elide_wrkdir_path(
self.remote.wrkdir.as_path(), self.remote().wrkdir.as_path(),
params.address.as_str(), params.address.as_str(),
width width
) )
.display() .display()
); );
let files: Vec<String> = self let files: Vec<String> = self
.remote .remote()
.iter_files() .iter_files()
.map(|x: &FsEntry| self.remote.fmt_file(x)) .map(|x: &FsEntry| self.remote().fmt_file(x))
.collect(); .collect();
// Update // Update
let props = FileListPropsBuilder::from(props) let props = FileListPropsBuilder::from(props)
@@ -807,19 +766,9 @@ impl FileTransferActivity {
pub(super) fn update_logbox(&mut self) -> Option<(String, Msg)> { pub(super) fn update_logbox(&mut self) -> Option<(String, Msg)> {
match self.view.get_props(super::COMPONENT_LOG_BOX) { match self.view.get_props(super::COMPONENT_LOG_BOX) {
Some(props) => { Some(props) => {
// Get width
let width: usize = self
.context
.as_ref()
.unwrap()
.store
.get_unsigned(super::STORAGE_LOGBOX_WIDTH)
.unwrap_or(256);
// Make log entries // Make log entries
let mut table: TableBuilder = TableBuilder::default(); let mut table: TableBuilder = TableBuilder::default();
for (idx, record) in self.log_records.iter().enumerate() { for (idx, record) in self.log_records.iter().enumerate() {
// Split rows by width NOTE: -37 'cause log prefix -3 cause of log line cursor
let record_rows = textwrap::wrap(record.msg.as_str(), (width as usize) - 40);
// Add row if not first row // Add row if not first row
if idx > 0 { if idx > 0 {
table.add_row(); table.add_row();
@@ -829,42 +778,29 @@ impl FileTransferActivity {
LogLevel::Warn => Color::Yellow, LogLevel::Warn => Color::Yellow,
LogLevel::Info => Color::Green, LogLevel::Info => Color::Green,
}; };
for (idx, row) in record_rows.iter().enumerate() { table
match idx { .add_col(TextSpan::from(format!(
0 => { "{}",
// First row record.time.format("%Y-%m-%dT%H:%M:%S%Z")
table )))
.add_col(TextSpan::from(format!( .add_col(TextSpan::from(" ["))
"{}", .add_col(
record.time.format("%Y-%m-%dT%H:%M:%S%Z") TextSpanBuilder::new(
))) format!(
.add_col(TextSpan::from(" [")) "{:5}",
.add_col( match record.level {
TextSpanBuilder::new( LogLevel::Error => "ERROR",
format!( LogLevel::Warn => "WARN",
"{:5}", LogLevel::Info => "INFO",
match record.level { }
LogLevel::Error => "ERROR", )
LogLevel::Warn => "WARN", .as_str(),
LogLevel::Info => "INFO", )
} .with_foreground(fg)
) .build(),
.as_str(), )
) .add_col(TextSpan::from("]: "))
.with_foreground(fg) .add_col(TextSpan::from(record.msg.as_ref()));
.build(),
)
.add_col(TextSpan::from("]: "))
.add_col(TextSpan::from(row.as_ref()));
}
_ => {
table.add_col(TextSpan::from(textwrap::indent(
row.as_ref(),
" ",
)));
}
}
}
} }
let table = table.build(); let table = table.build();
let props = LogboxPropsBuilder::from(props) let props = LogboxPropsBuilder::from(props)
@@ -911,13 +847,13 @@ impl FileTransferActivity {
/// Finalize find process /// Finalize find process
fn finalize_find(&mut self) { fn finalize_find(&mut self) {
// Set found to none // Set found to none
self.found = None; self.browser.del_found();
// Restore tab // Restore tab
self.tab = match self.tab { self.browser.change_tab(match self.browser.tab() {
FileExplorerTab::FindLocal => FileExplorerTab::Local, FileExplorerTab::FindLocal => FileExplorerTab::Local,
FileExplorerTab::FindRemote => FileExplorerTab::Remote, FileExplorerTab::FindRemote => FileExplorerTab::Remote,
_ => FileExplorerTab::Local, _ => FileExplorerTab::Local,
}; });
} }
fn update_find_list(&mut self) -> Option<(String, Msg)> { fn update_find_list(&mut self) -> Option<(String, Msg)> {
@@ -927,11 +863,10 @@ impl FileTransferActivity {
let title: String = props.texts.title.clone().unwrap_or_default(); let title: String = props.texts.title.clone().unwrap_or_default();
// Prepare files // Prepare files
let files: Vec<String> = self let files: Vec<String> = self
.found .found()
.as_ref()
.unwrap() .unwrap()
.iter_files() .iter_files()
.map(|x: &FsEntry| self.found.as_ref().unwrap().fmt_file(x)) .map(|x: &FsEntry| self.found().unwrap().fmt_file(x))
.collect(); .collect();
let props = FileListPropsBuilder::from(props) let props = FileListPropsBuilder::from(props)
.with_files(Some(title), files) .with_files(Some(title), files)
@@ -31,7 +31,7 @@ extern crate hostname;
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
extern crate users; extern crate users;
// locals // locals
use super::{Context, FileExplorerTab, FileTransferActivity}; use super::{browser::FileExplorerTab, Context, FileTransferActivity};
use crate::fs::explorer::FileSorting; use crate::fs::explorer::FileSorting;
use crate::fs::FsEntry; use crate::fs::FsEntry;
use crate::ui::components::{ use crate::ui::components::{
@@ -148,12 +148,9 @@ impl FileTransferActivity {
if !store.isset(super::STORAGE_EXPLORER_WIDTH) { if !store.isset(super::STORAGE_EXPLORER_WIDTH) {
store.set_unsigned(super::STORAGE_EXPLORER_WIDTH, tabs_chunks[0].width as usize); store.set_unsigned(super::STORAGE_EXPLORER_WIDTH, tabs_chunks[0].width as usize);
} }
if !store.isset(super::STORAGE_LOGBOX_WIDTH) {
store.set_unsigned(super::STORAGE_LOGBOX_WIDTH, chunks[1].width as usize);
}
// Draw explorers // Draw explorers
// @! Local explorer (Find or default) // @! Local explorer (Find or default)
match self.tab { match self.browser.tab() {
FileExplorerTab::FindLocal => { FileExplorerTab::FindLocal => {
self.view self.view
.render(super::COMPONENT_EXPLORER_FIND, f, tabs_chunks[0]) .render(super::COMPONENT_EXPLORER_FIND, f, tabs_chunks[0])
@@ -163,7 +160,7 @@ impl FileTransferActivity {
.render(super::COMPONENT_EXPLORER_LOCAL, f, tabs_chunks[0]), .render(super::COMPONENT_EXPLORER_LOCAL, f, tabs_chunks[0]),
} }
// @! Remote explorer (Find or default) // @! Remote explorer (Find or default)
match self.tab { match self.browser.tab() {
FileExplorerTab::FindRemote => { FileExplorerTab::FindRemote => {
self.view self.view
.render(super::COMPONENT_EXPLORER_FIND, f, tabs_chunks[1]) .render(super::COMPONENT_EXPLORER_FIND, f, tabs_chunks[1])
@@ -489,7 +486,7 @@ impl FileTransferActivity {
pub(super) fn mount_find(&mut self, search: &str) { pub(super) fn mount_find(&mut self, search: &str) {
// Get color // Get color
let color: Color = match self.tab { let color: Color = match self.browser.tab() {
FileExplorerTab::Local | FileExplorerTab::FindLocal => Color::Yellow, FileExplorerTab::Local | FileExplorerTab::FindLocal => Color::Yellow,
FileExplorerTab::Remote | FileExplorerTab::FindRemote => Color::LightBlue, FileExplorerTab::Remote | FileExplorerTab::FindRemote => Color::LightBlue,
}; };
@@ -637,9 +634,9 @@ impl FileTransferActivity {
} }
pub(super) fn mount_file_sorting(&mut self) { pub(super) fn mount_file_sorting(&mut self) {
let sorting: FileSorting = match self.tab { let sorting: FileSorting = match self.browser.tab() {
FileExplorerTab::Local => self.local.get_file_sorting(), FileExplorerTab::Local => self.local().get_file_sorting(),
FileExplorerTab::Remote => self.remote.get_file_sorting(), FileExplorerTab::Remote => self.remote().get_file_sorting(),
_ => panic!("You can't mount file sorting when in found result"), _ => panic!("You can't mount file sorting when in found result"),
}; };
let index: usize = match sorting { let index: usize = match sorting {
@@ -824,14 +821,14 @@ impl FileTransferActivity {
TextSpanBuilder::new(" Localhost file sorting: ") TextSpanBuilder::new(" Localhost file sorting: ")
.with_foreground(Color::LightYellow) .with_foreground(Color::LightYellow)
.build(), .build(),
TextSpanBuilder::new(Self::get_file_sorting_str(self.local.get_file_sorting())) TextSpanBuilder::new(Self::get_file_sorting_str(self.local().get_file_sorting()))
.with_foreground(Color::LightYellow) .with_foreground(Color::LightYellow)
.reversed() .reversed()
.build(), .build(),
TextSpanBuilder::new(" Remote host file sorting: ") TextSpanBuilder::new(" Remote host file sorting: ")
.with_foreground(Color::LightBlue) .with_foreground(Color::LightBlue)
.build(), .build(),
TextSpanBuilder::new(Self::get_file_sorting_str(self.remote.get_file_sorting())) TextSpanBuilder::new(Self::get_file_sorting_str(self.remote().get_file_sorting()))
.with_foreground(Color::LightBlue) .with_foreground(Color::LightBlue)
.reversed() .reversed()
.build(), .build(),
+2 -1
View File
@@ -202,12 +202,13 @@ impl Component for LogBox {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
fn render(&self, render: &mut Canvas, area: Rect) { fn render(&self, render: &mut Canvas, area: Rect) {
if self.props.visible { if self.props.visible {
let width: usize = area.width as usize - 4;
// Make list // Make list
let list_items: Vec<ListItem> = match self.props.texts.table.as_ref() { let list_items: Vec<ListItem> = match self.props.texts.table.as_ref() {
None => Vec::new(), None => Vec::new(),
Some(table) => table Some(table) => table
.iter() .iter()
.map(|row| ListItem::new(wrap_spans(row, area.width.into(), &self.props))) .map(|row| ListItem::new(wrap_spans(row, width, &self.props)))
.collect(), // Make List item from TextSpan .collect(), // Make List item from TextSpan
}; };
let w = List::new(list_items) let w = List::new(list_items)
+6 -17
View File
@@ -33,7 +33,6 @@ extern crate tuirealm;
use super::input::InputHandler; use super::input::InputHandler;
use super::store::Store; use super::store::Store;
use crate::filetransfer::FileTransferProtocol; use crate::filetransfer::FileTransferProtocol;
use crate::host::Localhost;
use crate::system::config_client::ConfigClient; use crate::system::config_client::ConfigClient;
// Includes // Includes
@@ -49,7 +48,6 @@ use tuirealm::tui::Terminal;
/// ///
/// Context holds data structures used by the ui /// Context holds data structures used by the ui
pub struct Context { pub struct Context {
pub local: Localhost,
pub ft_params: Option<FileTransferParams>, pub ft_params: Option<FileTransferParams>,
pub(crate) config_client: Option<ConfigClient>, pub(crate) config_client: Option<ConfigClient>,
pub(crate) store: Store, pub(crate) store: Store,
@@ -74,16 +72,11 @@ impl Context {
/// ### new /// ### new
/// ///
/// Instantiates a new Context /// Instantiates a new Context
pub fn new( pub fn new(config_client: Option<ConfigClient>, error: Option<String>) -> Context {
local: Localhost,
config_client: Option<ConfigClient>,
error: Option<String>,
) -> Context {
// Create terminal // Create terminal
let mut stdout = stdout(); let mut stdout = stdout();
assert!(execute!(stdout, EnterAlternateScreen).is_ok()); assert!(execute!(stdout, EnterAlternateScreen).is_ok());
Context { Context {
local,
ft_params: None, ft_params: None,
config_client, config_client,
store: Store::init(), store: Store::init(),
@@ -93,14 +86,12 @@ impl Context {
} }
} }
/* NOTE: in case is necessary
/// ### set_error /// ### set_error
/// ///
/// Set context error /// Set context error
pub fn set_error(&mut self, err: String) { pub fn set_error(&mut self, err: String) {
self.error = Some(err); self.error = Some(err);
} }
*/
/// ### get_error /// ### get_error
/// ///
@@ -165,7 +156,6 @@ mod tests {
use super::*; use super::*;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use std::path::PathBuf;
#[test] #[test]
fn test_ui_context_ft_params() { fn test_ui_context_ft_params() {
@@ -181,16 +171,15 @@ mod tests {
#[cfg(not(feature = "githubActions"))] #[cfg(not(feature = "githubActions"))]
fn test_ui_context() { fn test_ui_context() {
// Prepare stuff // Prepare stuff
let wrkdir: PathBuf = std::env::current_dir().unwrap_or(PathBuf::from("/")); let mut ctx: Context = Context::new(None, Some(String::from("alles kaput")));
let mut ctx: Context = Context::new(
Localhost::new(wrkdir).ok().unwrap(),
None,
Some(String::from("alles kaput")),
);
assert!(ctx.error.is_some()); assert!(ctx.error.is_some());
assert_eq!(ctx.get_error().unwrap().as_str(), "alles kaput"); assert_eq!(ctx.get_error().unwrap().as_str(), "alles kaput");
assert!(ctx.error.is_none()); assert!(ctx.error.is_none());
assert!(ctx.get_error().is_none()); assert!(ctx.get_error().is_none());
ctx.set_error(String::from("err"));
assert!(ctx.error.is_some());
assert!(ctx.get_error().is_some());
assert!(ctx.get_error().is_none());
// Try other methods // Try other methods
ctx.enter_alternate_screen(); ctx.enter_alternate_screen();
ctx.clear_screen(); ctx.clear_screen();