Migrated termscp to tui-realm 1.x

This commit is contained in:
veeso
2021-11-21 10:02:03 +01:00
committed by Christian Visintin
parent 30851a78e8
commit 54b5583d1a
54 changed files with 10994 additions and 7691 deletions

View File

@@ -1,6 +1,6 @@
//! ## AuthActivity
//! ## Update
//!
//! `auth_activity` is the module which implements the authentication activity
//! Update impl
/**
* MIT License
@@ -25,415 +25,222 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{
AuthActivity, FileTransferProtocol, InputMask, COMPONENT_BOOKMARKS_LIST, COMPONENT_INPUT_ADDR,
COMPONENT_INPUT_BOOKMARK_NAME, COMPONENT_INPUT_PASSWORD, COMPONENT_INPUT_PORT,
COMPONENT_INPUT_S3_BUCKET, COMPONENT_INPUT_S3_PROFILE, COMPONENT_INPUT_S3_REGION,
COMPONENT_INPUT_USERNAME, COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
COMPONENT_RADIO_BOOKMARK_DEL_RECENT, COMPONENT_RADIO_BOOKMARK_SAVE_PWD,
COMPONENT_RADIO_INSTALL_UPDATE, COMPONENT_RADIO_PROTOCOL, COMPONENT_RADIO_QUIT,
COMPONENT_RECENTS_LIST, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP, COMPONENT_TEXT_INFO,
COMPONENT_TEXT_NEW_VERSION_NOTES, COMPONENT_TEXT_SIZE_ERR, COMPONENT_TEXT_WAIT,
};
use crate::ui::keymap::*;
use tui_realm_stdlib::InputPropsBuilder;
use tuirealm::{Msg, Payload, PropsBuilder, Update, Value};
use super::{AuthActivity, ExitReason, Id, InputMask, Msg, Update};
// -- update
use tuirealm::{State, StateValue};
impl Update for AuthActivity {
/// ### update
///
/// Update auth activity model based on msg
/// The function exits when returns None
fn update(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
let ref_msg: Option<(&str, &Msg)> = msg.as_ref().map(|(s, msg)| (s.as_str(), msg));
// Match msg
match ref_msg {
None => None, // Exit after None
Some(msg) => match msg {
// Focus ( DOWN )
(COMPONENT_RADIO_PROTOCOL, key) if key == &MSG_KEY_DOWN => {
// Give focus based on current mask
match self.input_mask() {
InputMask::Generic => self.view.active(COMPONENT_INPUT_ADDR),
InputMask::AwsS3 => self.view.active(COMPONENT_INPUT_S3_BUCKET),
};
None
}
// -- generic mask (DOWN)
(COMPONENT_INPUT_ADDR, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_PORT);
None
}
(COMPONENT_INPUT_PORT, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_USERNAME);
None
}
(COMPONENT_INPUT_USERNAME, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_PASSWORD);
None
}
(COMPONENT_INPUT_PASSWORD, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_RADIO_PROTOCOL);
None
}
// -- s3 mask (DOWN)
(COMPONENT_INPUT_S3_BUCKET, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_S3_REGION);
None
}
(COMPONENT_INPUT_S3_REGION, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_INPUT_S3_PROFILE);
None
}
(COMPONENT_INPUT_S3_PROFILE, key) if key == &MSG_KEY_DOWN => {
self.view.active(COMPONENT_RADIO_PROTOCOL);
None
}
// Focus ( UP )
// -- generic (UP)
(COMPONENT_INPUT_PASSWORD, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_INPUT_USERNAME);
None
}
(COMPONENT_INPUT_USERNAME, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_INPUT_PORT);
None
}
(COMPONENT_INPUT_PORT, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_INPUT_ADDR);
None
}
(COMPONENT_INPUT_ADDR, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_RADIO_PROTOCOL);
None
}
// -- s3 (UP)
(COMPONENT_INPUT_S3_BUCKET, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_RADIO_PROTOCOL);
None
}
(COMPONENT_INPUT_S3_REGION, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_INPUT_S3_BUCKET);
None
}
(COMPONENT_INPUT_S3_PROFILE, key) if key == &MSG_KEY_UP => {
self.view.active(COMPONENT_INPUT_S3_REGION);
None
}
(COMPONENT_RADIO_PROTOCOL, key) if key == &MSG_KEY_UP => {
// Give focus based on current mask
match self.input_mask() {
InputMask::Generic => self.view.active(COMPONENT_INPUT_PASSWORD),
InputMask::AwsS3 => self.view.active(COMPONENT_INPUT_S3_PROFILE),
};
None
}
// Protocol - On Change
(COMPONENT_RADIO_PROTOCOL, Msg::OnChange(Payload::One(Value::Usize(protocol)))) => {
// If port is standard, update the current port with default for selected protocol
let protocol: FileTransferProtocol = Self::protocol_opt_to_enum(*protocol);
// Get port
let port: u16 = self.get_input_port();
match Self::is_port_standard(port) {
false => None, // Return None
true => {
self.update_input_port(Self::get_default_port_for_protocol(protocol))
}
impl Update<Msg> for AuthActivity {
fn update(&mut self, msg: Option<Msg>) -> Option<Msg> {
self.redraw = true;
match msg.unwrap_or(Msg::None) {
Msg::AddressBlurDown => {
assert!(self.app.active(&Id::Port).is_ok());
}
Msg::AddressBlurUp => {
assert!(self.app.active(&Id::Protocol).is_ok());
}
Msg::BookmarksListBlur => {
assert!(self.app.active(&Id::RecentsList).is_ok());
}
Msg::BookmarkNameBlur => {
assert!(self.app.active(&Id::BookmarkSavePassword).is_ok());
}
Msg::BookmarksTabBlur => {
assert!(self.app.active(&Id::Protocol).is_ok());
}
Msg::CloseDeleteBookmark => {
assert!(self.app.umount(&Id::DeleteBookmarkPopup).is_ok());
}
Msg::CloseDeleteRecent => {
assert!(self.app.umount(&Id::DeleteRecentPopup).is_ok());
}
Msg::CloseErrorPopup => {
self.umount_error();
}
Msg::CloseInfoPopup => {
self.umount_info();
}
Msg::CloseInstallUpdatePopup => {
assert!(self.app.umount(&Id::NewVersionChangelog).is_ok());
assert!(self.app.umount(&Id::InstallUpdatePopup).is_ok());
}
Msg::CloseKeybindingsPopup => {
self.umount_help();
}
Msg::CloseQuitPopup => self.umount_quit(),
Msg::CloseSaveBookmark => {
assert!(self.app.umount(&Id::BookmarkName).is_ok());
assert!(self.app.umount(&Id::BookmarkSavePassword).is_ok());
}
Msg::Connect => {
match self.collect_host_params() {
Err(err) => {
// mount error
self.mount_error(err);
}
Ok(params) => {
self.save_recent();
// Set file transfer params to context
self.context_mut().set_ftparams(params);
// Set exit reason
self.exit_reason = Some(super::ExitReason::Connect);
}
}
// Bookmarks commands
// <RIGHT> / <LEFT>
(COMPONENT_BOOKMARKS_LIST, key) if key == &MSG_KEY_RIGHT => {
// Give focus to recents
self.view.active(COMPONENT_RECENTS_LIST);
None
}
(COMPONENT_RECENTS_LIST, key) if key == &MSG_KEY_LEFT => {
// Give focus to bookmarks
self.view.active(COMPONENT_BOOKMARKS_LIST);
None
}
// <DEL | 'E'>
(COMPONENT_BOOKMARKS_LIST, key)
if key == &MSG_KEY_DEL || key == &MSG_KEY_CHAR_E =>
{
// Show delete popup
self.mount_bookmark_del_dialog();
None
}
(COMPONENT_RECENTS_LIST, key) if key == &MSG_KEY_DEL || key == &MSG_KEY_CHAR_E => {
// Show delete popup
self.mount_recent_del_dialog();
None
}
// Enter
(COMPONENT_BOOKMARKS_LIST, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
self.load_bookmark(*idx);
// Give focus to input password (or to protocol if not generic)
self.view.active(match self.input_mask() {
InputMask::Generic => COMPONENT_INPUT_PASSWORD,
InputMask::AwsS3 => COMPONENT_INPUT_S3_BUCKET,
});
None
}
(COMPONENT_RECENTS_LIST, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
self.load_recent(*idx);
// Give focus to input password
self.view.active(match self.input_mask() {
InputMask::Generic => COMPONENT_INPUT_PASSWORD,
InputMask::AwsS3 => COMPONENT_INPUT_S3_BUCKET,
});
None
}
// Bookmark radio
// Del bookmarks
(
COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
Msg::OnSubmit(Payload::One(Value::Usize(index))),
) => {
// hide bookmark delete
}
Msg::DeleteBookmark => {
if let Ok(State::One(StateValue::Usize(idx))) = self.app.state(&Id::BookmarksList) {
// Umount dialog
self.umount_bookmark_del_dialog();
// Index must be 0 => YES
match *index {
0 => {
// Get selected bookmark
match self.view.get_state(COMPONENT_BOOKMARKS_LIST) {
Some(Payload::One(Value::Usize(index))) => {
// Delete bookmark
self.del_bookmark(index);
// Update bookmarks
self.view_bookmarks()
}
_ => None,
}
}
_ => None,
}
}
(
COMPONENT_RADIO_BOOKMARK_DEL_RECENT,
Msg::OnSubmit(Payload::One(Value::Usize(index))),
) => {
// hide bookmark delete
self.umount_recent_del_dialog();
// Index must be 0 => YES
match *index {
0 => {
// Get selected bookmark
match self.view.get_state(COMPONENT_RECENTS_LIST) {
Some(Payload::One(Value::Usize(index))) => {
// Delete recent
self.del_recent(index);
// Update bookmarks
self.view_recent_connections()
}
_ => None,
}
}
_ => None,
}
}
// <ESC> hide tab
(COMPONENT_RADIO_BOOKMARK_DEL_RECENT, key) if key == &MSG_KEY_ESC => {
self.umount_recent_del_dialog();
None
}
(COMPONENT_RADIO_BOOKMARK_DEL_RECENT, _) => None,
(COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, key) if key == &MSG_KEY_ESC => {
self.umount_bookmark_del_dialog();
None
}
(COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK, _) => None,
// Error message
(COMPONENT_TEXT_ERROR, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
// Umount text error
self.umount_error();
None
}
// -- Text info
(COMPONENT_TEXT_INFO, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
// Umount text info
self.umount_info();
None
}
(COMPONENT_TEXT_ERROR, _) | (COMPONENT_TEXT_INFO, _) => None,
// -- Text wait
(COMPONENT_TEXT_WAIT, _) => None,
// -- Release notes
(COMPONENT_TEXT_NEW_VERSION_NOTES, key) if key == &MSG_KEY_ESC => {
// Umount release notes
self.umount_release_notes();
None
}
(COMPONENT_TEXT_NEW_VERSION_NOTES, key) if key == &MSG_KEY_TAB => {
// Focus to radio update
self.view.active(COMPONENT_RADIO_INSTALL_UPDATE);
None
}
(COMPONENT_TEXT_NEW_VERSION_NOTES, _) => None,
// -- Install update radio
(COMPONENT_RADIO_INSTALL_UPDATE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Install update
self.install_update();
None
}
(COMPONENT_RADIO_INSTALL_UPDATE, Msg::OnSubmit(Payload::One(Value::Usize(1)))) => {
// Umount
self.umount_release_notes();
None
}
(COMPONENT_RADIO_INSTALL_UPDATE, key) if key == &MSG_KEY_TAB => {
// Focus to changelog
self.view.active(COMPONENT_TEXT_NEW_VERSION_NOTES);
None
}
(COMPONENT_RADIO_INSTALL_UPDATE, _) => None,
// Help
(_, key) if key == &MSG_KEY_CTRL_H => {
// Show help
self.mount_help();
None
}
// Release notes
(_, key) if key == &MSG_KEY_CTRL_R => {
// Show release notes
self.mount_release_notes();
None
}
(COMPONENT_TEXT_HELP, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
// Hide text help
self.umount_help();
None
}
(COMPONENT_TEXT_HELP, _) => None,
// Enter setup
(_, key) if key == &MSG_KEY_CTRL_C => {
self.exit_reason = Some(super::ExitReason::EnterSetup);
None
}
// Save bookmark; show popup
(_, key) if key == &MSG_KEY_CTRL_S => {
// Show popup
self.mount_bookmark_save_dialog();
// Give focus to bookmark name
self.view.active(COMPONENT_INPUT_BOOKMARK_NAME);
None
}
(COMPONENT_INPUT_BOOKMARK_NAME, key) if key == &MSG_KEY_DOWN => {
// Give focus to pwd
self.view.active(COMPONENT_RADIO_BOOKMARK_SAVE_PWD);
None
}
(COMPONENT_RADIO_BOOKMARK_SAVE_PWD, key) if key == &MSG_KEY_UP => {
// Give focus to pwd
self.view.active(COMPONENT_INPUT_BOOKMARK_NAME);
None
}
// Save bookmark
(COMPONENT_INPUT_BOOKMARK_NAME, Msg::OnSubmit(_))
| (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, Msg::OnSubmit(_)) => {
// Get values
let bookmark_name: String =
match self.view.get_state(COMPONENT_INPUT_BOOKMARK_NAME) {
Some(Payload::One(Value::Str(s))) => s,
_ => String::new(),
};
let save_pwd: bool = matches!(
self.view.get_state(COMPONENT_RADIO_BOOKMARK_SAVE_PWD),
Some(Payload::One(Value::Usize(0)))
);
// Save bookmark
if !bookmark_name.is_empty() {
self.save_bookmark(bookmark_name, save_pwd);
}
// Umount popup
self.umount_bookmark_save_dialog();
// Reload bookmarks
// Delete bookmark
self.del_bookmark(idx);
// Update bookmarks
self.view_bookmarks()
}
// Hide save bookmark
(COMPONENT_INPUT_BOOKMARK_NAME, key) | (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, key)
if key == &MSG_KEY_ESC =>
{
// Umount popup
self.umount_bookmark_save_dialog();
None
}
(COMPONENT_INPUT_BOOKMARK_NAME, _) | (COMPONENT_RADIO_BOOKMARK_SAVE_PWD, _) => None,
// Quit dialog
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(choice)))) => {
// If choice is 0, quit termscp
if *choice == 0 {
self.exit_reason = Some(super::ExitReason::Quit);
}
self.umount_quit();
None
}
(COMPONENT_RADIO_QUIT, key) if key == &MSG_KEY_ESC => {
self.umount_quit();
None
}
// -- text size error; block everything
(COMPONENT_TEXT_SIZE_ERR, _) => None,
// <TAB> bookmarks
(COMPONENT_BOOKMARKS_LIST, key) | (COMPONENT_RECENTS_LIST, key)
if key == &MSG_KEY_TAB =>
{
// Give focus to address
self.view.active(COMPONENT_RADIO_PROTOCOL);
None
}
// Any <TAB>, go to bookmarks
(_, key) if key == &MSG_KEY_TAB => {
self.view.active(COMPONENT_BOOKMARKS_LIST);
None
}
// On submit on any unhandled (connect)
(_, Msg::OnSubmit(_)) => self.on_unhandled_submit(),
(_, key) if key == &MSG_KEY_ENTER => self.on_unhandled_submit(),
// <ESC> => Quit
(_, key) if key == &MSG_KEY_ESC => {
self.mount_quit();
None
}
(_, _) => None, // Ignore other events
},
}
}
}
impl AuthActivity {
fn update_input_port(&mut self, port: u16) -> Option<(String, Msg)> {
match self.view.get_props(COMPONENT_INPUT_PORT) {
None => None,
Some(props) => {
let props = InputPropsBuilder::from(props)
.with_value(port.to_string())
.build();
self.view.update(COMPONENT_INPUT_PORT, props)
}
}
}
fn on_unhandled_submit(&mut self) -> Option<(String, Msg)> {
// Validate fields
match self.collect_host_params() {
Err(err) => {
// mount error
self.mount_error(err);
Msg::DeleteRecent => {
if let Ok(State::One(StateValue::Usize(idx))) = self.app.state(&Id::RecentsList) {
// Umount dialog
self.umount_recent_del_dialog();
// Delete recent
self.del_recent(idx);
// Update recents
self.view_recent_connections();
}
}
Ok(params) => {
self.save_recent();
// Set file transfer params to context
self.context_mut().set_ftparams(params);
// Set exit reason
self.exit_reason = Some(super::ExitReason::Connect);
Msg::EnterSetup => {
self.exit_reason = Some(ExitReason::EnterSetup);
}
Msg::InstallUpdate => {
self.install_update();
}
Msg::LoadBookmark(i) => {
self.load_bookmark(i);
// Give focus to input password (or to protocol if not generic)
assert!(self
.app
.active(match self.input_mask() {
InputMask::Generic => &Id::Password,
InputMask::AwsS3 => &Id::S3Bucket,
})
.is_ok());
}
Msg::LoadRecent(i) => {
self.load_recent(i);
// Give focus to input password (or to protocol if not generic)
assert!(self
.app
.active(match self.input_mask() {
InputMask::Generic => &Id::Password,
InputMask::AwsS3 => &Id::S3Bucket,
})
.is_ok());
}
Msg::ParamsFormBlur => {
assert!(self.app.active(&Id::BookmarksList).is_ok());
}
Msg::PasswordBlurDown => {
assert!(self.app.active(&Id::Protocol).is_ok());
}
Msg::PasswordBlurUp => {
assert!(self.app.active(&Id::Username).is_ok());
}
Msg::PortBlurDown => {
assert!(self.app.active(&Id::Username).is_ok());
}
Msg::PortBlurUp => {
assert!(self.app.active(&Id::Address).is_ok());
}
Msg::ProtocolBlurDown => {
assert!(self
.app
.active(match self.input_mask() {
InputMask::Generic => &Id::Address,
InputMask::AwsS3 => &Id::S3Bucket,
})
.is_ok());
}
Msg::ProtocolBlurUp => {
assert!(self
.app
.active(match self.input_mask() {
InputMask::Generic => &Id::Password,
InputMask::AwsS3 => &Id::S3Profile,
})
.is_ok());
}
Msg::ProtocolChanged(protocol) => {
self.protocol = protocol;
// Update port
let port: u16 = self.get_input_port();
if Self::is_port_standard(port) {
self.mount_port(Self::get_default_port_for_protocol(protocol));
}
}
Msg::Quit => {
self.exit_reason = Some(ExitReason::Quit);
}
Msg::RececentsListBlur => {
assert!(self.app.active(&Id::BookmarksList).is_ok());
}
Msg::S3BucketBlurDown => {
assert!(self.app.active(&Id::S3Region).is_ok());
}
Msg::S3BucketBlurUp => {
assert!(self.app.active(&Id::Protocol).is_ok());
}
Msg::S3RegionBlurDown => {
assert!(self.app.active(&Id::S3Profile).is_ok());
}
Msg::S3RegionBlurUp => {
assert!(self.app.active(&Id::S3Bucket).is_ok());
}
Msg::S3ProfileBlurDown => {
assert!(self.app.active(&Id::Protocol).is_ok());
}
Msg::S3ProfileBlurUp => {
assert!(self.app.active(&Id::S3Region).is_ok());
}
Msg::SaveBookmark => {
// get bookmark name
let (name, save_password) = self.get_new_bookmark();
// Save bookmark
if !name.is_empty() {
self.save_bookmark(name, save_password);
}
// Umount popup
self.umount_bookmark_save_dialog();
// Reload bookmarks
self.view_bookmarks()
}
Msg::SaveBookmarkPasswordBlur => {
assert!(self.app.active(&Id::BookmarkName).is_ok());
}
Msg::ShowDeleteBookmarkPopup => {
self.mount_bookmark_del_dialog();
}
Msg::ShowDeleteRecentPopup => {
self.mount_recent_del_dialog();
}
Msg::ShowKeybindingsPopup => {
self.mount_keybindings();
}
Msg::ShowQuitPopup => {
self.mount_quit();
}
Msg::ShowReleaseNotes => {
self.mount_release_notes();
}
Msg::ShowSaveBookmarkPopup => {
self.mount_bookmark_save_dialog();
}
Msg::UsernameBlurDown => {
assert!(self.app.active(&Id::Password).is_ok());
}
Msg::UsernameBlurUp => {
assert!(self.app.active(&Id::Port).is_ok());
}
Msg::None => {}
}
// Return None
None
}
}