From e6b44e146192ce6c81fb5274ba72cc39a8716f05 Mon Sep 17 00:00:00 2001 From: veeso Date: Thu, 8 Jul 2021 15:07:24 +0200 Subject: [PATCH] ConfigClient is an option no more; config client degraded mode --- src/activity_manager.rs | 6 +- src/system/config_client.rs | 60 +++++- src/ui/activities/auth/mod.rs | 53 +++--- src/ui/activities/auth/view.rs | 11 +- src/ui/activities/filetransfer/misc.rs | 5 +- src/ui/activities/filetransfer/mod.rs | 9 +- src/ui/activities/setup/actions.rs | 153 ++++++++------- src/ui/activities/setup/config.rs | 84 +++------ src/ui/activities/setup/mod.rs | 11 +- src/ui/activities/setup/view/setup.rs | 229 +++++++++++------------ src/ui/activities/setup/view/ssh_keys.rs | 31 ++- src/ui/context.rs | 8 +- 12 files changed, 350 insertions(+), 310 deletions(-) diff --git a/src/activity_manager.rs b/src/activity_manager.rs index 19dfb65..4fc21e5 100644 --- a/src/activity_manager.rs +++ b/src/activity_manager.rs @@ -67,12 +67,12 @@ impl ActivityManager { pub fn new(local_dir: &Path, interval: Duration) -> Result { // Prepare Context // Initialize configuration client - let (config_client, error): (Option, Option) = + let (config_client, error): (ConfigClient, Option) = 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(); diff --git a/src/system/config_client.rs b/src/system/config_client.rs index fd0a481..12a77d3 100644 --- a/src/system/config_client.rs +++ b/src/system/config_client.rs @@ -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 { + pub fn new(config_path: &Path, ssh_key_dir: &Path) -> Result { // 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> { + 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!( diff --git a/src/ui/activities/auth/mod.rs b/src/ui/activities/auth/mod.rs index 0a53272..e71d997 100644 --- a/src/ui/activities/auth/mod.rs +++ b/src/ui/activities/auth/mod.rs @@ -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 = 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"); diff --git a/src/ui/activities/auth/view.rs b/src/ui/activities/auth/view.rs index b256915..e196d58 100644 --- a/src/ui/activities/auth/view.rs +++ b/src/ui/activities/auth/view.rs @@ -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, diff --git a/src/ui/activities/filetransfer/misc.rs b/src/ui/activities/filetransfer/misc.rs index ca14fad..a238a7e 100644 --- a/src/ui/activities/filetransfer/misc.rs +++ b/src/ui/activities/filetransfer/misc.rs @@ -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 diff --git a/src/ui/activities/filetransfer/mod.rs b/src/ui/activities/filetransfer/mod.rs index ca4bbc5..fd97ca7 100644 --- a/src/ui/activities/filetransfer/mod.rs +++ b/src/ui/activities/filetransfer/mod.rs @@ -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); } diff --git a/src/ui/activities/setup/actions.rs b/src/ui/activities/setup/actions.rs index 5b71dda..7c1e56d 100644 --- a/src/ui/activities/setup/actions.rs +++ b/src/ui/activities/setup/actions.rs @@ -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 = 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 = 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 = 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 = 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 diff --git a/src/ui/activities/setup/config.rs b/src/ui/activities/setup/config.rs index 6e65b36..5e27dfd 100644 --- a/src/ui/activities/setup/config.rs +++ b/src/ui/activities/setup/config.rs @@ -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)) } } diff --git a/src/ui/activities/setup/mod.rs b/src/ui/activities/setup/mod.rs index 0a19893..21299f2 100644 --- a/src/ui/activities/setup/mod.rs +++ b/src/ui/activities/setup/mod.rs @@ -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()); } } diff --git a/src/ui/activities/setup/view/setup.rs b/src/ui/activities/setup/view/setup.rs index dcbb68c..98aa239 100644 --- a/src/ui/activities/setup/view/setup.rs +++ b/src/ui/activities/setup/view/setup.rs @@ -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 = 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 = match opt { + 0 => Some(GroupDirs::First), + 1 => Some(GroupDirs::Last), + _ => None, + }; + self.config_mut().set_group_dirs(dirs); } } } diff --git a/src/ui/activities/setup/view/ssh_keys.rs b/src/ui/activities/setup/view/ssh_keys.rs index 107846f..499fff8 100644 --- a/src/ui/activities/setup/view/ssh_keys.rs +++ b/src/ui/activities/setup/view/ssh_keys.rs @@ -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 = 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 = 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); } } } diff --git a/src/ui/context.rs b/src/ui/context.rs index b111bfe..0364435 100644 --- a/src/ui/context.rs +++ b/src/ui/context.rs @@ -46,7 +46,7 @@ use tuirealm::tui::Terminal; /// Context holds data structures used by the ui pub struct Context { pub ft_params: Option, - pub(crate) config_client: Option, + pub(crate) config_client: ConfigClient, pub(crate) store: Store, pub(crate) input_hnd: InputHandler, pub(crate) terminal: Terminal>, @@ -71,7 +71,7 @@ impl Context { /// /// Instantiates a new Context pub fn new( - config_client: Option, + config_client: ConfigClient, theme_provider: ThemeProvider, error: Option, ) -> 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 { + pub fn error(&mut self) -> Option { self.error.take() }