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::{Arc, 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(transparent)]
105 Shared(Arc<VortexError>),
106 #[error("{0}\nBacktrace:\n{1}")]
108 ArrowError(arrow_schema::ArrowError, Backtrace),
109 #[cfg(feature = "flatbuffers")]
111 #[error(transparent)]
112 FlatBuffersError(
113 #[from]
114 #[backtrace]
115 flatbuffers::InvalidFlatbuffer,
116 ),
117 #[cfg(feature = "flexbuffers")]
119 #[error(transparent)]
120 FlexBuffersReaderError(
121 #[from]
122 #[backtrace]
123 flexbuffers::ReaderError,
124 ),
125 #[cfg(feature = "flexbuffers")]
127 #[error(transparent)]
128 FlexBuffersDeError(
129 #[from]
130 #[backtrace]
131 flexbuffers::DeserializationError,
132 ),
133 #[cfg(feature = "flexbuffers")]
135 #[error(transparent)]
136 FlexBuffersSerError(
137 #[from]
138 #[backtrace]
139 flexbuffers::SerializationError,
140 ),
141 #[error(transparent)]
143 FmtError(
144 #[from]
145 #[backtrace]
146 fmt::Error,
147 ),
148 #[error(transparent)]
150 IOError(
151 #[from]
152 #[backtrace]
153 io::Error,
154 ),
155 #[error(transparent)]
157 Utf8Error(
158 #[from]
159 #[backtrace]
160 std::str::Utf8Error,
161 ),
162 #[cfg(feature = "parquet")]
164 #[error(transparent)]
165 ParquetError(
166 #[from]
167 #[backtrace]
168 parquet::errors::ParquetError,
169 ),
170 #[error(transparent)]
172 TryFromSliceError(
173 #[from]
174 #[backtrace]
175 std::array::TryFromSliceError,
176 ),
177 #[cfg(feature = "worker")]
179 #[error(transparent)]
180 WorkerError(
181 #[from]
182 #[backtrace]
183 worker::Error,
184 ),
185 #[cfg(feature = "object_store")]
187 #[error(transparent)]
188 ObjectStore(
189 #[from]
190 #[backtrace]
191 object_store::Error,
192 ),
193 #[cfg(feature = "datafusion")]
195 #[error(transparent)]
196 DataFusion(
197 #[from]
198 #[backtrace]
199 datafusion_common::DataFusionError,
200 ),
201 #[error(transparent)]
203 JiffError(
204 #[from]
205 #[backtrace]
206 jiff::Error,
207 ),
208 #[cfg(feature = "tokio")]
210 #[error(transparent)]
211 JoinError(
212 #[from]
213 #[backtrace]
214 tokio::task::JoinError,
215 ),
216 #[error(transparent)]
218 UrlError(
219 #[from]
220 #[backtrace]
221 url::ParseError,
222 ),
223 #[error(transparent)]
225 TryFromInt(
226 #[from]
227 #[backtrace]
228 TryFromIntError,
229 ),
230 #[cfg(feature = "serde")]
232 #[error(transparent)]
233 SerdeJsonError(
234 #[from]
235 #[backtrace]
236 serde_json::Error,
237 ),
238 #[cfg(feature = "prost")]
240 #[error(transparent)]
241 ProstEncodeError(
242 #[from]
243 #[backtrace]
244 prost::EncodeError,
245 ),
246 #[cfg(feature = "prost")]
248 #[error(transparent)]
249 ProstDecodeError(
250 #[from]
251 #[backtrace]
252 prost::DecodeError,
253 ),
254 #[cfg(feature = "prost")]
256 #[error(transparent)]
257 ProstUnknownEnumValue(
258 #[from]
259 #[backtrace]
260 prost::UnknownEnumValue,
261 ),
262}
263
264impl VortexError {
265 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
277pub type VortexResult<T> = Result<T, VortexError>;
279
280pub 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 VortexError::Shared(Arc::clone(e_inner))
294 } else {
295 VortexError::Shared(Arc::clone(e))
296 }
297 }
298}
299
300pub trait VortexUnwrap {
302 type Output;
304
305 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
323pub trait VortexExpect {
325 type Output;
327
328 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#[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#[macro_export]
415macro_rules! vortex_bail {
416 ($($tt:tt)+) => {
417 return Err($crate::vortex_err!($($tt)+))
418 };
419}
420
421#[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#[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 Self::InvalidState("Lock poisoned".into(), Backtrace::capture())
519 }
520}