Beautify error logging

Nathan Sobo and Antonio Scandurra created

Co-Authored-By: Antonio Scandurra <me@as-cii.com>

Change summary

zed/src/channel.rs  | 11 ++++---
zed/src/util.rs     | 62 ++++++++++++++++++++++++++++++++++++++++++----
zed/src/worktree.rs | 46 +++++++++++++++++++---------------
3 files changed, 88 insertions(+), 31 deletions(-)

Detailed changes

zed/src/channel.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     rpc::{self, Client},
-    util::log_async_errors,
+    util::TryFutureExt,
 };
 use anyhow::{anyhow, Context, Result};
 use gpui::{
@@ -76,7 +76,7 @@ impl ChannelList {
     pub fn new(rpc: Arc<rpc::Client>, cx: &mut ModelContext<Self>) -> Self {
         cx.spawn(|this, mut cx| {
             let rpc = rpc.clone();
-            log_async_errors(async move {
+            async move {
                 let response = rpc
                     .request(proto::GetChannels {})
                     .await
@@ -87,7 +87,8 @@ impl ChannelList {
                     cx.notify();
                 });
                 Ok(())
-            })
+            }
+            .log_err()
         })
         .detach();
         Self {
@@ -185,7 +186,7 @@ impl Channel {
         });
         let rpc = self.rpc.clone();
         cx.spawn(|this, mut cx| {
-            log_async_errors(async move {
+            async move {
                 let request = rpc.request(proto::SendChannelMessage { channel_id, body });
                 let response = request.await?;
                 this.update(&mut cx, |this, cx| {
@@ -205,7 +206,7 @@ impl Channel {
                     }
                 });
                 Ok(())
-            })
+            }.log_err()
         })
         .detach();
         cx.notify();

zed/src/util.rs 🔗

@@ -1,7 +1,11 @@
-use futures::Future;
+use futures::{Future};
 pub use gpui::sum_tree::Bias;
 use rand::prelude::*;
-use std::cmp::Ordering;
+use std::{
+    cmp::Ordering,
+    pin::Pin,
+    task::{Context, Poll},
+};
 
 pub fn post_inc(value: &mut usize) -> usize {
     let prev = *value;
@@ -60,12 +64,58 @@ impl<T: Rng> Iterator for RandomCharIter<T> {
     }
 }
 
-pub async fn log_async_errors<F>(f: F)
+pub trait ResultExt {
+    type Ok;
+
+    fn log_err(self) -> Option<Self::Ok>;
+}
+
+impl<T> ResultExt for anyhow::Result<T> {
+    type Ok = T;
+
+    fn log_err(self) -> Option<T> {
+        match self {
+            Ok(value) => Some(value),
+            Err(error) => {
+                log::error!("{:?}", error);
+                None
+            }
+        }
+    }
+}
+
+pub trait TryFutureExt {
+    fn log_err(self) -> LogErrorFuture<Self>
+    where
+        Self: Sized;
+}
+
+impl<F, T> TryFutureExt for F
 where
-    F: Future<Output = anyhow::Result<()>>,
+    F: Future<Output = anyhow::Result<T>>,
 {
-    if let Err(error) = f.await {
-        log::error!("{}", error)
+    fn log_err(self) -> LogErrorFuture<Self>
+    where
+        Self: Sized,
+    {
+        LogErrorFuture(self)
+    }
+}
+
+pub struct LogErrorFuture<F>(F);
+
+impl<F, T> Future for LogErrorFuture<F>
+where
+    F: Future<Output = anyhow::Result<T>>,
+{
+    type Output = Option<T>;
+
+    fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) };
+        match inner.poll(cx) {
+            Poll::Ready(output) => Poll::Ready(output.log_err()),
+            Poll::Pending => Poll::Pending,
+        }
     }
 }
 

zed/src/worktree.rs 🔗

@@ -9,7 +9,7 @@ use crate::{
     language::LanguageRegistry,
     rpc::{self, proto},
     time::{self, ReplicaId},
-    util::{log_async_errors, Bias},
+    util::{Bias, TryFutureExt},
 };
 use ::ignore::gitignore::Gitignore;
 use anyhow::{anyhow, Result};
@@ -330,10 +330,13 @@ impl Worktree {
             .open_remote_buffer(envelope, cx);
 
         cx.background()
-            .spawn(log_async_errors(async move {
-                rpc.respond(receipt, response.await?).await?;
-                Ok(())
-            }))
+            .spawn(
+                async move {
+                    rpc.respond(receipt, response.await?).await?;
+                    Ok(())
+                }
+                .log_err(),
+            )
             .detach();
 
         Ok(())
@@ -465,22 +468,25 @@ impl Worktree {
         });
 
         cx.background()
-            .spawn(log_async_errors(async move {
-                let (version, mtime) = save.await?;
-
-                rpc.respond(
-                    receipt,
-                    proto::BufferSaved {
-                        worktree_id,
-                        buffer_id,
-                        version: (&version).into(),
-                        mtime: Some(mtime.into()),
-                    },
-                )
-                .await?;
+            .spawn(
+                async move {
+                    let (version, mtime) = save.await?;
 
-                Ok(())
-            }))
+                    rpc.respond(
+                        receipt,
+                        proto::BufferSaved {
+                            worktree_id,
+                            buffer_id,
+                            version: (&version).into(),
+                            mtime: Some(mtime.into()),
+                        },
+                    )
+                    .await?;
+
+                    Ok(())
+                }
+                .log_err(),
+            )
             .detach();
 
         Ok(())