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 Object Store library.
110    #[cfg(feature = "object_store")]
111    ObjectStore(object_store::Error, Backtrace),
112    /// A wrapper for errors from the Jiff library.
113    JiffError(jiff::Error, Backtrace),
114    /// A wrapper for Tokio join error.
115    #[cfg(feature = "tokio")]
116    JoinError(tokio::task::JoinError, Backtrace),
117    /// A wrapper for URL parsing errors.
118    UrlError(url::ParseError, Backtrace),
119    /// Wrap errors for fallible integer casting.
120    TryFromInt(TryFromIntError, Backtrace),
121    /// Wrap serde and serde json errors
122    #[cfg(feature = "serde")]
123    SerdeJsonError(serde_json::Error, Backtrace),
124    /// Wrap prost encode error
125    #[cfg(feature = "prost")]
126    ProstEncodeError(prost::EncodeError, Backtrace),
127    /// Wrap prost decode error
128    #[cfg(feature = "prost")]
129    ProstDecodeError(prost::DecodeError, Backtrace),
130    /// Wrap prost unknown enum value
131    #[cfg(feature = "prost")]
132    ProstUnknownEnumValue(prost::UnknownEnumValue, Backtrace),
133}
134
135impl VortexError {
136    /// Adds additional context to an error.
137    pub fn with_context<T: Into<ErrString>>(self, msg: T) -> Self {
138        VortexError::Context(msg.into(), Box::new(self))
139    }
140}
141
142impl Display for VortexError {
143    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
144        match self {
145            VortexError::Generic(err, backtrace) => {
146                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
147            }
148            VortexError::OutOfBounds(idx, start, stop, backtrace) => {
149                write!(
150                    f,
151                    "index {} out of bounds from {} to {}\nBacktrace:\n{}",
152                    idx, start, stop, backtrace
153                )
154            }
155            VortexError::ComputeError(msg, backtrace) => {
156                write!(f, "{}\nBacktrace:\n{}", msg, backtrace)
157            }
158            VortexError::InvalidArgument(msg, backtrace) => {
159                write!(f, "{}\nBacktrace:\n{}", msg, backtrace)
160            }
161            VortexError::InvalidState(msg, backtrace) => {
162                write!(f, "{}\nBacktrace:\n{}", msg, backtrace)
163            }
164            VortexError::InvalidSerde(msg, backtrace) => {
165                write!(f, "{}\nBacktrace:\n{}", msg, backtrace)
166            }
167            VortexError::NotImplemented(func, by_whom, backtrace) => {
168                write!(
169                    f,
170                    "function {} not implemented for {}\nBacktrace:\n{}",
171                    func, by_whom, backtrace
172                )
173            }
174            VortexError::MismatchedTypes(expected, actual, backtrace) => {
175                write!(
176                    f,
177                    "expected type: {} but instead got {}\nBacktrace:\n{}",
178                    expected, actual, backtrace
179                )
180            }
181            VortexError::AssertionFailed(msg, backtrace) => {
182                write!(f, "{}\nBacktrace:\n{}", msg, backtrace)
183            }
184            VortexError::Context(msg, inner) => {
185                write!(f, "{}: {}", msg, inner)
186            }
187            VortexError::Shared(inner) => Display::fmt(inner, f),
188            VortexError::ArrowError(err, backtrace) => {
189                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
190            }
191            #[cfg(feature = "flatbuffers")]
192            VortexError::FlatBuffersError(err, backtrace) => {
193                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
194            }
195            VortexError::FmtError(err, backtrace) => {
196                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
197            }
198            VortexError::IOError(err, backtrace) => {
199                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
200            }
201            VortexError::Utf8Error(err, backtrace) => {
202                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
203            }
204            #[cfg(feature = "parquet")]
205            VortexError::ParquetError(err, backtrace) => {
206                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
207            }
208            VortexError::TryFromSliceError(err, backtrace) => {
209                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
210            }
211            #[cfg(feature = "object_store")]
212            VortexError::ObjectStore(err, backtrace) => {
213                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
214            }
215            VortexError::JiffError(err, backtrace) => {
216                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
217            }
218            #[cfg(feature = "tokio")]
219            VortexError::JoinError(err, backtrace) => {
220                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
221            }
222            VortexError::UrlError(err, backtrace) => {
223                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
224            }
225            VortexError::TryFromInt(err, backtrace) => {
226                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
227            }
228            #[cfg(feature = "serde")]
229            VortexError::SerdeJsonError(err, backtrace) => {
230                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
231            }
232            #[cfg(feature = "prost")]
233            VortexError::ProstEncodeError(err, backtrace) => {
234                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
235            }
236            #[cfg(feature = "prost")]
237            VortexError::ProstDecodeError(err, backtrace) => {
238                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
239            }
240            #[cfg(feature = "prost")]
241            VortexError::ProstUnknownEnumValue(err, backtrace) => {
242                write!(f, "{}\nBacktrace:\n{}", err, backtrace)
243            }
244        }
245    }
246}
247
248impl Error for VortexError {
249    fn source(&self) -> Option<&(dyn Error + 'static)> {
250        match self {
251            VortexError::Generic(err, _) => Some(err.as_ref()),
252            VortexError::Context(_, inner) => inner.source(),
253            VortexError::Shared(inner) => inner.source(),
254            VortexError::ArrowError(err, _) => Some(err),
255            #[cfg(feature = "flatbuffers")]
256            VortexError::FlatBuffersError(err, _) => Some(err),
257            VortexError::FmtError(err, _) => Some(err),
258            VortexError::IOError(err, _) => Some(err),
259            VortexError::Utf8Error(err, _) => Some(err),
260            #[cfg(feature = "parquet")]
261            VortexError::ParquetError(err, _) => Some(err),
262            VortexError::TryFromSliceError(err, _) => Some(err),
263            #[cfg(feature = "object_store")]
264            VortexError::ObjectStore(err, _) => Some(err),
265            VortexError::JiffError(err, _) => Some(err),
266            #[cfg(feature = "tokio")]
267            VortexError::JoinError(err, _) => Some(err),
268            VortexError::UrlError(err, _) => Some(err),
269            VortexError::TryFromInt(err, _) => Some(err),
270            #[cfg(feature = "serde")]
271            VortexError::SerdeJsonError(err, _) => Some(err),
272            #[cfg(feature = "prost")]
273            VortexError::ProstEncodeError(err, _) => Some(err),
274            #[cfg(feature = "prost")]
275            VortexError::ProstDecodeError(err, _) => Some(err),
276            #[cfg(feature = "prost")]
277            VortexError::ProstUnknownEnumValue(err, _) => Some(err),
278            _ => None,
279        }
280    }
281}
282
283impl Debug for VortexError {
284    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
285        Display::fmt(self, f)
286    }
287}
288
289/// A type alias for Results that return VortexErrors as their error type.
290pub type VortexResult<T> = Result<T, VortexError>;
291
292/// A vortex result that can be shared or cloned.
293pub type SharedVortexResult<T> = Result<T, Arc<VortexError>>;
294
295impl From<Arc<VortexError>> for VortexError {
296    fn from(value: Arc<VortexError>) -> Self {
297        Self::from(&value)
298    }
299}
300
301impl From<&Arc<VortexError>> for VortexError {
302    fn from(e: &Arc<VortexError>) -> Self {
303        if let VortexError::Shared(e_inner) = e.as_ref() {
304            // don't re-wrap
305            VortexError::Shared(Arc::clone(e_inner))
306        } else {
307            VortexError::Shared(Arc::clone(e))
308        }
309    }
310}
311
312/// A trait for unwrapping a VortexResult.
313pub trait VortexUnwrap {
314    /// The type of the value being unwrapped.
315    type Output;
316
317    /// Returns the value of the result if it is Ok, otherwise panics with the error.
318    /// Should be called only in contexts where the error condition represents a bug (programmer error).
319    fn vortex_unwrap(self) -> Self::Output;
320}
321
322impl<T, E> VortexUnwrap for Result<T, E>
323where
324    E: Into<VortexError>,
325{
326    type Output = T;
327
328    #[inline(always)]
329    fn vortex_unwrap(self) -> Self::Output {
330        self.map_err(|err| err.into())
331            .unwrap_or_else(|err| vortex_panic!(err))
332    }
333}
334
335/// A trait for expect-ing a VortexResult or an Option.
336pub trait VortexExpect {
337    /// The type of the value being expected.
338    type Output;
339
340    /// Returns the value of the result if it is Ok, otherwise panics with the error.
341    /// Should be called only in contexts where the error condition represents a bug (programmer error).
342    fn vortex_expect(self, msg: &str) -> Self::Output;
343}
344
345impl<T, E> VortexExpect for Result<T, E>
346where
347    E: Into<VortexError>,
348{
349    type Output = T;
350
351    #[inline(always)]
352    fn vortex_expect(self, msg: &str) -> Self::Output {
353        self.map_err(|err| err.into())
354            .unwrap_or_else(|e| vortex_panic!(e.with_context(msg.to_string())))
355    }
356}
357
358impl<T> VortexExpect for Option<T> {
359    type Output = T;
360
361    #[inline(always)]
362    fn vortex_expect(self, msg: &str) -> Self::Output {
363        self.unwrap_or_else(|| {
364            let err = VortexError::AssertionFailed(msg.to_string().into(), Backtrace::capture());
365            vortex_panic!(err)
366        })
367    }
368}
369
370/// A convenient macro for creating a VortexError.
371#[macro_export]
372macro_rules! vortex_err {
373    (AssertionFailed: $($tts:tt)*) => {{
374        use std::backtrace::Backtrace;
375        let err_string = format!($($tts)*);
376        $crate::__private::must_use(
377            $crate::VortexError::AssertionFailed(err_string.into(), Backtrace::capture())
378        )
379    }};
380    (IOError: $($tts:tt)*) => {{
381        use std::backtrace::Backtrace;
382        $crate::__private::must_use(
383            $crate::VortexError::IOError(err_string.into(), Backtrace::capture())
384        )
385    }};
386    (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
387        use std::backtrace::Backtrace;
388        $crate::__private::must_use(
389            $crate::VortexError::OutOfBounds($idx, $start, $stop, Backtrace::capture())
390        )
391    }};
392    (NotImplemented: $func:expr, $by_whom:expr) => {{
393        use std::backtrace::Backtrace;
394        $crate::__private::must_use(
395            $crate::VortexError::NotImplemented($func.into(), format!("{}", $by_whom).into(), Backtrace::capture())
396        )
397    }};
398    (MismatchedTypes: $expected:literal, $actual:expr) => {{
399        use std::backtrace::Backtrace;
400        $crate::__private::must_use(
401            $crate::VortexError::MismatchedTypes($expected.into(), $actual.to_string().into(), Backtrace::capture())
402        )
403    }};
404    (MismatchedTypes: $expected:expr, $actual:expr) => {{
405        use std::backtrace::Backtrace;
406        $crate::__private::must_use(
407            $crate::VortexError::MismatchedTypes($expected.to_string().into(), $actual.to_string().into(), Backtrace::capture())
408        )
409    }};
410    (Context: $msg:literal, $err:expr) => {{
411        $crate::__private::must_use(
412            $crate::VortexError::Context($msg.into(), Box::new($err))
413        )
414    }};
415    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {{
416        use std::backtrace::Backtrace;
417        $crate::__private::must_use(
418            $crate::VortexError::$variant(format!($fmt, $($arg),*).into(), Backtrace::capture())
419        )
420    }};
421    ($variant:ident: $err:expr $(,)?) => {
422        $crate::__private::must_use(
423            $crate::VortexError::$variant($err)
424        )
425    };
426    ($fmt:literal $(, $arg:expr)* $(,)?) => {
427        $crate::vortex_err!(InvalidArgument: $fmt, $($arg),*)
428    };
429}
430
431/// A convenient macro for returning a VortexError.
432#[macro_export]
433macro_rules! vortex_bail {
434    ($($tt:tt)+) => {
435        return Err($crate::vortex_err!($($tt)+))
436    };
437}
438
439/// A convenient macro for panicking with a VortexError in the presence of a programmer error
440/// (e.g., an invariant has been violated).
441#[macro_export]
442macro_rules! vortex_panic {
443    (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
444        $crate::vortex_panic!($crate::vortex_err!(OutOfBounds: $idx, $start, $stop))
445    }};
446    (NotImplemented: $func:expr, $for_whom:expr) => {{
447        $crate::vortex_panic!($crate::vortex_err!(NotImplemented: $func, $for_whom))
448    }};
449    (MismatchedTypes: $expected:literal, $actual:expr) => {{
450        $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
451    }};
452    (MismatchedTypes: $expected:expr, $actual:expr) => {{
453        $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
454    }};
455    (Context: $msg:literal, $err:expr) => {{
456        $crate::vortex_panic!($crate::vortex_err!(Context: $msg, $err))
457    }};
458    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
459        $crate::vortex_panic!($crate::vortex_err!($variant: $fmt, $($arg),*))
460    };
461    ($err:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
462        let err: $crate::VortexError = $err;
463        panic!("{}", err.with_context(format!($fmt, $($arg),*)))
464    }};
465    ($fmt:literal $(, $arg:expr)* $(,)?) => {
466        $crate::vortex_panic!($crate::vortex_err!($fmt, $($arg),*))
467    };
468    ($err:expr) => {{
469        let err: $crate::VortexError = $err;
470        panic!("{}", err)
471    }};
472}
473
474impl From<arrow_schema::ArrowError> for VortexError {
475    fn from(value: arrow_schema::ArrowError) -> Self {
476        VortexError::ArrowError(value, Backtrace::capture())
477    }
478}
479
480#[cfg(feature = "flatbuffers")]
481impl From<flatbuffers::InvalidFlatbuffer> for VortexError {
482    fn from(value: flatbuffers::InvalidFlatbuffer) -> Self {
483        VortexError::FlatBuffersError(value, Backtrace::capture())
484    }
485}
486
487impl From<fmt::Error> for VortexError {
488    fn from(value: fmt::Error) -> Self {
489        VortexError::FmtError(value, Backtrace::capture())
490    }
491}
492
493impl From<io::Error> for VortexError {
494    fn from(value: io::Error) -> Self {
495        VortexError::IOError(value, Backtrace::capture())
496    }
497}
498
499impl From<std::str::Utf8Error> for VortexError {
500    fn from(value: std::str::Utf8Error) -> Self {
501        VortexError::Utf8Error(value, Backtrace::capture())
502    }
503}
504
505#[cfg(feature = "parquet")]
506impl From<parquet::errors::ParquetError> for VortexError {
507    fn from(value: parquet::errors::ParquetError) -> Self {
508        VortexError::ParquetError(value, Backtrace::capture())
509    }
510}
511
512impl From<std::array::TryFromSliceError> for VortexError {
513    fn from(value: std::array::TryFromSliceError) -> Self {
514        VortexError::TryFromSliceError(value, Backtrace::capture())
515    }
516}
517
518#[cfg(feature = "object_store")]
519impl From<object_store::Error> for VortexError {
520    fn from(value: object_store::Error) -> Self {
521        VortexError::ObjectStore(value, Backtrace::capture())
522    }
523}
524
525impl From<jiff::Error> for VortexError {
526    fn from(value: jiff::Error) -> Self {
527        VortexError::JiffError(value, Backtrace::capture())
528    }
529}
530
531#[cfg(feature = "tokio")]
532impl From<tokio::task::JoinError> for VortexError {
533    fn from(value: tokio::task::JoinError) -> Self {
534        VortexError::JoinError(value, Backtrace::capture())
535    }
536}
537
538impl From<url::ParseError> for VortexError {
539    fn from(value: url::ParseError) -> Self {
540        VortexError::UrlError(value, Backtrace::capture())
541    }
542}
543
544impl From<TryFromIntError> for VortexError {
545    fn from(value: TryFromIntError) -> Self {
546        VortexError::TryFromInt(value, Backtrace::capture())
547    }
548}
549
550#[cfg(feature = "serde")]
551impl From<serde_json::Error> for VortexError {
552    fn from(value: serde_json::Error) -> Self {
553        VortexError::SerdeJsonError(value, Backtrace::capture())
554    }
555}
556
557#[cfg(feature = "prost")]
558impl From<prost::EncodeError> for VortexError {
559    fn from(value: prost::EncodeError) -> Self {
560        VortexError::ProstEncodeError(value, Backtrace::capture())
561    }
562}
563
564#[cfg(feature = "prost")]
565impl From<prost::DecodeError> for VortexError {
566    fn from(value: prost::DecodeError) -> Self {
567        VortexError::ProstDecodeError(value, Backtrace::capture())
568    }
569}
570
571#[cfg(feature = "prost")]
572impl From<prost::UnknownEnumValue> for VortexError {
573    fn from(value: prost::UnknownEnumValue) -> Self {
574        VortexError::ProstUnknownEnumValue(value, Backtrace::capture())
575    }
576}
577
578// Not public, referenced by macros only.
579#[doc(hidden)]
580pub mod __private {
581    #[doc(hidden)]
582    #[inline]
583    #[cold]
584    #[must_use]
585    pub const fn must_use(error: crate::VortexError) -> crate::VortexError {
586        error
587    }
588}
589
590impl<T> From<PoisonError<T>> for VortexError {
591    fn from(_value: PoisonError<T>) -> Self {
592        // We don't include the value since it may be sensitive.
593        Self::InvalidState("Lock poisoned".into(), Backtrace::capture())
594    }
595}