chiark / gitweb /
Store a log of errors in the Client.
authorSimon Tatham <anakin@pobox.com>
Fri, 26 Jan 2024 08:52:14 +0000 (08:52 +0000)
committerSimon Tatham <anakin@pobox.com>
Sat, 27 Jan 2024 09:32:59 +0000 (09:32 +0000)
Each entry is a ClientError and a datestamp. But errors generated in
the client don't _automatically_ get added to this log: they get
passed back to the caller of an individual Client method, the same as
they always have. That way the caller can decide whether the error is
one it can handle in some other way, and only if all else fails, call
client.add_to_error_log().

The next plan is for every client.add_to_error_log() to be followed by
throwing the TUI into the Error Log file.

src/client.rs
src/editor.rs
src/posting.rs
src/tui.rs

index 88afc894ceb4664911006522adab9ab4ffe6ba2e..98e1acf355d78adc35966ace443748007d0cac72 100644 (file)
@@ -1,3 +1,4 @@
+use chrono::{DateTime, Utc};
 use reqwest::Url;
 use std::collections::{HashMap, HashSet, VecDeque};
 use std::fs::File;
@@ -61,6 +62,33 @@ pub struct Feed {
     extend_future: Option<HashMap<String, String>>,
 }
 
+#[derive(Debug)]
+pub struct ErrorLog {
+    items: VecDeque<(ClientError, DateTime<Utc>)>,
+    origin: isize,
+}
+
+impl ErrorLog {
+    fn new() -> Self {
+        ErrorLog {
+            items: VecDeque::new(),
+            origin: 0, // FIXME: eventually prune these at the front
+        }
+    }
+
+    pub fn push_now(&mut self, error: ClientError) {
+        self.items.push_back((error, Utc::now()));
+    }
+
+    pub fn get_bounds(&self) -> (isize, isize) {
+        (self.origin, self.origin + self.items.len() as isize)
+    }
+
+    pub fn get(&self, index: isize) -> (ClientError, DateTime<Utc>) {
+        self.items[(index - self.origin) as usize].clone()
+    }
+}
+
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum Followness {
     NotFollowing,
@@ -118,6 +146,7 @@ pub struct Client {
     instance: Option<Instance>,
     permit_write: bool,
     logfile: Option<File>,
+    error_log: ErrorLog,
 }
 
 #[derive(Debug, PartialEq, Eq, Clone)]
@@ -520,6 +549,7 @@ impl Client {
             instance: None,
             permit_write: false,
             logfile: None,
+            error_log: ErrorLog::new(),
         })
     }
 
@@ -1741,4 +1771,8 @@ impl Client {
             Err(ClientError::from_response(urlstr, rsp))
         }
     }
+
+    pub fn add_to_error_log(&mut self, err: ClientError) {
+        self.error_log.push_now(err);
+    }
 }
index 9f1fbe43a61219cb728326c4f12b9305a029fa15..7c2834cd2fcc055eb12c7b9bb0e2f78ab2949908 100644 (file)
@@ -950,9 +950,10 @@ pub fn get_user_to_examine() -> Box<dyn ActivityState> {
                     ),
 
                     // FIXME: it would be nice to discriminate errors
-                    // better here, and maybe return anything worse
-                    // than 'user not found' to the Error Log
-                    Err(_) => LogicalAction::PopOverlayBeep,
+                    // better here, and do something a bit less
+                    // terrifying for plain "account not found", like
+                    // allowing the user to re-edit
+                    Err(err) => LogicalAction::Error(err),
                 }
             }
         }),
@@ -973,9 +974,10 @@ pub fn get_post_id_to_read() -> Box<dyn ActivityState> {
                     ),
 
                     // FIXME: it would be nice to discriminate errors
-                    // better here, and maybe return anything worse
-                    // than 'post not found' to the Error Log
-                    Err(_) => LogicalAction::PopOverlayBeep,
+                    // better here, and do something a bit less
+                    // terrifying for plain "status not found", like
+                    // allowing the user to re-edit
+                    Err(err) => LogicalAction::Error(err),
                 }
             }
         }),
index 79ae0d937600f4bf1d3b63c73046a107989b86d5..e4d51554b659cb07c2cb870660f7a648d9a2eefb 100644 (file)
@@ -252,7 +252,11 @@ impl PostMenu {
 
         match client.post_status(&self.post) {
             Ok(_) => LogicalAction::Pop,
-            Err(_) => LogicalAction::Beep, // FIXME: report the error!
+
+            // FIXME: if we can identify errors of the form "refusal
+            // to post because of something the user can reasonably
+            // fix", we should stay in this menu and let them retry
+            Err(err) => LogicalAction::Error(err),
         }
     }
 }
index dfe2ddd530b94f6f4a7d607b4566122e56969ef0..1d1c36be826848ed798f56b6031a9ff07aabc627 100644 (file)
@@ -530,8 +530,11 @@ impl Tui {
                             vec![Todo::Stream(feeds_updated)]
                         }
 
-                        // FIXME: errors here should go in the Error Log
-                        _ => Vec::new(),
+                        Err(err) => {
+                            self.client.add_to_error_log(err);
+                            // FIXME: throw user into the Error Log
+                            Vec::new()
+                        }
                     }
                 }
                 Ok(SubthreadEvent::LDBCheckpointTimer) => {
@@ -807,7 +810,10 @@ impl TuiLogicalState {
                     self.pop_overlay_activity();
                     self.activity_state_mut().got_search_expression(dir, regex)
                 }
-                LogicalAction::Error(_) => break PhysicalAction::Beep, // FIXME: Error Log
+                LogicalAction::Error(err) => {
+                    client.add_to_error_log(err);
+                    break PhysicalAction::Beep;
+                }
                 LogicalAction::PostComposed(post) => {
                     let newact = match self.activity_stack.top() {
                         Activity::Compose(
@@ -1098,7 +1104,13 @@ impl TuiLogicalState {
             }
         };
 
-        result.expect("FIXME: need to implement the Error Log here")
+        match result {
+            Ok(state) => state,
+            Err(err) => {
+                client.add_to_error_log(err);
+                panic!("FIXME: need to implement the Error Log here");
+            }
+        }
     }
 
     fn save_ldb(&self) -> Result<(), TuiError> {