vortex_error/
lib.rs

1#![feature(error_generic_member_access)]
2#![deny(missing_docs)]
3
4//! This crate defines error & result types for Vortex.
5//! It also contains a variety of useful macros for error handling.
6
7#[cfg(feature = "python")]
8pub mod python;
9
10mod ext;
11
12use std::backtrace::Backtrace;
13use std::borrow::Cow;
14use std::convert::Infallible;
15use std::error::Error;
16use std::fmt::{Debug, Display, Formatter};
17use std::num::TryFromIntError;
18use std::ops::Deref;
19use std::sync::PoisonError;
20use std::{env, fmt, io};
21
22pub use ext::*;
23
24/// A string that can be used as an error message.
25#[derive(Debug)]
26pub struct ErrString(Cow<'static, str>);
27
28#[allow(clippy::fallible_impl_from)]
29impl<T> From<T> for ErrString
30where
31    T: Into<Cow<'static, str>>,
32{
33    #[allow(clippy::panic)]
34    fn from(msg: T) -> Self {
35        if env::var("VORTEX_PANIC_ON_ERR").as_deref().unwrap_or("") == "1" {
36            panic!("{}\nBacktrace:\n{}", msg.into(), Backtrace::capture());
37        } else {
38            Self(msg.into())
39        }
40    }
41}
42
43impl AsRef<str> for ErrString {
44    fn as_ref(&self) -> &str {
45        &self.0
46    }
47}
48
49impl Deref for ErrString {
50    type Target = str;
51
52    fn deref(&self) -> &Self::Target {
53        &self.0
54    }
55}
56
57impl Display for ErrString {
58    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
59        Display::fmt(&self.0, f)
60    }
61}
62
63impl From<Infallible> for VortexError {
64    fn from(_: Infallible) -> Self {
65        unreachable!()
66    }
67}
68
69/// The top-level error type for Vortex.
70#[derive(thiserror::Error)]
71#[non_exhaustive]
72pub enum VortexError {
73    /// A wrapped generic error
74    #[error("{0}\nBacktrace:\n{1}")]
75    Generic(Box<dyn Error + Send + Sync + 'static>, Backtrace),
76    /// An index is out of bounds.
77    #[error("index {0} out of bounds from {1} to {2}\nBacktrace:\n{3}")]
78    OutOfBounds(usize, usize, usize, Backtrace),
79    /// An error occurred while executing a compute kernel.
80    #[error("{0}\nBacktrace:\n{1}")]
81    ComputeError(ErrString, Backtrace),
82    /// An invalid argument was provided.
83    #[error("{0}\nBacktrace:\n{1}")]
84    InvalidArgument(ErrString, Backtrace),
85    /// The system has reached an invalid state,
86    #[error("{0}\nBacktrace:\n{1}")]
87    InvalidState(ErrString, Backtrace),
88    /// An error occurred while serializing or deserializing.
89    #[error("{0}\nBacktrace:\n{1}")]
90    InvalidSerde(ErrString, Backtrace),
91    /// An unimplemented function was called.
92    #[error("function {0} not implemented for {1}\nBacktrace:\n{2}")]
93    NotImplemented(ErrString, ErrString, Backtrace),
94    /// A type mismatch occurred.
95    #[error("expected type: {0} but instead got {1}\nBacktrace:\n{2}")]
96    MismatchedTypes(ErrString, ErrString, Backtrace),
97    /// An assertion failed.
98    #[error("{0}\nBacktrace:\n{1}")]
99    AssertionFailed(ErrString, Backtrace),
100    /// A wrapper for other errors, carrying additional context.
101    #[error("{0}: {1}")]
102    Context(ErrString, #[source] Box<VortexError>),
103    /// A wrapper for errors from the Arrow library.
104    #[error("{0}\nBacktrace:\n{1}")]
105    ArrowError(arrow_schema::ArrowError, Backtrace),
106    /// A wrapper for errors from the FlatBuffers library.
107    #[cfg(feature = "flatbuffers")]
108    #[error(transparent)]
109    FlatBuffersError(
110        #[from]
111        #[backtrace]
112        flatbuffers::InvalidFlatbuffer,
113    ),
114    /// A wrapper for reader errors from the FlexBuffers library.
115    #[cfg(feature = "flexbuffers")]
116    #[error(transparent)]
117    FlexBuffersReaderError(
118        #[from]
119        #[backtrace]
120        flexbuffers::ReaderError,
121    ),
122    /// A wrapper for deserialization errors from the FlexBuffers library.
123    #[cfg(feature = "flexbuffers")]
124    #[error(transparent)]
125    FlexBuffersDeError(
126        #[from]
127        #[backtrace]
128        flexbuffers::DeserializationError,
129    ),
130    /// A wrapper for serialization errors from the FlexBuffers library.
131    #[cfg(feature = "flexbuffers")]
132    #[error(transparent)]
133    FlexBuffersSerError(
134        #[from]
135        #[backtrace]
136        flexbuffers::SerializationError,
137    ),
138    /// A wrapper for formatting errors.
139    #[error(transparent)]
140    FmtError(
141        #[from]
142        #[backtrace]
143        fmt::Error,
144    ),
145    /// A wrapper for IO errors.
146    #[error(transparent)]
147    IOError(
148        #[from]
149        #[backtrace]
150        io::Error,
151    ),
152    /// A wrapper for UTF-8 conversion errors.
153    #[error(transparent)]
154    Utf8Error(
155        #[from]
156        #[backtrace]
157        std::str::Utf8Error,
158    ),
159    /// A wrapper for errors from the Parquet library.
160    #[cfg(feature = "parquet")]
161    #[error(transparent)]
162    ParquetError(
163        #[from]
164        #[backtrace]
165        parquet::errors::ParquetError,
166    ),
167    /// A wrapper for errors from the standard library when converting a slice to an array.
168    #[error(transparent)]
169    TryFromSliceError(
170        #[from]
171        #[backtrace]
172        std::array::TryFromSliceError,
173    ),
174    /// A wrapper for errors from the Cloudflare Workers library.
175    #[cfg(feature = "worker")]
176    #[error(transparent)]
177    WorkerError(
178        #[from]
179        #[backtrace]
180        worker::Error,
181    ),
182    /// A wrapper for errors from the Object Store library.
183    #[cfg(feature = "object_store")]
184    #[error(transparent)]
185    ObjectStore(
186        #[from]
187        #[backtrace]
188        object_store::Error,
189    ),
190    /// A wrapper for errors from DataFusion.
191    #[cfg(feature = "datafusion")]
192    #[error(transparent)]
193    DataFusion(
194        #[from]
195        #[backtrace]
196        datafusion_common::DataFusionError,
197    ),
198    /// A wrapper for errors from the Jiff library.
199    #[error(transparent)]
200    JiffError(
201        #[from]
202        #[backtrace]
203        jiff::Error,
204    ),
205    /// A wrapper for Tokio join error.
206    #[cfg(feature = "tokio")]
207    #[error(transparent)]
208    JoinError(
209        #[from]
210        #[backtrace]
211        tokio::task::JoinError,
212    ),
213    /// A wrapper for URL parsing errors.
214    #[error(transparent)]
215    UrlError(
216        #[from]
217        #[backtrace]
218        url::ParseError,
219    ),
220    /// Wrap errors for fallible integer casting.
221    #[error(transparent)]
222    TryFromInt(
223        #[from]
224        #[backtrace]
225        TryFromIntError,
226    ),
227    /// Wrap serde and serde json errors
228    #[cfg(feature = "serde")]
229    #[error(transparent)]
230    SerdeJsonError(
231        #[from]
232        #[backtrace]
233        serde_json::Error,
234    ),
235    /// Wrap prost encode error
236    #[cfg(feature = "prost")]
237    #[error(transparent)]
238    ProstEncodeError(
239        #[from]
240        #[backtrace]
241        prost::EncodeError,
242    ),
243    /// Wrap prost decode error
244    #[cfg(feature = "prost")]
245    #[error(transparent)]
246    ProstDecodeError(
247        #[from]
248        #[backtrace]
249        prost::DecodeError,
250    ),
251    /// Wrap prost unknown enum value
252    #[cfg(feature = "prost")]
253    #[error(transparent)]
254    ProstUnknownEnumValue(
255        #[from]
256        #[backtrace]
257        prost::UnknownEnumValue,
258    ),
259}
260
261impl VortexError {
262    /// Adds additional context to an error.
263    pub fn with_context<T: Into<ErrString>>(self, msg: T) -> Self {
264        VortexError::Context(msg.into(), Box::new(self))
265    }
266}
267
268impl Debug for VortexError {
269    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
270        Display::fmt(self, f)
271    }
272}
273
274/// A type alias for Results that return VortexErrors as their error type.
275pub type VortexResult<T> = Result<T, VortexError>;
276
277/// A trait for unwrapping a VortexResult.
278pub trait VortexUnwrap {
279    /// The type of the value being unwrapped.
280    type Output;
281
282    /// Returns the value of the result if it is Ok, otherwise panics with the error.
283    /// Should be called only in contexts where the error condition represents a bug (programmer error).
284    fn vortex_unwrap(self) -> Self::Output;
285}
286
287impl<T, E> VortexUnwrap for Result<T, E>
288where
289    E: Into<VortexError>,
290{
291    type Output = T;
292
293    #[inline(always)]
294    fn vortex_unwrap(self) -> Self::Output {
295        self.map_err(|err| err.into())
296            .unwrap_or_else(|err| vortex_panic!(err))
297    }
298}
299
300/// A trait for expect-ing a VortexResult or an Option.
301pub trait VortexExpect {
302    /// The type of the value being expected.
303    type Output;
304
305    /// Returns the value of the result if it is Ok, otherwise panics with the error.
306    /// Should be called only in contexts where the error condition represents a bug (programmer error).
307    fn vortex_expect(self, msg: &str) -> Self::Output;
308}
309
310impl<T, E> VortexExpect for Result<T, E>
311where
312    E: Into<VortexError>,
313{
314    type Output = T;
315
316    #[inline(always)]
317    fn vortex_expect(self, msg: &str) -> Self::Output {
318        self.map_err(|err| err.into())
319            .unwrap_or_else(|e| vortex_panic!(e.with_context(msg.to_string())))
320    }
321}
322
323impl<T> VortexExpect for Option<T> {
324    type Output = T;
325
326    #[inline(always)]
327    fn vortex_expect(self, msg: &str) -> Self::Output {
328        self.unwrap_or_else(|| {
329            let err = VortexError::AssertionFailed(msg.to_string().into(), Backtrace::capture());
330            vortex_panic!(err)
331        })
332    }
333}
334
335/// A convenient macro for creating a VortexError.
336#[macro_export]
337macro_rules! vortex_err {
338    (AssertionFailed: $($tts:tt)*) => {{
339        use std::backtrace::Backtrace;
340        let err_string = format!($($tts)*);
341        $crate::__private::must_use(
342            $crate::VortexError::AssertionFailed(err_string.into(), Backtrace::capture())
343        )
344    }};
345    (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
346        use std::backtrace::Backtrace;
347        $crate::__private::must_use(
348            $crate::VortexError::OutOfBounds($idx, $start, $stop, Backtrace::capture())
349        )
350    }};
351    (NotImplemented: $func:expr, $by_whom:expr) => {{
352        use std::backtrace::Backtrace;
353        $crate::__private::must_use(
354            $crate::VortexError::NotImplemented($func.into(), format!("{}", $by_whom).into(), Backtrace::capture())
355        )
356    }};
357    (MismatchedTypes: $expected:literal, $actual:expr) => {{
358        use std::backtrace::Backtrace;
359        $crate::__private::must_use(
360            $crate::VortexError::MismatchedTypes($expected.into(), $actual.to_string().into(), Backtrace::capture())
361        )
362    }};
363    (MismatchedTypes: $expected:expr, $actual:expr) => {{
364        use std::backtrace::Backtrace;
365        $crate::__private::must_use(
366            $crate::VortexError::MismatchedTypes($expected.to_string().into(), $actual.to_string().into(), Backtrace::capture())
367        )
368    }};
369    (Context: $msg:literal, $err:expr) => {{
370        $crate::__private::must_use(
371            $crate::VortexError::Context($msg.into(), Box::new($err))
372        )
373    }};
374    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {{
375        use std::backtrace::Backtrace;
376        $crate::__private::must_use(
377            $crate::VortexError::$variant(format!($fmt, $($arg),*).into(), Backtrace::capture())
378        )
379    }};
380    ($variant:ident: $err:expr $(,)?) => {
381        $crate::__private::must_use(
382            $crate::VortexError::$variant($err)
383        )
384    };
385    ($fmt:literal $(, $arg:expr)* $(,)?) => {
386        $crate::vortex_err!(InvalidArgument: $fmt, $($arg),*)
387    };
388}
389
390/// A convenient macro for returning a VortexError.
391#[macro_export]
392macro_rules! vortex_bail {
393    ($($tt:tt)+) => {
394        return Err($crate::vortex_err!($($tt)+))
395    };
396}
397
398/// A convenient macro for panicking with a VortexError in the presence of a programmer error
399/// (e.g., an invariant has been violated).
400#[macro_export]
401macro_rules! vortex_panic {
402    (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
403        $crate::vortex_panic!($crate::vortex_err!(OutOfBounds: $idx, $start, $stop))
404    }};
405    (NotImplemented: $func:expr, $for_whom:expr) => {{
406        $crate::vortex_panic!($crate::vortex_err!(NotImplemented: $func, $for_whom))
407    }};
408    (MismatchedTypes: $expected:literal, $actual:expr) => {{
409        $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
410    }};
411    (MismatchedTypes: $expected:expr, $actual:expr) => {{
412        $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
413    }};
414    (Context: $msg:literal, $err:expr) => {{
415        $crate::vortex_panic!($crate::vortex_err!(Context: $msg, $err))
416    }};
417    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
418        $crate::vortex_panic!($crate::vortex_err!($variant: $fmt, $($arg),*))
419    };
420    ($err:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
421        let err: $crate::VortexError = $err;
422        panic!("{}", err.with_context(format!($fmt, $($arg),*)))
423    }};
424    ($fmt:literal $(, $arg:expr)* $(,)?) => {
425        $crate::vortex_panic!($crate::vortex_err!($fmt, $($arg),*))
426    };
427    ($err:expr) => {{
428        let err: $crate::VortexError = $err;
429        panic!("{}", err)
430    }};
431}
432
433impl From<arrow_schema::ArrowError> for VortexError {
434    fn from(value: arrow_schema::ArrowError) -> Self {
435        VortexError::ArrowError(value, Backtrace::capture())
436    }
437}
438
439#[cfg(feature = "datafusion")]
440impl From<VortexError> for datafusion_common::DataFusionError {
441    fn from(value: VortexError) -> Self {
442        Self::External(Box::new(value))
443    }
444}
445
446#[cfg(feature = "datafusion")]
447impl From<VortexError> for datafusion_common::arrow::error::ArrowError {
448    fn from(value: VortexError) -> Self {
449        match value {
450            VortexError::ArrowError(e, _) => e,
451            _ => Self::from_external_error(Box::new(value)),
452        }
453    }
454}
455
456// Not public, referenced by macros only.
457#[doc(hidden)]
458pub mod __private {
459    #[doc(hidden)]
460    #[inline]
461    #[cold]
462    #[must_use]
463    pub const fn must_use(error: crate::VortexError) -> crate::VortexError {
464        error
465    }
466}
467
468#[cfg(feature = "rancor")]
469impl rancor::Source for VortexError {
470    fn new<T: Error + Send + Sync + 'static>(source: T) -> Self {
471        VortexError::Generic(Box::new(source), Backtrace::capture())
472    }
473}
474
475#[cfg(feature = "rancor")]
476impl rancor::Trace for VortexError {
477    fn trace<R>(self, trace: R) -> Self
478    where
479        R: Debug + Display + Send + Sync + 'static,
480    {
481        VortexError::Context(trace.to_string().into(), Box::new(self))
482    }
483}
484
485#[cfg(feature = "worker")]
486impl From<VortexError> for worker::Error {
487    fn from(value: VortexError) -> Self {
488        Self::RustError(value.to_string())
489    }
490}
491
492impl<T> From<PoisonError<T>> for VortexError {
493    fn from(_value: PoisonError<T>) -> Self {
494        // We don't include the value since it may be sensitive.
495        Self::InvalidState("Lock poisoned".into(), Backtrace::capture())
496    }
497}