1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::error::Error;
use arrow_schema::ArrowError;
/// Errors for the Apache Arrow Flight crate
#[derive(Debug)]
pub enum FlightError {
/// Underlying arrow error
Arrow(ArrowError),
/// Returned when functionality is not yet available.
NotYetImplemented(String),
/// Error from the underlying tonic library
Tonic(tonic::Status),
/// Some unexpected message was received
ProtocolError(String),
/// An error occurred during decoding
DecodeError(String),
/// External error that can provide source of error by calling `Error::source`.
ExternalError(Box<dyn Error + Send + Sync>),
}
impl FlightError {
pub fn protocol(message: impl Into<String>) -> Self {
Self::ProtocolError(message.into())
}
/// Wraps an external error in an `ArrowError`.
pub fn from_external_error(error: Box<dyn Error + Send + Sync>) -> Self {
Self::ExternalError(error)
}
}
impl std::fmt::Display for FlightError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// TODO better format / error
write!(f, "{self:?}")
}
}
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 {
Self::Tonic(status)
}
}
impl From<ArrowError> for FlightError {
fn from(value: ArrowError) -> Self {
Self::Arrow(value)
}
}
// default conversion from FlightError to tonic treats everything
// other than `Status` as an internal error
impl From<FlightError> for tonic::Status {
fn from(value: FlightError) -> Self {
match value {
FlightError::Arrow(e) => tonic::Status::internal(e.to_string()),
FlightError::NotYetImplemented(e) => tonic::Status::internal(e),
FlightError::Tonic(status) => status,
FlightError::ProtocolError(e) => tonic::Status::internal(e),
FlightError::DecodeError(e) => tonic::Status::internal(e),
FlightError::ExternalError(e) => tonic::Status::internal(e.to_string()),
}
}
}
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(_)));
}
}