Remote file syntax for formatter

This commit is contained in:
veeso
2021-05-01 17:12:48 +02:00
parent 41e89605f0
commit 3704d2520d
10 changed files with 166 additions and 50 deletions

View File

@@ -21,6 +21,8 @@
Released on FIXME: ??
- **Remote and Local hosts file formatter**:
- Added the possibility to set different formatters for local and remote hosts
- Bugfix:
- Fixed wrong text wrap in log box
- Dependencies:

View File

@@ -196,7 +196,7 @@ You can access the SSH key storage, from configuration moving to the `SSH Keys`
### File Explorer Format
It is possible through configuration to define a custom format for the file explorer. This field, with name `File formatter syntax` will define how the file entries will be displayed in the file explorer.
It is possible through configuration to define a custom format for the file explorer. This is possible both for local and remote host, so you can have two different syntax in use. These fields, with name `File formatter syntax (local)` and `File formatter syntax (remote)` will define how the file entries will be displayed in the file explorer.
The syntax for the formatter is the following `{KEY1}... {KEY2:LENGTH}... {KEY3:LENGTH:EXTRA} {KEYn}...`.
Each key in bracket will be replaced with the related attribute, while everything outside brackets will be left unchanged.

View File

@@ -60,7 +60,8 @@ pub struct UserInterfaceConfig {
pub show_hidden_files: bool,
pub check_for_updates: Option<bool>, // @! Since 0.3.3
pub group_dirs: Option<String>,
pub file_fmt: Option<String>,
pub file_fmt: Option<String>, // Refers to local host (for backward compatibility)
pub remote_file_fmt: Option<String>, // @! Since 0.5.0
}
#[derive(Deserialize, Serialize, std::fmt::Debug)]
@@ -92,6 +93,7 @@ impl Default for UserInterfaceConfig {
check_for_updates: Some(true),
group_dirs: None,
file_fmt: None,
remote_file_fmt: None,
}
}
}
@@ -178,6 +180,7 @@ mod tests {
check_for_updates: Some(true),
group_dirs: Some(String::from("first")),
file_fmt: Some(String::from("{NAME}")),
remote_file_fmt: Some(String::from("{USER}")),
};
let cfg: UserConfig = UserConfig {
user_interface: ui,
@@ -196,6 +199,10 @@ mod tests {
assert_eq!(cfg.user_interface.check_for_updates, Some(true));
assert_eq!(cfg.user_interface.group_dirs, Some(String::from("first")));
assert_eq!(cfg.user_interface.file_fmt, Some(String::from("{NAME}")));
assert_eq!(
cfg.user_interface.remote_file_fmt,
Some(String::from("{USER}"))
);
}
#[test]
@@ -218,6 +225,8 @@ mod tests {
);
assert_eq!(cfg.user_interface.check_for_updates.unwrap(), true);
assert_eq!(cfg.remote.ssh_keys.len(), 0);
assert!(cfg.user_interface.file_fmt.is_none());
assert!(cfg.user_interface.remote_file_fmt.is_none());
}
#[test]

View File

