ConfigClient is an option no more; config client degraded mode
This commit is contained in:
parent
b9cb961da6
commit
e6b44e1461
|
@ -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();
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue