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 #[error(transparent)]
195 JiffError(
196 #[from]
197 #[backtrace]
198 jiff::Error,
199 ),
200 #[cfg(feature = "tokio")]
202 #[error(transparent)]
203 JoinError(
204 #[from]
205 #[backtrace]
206 tokio::task::JoinError,
207 ),
208 #[error(transparent)]
210 UrlError(
211 #[from]
212 #[backtrace]
213 url::ParseError,
214 ),
215 #[error(transparent)]
217 TryFromInt(
218 #[from]
219 #[backtrace]
220 TryFromIntError,
221 ),
222 #[cfg(feature = "serde")]
224 #[error(transparent)]
225 SerdeJsonError(
226 #[from]
227 #[backtrace]
228 serde_json::Error,
229 ),
230 #[cfg(feature = "prost")]
232 #[error(transparent)]
233 ProstEncodeError(
234 #[from]
235 #[backtrace]
236 prost::EncodeError,
237 ),
238 #[cfg(feature = "prost")]
240 #[error(transparent)]
241 ProstDecodeError(
242 #[from]
243 #[backtrace]
244 prost::DecodeError,
245 ),
246 #[cfg(feature = "prost")]
248 #[error(transparent)]
249 ProstUnknownEnumValue(
250 #[from]
251 #[backtrace]
252 prost::UnknownEnumValue,
253 ),
254}
255
256impl VortexError {
257 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
269pub type VortexResult<T> = Result<T, VortexError>;
271
272pub 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 VortexError::Shared(Arc::clone(e_inner))
286 } else {
287 VortexError::Shared(Arc::clone(e))
288 }
289 }
290}
291
292pub trait VortexUnwrap {
294 type Output;
296
297 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
315pub trait VortexExpect {
317 type Output;
319
320 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#[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#[macro_export]
407macro_rules! vortex_bail {
408 ($($tt:tt)+) => {
409 return Err($crate::vortex_err!($($tt)+))
410 };
411}
412
413#[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#[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 Self::InvalidState("Lock poisoned".into(), Backtrace::capture())
494 }
495}