1#![feature(error_generic_member_access)]
2#![deny(missing_docs)]
3
4#[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#[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#[derive(thiserror::Error)]
71#[non_exhaustive]
72pub enum VortexError {
73 #[error("{0}\nBacktrace:\n{1}")]
75 Generic(Box<dyn Error + Send + Sync + 'static>, Backtrace),
76 #[error("index {0} out of bounds from {1} to {2}\nBacktrace:\n{3}")]
78 OutOfBounds(usize, usize, usize, Backtrace),
79 #[error("{0}\nBacktrace:\n{1}")]
81 ComputeError(ErrString, Backtrace),
82 #[error("{0}\nBacktrace:\n{1}")]
84 InvalidArgument(ErrString, Backtrace),
85 #[error("{0}\nBacktrace:\n{1}")]
87 InvalidState(ErrString, Backtrace),
88 #[error("{0}\nBacktrace:\n{1}")]
90 InvalidSerde(ErrString, Backtrace),
91 #[error("function {0} not implemented for {1}\nBacktrace:\n{2}")]
93 NotImplemented(ErrString, ErrString, Backtrace),
94 #[error("expected type: {0} but instead got {1}\nBacktrace:\n{2}")]
96 MismatchedTypes(ErrString, ErrString, Backtrace),
97 #[error("{0}\nBacktrace:\n{1}")]
99 AssertionFailed(ErrString, Backtrace),
100 #[error("{0}: {1}")]
102 Context(ErrString, #[source] Box<VortexError>),
103 #[error("{0}\nBacktrace:\n{1}")]
105 ArrowError(arrow_schema::ArrowError, Backtrace),
106 #[cfg(feature = "flatbuffers")]
108 #[error(transparent)]
109 FlatBuffersError(
110 #[from]
111 #[backtrace]
112 flatbuffers::InvalidFlatbuffer,
113 ),
114 #[cfg(feature = "flexbuffers")]
116 #[error(transparent)]
117 FlexBuffersReaderError(
118 #[from]
119 #[backtrace]
120 flexbuffers::ReaderError,
121 ),
122 #[cfg(feature = "flexbuffers")]
124 #[error(transparent)]
125 FlexBuffersDeError(
126 #[from]
127 #[backtrace]
128 flexbuffers::DeserializationError,
129 ),
130 #[cfg(feature = "flexbuffers")]
132 #[error(transparent)]
133 FlexBuffersSerError(
134 #[from]
135 #[backtrace]
136 flexbuffers::SerializationError,
137 ),
138 #[error(transparent)]
140 FmtError(
141 #[from]
142 #[backtrace]
143 fmt::Error,
144 ),
145 #[error(transparent)]
147 IOError(
148 #[from]
149 #[backtrace]
150 io::Error,
151 ),
152 #[error(transparent)]
154 Utf8Error(
155 #[from]
156 #[backtrace]
157 std::str::Utf8Error,
158 ),
159 #[cfg(feature = "parquet")]
161 #[error(transparent)]
162 ParquetError(
163 #[from]
164 #[backtrace]
165 parquet::errors::ParquetError,
166 ),
167 #[error(transparent)]
169 TryFromSliceError(
170 #[from]
171 #[backtrace]
172 std::array::TryFromSliceError,
173 ),
174 #[cfg(feature = "worker")]
176 #[error(transparent)]
177 WorkerError(
178 #[from]
179 #[backtrace]
180 worker::Error,
181 ),
182 #[cfg(feature = "object_store")]
184 #[error(transparent)]
185 ObjectStore(
186 #[from]
187 #[backtrace]
188 object_store::Error,
189 ),
190 #[cfg(feature = "datafusion")]
192 #[error(transparent)]
193 DataFusion(
194 #[from]
195 #[backtrace]
196 datafusion_common::DataFusionError,
197 ),
198 #[error(transparent)]
200 JiffError(
201 #[from]
202 #[backtrace]
203 jiff::Error,
204 ),
205 #[cfg(feature = "tokio")]
207 #[error(transparent)]
208 JoinError(
209 #[from]
210 #[backtrace]
211 tokio::task::JoinError,
212 ),
213 #[error(transparent)]
215 UrlError(
216 #[from]
217 #[backtrace]
218 url::ParseError,
219 ),
220 #[error(transparent)]
222 TryFromInt(
223 #[from]
224 #[backtrace]
225 TryFromIntError,
226 ),
227 #[cfg(feature = "serde")]
229 #[error(transparent)]
230 SerdeJsonError(
231 #[from]
232 #[backtrace]
233 serde_json::Error,
234 ),
235 #[cfg(feature = "prost")]
237 #[error(transparent)]
238 ProstEncodeError(
239 #[from]
240 #[backtrace]
241 prost::EncodeError,
242 ),
243 #[cfg(feature = "prost")]
245 #[error(transparent)]
246 ProstDecodeError(
247 #[from]
248 #[backtrace]
249 prost::DecodeError,
250 ),
251 #[cfg(feature = "prost")]
253 #[error(transparent)]
254 ProstUnknownEnumValue(
255 #[from]
256 #[backtrace]
257 prost::UnknownEnumValue,
258 ),
259}
260
261impl VortexError {
262 pub fn with_context<T: Into<ErrString>>(self, msg: T) -> Self {
264 VortexError::Context(msg.into(), Box::new(self))
265 }
266}
267
268impl Debug for VortexError {
269 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
270 Display::fmt(self, f)
271 }
272}
273
274pub type VortexResult<T> = Result<T, VortexError>;
276
277pub trait VortexUnwrap {
279 type Output;
281
282 fn vortex_unwrap(self) -> Self::Output;
285}
286
287impl<T, E> VortexUnwrap for Result<T, E>
288where
289 E: Into<VortexError>,
290{
291 type Output = T;
292
293 #[inline(always)]
294 fn vortex_unwrap(self) -> Self::Output {
295 self.map_err(|err| err.into())
296 .unwrap_or_else(|err| vortex_panic!(err))
297 }
298}
299
300pub trait VortexExpect {
302 type Output;
304
305 fn vortex_expect(self, msg: &str) -> Self::Output;
308}
309
310impl<T, E> VortexExpect for Result<T, E>
311where
312 E: Into<VortexError>,
313{
314 type Output = T;
315
316 #[inline(always)]
317 fn vortex_expect(self, msg: &str) -> Self::Output {
318 self.map_err(|err| err.into())
319 .unwrap_or_else(|e| vortex_panic!(e.with_context(msg.to_string())))
320 }
321}
322
323impl<T> VortexExpect for Option<T> {
324 type Output = T;
325
326 #[inline(always)]
327 fn vortex_expect(self, msg: &str) -> Self::Output {
328 self.unwrap_or_else(|| {
329 let err = VortexError::AssertionFailed(msg.to_string().into(), Backtrace::capture());
330 vortex_panic!(err)
331 })
332 }
333}
334
335#[macro_export]
337macro_rules! vortex_err {
338 (AssertionFailed: $($tts:tt)*) => {{
339 use std::backtrace::Backtrace;
340 let err_string = format!($($tts)*);
341 $crate::__private::must_use(
342 $crate::VortexError::AssertionFailed(err_string.into(), Backtrace::capture())
343 )
344 }};
345 (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
346 use std::backtrace::Backtrace;
347 $crate::__private::must_use(
348 $crate::VortexError::OutOfBounds($idx, $start, $stop, Backtrace::capture())
349 )
350 }};
351 (NotImplemented: $func:expr, $by_whom:expr) => {{
352 use std::backtrace::Backtrace;
353 $crate::__private::must_use(
354 $crate::VortexError::NotImplemented($func.into(), format!("{}", $by_whom).into(), Backtrace::capture())
355 )
356 }};
357 (MismatchedTypes: $expected:literal, $actual:expr) => {{
358 use std::backtrace::Backtrace;
359 $crate::__private::must_use(
360 $crate::VortexError::MismatchedTypes($expected.into(), $actual.to_string().into(), Backtrace::capture())
361 )
362 }};
363 (MismatchedTypes: $expected:expr, $actual:expr) => {{
364 use std::backtrace::Backtrace;
365 $crate::__private::must_use(
366 $crate::VortexError::MismatchedTypes($expected.to_string().into(), $actual.to_string().into(), Backtrace::capture())
367 )
368 }};
369 (Context: $msg:literal, $err:expr) => {{
370 $crate::__private::must_use(
371 $crate::VortexError::Context($msg.into(), Box::new($err))
372 )
373 }};
374 ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {{
375 use std::backtrace::Backtrace;
376 $crate::__private::must_use(
377 $crate::VortexError::$variant(format!($fmt, $($arg),*).into(), Backtrace::capture())
378 )
379 }};
380 ($variant:ident: $err:expr $(,)?) => {
381 $crate::__private::must_use(
382 $crate::VortexError::$variant($err)
383 )
384 };
385 ($fmt:literal $(, $arg:expr)* $(,)?) => {
386 $crate::vortex_err!(InvalidArgument: $fmt, $($arg),*)
387 };
388}
389
390#[macro_export]
392macro_rules! vortex_bail {
393 ($($tt:tt)+) => {
394 return Err($crate::vortex_err!($($tt)+))
395 };
396}
397
398#[macro_export]
401macro_rules! vortex_panic {
402 (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
403 $crate::vortex_panic!($crate::vortex_err!(OutOfBounds: $idx, $start, $stop))
404 }};
405 (NotImplemented: $func:expr, $for_whom:expr) => {{
406 $crate::vortex_panic!($crate::vortex_err!(NotImplemented: $func, $for_whom))
407 }};
408 (MismatchedTypes: $expected:literal, $actual:expr) => {{
409 $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
410 }};
411 (MismatchedTypes: $expected:expr, $actual:expr) => {{
412 $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
413 }};
414 (Context: $msg:literal, $err:expr) => {{
415 $crate::vortex_panic!($crate::vortex_err!(Context: $msg, $err))
416 }};
417 ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
418 $crate::vortex_panic!($crate::vortex_err!($variant: $fmt, $($arg),*))
419 };
420 ($err:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
421 let err: $crate::VortexError = $err;
422 panic!("{}", err.with_context(format!($fmt, $($arg),*)))
423 }};
424 ($fmt:literal $(, $arg:expr)* $(,)?) => {
425 $crate::vortex_panic!($crate::vortex_err!($fmt, $($arg),*))
426 };
427 ($err:expr) => {{
428 let err: $crate::VortexError = $err;
429 panic!("{}", err)
430 }};
431}
432
433impl From<arrow_schema::ArrowError> for VortexError {
434 fn from(value: arrow_schema::ArrowError) -> Self {
435 VortexError::ArrowError(value, Backtrace::capture())
436 }
437}
438
439#[cfg(feature = "datafusion")]
440impl From<VortexError> for datafusion_common::DataFusionError {
441 fn from(value: VortexError) -> Self {
442 Self::External(Box::new(value))
443 }
444}
445
446#[cfg(feature = "datafusion")]
447impl From<VortexError> for datafusion_common::arrow::error::ArrowError {
448 fn from(value: VortexError) -> Self {
449 match value {
450 VortexError::ArrowError(e, _) => e,
451 _ => Self::from_external_error(Box::new(value)),
452 }
453 }
454}
455
456#[doc(hidden)]
458pub mod __private {
459 #[doc(hidden)]
460 #[inline]
461 #[cold]
462 #[must_use]
463 pub const fn must_use(error: crate::VortexError) -> crate::VortexError {
464 error
465 }
466}
467
468#[cfg(feature = "rancor")]
469impl rancor::Source for VortexError {
470 fn new<T: Error + Send + Sync + 'static>(source: T) -> Self {
471 VortexError::Generic(Box::new(source), Backtrace::capture())
472 }
473}
474
475#[cfg(feature = "rancor")]
476impl rancor::Trace for VortexError {
477 fn trace<R>(self, trace: R) -> Self
478 where
479 R: Debug + Display + Send + Sync + 'static,
480 {
481 VortexError::Context(trace.to_string().into(), Box::new(self))
482 }
483}
484
485#[cfg(feature = "worker")]
486impl From<VortexError> for worker::Error {
487 fn from(value: VortexError) -> Self {
488 Self::RustError(value.to_string())
489 }
490}
491
492impl<T> From<PoisonError<T>> for VortexError {
493 fn from(_value: PoisonError<T>) -> Self {
494 Self::InvalidState("Lock poisoned".into(), Backtrace::capture())
496 }
497}