From: Simon Tatham Date: Wed, 24 Jan 2024 08:44:59 +0000 (+0000) Subject: Discriminate error types in login. X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=029bf84be0c0d6931d04da99a288b2a72c098469;p=mastodonochrome.git Discriminate error types in login. ClientError and ConfigError should be handled differently. The former suggests you did something wrong in account setup, and you might be able to recover by doing it again properly (e.g. not misspelling the server hostname), so we print the error in the TUI and let the user try again. But the latter probably means you have to sort out your local account setup (e.g. XDG dirs aren't working, or %APPDATA% isn't set), and that's more like the kind of fatal error where you quit the client and fix it externally. So I've made a new LoginError enum to act as the union of those two types and also indicate which you have. ConfigError is now passed back to terminate the TUI completely. This also means I'm no longer abusing TopLevelError for the union error type returned by e.g. confirm_registration_fallible. That was bad in its own way because it prefixed 'mastodonochrome: error:' to the error description as if it were reporting on the terminal. --- diff --git a/src/login.rs b/src/login.rs index 0e9cf4e..6ea1b7a 100644 --- a/src/login.rs +++ b/src/login.rs @@ -6,19 +6,18 @@ use std::str::FromStr; use sys_locale::get_locale; use super::activity_stack::UtilityActivity; -use super::auth::AuthConfig; +use super::auth::{AuthConfig, AuthError}; use super::client::{AppTokenType, Client, ClientError}; use super::coloured_string::*; -use super::config::ConfigLocation; +use super::config::{ConfigError, ConfigLocation}; use super::editor::{ count_edit_chars, EditableMenuLine, EditableMenuLineData, }; use super::text::*; use super::tui::{ - ActivityState, CursorPosition, LogicalAction, OurKey, OurKey::*, TuiError, + ActivityState, CursorPosition, LogicalAction, OurKey, OurKey::*, }; use super::types::{Account, Application, Instance}; -use super::TopLevelError; struct Username { name: String, @@ -191,6 +190,26 @@ impl EditableMenuLineData for Password { } } +pub enum LoginError { + Recoverable(ClientError), + Fatal(String), +} +impl From for LoginError { + fn from(err: ClientError) -> Self { + LoginError::Recoverable(err) + } +} +impl From for LoginError { + fn from(err: ConfigError) -> Self { + LoginError::Fatal(err.to_string()) + } +} +impl From for LoginError { + fn from(err: AuthError) -> Self { + LoginError::Fatal(err.to_string()) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum LoginState { Initial, @@ -568,7 +587,7 @@ impl LoginMenu { fn verify_login_code_fallible( &mut self, client: &mut Client, - ) -> Result<(), TopLevelError> { + ) -> Result<(), LoginError> { let code = self.el_logincode.get_data().trim(); let app = self .application @@ -607,7 +626,7 @@ impl LoginMenu { self.state = LoginState::LoginSuccess; LogicalAction::Nothing } - Err(e) => { + Err(LoginError::Recoverable(e)) => { self.para_login_outcome.clear(); self.para_login_outcome.push_text( ColouredString::uniform( @@ -619,13 +638,14 @@ impl LoginMenu { self.state = LoginState::LoginFailure; LogicalAction::Beep } + Err(LoginError::Fatal(e)) => LogicalAction::Fatal(e.into()), } } fn register_account_fallible( &mut self, client: &mut Client, - ) -> Result<(), TopLevelError> { + ) -> Result<(), LoginError> { // Register the client and get its details let app = client.register_client(true)?; let app_token = client @@ -700,7 +720,7 @@ impl LoginMenu { self.state = LoginState::RegisterFinal; LogicalAction::Nothing } - Err(e) => { + Err(LoginError::Recoverable(e)) => { self.para_login_outcome.clear(); self.para_login_outcome.push_text( ColouredString::uniform( @@ -712,6 +732,7 @@ impl LoginMenu { self.state = LoginState::RegisterFailure; LogicalAction::Beep } + Err(LoginError::Fatal(e)) => LogicalAction::Fatal(e.into()), } } @@ -719,7 +740,7 @@ impl LoginMenu { &mut self, client: &mut Client, with_url: bool, - ) -> Result<(), TopLevelError> { + ) -> Result<(), LoginError> { if with_url { let url = self.el_register_confirm.get_data(); client.register_confirmation(url)?; @@ -734,7 +755,7 @@ impl LoginMenu { ) -> LogicalAction { match self.confirm_registration_fallible(client, with_url) { Ok(_) => LogicalAction::FinishedLoggingIn, - Err(e) => { + Err(LoginError::Recoverable(e)) => { self.para_regconfirm_outcome.clear(); self.para_regconfirm_outcome.push_text( ColouredString::uniform( @@ -745,6 +766,7 @@ impl LoginMenu { ); LogicalAction::Beep } + Err(LoginError::Fatal(e)) => LogicalAction::Fatal(e.into()), } } } @@ -1052,7 +1074,7 @@ impl ActivityState for LoginMenu { pub fn finish_account_setup( client: &mut Client, cfgloc: &ConfigLocation, -) -> Result<(), TuiError> { +) -> Result<(), LoginError> { let account: Account = client.verify_account_credentials()?; let instance: Instance = client.instance()?; diff --git a/src/tui.rs b/src/tui.rs index 83bfe20..74324dc 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -25,10 +25,10 @@ use super::client::{ Client, ClientError, FeedExtend, FeedId, StreamId, StreamUpdate, }; use super::coloured_string::*; -use super::config::ConfigLocation; +use super::config::{ConfigError, ConfigLocation}; use super::editor::*; use super::file::*; -use super::login::{finish_account_setup, login_menu}; +use super::login::{finish_account_setup, login_menu, LoginError}; use super::menu::*; use super::options::*; use super::posting::*; @@ -206,7 +206,7 @@ pub enum OurKey { Del, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct TuiError { message: String, } @@ -221,6 +221,11 @@ impl TuiError { impl super::TopLevelErrorCandidate for TuiError {} +impl From for TuiError { + fn from(message: String) -> Self { + TuiError { message } + } +} impl From for TuiError { fn from(err: std::io::Error) -> Self { TuiError { @@ -235,6 +240,13 @@ impl From for TuiError { } } } +impl From for TuiError { + fn from(err: ConfigError) -> Self { + TuiError { + message: err.to_string(), + } + } +} impl From for TuiError { fn from(err: AuthError) -> Self { TuiError { @@ -256,6 +268,16 @@ impl From for TuiError { } } } +impl From for TuiError { + fn from(err: LoginError) -> Self { + TuiError { + message: match err { + LoginError::Recoverable(err) => err.to_string(), + LoginError::Fatal(msg) => msg, + }, + } + } +} impl std::fmt::Display for TuiError { fn fmt( @@ -567,6 +589,7 @@ pub enum LogicalAction { Exit, Nothing, Error(ClientError), // throw UI into the Error Log + Fatal(TuiError), // exit the client completely and whinge PostComposed(Post), PostReEdit(Post), } @@ -746,6 +769,7 @@ impl TuiLogicalState { logact = match logact { LogicalAction::Beep => break PhysicalAction::Beep, LogicalAction::Exit => break PhysicalAction::Exit, + LogicalAction::Fatal(e) => break PhysicalAction::Error(e), LogicalAction::Nothing => break PhysicalAction::Nothing, LogicalAction::Goto(activity) => { self.activity_stack.goto(activity);