You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by al...@apache.org on 2023/01/19 22:08:53 UTC

[arrow-rs] branch master updated: Implement `std::error::Error::source` for `ArrowError` and `FlightError` (#3567)

This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 9bb6aaf38 Implement `std::error::Error::source` for `ArrowError` and `FlightError` (#3567)
9bb6aaf38 is described below

commit 9bb6aaf38c17d63b8b6201f03cd5adb330b64fa7
Author: Andrew Lamb <an...@nerdnetworks.org>
AuthorDate: Thu Jan 19 23:08:48 2023 +0100

    Implement `std::error::Error::source` for `ArrowError` and `FlightError` (#3567)
    
    * impl Error::source for ArrowError
    
    * Add source() for FlightError
    
    * clippy
    
    * Update arrow-flight/src/error.rs
    
    Co-authored-by: Liang-Chi Hsieh <vi...@gmail.com>
    
    Co-authored-by: Liang-Chi Hsieh <vi...@gmail.com>
---
 arrow-flight/src/error.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++---
 arrow-schema/src/error.rs | 40 ++++++++++++++++++++++++++++-
 2 files changed, 100 insertions(+), 5 deletions(-)

diff --git a/arrow-flight/src/error.rs b/arrow-flight/src/error.rs
index 11e0ae5c9..7a43e537a 100644
--- a/arrow-flight/src/error.rs
+++ b/arrow-flight/src/error.rs
@@ -15,6 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use std::error::Error;
+
 use arrow_schema::ArrowError;
 
 /// Errors for the Apache Arrow Flight crate
@@ -30,8 +32,8 @@ pub enum FlightError {
     ProtocolError(String),
     /// An error occured during decoding
     DecodeError(String),
-    /// Some other (opaque) error
-    ExternalError(Box<dyn std::error::Error + Send + Sync>),
+    /// External error that can provide source of error by calling `Error::source`.
+    ExternalError(Box<dyn Error + Send + Sync>),
 }
 
 impl FlightError {
@@ -40,7 +42,7 @@ impl FlightError {
     }
 
     /// Wraps an external error in an `ArrowError`.
-    pub fn from_external_error(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
+    pub fn from_external_error(error: Box<dyn Error + Send + Sync>) -> Self {
         Self::ExternalError(error)
     }
 }
@@ -52,7 +54,15 @@ impl std::fmt::Display for FlightError {
     }
 }
 
-impl std::error::Error for FlightError {}
+impl Error for FlightError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        if let Self::ExternalError(e) = self {
+            Some(e.as_ref())
+        } else {
+            None
+        }
+    }
+}
 
 impl From<tonic::Status> for FlightError {
     fn from(status: tonic::Status) -> Self {
@@ -82,3 +92,50 @@ impl From<FlightError> for tonic::Status {
 }
 
 pub type Result<T> = std::result::Result<T, FlightError>;
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn error_source() {
+        let e1 = FlightError::DecodeError("foo".into());
+        assert!(e1.source().is_none());
+
+        // one level of wrapping
+        let e2 = FlightError::ExternalError(Box::new(e1));
+        let source = e2.source().unwrap().downcast_ref::<FlightError>().unwrap();
+        assert!(matches!(source, FlightError::DecodeError(_)));
+
+        let e3 = FlightError::ExternalError(Box::new(e2));
+        let source = e3
+            .source()
+            .unwrap()
+            .downcast_ref::<FlightError>()
+            .unwrap()
+            .source()
+            .unwrap()
+            .downcast_ref::<FlightError>()
+            .unwrap();
+
+        assert!(matches!(source, FlightError::DecodeError(_)));
+    }
+
+    #[test]
+    fn error_through_arrow() {
+        // flight error that wraps an arrow error that wraps a flight error
+        let e1 = FlightError::DecodeError("foo".into());
+        let e2 = ArrowError::ExternalError(Box::new(e1));
+        let e3 = FlightError::ExternalError(Box::new(e2));
+
+        // ensure we can find the lowest level error by following source()
+        let mut root_error: &dyn Error = &e3;
+        while let Some(source) = root_error.source() {
+            // walk the next level
+            root_error = source;
+        }
+
+        let source = root_error.downcast_ref::<FlightError>().unwrap();
+        assert!(matches!(source, FlightError::DecodeError(_)));
+    }
+}
diff --git a/arrow-schema/src/error.rs b/arrow-schema/src/error.rs
index 0d7a35a9d..ea60572b3 100644
--- a/arrow-schema/src/error.rs
+++ b/arrow-schema/src/error.rs
@@ -100,4 +100,42 @@ impl Display for ArrowError {
     }
 }
 
-impl Error for ArrowError {}
+impl Error for ArrowError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        if let Self::ExternalError(e) = self {
+            Some(e.as_ref())
+        } else {
+            None
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn error_source() {
+        let e1 = ArrowError::DivideByZero;
+        assert!(e1.source().is_none());
+
+        // one level of wrapping
+        let e2 = ArrowError::ExternalError(Box::new(e1));
+        let source = e2.source().unwrap().downcast_ref::<ArrowError>().unwrap();
+        assert!(matches!(source, ArrowError::DivideByZero));
+
+        // two levels of wrapping
+        let e3 = ArrowError::ExternalError(Box::new(e2));
+        let source = e3
+            .source()
+            .unwrap()
+            .downcast_ref::<ArrowError>()
+            .unwrap()
+            .source()
+            .unwrap()
+            .downcast_ref::<ArrowError>()
+            .unwrap();
+
+        assert!(matches!(source, ArrowError::DivideByZero));
+    }
+}