chiark / gitweb /
Discriminate error types in login.
authorSimon Tatham <anakin@pobox.com>
Wed, 24 Jan 2024 08:44:59 +0000 (08:44 +0000)
committerSimon Tatham <anakin@pobox.com>
Wed, 24 Jan 2024 20:08:37 +0000 (20:08 +0000)
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.

src/login.rs
src/tui.rs

index 0e9cf4ebf81c5f9a85fcba2f459cd8d0abd51d48..6ea1b7ae58b4adad46948e3789d8d301baf74167 100644 (file)
@@ -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<ClientError> for LoginError {
+    fn from(err: ClientError) -> Self {
+        LoginError::Recoverable(err)
+    }
+}
+impl From<ConfigError> for LoginError {
+    fn from(err: ConfigError) -> Self {
+        LoginError::Fatal(err.to_string())
+    }
+}
+impl From<AuthError> 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()?;
 
index 83bfe20c0a9a19bb2cdd096f38b971cc42cb4100..74324dcbdd4aea298d5cbac0dd1c5b0567cd51b1 100644 (file)
@@ -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<String> for TuiError {
+    fn from(message: String) -> Self {
+        TuiError { message }
+    }
+}
 impl From<std::io::Error> for TuiError {
     fn from(err: std::io::Error) -> Self {
         TuiError {
@@ -235,6 +240,13 @@ impl From<ClientError> for TuiError {
         }
     }
 }
+impl From<ConfigError> for TuiError {
+    fn from(err: ConfigError) -> Self {
+        TuiError {
+            message: err.to_string(),
+        }
+    }
+}
 impl From<AuthError> for TuiError {
     fn from(err: AuthError) -> Self {
         TuiError {
@@ -256,6 +268,16 @@ impl From<serde_json::Error> for TuiError {
         }
     }
 }
+impl From<LoginError> 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);