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 the Jiff library.
194    #[error(transparent)]
195    JiffError(
196        #[from]
197        #[backtrace]
198        jiff::Error,
199    ),
200    /// A wrapper for Tokio join error.
201    #[cfg(feature = "tokio")]
202    #[error(transparent)]
203    JoinError(
204        #[from]
205        #[backtrace]
206        tokio::task::JoinError,
207    ),
208    /// A wrapper for URL parsing errors.
209    #[error(transparent)]
210    UrlError(
211        #[from]
212        #[backtrace]
213        url::ParseError,
214    ),
215    /// Wrap errors for fallible integer casting.
216    #[error(transparent)]
217    TryFromInt(
218        #[from]
219        #[backtrace]
220        TryFromIntError,
221    ),
222    /// Wrap serde and serde json errors
223    #[cfg(feature = "serde")]
224    #[error(transparent)]
225    SerdeJsonError(
226        #[from]
227        #[backtrace]
228        serde_json::Error,
229    ),
230    /// Wrap prost encode error
231    #[cfg(feature = "prost")]
232    #[error(transparent)]
233    ProstEncodeError(
234        #[from]
235        #[backtrace]
236        prost::EncodeError,
237    ),
238    /// Wrap prost decode error
239    #[cfg(feature = "prost")]
240    #[error(transparent)]
241    ProstDecodeError(
242        #[from]
243        #[backtrace]
244        prost::DecodeError,
245    ),
246    /// Wrap prost unknown enum value
247    #[cfg(feature = "prost")]
248    #[error(transparent)]
249    ProstUnknownEnumValue(
250        #[from]
251        #[backtrace]
252        prost::UnknownEnumValue,
253    ),
254}
255
256impl VortexError {
257    /// Adds additional context to an error.
258    pub fn with_context<T: Into<ErrString>>(self, msg: T) -> Self {
259        VortexError::Context(msg.into(), Box::new(self))
260    }
261}
262
263impl Debug for VortexError {
264    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
265        Display::fmt(self, f)
266    }
267}
268
269/// A type alias for Results that return VortexErrors as their error type.
270pub type VortexResult<T> = Result<T, VortexError>;
271
272/// A vortex result that can be shared or cloned.
273pub type SharedVortexResult<T> = Result<T, Arc<VortexError>>;
274
275impl From<Arc<VortexError>> for VortexError {
276    fn from(value: Arc<VortexError>) -> Self {
277        Self::from(&value)
278    }
279}
280
281impl From<&Arc<VortexError>> for VortexError {
282    fn from(e: &Arc<VortexError>) -> Self {
283        if let VortexError::Shared(e_inner) = e.as_ref() {
284            // don't re-wrap
285            VortexError::Shared(Arc::clone(e_inner))
286        } else {
287            VortexError::Shared(Arc::clone(e))
288        }
289    }
290}
291
292/// A trait for unwrapping a VortexResult.
293pub trait VortexUnwrap {
294    /// The type of the value being unwrapped.
295    type Output;
296
297    /// Returns the value of the result if it is Ok, otherwise panics with the error.
298    /// Should be called only in contexts where the error condition represents a bug (programmer error).
299    fn vortex_unwrap(self) -> Self::Output;
300}
301
302impl<T, E> VortexUnwrap for Result<T, E>
303where
304    E: Into<VortexError>,
305{
306    type Output = T;
307
308    #[inline(always)]
309    fn vortex_unwrap(self) -> Self::Output {
310        self.map_err(|err| err.into())
311            .unwrap_or_else(|err| vortex_panic!(err))
312    }
313}
314
315/// A trait for expect-ing a VortexResult or an Option.
316pub trait VortexExpect {
317    /// The type of the value being expected.
318    type Output;
319
320    /// Returns the value of the result if it is Ok, otherwise panics with the error.
321    /// Should be called only in contexts where the error condition represents a bug (programmer error).
322    fn vortex_expect(self, msg: &str) -> Self::Output;
323}
324
325impl<T, E> VortexExpect for Result<T, E>
326where
327    E: Into<VortexError>,
328{
329    type Output = T;
330
331    #[inline(always)]
332    fn vortex_expect(self, msg: &str) -> Self::Output {
333        self.map_err(|err| err.into())
334            .unwrap_or_else(|e| vortex_panic!(e.with_context(msg.to_string())))
335    }
336}
337
338impl<T> VortexExpect for Option<T> {
339    type Output = T;
340
341    #[inline(always)]
342    fn vortex_expect(self, msg: &str) -> Self::Output {
343        self.unwrap_or_else(|| {
344            let err = VortexError::AssertionFailed(msg.to_string().into(), Backtrace::capture());
345            vortex_panic!(err)
346        })
347    }
348}
349
350/// A convenient macro for creating a VortexError.
351#[macro_export]
352macro_rules! vortex_err {
353    (AssertionFailed: $($tts:tt)*) => {{
354        use std::backtrace::Backtrace;
355        let err_string = format!($($tts)*);
356        $crate::__private::must_use(
357            $crate::VortexError::AssertionFailed(err_string.into(), Backtrace::capture())
358        )
359    }};
360    (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
361        use std::backtrace::Backtrace;
362        $crate::__private::must_use(
363            $crate::VortexError::OutOfBounds($idx, $start, $stop, Backtrace::capture())
364        )
365    }};
366    (NotImplemented: $func:expr, $by_whom:expr) => {{
367        use std::backtrace::Backtrace;
368        $crate::__private::must_use(
369            $crate::VortexError::NotImplemented($func.into(), format!("{}", $by_whom).into(), Backtrace::capture())
370        )
371    }};
372    (MismatchedTypes: $expected:literal, $actual:expr) => {{
373        use std::backtrace::Backtrace;
374        $crate::__private::must_use(
375            $crate::VortexError::MismatchedTypes($expected.into(), $actual.to_string().into(), Backtrace::capture())
376        )
377    }};
378    (MismatchedTypes: $expected:expr, $actual:expr) => {{
379        use std::backtrace::Backtrace;
380        $crate::__private::must_use(
381            $crate::VortexError::MismatchedTypes($expected.to_string().into(), $actual.to_string().into(), Backtrace::capture())
382        )
383    }};
384    (Context: $msg:literal, $err:expr) => {{
385        $crate::__private::must_use(
386            $crate::VortexError::Context($msg.into(), Box::new($err))
387        )
388    }};
389    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {{
390        use std::backtrace::Backtrace;
391        $crate::__private::must_use(
392            $crate::VortexError::$variant(format!($fmt, $($arg),*).into(), Backtrace::capture())
393        )
394    }};
395    ($variant:ident: $err:expr $(,)?) => {
396        $crate::__private::must_use(
397            $crate::VortexError::$variant($err)
398        )
399    };
400    ($fmt:literal $(, $arg:expr)* $(,)?) => {
401        $crate::vortex_err!(InvalidArgument: $fmt, $($arg),*)
402    };
403}
404
405/// A convenient macro for returning a VortexError.
406#[macro_export]
407macro_rules! vortex_bail {
408    ($($tt:tt)+) => {
409        return Err($crate::vortex_err!($($tt)+))
410    };
411}
412
413/// A convenient macro for panicking with a VortexError in the presence of a programmer error
414/// (e.g., an invariant has been violated).
415#[macro_export]
416macro_rules! vortex_panic {
417    (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
418        $crate::vortex_panic!($crate::vortex_err!(OutOfBounds: $idx, $start, $stop))
419    }};
420    (NotImplemented: $func:expr, $for_whom:expr) => {{
421        $crate::vortex_panic!($crate::vortex_err!(NotImplemented: $func, $for_whom))
422    }};
423    (MismatchedTypes: $expected:literal, $actual:expr) => {{
424        $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
425    }};
426    (MismatchedTypes: $expected:expr, $actual:expr) => {{
427        $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
428    }};
429    (Context: $msg:literal, $err:expr) => {{
430        $crate::vortex_panic!($crate::vortex_err!(Context: $msg, $err))
431    }};
432    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
433        $crate::vortex_panic!($crate::vortex_err!($variant: $fmt, $($arg),*))
434    };
435    ($err:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
436        let err: $crate::VortexError = $err;
437        panic!("{}", err.with_context(format!($fmt, $($arg),*)))
438    }};
439    ($fmt:literal $(, $arg:expr)* $(,)?) => {
440        $crate::vortex_panic!($crate::vortex_err!($fmt, $($arg),*))
441    };
442    ($err:expr) => {{
443        let err: $crate::VortexError = $err;
444        panic!("{}", err)
445    }};
446}
447
448impl From<arrow_schema::ArrowError> for VortexError {
449    fn from(value: arrow_schema::ArrowError) -> Self {
450        VortexError::ArrowError(value, Backtrace::capture())
451    }
452}
453
454// Not public, referenced by macros only.
455#[doc(hidden)]
456pub mod __private {
457    #[doc(hidden)]
458    #[inline]
459    #[cold]
460    #[must_use]
461    pub const fn must_use(error: crate::VortexError) -> crate::VortexError {
462        error
463    }
464}
465
466#[cfg(feature = "rancor")]
467impl rancor::Source for VortexError {
468    fn new<T: Error + Send + Sync + 'static>(source: T) -> Self {
469        VortexError::Generic(Box::new(source), Backtrace::capture())
470    }
471}
472
473#[cfg(feature = "rancor")]
474impl rancor::Trace for VortexError {
475    fn trace<R>(self, trace: R) -> Self
476    where
477        R: Debug + Display + Send + Sync + 'static,
478    {
479        VortexError::Context(trace.to_string().into(), Box::new(self))
480    }
481}
482
483#[cfg(feature = "worker")]
484impl From<VortexError> for worker::Error {
485    fn from(value: VortexError) -> Self {
486        Self::RustError(value.to_string())
487    }
488}
489
490impl<T> From<PoisonError<T>> for VortexError {
491    fn from(_value: PoisonError<T>) -> Self {
492        // We don't include the value since it may be sensitive.
493        Self::InvalidState("Lock poisoned".into(), Backtrace::capture())
494    }
495}