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