ConfigClient is an option no more; config client degraded mode

This commit is contained in:
veeso 2021-07-08 15:07:24 +02:00
parent b9cb961da6
commit e6b44e1461
12 changed files with 350 additions and 310 deletions

View file

@ -67,12 +67,12 @@ impl ActivityManager {
pub fn new(local_dir: &Path, interval: Duration) -> Result<ActivityManager, HostError> {
// Prepare Context
// Initialize configuration client
let (config_client, error): (Option<ConfigClient>, Option<String>) =
let (config_client, error): (ConfigClient, Option<String>) =
match Self::init_config_client() {
Ok(cli) => (Some(cli), None),
Ok(cli) => (cli, None),
Err(err) => {
error!("Failed to initialize config client: {}", err);
(None, Some(err))
(ConfigClient::degraded(), Some(err))
}
};
let theme_provider: ThemeProvider = Self::init_theme_provider();

View file

@ -49,13 +49,14 @@ pub struct ConfigClient {
config: UserConfig, // Configuration loaded
config_path: PathBuf, // Configuration TOML Path
ssh_key_dir: PathBuf, // SSH Key storage directory
degraded: bool, // Indicates the `ConfigClient` is working in degraded mode
}
impl ConfigClient {
/// ### new
///
/// Instantiate a new `ConfigClient` with provided path
pub fn new(config_path: &Path, ssh_key_dir: &Path) -> Result<ConfigClient, SerializerError> {
pub fn new(config_path: &Path, ssh_key_dir: &Path) -> Result<Self, SerializerError> {
// Initialize a default configuration
let default_config: UserConfig = UserConfig::default();
info!(
@ -68,6 +69,7 @@ impl ConfigClient {
config: default_config,
config_path: PathBuf::from(config_path),
ssh_key_dir: PathBuf::from(ssh_key_dir),
degraded: false,
};
// If ssh key directory doesn't exist, create it
if !ssh_key_dir.exists() {
@ -102,6 +104,20 @@ impl ConfigClient {
Ok(client)
}
/// ### degraded
///
/// Instantiate a ConfigClient in degraded mode.
/// When in degraded mode, the configuration in use will be the default configuration
/// and the IO operation on configuration won't be available
pub fn degraded() -> Self {
Self {
config: UserConfig::default(),
config_path: PathBuf::default(),
ssh_key_dir: PathBuf::default(),
degraded: true,
}
}
// Text editor
/// ### get_text_editor
@ -234,6 +250,12 @@ impl ConfigClient {
username: &str,
ssh_key: &str,
) -> Result<(), SerializerError> {
if self.degraded {
return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError,
String::from("Configuration won't be saved, since in degraded mode"),
));
}
let host_name: String = Self::make_ssh_host_key(host, username);
// Get key path
let ssh_key_path: PathBuf = {
@ -267,6 +289,12 @@ impl ConfigClient {
/// This operation also unlinks the key file in `ssh_key_dir`
/// and also commits changes to configuration, to prevent incoerent data
pub fn del_ssh_key(&mut self, host: &str, username: &str) -> Result<(), SerializerError> {
if self.degraded {
return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError,
String::from("Configuration won't be saved, since in degraded mode"),
));
}
// Remove key from configuration and get key path
info!("Removing key for {}@{}", host, username);
let key_path: PathBuf = match self
@ -293,6 +321,9 @@ impl ConfigClient {
/// None is returned if key doesn't exist
/// `std::io::Error` is returned in case it was not possible to read the key file
pub fn get_ssh_key(&self, mkey: &str) -> std::io::Result<Option<SshHost>> {
if self.degraded {
return Ok(None);
}
// Check if Key exists
match self.config.remote.ssh_keys.get(mkey) {
None => Ok(None),
@ -318,6 +349,12 @@ impl ConfigClient {
///
/// Write configuration to file
pub fn write_config(&self) -> Result<(), SerializerError> {
if self.degraded {
return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError,
String::from("Configuration won't be saved, since in degraded mode"),
));
}
// Open file
match OpenOptions::new()
.create(true)
@ -340,6 +377,12 @@ impl ConfigClient {
///
/// Read configuration from file (or reload it if already read)
pub fn read_config(&mut self) -> Result<(), SerializerError> {
if self.degraded {
return Err(SerializerError::new_ex(
SerializerErrorKind::GenericError,
String::from("Configuration won't be loaded, since in degraded mode"),
));
}
// Open bookmarks file for read
match OpenOptions::new()
.read(true)
@ -415,6 +458,7 @@ mod tests {
.unwrap();
// Verify parameters
let default_config: UserConfig = UserConfig::default();
assert_eq!(client.degraded, false);
assert_eq!(client.config.remote.ssh_keys.len(), 0);
assert_eq!(
client.config.user_interface.default_protocol,
@ -428,6 +472,20 @@ mod tests {
assert_eq!(client.ssh_key_dir, ssh_keys_path);
}
#[test]
fn test_system_config_degraded() {
let mut client: ConfigClient = ConfigClient::degraded();
assert_eq!(client.degraded, true);
assert_eq!(client.config_path, PathBuf::default());
assert_eq!(client.ssh_key_dir, PathBuf::default());
// I/O
assert!(client.add_ssh_key("Omar", "omar", "omar").is_err());
assert!(client.del_ssh_key("omar", "omar").is_err());
assert!(client.get_ssh_key("omar").ok().unwrap().is_none());
assert!(client.write_config().is_err());
assert!(client.read_config().is_err());
}
#[test]
fn test_system_config_new_err() {
assert!(

View file

@ -111,33 +111,36 @@ impl AuthActivity {
fn check_for_updates(&mut self) {
debug!("Check for updates...");
// Check version only if unset in the store
let ctx: &Context = self.context.as_ref().unwrap();
let ctx: &mut Context = self.context.as_mut().unwrap();
if !ctx.store.isset(STORE_KEY_LATEST_VERSION) {
debug!("Version is not set in storage");
let mut github_tag: Option<git::GithubTag> = match ctx.config_client.as_ref() {
Some(client) => {
if client.get_check_for_updates() {
debug!("Check for updates is enabled");
// Send request
match git::check_for_updates(env!("CARGO_PKG_VERSION")) {
Ok(github_tag) => github_tag,
Err(err) => {
// Report error
error!("Failed to get latest version: {}", err);
self.mount_error(
format!("Could not check for new updates: {}", err).as_str(),
);
// None
None
}
}
} else {
info!("Check for updates is disabled");
None
if ctx.config_client.get_check_for_updates() {
debug!("Check for updates is enabled");
// Send request
match git::check_for_updates(env!("CARGO_PKG_VERSION")) {
Ok(Some(git::GithubTag { tag_name, body })) => {
// If some, store version and release notes
info!("Latest version is: {}", tag_name);
ctx.store.set_string(STORE_KEY_LATEST_VERSION, tag_name);
ctx.store.set_string(STORE_KEY_RELEASE_NOTES, body);
}
Ok(None) => {
info!("Latest version is: {} (current)", env!("CARGO_PKG_VERSION"));
// Just set flag as check
ctx.store.set(STORE_KEY_LATEST_VERSION);
}
Err(err) => {
// Report error
error!("Failed to get latest version: {}", err);
self.mount_error(
format!("Could not check for new updates: {}", err).as_str(),
);
}
}
None => None,
};
} else {
info!("Check for updates is disabled");
}
/*
let ctx: &mut Context = self.context.as_mut().unwrap();
// Set version into the store (or just a flag)
match github_tag.take() {
@ -152,7 +155,7 @@ impl AuthActivity {
// Just set flag as check
ctx.store.set(STORE_KEY_LATEST_VERSION);
}
}
}*/
}
}
@ -194,7 +197,7 @@ impl Activity for AuthActivity {
self.view_recent_connections();
}
// Verify error state from context
if let Some(err) = self.context.as_mut().unwrap().get_error() {
if let Some(err) = self.context.as_mut().unwrap().error() {
self.mount_error(err.as_str());
}
info!("Activity initialized");

View file

@ -109,11 +109,12 @@ impl AuthActivity {
)),
);
// Get default protocol
let default_protocol: FileTransferProtocol =
match self.context.as_ref().unwrap().config_client.as_ref() {
Some(cli) => cli.get_default_protocol(),
None => FileTransferProtocol::Sftp,
};
let default_protocol: FileTransferProtocol = self
.context
.as_ref()
.unwrap()
.config_client
.get_default_protocol();
// Protocol
self.view.mount(
super::COMPONENT_RADIO_PROTOCOL,

View file

@ -103,10 +103,7 @@ impl FileTransferActivity {
///
/// Set text editor to use
pub(super) fn setup_text_editor(&self) {
if let Some(config_cli) = self.context.as_ref().unwrap().config_client.as_ref() {
// Set text editor
env::set_var("EDITOR", config_cli.get_text_editor());
}
env::set_var("EDITOR", self.config().get_text_editor());
}
/// ### read_input_event

View file

@ -210,6 +210,13 @@ impl FileTransferActivity {
})
}
/// ### config
///
/// Returns config client reference
fn config(&self) -> &ConfigClient {
&self.context.as_ref().unwrap().config_client
}
/// ### theme
///
/// Get a reference to `Theme`
@ -249,7 +256,7 @@ impl Activity for FileTransferActivity {
self.init();
debug!("Initialized view");
// Verify error state from context
if let Some(err) = self.context.as_mut().unwrap().get_error() {
if let Some(err) = self.context.as_mut().unwrap().error() {
error!("Fatal error on create: {}", err);
self.mount_fatal(&err);
}

View file

@ -80,32 +80,29 @@ impl SetupActivity {
/// delete of a ssh key
pub(super) fn action_delete_ssh_key(&mut self) {
// Get key
if let Some(config_cli) = self.context.as_mut().unwrap().config_client.as_mut() {
// get index
let idx: Option<usize> = match self.view.get_state(super::COMPONENT_LIST_SSH_KEYS) {
Some(Payload::One(Value::Usize(idx))) => Some(idx),
_ => None,
};
if let Some(idx) = idx {
let key: Option<String> = config_cli.iter_ssh_keys().nth(idx).cloned();
if let Some(key) = key {
match config_cli.get_ssh_key(&key) {
Ok(opt) => {
if let Some((host, username, _)) = opt {
if let Err(err) =
self.delete_ssh_key(host.as_str(), username.as_str())
{
// Report error
self.mount_error(err.as_str());
}
// get index
let idx: Option<usize> = match self.view.get_state(super::COMPONENT_LIST_SSH_KEYS) {
Some(Payload::One(Value::Usize(idx))) => Some(idx),
_ => None,
};
if let Some(idx) = idx {
let key: Option<String> = self.config().iter_ssh_keys().nth(idx).cloned();
if let Some(key) = key {
match self.config().get_ssh_key(&key) {
Ok(opt) => {
if let Some((host, username, _)) = opt {
if let Err(err) = self.delete_ssh_key(host.as_str(), username.as_str())
{
// Report error
self.mount_error(err.as_str());
}
}
Err(err) => {
// Report error
self.mount_error(
format!("Could not get ssh key \"{}\": {}", key, err).as_str(),
);
}
}
Err(err) => {
// Report error
self.mount_error(
format!("Could not get ssh key \"{}\": {}", key, err).as_str(),
);
}
}
}
@ -116,67 +113,63 @@ impl SetupActivity {
///
/// Create a new ssh key
pub(super) fn action_new_ssh_key(&mut self) {
if let Some(cli) = self.context.as_mut().unwrap().config_client.as_mut() {
// get parameters
let host: String = match self.view.get_state(super::COMPONENT_INPUT_SSH_HOST) {
Some(Payload::One(Value::Str(host))) => host,
_ => String::new(),
};
let username: String = match self.view.get_state(super::COMPONENT_INPUT_SSH_USERNAME) {
Some(Payload::One(Value::Str(user))) => user,
_ => String::new(),
};
// Prepare text editor
env::set_var("EDITOR", cli.get_text_editor());
let placeholder: String = format!("# Type private SSH key for {}@{}\n", username, host);
// Put input mode back to normal
if let Err(err) = disable_raw_mode() {
error!("Failed to disable raw mode: {}", err);
}
// Leave alternate mode
#[cfg(not(target_os = "windows"))]
if let Some(ctx) = self.context.as_mut() {
ctx.leave_alternate_screen();
}
// Re-enable raw mode
if let Err(err) = enable_raw_mode() {
error!("Failed to enter raw mode: {}", err);
}
// Write key to file
match edit::edit(placeholder.as_bytes()) {
Ok(rsa_key) => {
// Remove placeholder from `rsa_key`
let rsa_key: String = rsa_key.as_str().replace(placeholder.as_str(), "");
if rsa_key.is_empty() {
// Report error: empty key
self.mount_error("SSH key is empty!");
} else {
// Add key
if let Err(err) =
self.add_ssh_key(host.as_str(), username.as_str(), rsa_key.as_str())
{
self.mount_error(
format!("Could not create new private key: {}", err).as_str(),
);
}
// get parameters
let host: String = match self.view.get_state(super::COMPONENT_INPUT_SSH_HOST) {
Some(Payload::One(Value::Str(host))) => host,
_ => String::new(),
};
let username: String = match self.view.get_state(super::COMPONENT_INPUT_SSH_USERNAME) {
Some(Payload::One(Value::Str(user))) => user,
_ => String::new(),
};
// Prepare text editor
env::set_var("EDITOR", self.config().get_text_editor());
let placeholder: String = format!("# Type private SSH key for {}@{}\n", username, host);
// Put input mode back to normal
if let Err(err) = disable_raw_mode() {
error!("Failed to disable raw mode: {}", err);
}
// Leave alternate mode
#[cfg(not(target_os = "windows"))]
if let Some(ctx) = self.context.as_mut() {
ctx.leave_alternate_screen();
}
// Re-enable raw mode
if let Err(err) = enable_raw_mode() {
error!("Failed to enter raw mode: {}", err);
}
// Write key to file
match edit::edit(placeholder.as_bytes()) {
Ok(rsa_key) => {
// Remove placeholder from `rsa_key`
let rsa_key: String = rsa_key.as_str().replace(placeholder.as_str(), "");
if rsa_key.is_empty() {
// Report error: empty key
self.mount_error("SSH key is empty!");
} else {
// Add key
if let Err(err) =
self.add_ssh_key(host.as_str(), username.as_str(), rsa_key.as_str())
{
self.mount_error(
format!("Could not create new private key: {}", err).as_str(),
);
}
}
Err(err) => {
// Report error
self.mount_error(
format!("Could not write private key to file: {}", err).as_str(),
);
}
}
// Restore terminal
#[cfg(not(target_os = "windows"))]
if let Some(ctx) = self.context.as_mut() {
// Clear screen
ctx.clear_screen();
// Enter alternate mode
ctx.enter_alternate_screen();
Err(err) => {
// Report error
self.mount_error(format!("Could not write private key to file: {}", err).as_str());
}
}
// Restore terminal
#[cfg(not(target_os = "windows"))]
if let Some(ctx) = self.context.as_mut() {
// Clear screen
ctx.clear_screen();
// Enter alternate mode
ctx.enter_alternate_screen();
}
}
/// ### set_color

View file

@ -37,12 +37,9 @@ impl SetupActivity {
///
/// Save configuration
pub(super) fn save_config(&mut self) -> Result<(), String> {
match self.context.as_ref().unwrap().config_client.as_ref() {
Some(cli) => match cli.write_config() {
Ok(_) => Ok(()),
Err(err) => Err(format!("Could not save configuration: {}", err)),
},
None => Ok(()),
match self.config().write_config() {
Ok(_) => Ok(()),
Err(err) => Err(format!("Could not save configuration: {}", err)),
}
}
@ -51,13 +48,9 @@ impl SetupActivity {
/// Reset configuration changes; pratically read config from file, overwriting any change made
/// since last write action
pub(super) fn reset_config_changes(&mut self) -> Result<(), String> {
match self.context.as_mut().unwrap().config_client.as_mut() {
Some(cli) => match cli.read_config() {
Ok(_) => Ok(()),
Err(err) => Err(format!("Could not restore configuration: {}", err)),
},
None => Ok(()),
}
self.config_mut()
.read_config()
.map_err(|e| format!("Could not reload configuration: {}", e))
}
/// ### save_theme
@ -82,15 +75,12 @@ impl SetupActivity {
///
/// Delete ssh key from config cli
pub(super) fn delete_ssh_key(&mut self, host: &str, username: &str) -> Result<(), String> {
match self.context.as_mut().unwrap().config_client.as_mut() {
Some(cli) => match cli.del_ssh_key(host, username) {
Ok(_) => Ok(()),
Err(err) => Err(format!(
"Could not delete ssh key \"{}@{}\": {}",
host, username, err
)),
},
None => Ok(()),
match self.config_mut().del_ssh_key(host, username) {
Ok(_) => Ok(()),
Err(err) => Err(format!(
"Could not delete ssh key \"{}@{}\": {}",
host, username, err
)),
}
}
@ -102,9 +92,7 @@ impl SetupActivity {
None => Ok(()),
Some(ctx) => {
// Set editor if config client exists
if let Some(config_cli) = ctx.config_client.as_ref() {
env::set_var("EDITOR", config_cli.get_text_editor());
}
env::set_var("EDITOR", ctx.config_client.get_text_editor());
// Prepare terminal
if let Err(err) = disable_raw_mode() {
error!("Failed to disable raw mode: {}", err);
@ -113,27 +101,22 @@ impl SetupActivity {
#[cfg(not(target_os = "windows"))]
ctx.leave_alternate_screen();
// Get result
let result: Result<(), String> = match ctx.config_client.as_ref() {
Some(config_cli) => match config_cli.iter_ssh_keys().nth(idx) {
Some(key) => {
// Get key path
match config_cli.get_ssh_key(key) {
Ok(ssh_key) => match ssh_key {
None => Ok(()),
Some((_, _, key_path)) => {
match edit::edit_file(key_path.as_path()) {
Ok(_) => Ok(()),
Err(err) => {
Err(format!("Could not edit ssh key: {}", err))
}
}
let result: Result<(), String> = match ctx.config_client.iter_ssh_keys().nth(idx) {
Some(key) => {
// Get key path
match ctx.config_client.get_ssh_key(key) {
Ok(ssh_key) => match ssh_key {
None => Ok(()),
Some((_, _, key_path)) => {
match edit::edit_file(key_path.as_path()) {
Ok(_) => Ok(()),
Err(err) => Err(format!("Could not edit ssh key: {}", err)),
}
},
Err(err) => Err(format!("Could not read ssh key: {}", err)),
}
}
},
Err(err) => Err(format!("Could not read ssh key: {}", err)),
}
None => Ok(()),
},
}
None => Ok(()),
};
// Restore terminal
@ -161,15 +144,8 @@ impl SetupActivity {
username: &str,
rsa_key: &str,
) -> Result<(), String> {
match self.context.as_mut().unwrap().config_client.as_mut() {
Some(cli) => {
// Add key to client
match cli.add_ssh_key(host, username, rsa_key) {
Ok(_) => Ok(()),
Err(err) => Err(format!("Could not add SSH key: {}", err)),
}
}
None => Ok(()),
}
self.config_mut()
.add_ssh_key(host, username, rsa_key)
.map_err(|e| format!("Could not add SSH key: {}", e))
}
}

View file

@ -35,6 +35,7 @@ mod view;
// Locals
use super::{Activity, Context, ExitReason};
use crate::config::themes::Theme;
use crate::system::config_client::ConfigClient;
use crate::system::theme_provider::ThemeProvider;
// Ext
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
@ -133,6 +134,14 @@ impl Default for SetupActivity {
}
impl SetupActivity {
fn config(&self) -> &ConfigClient {
&self.context.as_ref().unwrap().config_client
}
fn config_mut(&mut self) -> &mut ConfigClient {
&mut self.context.as_mut().unwrap().config_client
}
fn theme(&self) -> &Theme {
self.context.as_ref().unwrap().theme_provider.theme()
}
@ -164,7 +173,7 @@ impl Activity for SetupActivity {
// Init view
self.init(ViewLayout::SetupForm);
// Verify error state from context
if let Some(err) = self.context.as_mut().unwrap().get_error() {
if let Some(err) = self.context.as_mut().unwrap().error() {
self.mount_error(err.as_str());
}
}

View file

@ -286,73 +286,71 @@ impl SetupActivity {
///
/// Load values from configuration into input fields
pub(crate) fn load_input_values(&mut self) {
if let Some(cli) = self.context.as_mut().unwrap().config_client.as_mut() {
// Text editor
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_TEXT_EDITOR) {
let text_editor: String =
String::from(cli.get_text_editor().as_path().to_string_lossy());
let props = InputPropsBuilder::from(props)
.with_value(text_editor)
.build();
let _ = self.view.update(super::COMPONENT_INPUT_TEXT_EDITOR, props);
}
// Protocol
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DEFAULT_PROTOCOL) {
let protocol: usize = match cli.get_default_protocol() {
FileTransferProtocol::Sftp => 0,
FileTransferProtocol::Scp => 1,
FileTransferProtocol::Ftp(false) => 2,
FileTransferProtocol::Ftp(true) => 3,
};
let props = RadioPropsBuilder::from(props).with_value(protocol).build();
let _ = self
.view
.update(super::COMPONENT_RADIO_DEFAULT_PROTOCOL, props);
}
// Hidden files
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_HIDDEN_FILES) {
let hidden: usize = match cli.get_show_hidden_files() {
true => 0,
false => 1,
};
let props = RadioPropsBuilder::from(props).with_value(hidden).build();
let _ = self.view.update(super::COMPONENT_RADIO_HIDDEN_FILES, props);
}
// Updates
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_UPDATES) {
let updates: usize = match cli.get_check_for_updates() {
true => 0,
false => 1,
};
let props = RadioPropsBuilder::from(props).with_value(updates).build();
let _ = self.view.update(super::COMPONENT_RADIO_UPDATES, props);
}
// Group dirs
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_GROUP_DIRS) {
let dirs: usize = match cli.get_group_dirs() {
Some(GroupDirs::First) => 0,
Some(GroupDirs::Last) => 1,
None => 2,
};
let props = RadioPropsBuilder::from(props).with_value(dirs).build();
let _ = self.view.update(super::COMPONENT_RADIO_GROUP_DIRS, props);
}
// 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_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);
}
// Text editor
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_TEXT_EDITOR) {
let text_editor: String =
String::from(self.config().get_text_editor().as_path().to_string_lossy());
let props = InputPropsBuilder::from(props)
.with_value(text_editor)
.build();
let _ = self.view.update(super::COMPONENT_INPUT_TEXT_EDITOR, props);
}
// Protocol
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_DEFAULT_PROTOCOL) {
let protocol: usize = match self.config().get_default_protocol() {
FileTransferProtocol::Sftp => 0,
FileTransferProtocol::Scp => 1,
FileTransferProtocol::Ftp(false) => 2,
FileTransferProtocol::Ftp(true) => 3,
};
let props = RadioPropsBuilder::from(props).with_value(protocol).build();
let _ = self
.view
.update(super::COMPONENT_RADIO_DEFAULT_PROTOCOL, props);
}
// Hidden files
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_HIDDEN_FILES) {
let hidden: usize = match self.config().get_show_hidden_files() {
true => 0,
false => 1,
};
let props = RadioPropsBuilder::from(props).with_value(hidden).build();
let _ = self.view.update(super::COMPONENT_RADIO_HIDDEN_FILES, props);
}
// Updates
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_UPDATES) {
let updates: usize = match self.config().get_check_for_updates() {
true => 0,
false => 1,
};
let props = RadioPropsBuilder::from(props).with_value(updates).build();
let _ = self.view.update(super::COMPONENT_RADIO_UPDATES, props);
}
// Group dirs
if let Some(props) = self.view.get_props(super::COMPONENT_RADIO_GROUP_DIRS) {
let dirs: usize = match self.config().get_group_dirs() {
Some(GroupDirs::First) => 0,
Some(GroupDirs::Last) => 1,
None => 2,
};
let props = RadioPropsBuilder::from(props).with_value(dirs).build();
let _ = self.view.update(super::COMPONENT_RADIO_GROUP_DIRS, props);
}
// Local File Fmt
if let Some(props) = self.view.get_props(super::COMPONENT_INPUT_LOCAL_FILE_FMT) {
let file_fmt: String = self.config().get_local_file_fmt().unwrap_or_default();
let props = InputPropsBuilder::from(props).with_value(file_fmt).build();
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 = self.config().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);
}
}
@ -360,55 +358,54 @@ impl SetupActivity {
///
/// Collect values from input and put them into the configuration
pub(crate) fn collect_input_values(&mut self) {
if let Some(cli) = self.context.as_mut().unwrap().config_client.as_mut() {
if let Some(Payload::One(Value::Str(editor))) =
self.view.get_state(super::COMPONENT_INPUT_TEXT_EDITOR)
{
cli.set_text_editor(PathBuf::from(editor.as_str()));
}
if let Some(Payload::One(Value::Usize(protocol))) =
self.view.get_state(super::COMPONENT_RADIO_DEFAULT_PROTOCOL)
{
let protocol: FileTransferProtocol = match protocol {
1 => FileTransferProtocol::Scp,
2 => FileTransferProtocol::Ftp(false),
3 => FileTransferProtocol::Ftp(true),
_ => FileTransferProtocol::Sftp,
};
cli.set_default_protocol(protocol);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_HIDDEN_FILES)
{
let show: bool = matches!(opt, 0);
cli.set_show_hidden_files(show);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_UPDATES)
{
let check: bool = matches!(opt, 0);
cli.set_check_for_updates(check);
}
if let Some(Payload::One(Value::Str(fmt))) =
self.view.get_state(super::COMPONENT_INPUT_LOCAL_FILE_FMT)
{
cli.set_local_file_fmt(fmt);
}
if let Some(Payload::One(Value::Str(fmt))) =
self.view.get_state(super::COMPONENT_INPUT_REMOTE_FILE_FMT)
{
cli.set_remote_file_fmt(fmt);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_GROUP_DIRS)
{
let dirs: Option<GroupDirs> = match opt {
0 => Some(GroupDirs::First),
1 => Some(GroupDirs::Last),
_ => None,
};
cli.set_group_dirs(dirs);
}
if let Some(Payload::One(Value::Str(editor))) =
self.view.get_state(super::COMPONENT_INPUT_TEXT_EDITOR)
{
self.config_mut()
.set_text_editor(PathBuf::from(editor.as_str()));
}
if let Some(Payload::One(Value::Usize(protocol))) =
self.view.get_state(super::COMPONENT_RADIO_DEFAULT_PROTOCOL)
{
let protocol: FileTransferProtocol = match protocol {
1 => FileTransferProtocol::Scp,
2 => FileTransferProtocol::Ftp(false),
3 => FileTransferProtocol::Ftp(true),
_ => FileTransferProtocol::Sftp,
};
self.config_mut().set_default_protocol(protocol);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_HIDDEN_FILES)
{
let show: bool = matches!(opt, 0);
self.config_mut().set_show_hidden_files(show);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_UPDATES)
{
let check: bool = matches!(opt, 0);
self.config_mut().set_check_for_updates(check);
}
if let Some(Payload::One(Value::Str(fmt))) =
self.view.get_state(super::COMPONENT_INPUT_LOCAL_FILE_FMT)
{
self.config_mut().set_local_file_fmt(fmt);
}
if let Some(Payload::One(Value::Str(fmt))) =
self.view.get_state(super::COMPONENT_INPUT_REMOTE_FILE_FMT)
{
self.config_mut().set_remote_file_fmt(fmt);
}
if let Some(Payload::One(Value::Usize(opt))) =
self.view.get_state(super::COMPONENT_RADIO_GROUP_DIRS)
{
let dirs: Option<GroupDirs> = match opt {
0 => Some(GroupDirs::First),
1 => Some(GroupDirs::Last),
_ => None,
};
self.config_mut().set_group_dirs(dirs);
}
}
}

View file

@ -275,22 +275,21 @@ impl SetupActivity {
///
/// Reload ssh keys
pub(crate) fn reload_ssh_keys(&mut self) {
if let Some(cli) = self.context.as_ref().unwrap().config_client.as_ref() {
// get props
if let Some(props) = self.view.get_props(super::COMPONENT_LIST_SSH_KEYS) {
// Create texts
let keys: Vec<String> = cli
.iter_ssh_keys()
.map(|x| {
let (addr, username, _) = cli.get_ssh_key(x).ok().unwrap().unwrap();
format!("{} at {}", addr, username)
})
.collect();
let props = BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("SSH Keys")), keys)
.build();
self.view.update(super::COMPONENT_LIST_SSH_KEYS, props);
}
// get props
if let Some(props) = self.view.get_props(super::COMPONENT_LIST_SSH_KEYS) {
// Create texts
let keys: Vec<String> = self
.config()
.iter_ssh_keys()
.map(|x| {
let (addr, username, _) = self.config().get_ssh_key(x).ok().unwrap().unwrap();
format!("{} at {}", addr, username)
})
.collect();
let props = BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("SSH Keys")), keys)
.build();
self.view.update(super::COMPONENT_LIST_SSH_KEYS, props);
}
}
}

View file

@ -46,7 +46,7 @@ use tuirealm::tui::Terminal;
/// Context holds data structures used by the ui
pub struct Context {
pub ft_params: Option<FileTransferParams>,
pub(crate) config_client: Option<ConfigClient>,
pub(crate) config_client: ConfigClient,
pub(crate) store: Store,
pub(crate) input_hnd: InputHandler,
pub(crate) terminal: Terminal<CrosstermBackend<Stdout>>,
@ -71,7 +71,7 @@ impl Context {
///
/// Instantiates a new Context
pub fn new(
config_client: Option<ConfigClient>,
config_client: ConfigClient,
theme_provider: ThemeProvider,
error: Option<String>,
) -> Context {
@ -96,10 +96,10 @@ impl Context {
self.error = Some(err);
}
/// ### get_error
/// ### error
///
/// Get error message and remove it from the context
pub fn get_error(&mut self) -> Option<String> {
pub fn error(&mut self) -> Option<String> {
self.error.take()
}