1#![deny(missing_docs)]
5
6use std::backtrace::Backtrace;
10use std::backtrace::BacktraceStatus;
11use std::borrow::Cow;
12use std::convert::Infallible;
13use std::env;
14use std::error::Error;
15use std::fmt;
16use std::fmt::Debug;
17use std::fmt::Display;
18use std::fmt::Formatter;
19use std::io;
20use std::num::TryFromIntError;
21use std::ops::Deref;
22use std::sync::Arc;
23use std::sync::LazyLock;
24
25#[derive(Debug)]
27pub struct ErrString(Cow<'static, str>);
28
29#[expect(
30 clippy::fallible_impl_from,
31 reason = "intentionally panic in debug mode when VORTEX_PANIC_ON_ERR is set"
32)]
33impl<T> From<T> for ErrString
34where
35 T: Into<Cow<'static, str>>,
36{
37 #[expect(
38 clippy::panic,
39 reason = "intentionally panic in debug mode when VORTEX_PANIC_ON_ERR is set"
40 )]
41 fn from(msg: T) -> Self {
42 if panic_on_err() {
43 panic!("{}\nBacktrace:\n{}", msg.into(), Backtrace::capture());
44 } else {
45 Self(msg.into())
46 }
47 }
48}
49
50fn panic_on_err() -> bool {
51 static PANIC_ON_ERR: LazyLock<bool> =
52 LazyLock::new(|| env::var("VORTEX_PANIC_ON_ERR").is_ok_and(|v| v == "1"));
53 *PANIC_ON_ERR
54}
55
56impl AsRef<str> for ErrString {
57 fn as_ref(&self) -> &str {
58 &self.0
59 }
60}
61
62impl Deref for ErrString {
63 type Target = str;
64
65 fn deref(&self) -> &Self::Target {
66 &self.0
67 }
68}
69
70impl Display for ErrString {
71 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
72 Display::fmt(&self.0, f)
73 }
74}
75
76impl From<Infallible> for VortexError {
77 fn from(_: Infallible) -> Self {
78 unreachable!()
79 }
80}
81
82const _: () = {
83 assert!(size_of::<VortexError>() < 128);
84};
85
86#[non_exhaustive]
88pub enum VortexError {
89 Other(ErrString, Box<Backtrace>),
91 External(Box<dyn Error + Send + Sync + 'static>, Box<Backtrace>),
93 OutOfBounds(usize, usize, usize, Box<Backtrace>),
95 Compute(ErrString, Box<Backtrace>),
97 InvalidArgument(ErrString, Box<Backtrace>),
99 Serde(ErrString, Box<Backtrace>),
101 NotImplemented(ErrString, ErrString, Box<Backtrace>),
103 MismatchedTypes(ErrString, ErrString, Box<Backtrace>),
105 AssertionFailed(ErrString, Box<Backtrace>),
107 Context(ErrString, Box<VortexError>),
109 Shared(Arc<VortexError>),
111 Arrow(arrow_schema::ArrowError, Box<Backtrace>),
113 #[cfg(feature = "flatbuffers")]
115 FlatBuffers(flatbuffers::InvalidFlatbuffer, Box<Backtrace>),
116 Fmt(fmt::Error, Box<Backtrace>),
118 Io(io::Error, Box<Backtrace>),
120 #[cfg(feature = "object_store")]
122 ObjectStore(object_store::Error, Box<Backtrace>),
123 Jiff(jiff::Error, Box<Backtrace>),
125 #[cfg(feature = "tokio")]
127 Join(tokio::task::JoinError, Box<Backtrace>),
128 TryFromInt(TryFromIntError, Box<Backtrace>),
130 Prost(Box<dyn Error + Send + Sync + 'static>, Box<Backtrace>),
132}
133
134impl VortexError {
135 pub fn with_context<T: Into<ErrString>>(self, msg: T) -> Self {
137 VortexError::Context(msg.into(), Box::new(self))
138 }
139
140 fn variant_prefix(&self) -> &'static str {
142 use VortexError::*;
143
144 match self {
145 Other(..) => "Other error: ",
146 External(..) => "External error: ",
147 OutOfBounds(..) => "Out of bounds error: ",
148 Compute(..) => "Compute error: ",
149 InvalidArgument(..) => "Invalid argument error: ",
150 Serde(..) => "Serde error: ",
151 NotImplemented(..) => "Not implemented error: ",
152 MismatchedTypes(..) => "Mismatched types error: ",
153 AssertionFailed(..) => "Assertion failed error: ",
154 Context(..) | Shared(..) => "", Arrow(..) => "Arrow error: ",
156 #[cfg(feature = "flatbuffers")]
157 FlatBuffers(..) => "Flat buffers error: ",
158 Fmt(..) => "Fmt: ",
159 Io(..) => "Io: ",
160 #[cfg(feature = "object_store")]
161 ObjectStore(..) => "Object store error: ",
162 Jiff(..) => "Jiff error: ",
163 #[cfg(feature = "tokio")]
164 Join(..) => "Tokio join error:",
165 TryFromInt(..) => "Try from int error:",
166 Prost(..) => "Prost error:",
167 }
168 }
169
170 fn backtrace(&self) -> Option<&Backtrace> {
171 use VortexError::*;
172
173 match self {
174 Other(.., bt) => Some(bt.as_ref()),
175 External(.., bt) => Some(bt.as_ref()),
176 OutOfBounds(.., bt) => Some(bt.as_ref()),
177 Compute(.., bt) => Some(bt.as_ref()),
178 InvalidArgument(.., bt) => Some(bt.as_ref()),
179 Serde(.., bt) => Some(bt.as_ref()),
180 NotImplemented(.., bt) => Some(bt.as_ref()),
181 MismatchedTypes(.., bt) => Some(bt.as_ref()),
182 AssertionFailed(.., bt) => Some(bt.as_ref()),
183 Arrow(.., bt) => Some(bt.as_ref()),
184 #[cfg(feature = "flatbuffers")]
185 FlatBuffers(.., bt) => Some(bt.as_ref()),
186 Fmt(.., bt) => Some(bt.as_ref()),
187 Io(.., bt) => Some(bt.as_ref()),
188 #[cfg(feature = "object_store")]
189 ObjectStore(.., bt) => Some(bt.as_ref()),
190 Jiff(.., bt) => Some(bt.as_ref()),
191 #[cfg(feature = "tokio")]
192 Join(.., bt) => Some(bt.as_ref()),
193 TryFromInt(.., bt) => Some(bt.as_ref()),
194 Prost(.., bt) => Some(bt.as_ref()),
195 Context(_, inner) => inner.backtrace(),
196 Shared(inner) => inner.backtrace(),
197 }
198 }
199
200 fn message(&self) -> String {
201 use VortexError::*;
202
203 match self {
204 Other(msg, _) => msg.to_string(),
205 External(err, _) => err.to_string(),
206 OutOfBounds(idx, start, stop, _) => {
207 format!("index {idx} out of bounds from {start} to {stop}")
208 }
209 Compute(msg, _) | InvalidArgument(msg, _) | Serde(msg, _) | AssertionFailed(msg, _) => {
210 format!("{msg}")
211 }
212 NotImplemented(func, by_whom, _) => {
213 format!("function {func} not implemented for {by_whom}")
214 }
215 MismatchedTypes(expected, actual, _) => {
216 format!("expected type: {expected} but instead got {actual}")
217 }
218 Context(msg, inner) => {
219 format!("{msg}:\n {inner}")
220 }
221 Shared(inner) => inner.message(),
222 Arrow(err, _) => {
223 format!("{err}")
224 }
225 #[cfg(feature = "flatbuffers")]
226 FlatBuffers(err, _) => {
227 format!("{err}")
228 }
229 Fmt(err, _) => {
230 format!("{err}")
231 }
232 Io(err, _) => {
233 format!("{err}")
234 }
235 #[cfg(feature = "object_store")]
236 ObjectStore(err, _) => {
237 format!("{err}")
238 }
239 Jiff(err, _) => {
240 format!("{err}")
241 }
242 #[cfg(feature = "tokio")]
243 Join(err, _) => {
244 format!("{err}")
245 }
246 TryFromInt(err, _) => {
247 format!("{err}")
248 }
249 Prost(err, _) => {
250 format!("{err}")
251 }
252 }
253 }
254}
255
256impl Display for VortexError {
257 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
258 write!(f, "{}", self.variant_prefix())?;
259 write!(f, "{}", self.message())?;
260 if let Some(backtrace) = self.backtrace()
261 && backtrace.status() == BacktraceStatus::Captured
262 {
263 write!(f, "\nBacktrace:\n{backtrace}")?;
264 }
265
266 Ok(())
267 }
268}
269
270impl Debug for VortexError {
271 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
272 write!(f, "{self}")
273 }
274}
275
276impl Error for VortexError {
277 fn source(&self) -> Option<&(dyn Error + 'static)> {
278 use VortexError::*;
279
280 match self {
281 External(err, _) => Some(err.as_ref()),
282 Context(_, inner) => inner.source(),
283 Shared(inner) => inner.source(),
284 Arrow(err, _) => Some(err),
285 #[cfg(feature = "flatbuffers")]
286 FlatBuffers(err, _) => Some(err),
287 Io(err, _) => Some(err),
288 #[cfg(feature = "object_store")]
289 ObjectStore(err, _) => Some(err),
290 Jiff(err, _) => Some(err),
291 #[cfg(feature = "tokio")]
292 Join(err, _) => Some(err),
293 Prost(err, _) => Some(err.as_ref()),
294 _ => None,
295 }
296 }
297}
298
299pub type VortexResult<T> = Result<T, VortexError>;
301
302pub type SharedVortexResult<T> = Result<T, Arc<VortexError>>;
304
305impl From<Arc<VortexError>> for VortexError {
306 fn from(value: Arc<VortexError>) -> Self {
307 Self::from(&value)
308 }
309}
310
311impl From<&Arc<VortexError>> for VortexError {
312 fn from(e: &Arc<VortexError>) -> Self {
313 if let VortexError::Shared(e_inner) = e.as_ref() {
314 VortexError::Shared(Arc::clone(e_inner))
316 } else {
317 VortexError::Shared(Arc::clone(e))
318 }
319 }
320}
321
322pub trait VortexExpect {
324 type Output;
326
327 fn vortex_expect(self, msg: &'static str) -> Self::Output;
336}
337
338impl<T, E> VortexExpect for Result<T, E>
339where
340 E: Into<VortexError>,
341{
342 type Output = T;
343
344 #[inline(always)]
345 fn vortex_expect(self, msg: &'static str) -> Self::Output {
346 self.map_err(|err| err.into())
347 .unwrap_or_else(|e| vortex_panic!(e.with_context(msg.to_string())))
348 }
349}
350
351impl<T> VortexExpect for Option<T> {
352 type Output = T;
353
354 #[inline(always)]
355 fn vortex_expect(self, msg: &'static str) -> Self::Output {
356 self.unwrap_or_else(|| {
357 let err = VortexError::AssertionFailed(
358 msg.to_string().into(),
359 Box::new(Backtrace::capture()),
360 );
361 vortex_panic!(err)
362 })
363 }
364}
365
366#[macro_export]
368macro_rules! vortex_err {
369 (Other: $($tts:tt)*) => {{
370 use std::backtrace::Backtrace;
371 let err_string = format!($($tts)*);
372 $crate::__private::must_use(
373 $crate::VortexError::Other(err_string.into(), Box::new(Backtrace::capture()))
374 )
375 }};
376 (AssertionFailed: $($tts:tt)*) => {{
377 use std::backtrace::Backtrace;
378 let err_string = format!($($tts)*);
379 $crate::__private::must_use(
380 $crate::VortexError::AssertionFailed(err_string.into(), Box::new(Backtrace::capture()))
381 )
382 }};
383 (IOError: $($tts:tt)*) => {{
384 use std::backtrace::Backtrace;
385 $crate::__private::must_use(
386 $crate::VortexError::IOError(err_string.into(), Box::new(Backtrace::capture()))
387 )
388 }};
389 (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
390 use std::backtrace::Backtrace;
391 $crate::__private::must_use(
392 $crate::VortexError::OutOfBounds($idx, $start, $stop, Box::new(Backtrace::capture()))
393 )
394 }};
395 (NotImplemented: $func:expr, $by_whom:expr) => {{
396 use std::backtrace::Backtrace;
397 $crate::__private::must_use(
398 $crate::VortexError::NotImplemented($func.into(), format!("{}", $by_whom).into(), Box::new(Backtrace::capture()))
399 )
400 }};
401 (MismatchedTypes: $expected:literal, $actual:expr) => {{
402 use std::backtrace::Backtrace;
403 $crate::__private::must_use(
404 $crate::VortexError::MismatchedTypes($expected.into(), $actual.to_string().into(), Box::new(Backtrace::capture()))
405 )
406 }};
407 (MismatchedTypes: $expected:expr, $actual:expr) => {{
408 use std::backtrace::Backtrace;
409 $crate::__private::must_use(
410 $crate::VortexError::MismatchedTypes($expected.to_string().into(), $actual.to_string().into(), Box::new(Backtrace::capture()))
411 )
412 }};
413 (Context: $msg:literal, $err:expr) => {{
414 $crate::__private::must_use(
415 $crate::VortexError::Context($msg.into(), Box::new($err))
416 )
417 }};
418 (External: $err:expr) => {{
419 use std::backtrace::Backtrace;
420 $crate::__private::must_use(
421 $crate::VortexError::External($err.into(), Box::new(Backtrace::capture()))
422 )
423 }};
424 ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {{
425 use std::backtrace::Backtrace;
426 $crate::__private::must_use(
427 $crate::VortexError::$variant(format!($fmt, $($arg),*).into(), Box::new(Backtrace::capture()))
428 )
429 }};
430 ($variant:ident: $err:expr $(,)?) => {
431 $crate::__private::must_use(
432 $crate::VortexError::$variant($err)
433 )
434 };
435 ($fmt:literal $(, $arg:expr)* $(,)?) => {
436 $crate::vortex_err!(Other: $fmt, $($arg),*)
437 };
438}
439
440#[macro_export]
442macro_rules! vortex_bail {
443 ($($tt:tt)+) => {
444 return Err($crate::vortex_err!($($tt)+))
445 };
446}
447
448#[macro_export]
451macro_rules! vortex_ensure {
452 ($cond:expr) => {
453 vortex_ensure!($cond, AssertionFailed: "{}", stringify!($cond));
454 };
455 ($cond:expr, $($tt:tt)*) => {
456 if !$cond {
457 $crate::vortex_bail!($($tt)*);
458 }
459 };
460}
461
462#[macro_export]
465macro_rules! vortex_ensure_eq {
466 ($left:expr, $right:expr) => {
467 $crate::vortex_ensure_eq!($left, $right, AssertionFailed: "{} != {}: {:?} != {:?}", stringify!($left), stringify!($right), $left, $right);
468 };
469 ($left:expr, $right:expr, $($tt:tt)*) => {
470 if $left != $right {
471 $crate::vortex_bail!($($tt)*);
472 }
473 };
474}
475
476#[macro_export]
479macro_rules! vortex_panic {
480 (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
481 $crate::vortex_panic!($crate::vortex_err!(OutOfBounds: $idx, $start, $stop))
482 }};
483 (NotImplemented: $func:expr, $for_whom:expr) => {{
484 $crate::vortex_panic!($crate::vortex_err!(NotImplemented: $func, $for_whom))
485 }};
486 (MismatchedTypes: $expected:literal, $actual:expr) => {{
487 $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
488 }};
489 (MismatchedTypes: $expected:expr, $actual:expr) => {{
490 $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
491 }};
492 (Context: $msg:literal, $err:expr) => {{
493 $crate::vortex_panic!($crate::vortex_err!(Context: $msg, $err))
494 }};
495 ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
496 $crate::vortex_panic!($crate::vortex_err!($variant: $fmt, $($arg),*))
497 };
498 ($err:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
499 let err: $crate::VortexError = $err;
500 panic!("{}", err.with_context(format!($fmt, $($arg),*)))
501 }};
502 ($fmt:literal $(, $arg:expr)* $(,)?) => {
503 $crate::vortex_panic!($crate::vortex_err!($fmt, $($arg),*))
504 };
505 ($err:expr) => {{
506 let err: $crate::VortexError = $err;
507 panic!("{}", err)
508 }};
509}
510
511impl From<arrow_schema::ArrowError> for VortexError {
512 fn from(value: arrow_schema::ArrowError) -> Self {
513 VortexError::Arrow(value, Box::new(Backtrace::capture()))
514 }
515}
516
517#[cfg(feature = "flatbuffers")]
518impl From<flatbuffers::InvalidFlatbuffer> for VortexError {
519 fn from(value: flatbuffers::InvalidFlatbuffer) -> Self {
520 VortexError::FlatBuffers(value, Box::new(Backtrace::capture()))
521 }
522}
523
524impl From<io::Error> for VortexError {
525 fn from(value: io::Error) -> Self {
526 VortexError::Io(value, Box::new(Backtrace::capture()))
527 }
528}
529
530#[cfg(feature = "object_store")]
531impl From<object_store::Error> for VortexError {
532 fn from(value: object_store::Error) -> Self {
533 VortexError::ObjectStore(value, Box::new(Backtrace::capture()))
534 }
535}
536
537impl From<jiff::Error> for VortexError {
538 fn from(value: jiff::Error) -> Self {
539 VortexError::Jiff(value, Box::new(Backtrace::capture()))
540 }
541}
542
543#[cfg(feature = "tokio")]
544impl From<tokio::task::JoinError> for VortexError {
545 fn from(value: tokio::task::JoinError) -> Self {
546 if value.is_panic() {
547 std::panic::resume_unwind(value.into_panic())
548 } else {
549 VortexError::Join(value, Box::new(Backtrace::capture()))
550 }
551 }
552}
553
554impl From<TryFromIntError> for VortexError {
555 fn from(value: TryFromIntError) -> Self {
556 VortexError::TryFromInt(value, Box::new(Backtrace::capture()))
557 }
558}
559
560impl From<prost::EncodeError> for VortexError {
561 fn from(value: prost::EncodeError) -> Self {
562 Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
563 }
564}
565
566impl From<prost::DecodeError> for VortexError {
567 fn from(value: prost::DecodeError) -> Self {
568 Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
569 }
570}
571
572impl From<prost::UnknownEnumValue> for VortexError {
573 fn from(value: prost::UnknownEnumValue) -> Self {
574 Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
575 }
576}
577
578#[doc(hidden)]
580pub mod __private {
581 #[doc(hidden)]
582 #[inline]
583 #[cold]
584 #[must_use]
585 pub const fn must_use(error: crate::VortexError) -> crate::VortexError {
586 error
587 }
588}