vortex_error/
lib.rs

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