@@ -114,6 +114,10 @@ mod tests {
cfg.user_interface.file_fmt,
Some(String::from("{NAME} {PEX}"))
);
assert_eq!(
cfg.user_interface.remote_file_fmt,
Some(String::from("{NAME} {USER}")),
);
// Verify keys
assert_eq!(
*cfg.remote
@@ -149,7 +153,8 @@ mod tests {
assert_eq!(cfg.user_interface.show_hidden_files, true);
assert_eq!(cfg.user_interface.group_dirs, None);
assert!(cfg.user_interface.check_for_updates.is_none());
assert_eq!(cfg.user_interface.file_fmt, None);
assert!(cfg.user_interface.file_fmt.is_none());
assert!(cfg.user_interface.remote_file_fmt.is_none());
// Verify keys
assert_eq!(
*cfg.remote
@@ -208,6 +213,7 @@ mod tests {
check_for_updates = true
group_dirs = "last"
file_fmt = "{NAME} {PEX}"
remote_file_fmt = "{NAME} {USER}"
[remote.ssh_keys]
"192.168.1.31" = "/home/omar/.ssh/raspberry.key"

View File

@@ -176,23 +176,40 @@ impl ConfigClient {
self.config.user_interface.group_dirs = val.map(|val| val.to_string());
}
/// ### get_file_fmt
/// ### get_local_file_fmt
///
/// Get current file fmt
pub fn get_file_fmt(&self) -> Option<String> {
/// Get current file fmt for local host
pub fn get_local_file_fmt(&self) -> Option<String> {
self.config.user_interface.file_fmt.clone()
}
/// ### set_file_fmt
/// ### set_local_file_fmt
///
/// Set file fmt parameter
pub fn set_file_fmt(&mut self, s: String) {
/// Set file fmt parameter for local host
pub fn set_local_file_fmt(&mut self, s: String) {
self.config.user_interface.file_fmt = match s.is_empty() {
true => None,
false => Some(s),
};
}
/// ### get_remote_file_fmt
///
/// Get current file fmt for remote host
pub fn get_remote_file_fmt(&self) -> Option<String> {
self.config.user_interface.remote_file_fmt.clone()
}
/// ### set_remote_file_fmt
///
/// Set file fmt parameter for remote host
pub fn set_remote_file_fmt(&mut self, s: String) {
self.config.user_interface.remote_file_fmt = match s.is_empty() {
true => None,
false => Some(s),
};
}
// SSH Keys
/// ### save_ssh_key
@@ -496,18 +513,36 @@ mod tests {
}
#[test]
fn test_system_config_file_fmt() {
fn test_system_config_local_file_fmt() {
let tmp_dir: tempfile::TempDir = create_tmp_dir();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
.unwrap();
assert_eq!(client.get_file_fmt(), None);
client.set_file_fmt(String::from("{NAME}"));
assert_eq!(client.get_file_fmt().unwrap(), String::from("{NAME}"));
assert_eq!(client.get_local_file_fmt(), None);
client.set_local_file_fmt(String::from("{NAME}"));
assert_eq!(client.get_local_file_fmt().unwrap(), String::from("{NAME}"));
// Delete
client.set_file_fmt(String::from(""));
assert_eq!(client.get_file_fmt(), None);
client.set_local_file_fmt(String::from(""));
assert_eq!(client.get_local_file_fmt(), None);
}
#[test]
fn test_system_config_remote_file_fmt() {
let tmp_dir: tempfile::TempDir = create_tmp_dir();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
.unwrap();
assert_eq!(client.get_remote_file_fmt(), None);
client.set_remote_file_fmt(String::from("{NAME}"));
assert_eq!(
client.get_remote_file_fmt().unwrap(),
String::from("{NAME}")
);
// Delete
client.set_remote_file_fmt(String::from(""));
assert_eq!(client.get_remote_file_fmt(), None);
}
#[test]

View File

@@ -94,21 +94,46 @@ impl FileTransferActivity {
/// ### build_explorer
///
/// Build explorer reading configuration from `ConfigClient`
pub(super) fn build_explorer(cli: Option<&ConfigClient>) -> FileExplorer {
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) => FileExplorerBuilder::new() // Build according to current configuration
.with_file_sorting(FileSorting::ByName)
.with_group_dirs(cli.get_group_dirs())
.with_hidden_files(cli.get_show_hidden_files())
.with_stack_size(16)
.with_formatter(cli.get_file_fmt().as_deref())
.build(),
None => FileExplorerBuilder::new() // Build default
.with_file_sorting(FileSorting::ByName)
.with_group_dirs(Some(GroupDirs::First))
.with_stack_size(16)
.build(),
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

View File

@@ -239,8 +239,8 @@ impl FileTransferActivity {
Self::make_ssh_storage(config_client.as_ref()),
)),
},
local: Self::build_explorer(config_client.as_ref()),
remote: Self::build_explorer(config_client.as_ref()),
local: Self::build_local_explorer(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

View File

@@ -53,7 +53,8 @@ const COMPONENT_RADIO_DEFAULT_PROTOCOL: &str = "RADIO_DEFAULT_PROTOCOL";
const COMPONENT_RADIO_HIDDEN_FILES: &str = "RADIO_HIDDEN_FILES";
const COMPONENT_RADIO_UPDATES: &str = "RADIO_CHECK_UPDATES";
const COMPONENT_RADIO_GROUP_DIRS: &str = "RADIO_GROUP_DIRS";
const COMPONENT_INPUT_FILE_FMT: &str = "INPUT_FILE_FMT";
const COMPONENT_INPUT_LOCAL_FILE_FMT: &str = "INPUT_LOCAL_FILE_FMT";
const COMPONENT_INPUT_REMOTE_FILE_FMT: &str = "INPUT_REMOTE_FILE_FMT";
const COMPONENT_RADIO_TAB: &str = "RADIO_TAB";
const COMPONENT_LIST_SSH_KEYS: &str = "LIST_SSH_KEYS";
const COMPONENT_INPUT_SSH_HOST: &str = "INPUT_SSH_HOST";

View File

@@ -28,11 +28,11 @@
*/
// locals
use super::{
SetupActivity, COMPONENT_INPUT_FILE_FMT, COMPONENT_INPUT_SSH_HOST,
COMPONENT_INPUT_SSH_USERNAME, COMPONENT_INPUT_TEXT_EDITOR, COMPONENT_LIST_SSH_KEYS,
COMPONENT_RADIO_DEFAULT_PROTOCOL, COMPONENT_RADIO_DEL_SSH_KEY, COMPONENT_RADIO_GROUP_DIRS,
COMPONENT_RADIO_HIDDEN_FILES, COMPONENT_RADIO_QUIT, COMPONENT_RADIO_SAVE,
COMPONENT_RADIO_UPDATES, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP,
SetupActivity, COMPONENT_INPUT_LOCAL_FILE_FMT, COMPONENT_INPUT_REMOTE_FILE_FMT,
COMPONENT_INPUT_SSH_HOST, COMPONENT_INPUT_SSH_USERNAME, COMPONENT_INPUT_TEXT_EDITOR,
COMPONENT_LIST_SSH_KEYS, COMPONENT_RADIO_DEFAULT_PROTOCOL, COMPONENT_RADIO_DEL_SSH_KEY,
COMPONENT_RADIO_GROUP_DIRS, COMPONENT_RADIO_HIDDEN_FILES, COMPONENT_RADIO_QUIT,
COMPONENT_RADIO_SAVE, COMPONENT_RADIO_UPDATES, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP,
};
use crate::ui::activities::keymap::*;
@@ -68,15 +68,23 @@ impl SetupActivity {
None
}
(COMPONENT_RADIO_GROUP_DIRS, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_INPUT_FILE_FMT);
self.view.active(COMPONENT_INPUT_LOCAL_FILE_FMT);
None
}
(COMPONENT_INPUT_FILE_FMT, &MSG_KEY_DOWN) => {
(COMPONENT_INPUT_LOCAL_FILE_FMT, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_INPUT_REMOTE_FILE_FMT);
None
}
(COMPONENT_INPUT_REMOTE_FILE_FMT, &MSG_KEY_DOWN) => {
self.view.active(COMPONENT_INPUT_TEXT_EDITOR);
None
}
// Input field <UP>
(COMPONENT_INPUT_FILE_FMT, &MSG_KEY_UP) => {
(COMPONENT_INPUT_REMOTE_FILE_FMT, &MSG_KEY_UP) => {
self.view.active(COMPONENT_INPUT_LOCAL_FILE_FMT);
None
}
(COMPONENT_INPUT_LOCAL_FILE_FMT, &MSG_KEY_UP) => {
self.view.active(COMPONENT_RADIO_GROUP_DIRS);
None
}
@@ -97,7 +105,7 @@ impl SetupActivity {
None
}
(COMPONENT_INPUT_TEXT_EDITOR, &MSG_KEY_UP) => {
self.view.active(COMPONENT_INPUT_FILE_FMT);
self.view.active(COMPONENT_INPUT_REMOTE_FILE_FMT);
None
}
// Error <ENTER> or <ESC>

View File

@@ -173,12 +173,22 @@ impl SetupActivity {
)),
);
self.view.mount(
super::COMPONENT_INPUT_FILE_FMT,
super::COMPONENT_INPUT_LOCAL_FILE_FMT,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightBlue)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightBlue)
.with_label(String::from("File formatter syntax"))
.with_label(String::from("File formatter syntax (local)"))
.build(),
)),
);
self.view.mount(
super::COMPONENT_INPUT_REMOTE_FILE_FMT,
Box::new(Input::new(
InputPropsBuilder::default()
.with_foreground(Color::LightGreen)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightGreen)
.with_label(String::from("File formatter syntax (remote)"))
.build(),
)),
);
@@ -280,7 +290,8 @@ impl SetupActivity {
Constraint::Length(3), // Hidden files
Constraint::Length(3), // Updates tab
Constraint::Length(3), // Group dirs
Constraint::Length(3), // Format input
Constraint::Length(3), // Local Format input
Constraint::Length(3), // Remote Format input
Constraint::Length(1), // Empty ?
]
.as_ref(),
@@ -297,7 +308,9 @@ impl SetupActivity {
self.view
.render(super::COMPONENT_RADIO_GROUP_DIRS, f, ui_cfg_chunks[4]);
self.view
.render(super::COMPONENT_INPUT_FILE_FMT, f, ui_cfg_chunks[5]);
.render(super::COMPONENT_INPUT_LOCAL_FILE_FMT, f, ui_cfg_chunks[5]);
self.view
.render(super::COMPONENT_INPUT_REMOTE_FILE_FMT, f, ui_cfg_chunks[6]);
}
ViewLayout::SshKeys => {
let sshcfg_chunks = Layout::default()
@@ -692,11 +705,21 @@ impl SetupActivity {
let props = RadioPropsBuilder::from(props).with_value(dirs).build();
let _ = self.view.update(super::COMPONENT_RADIO_GROUP_DIRS, props);
}
// File Fmt
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_FILE_FMT) {
let file_fmt: String = cli.get_file_fmt().unwrap_or_default();
// Local File Fmt
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_LOCAL_FILE_FMT) {
let file_fmt: String = cli.get_local_file_fmt().unwrap_or_default();
let props = InputPropsBuilder::from(props).with_value(file_fmt).build();
let _ = self.view.update(super::COMPONENT_INPUT_FILE_FMT, props);
let _ = self
.view
.update(super::COMPONENT_INPUT_LOCAL_FILE_FMT, props);
}
// Remote File Fmt
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_REMOTE_FILE_FMT) {
let file_fmt: String = cli.get_remote_file_fmt().unwrap_or_default();
let props = InputPropsBuilder::from(props).with_value(file_fmt).build();
let _ = self
.view
.update(super::COMPONENT_INPUT_REMOTE_FILE_FMT, props);
}
}
}
@@ -734,8 +757,15 @@ impl SetupActivity {
let check: bool = matches!(opt, 0);
cli.set_check_for_updates(check);
}
if let Some(Payload::Text(fmt)) = self.view.get_state(super::COMPONENT_INPUT_FILE_FMT) {
cli.set_file_fmt(fmt);
if let Some(Payload::Text(fmt)) =
self.view.get_state(super::COMPONENT_INPUT_LOCAL_FILE_FMT)
{
cli.set_local_file_fmt(fmt);
}
if let Some(Payload::Text(fmt)) =
self.view.get_state(super::COMPONENT_INPUT_REMOTE_FILE_FMT)
{
cli.set_remote_file_fmt(fmt);
}
if let Some(Payload::Unsigned(opt)) =
self.view.get_state(super::COMPONENT_RADIO_GROUP_DIRS)