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

View File

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

View File

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

View File

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

View File

@@ -103,10 +103,7 @@ impl FileTransferActivity {
/// ///
/// Set text editor to use /// Set text editor to use
pub(super) fn setup_text_editor(&self) { pub(super) fn setup_text_editor(&self) {
if let Some(config_cli) = self.context.as_ref().unwrap().config_client.as_ref() { env::set_var("EDITOR", self.config().get_text_editor());
// Set text editor
env::set_var("EDITOR", config_cli.get_text_editor());
}
} }
/// ### read_input_event /// ### 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 /// ### theme
/// ///
/// Get a reference to `Theme` /// Get a reference to `Theme`
@@ -249,7 +256,7 @@ impl Activity for FileTransferActivity {
self.init(); self.init();
debug!("Initialized view"); debug!("Initialized view");
// Verify error state from context // 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); error!("Fatal error on create: {}", err);
self.mount_fatal(&err); self.mount_fatal(&err);
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -275,22 +275,21 @@ impl SetupActivity {
/// ///
/// Reload ssh keys /// Reload ssh keys
pub(crate) fn reload_ssh_keys(&mut self) { pub(crate) fn reload_ssh_keys(&mut self) {
if let Some(cli) = self.context.as_ref().unwrap().config_client.as_ref() { // get props
// get props if let Some(props) = self.view.get_props(super::COMPONENT_LIST_SSH_KEYS) {
if let Some(props) = self.view.get_props(super::COMPONENT_LIST_SSH_KEYS) { // Create texts
// Create texts let keys: Vec<String> = self
let keys: Vec<String> = cli .config()
.iter_ssh_keys() .iter_ssh_keys()
.map(|x| { .map(|x| {
let (addr, username, _) = cli.get_ssh_key(x).ok().unwrap().unwrap(); let (addr, username, _) = self.config().get_ssh_key(x).ok().unwrap().unwrap();
format!("{} at {}", addr, username) format!("{} at {}", addr, username)
}) })
.collect(); .collect();
let props = BookmarkListPropsBuilder::from(props) let props = BookmarkListPropsBuilder::from(props)
.with_bookmarks(Some(String::from("SSH Keys")), keys) .with_bookmarks(Some(String::from("SSH Keys")), keys)
.build(); .build();
self.view.update(super::COMPONENT_LIST_SSH_KEYS, props); 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 /// Context holds data structures used by the ui
pub struct Context { pub struct Context {
pub ft_params: Option<FileTransferParams>, pub ft_params: Option<FileTransferParams>,
pub(crate) config_client: Option<ConfigClient>, pub(crate) config_client: ConfigClient,
pub(crate) store: Store, pub(crate) store: Store,
pub(crate) input_hnd: InputHandler, pub(crate) input_hnd: InputHandler,
pub(crate) terminal: Terminal<CrosstermBackend<Stdout>>, pub(crate) terminal: Terminal<CrosstermBackend<Stdout>>,
@@ -71,7 +71,7 @@ impl Context {
/// ///
/// Instantiates a new Context /// Instantiates a new Context
pub fn new( pub fn new(
config_client: Option<ConfigClient>, config_client: ConfigClient,
theme_provider: ThemeProvider, theme_provider: ThemeProvider,
error: Option<String>, error: Option<String>,
) -> Context { ) -> Context {
@@ -96,10 +96,10 @@ impl Context {
self.error = Some(err); self.error = Some(err);
} }
/// ### get_error /// ### error
/// ///
/// Get error message and remove it from the context /// 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() self.error.take()
} }