vortex_error/
lib.rs

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