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