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 _: () = assert!(size_of::<VortexError>() < 128);
83
84#[non_exhaustive]
86pub enum VortexError {
87 Other(ErrString, Box<Backtrace>),
89 External(Box<dyn Error + Send + Sync + 'static>, Box<Backtrace>),
91 OutOfBounds(usize, usize, usize, Box<Backtrace>),
93 Compute(ErrString, Box<Backtrace>),
95 InvalidArgument(ErrString, Box<Backtrace>),
97 Serde(ErrString, Box<Backtrace>),
99 NotImplemented(ErrString, ErrString, Box<Backtrace>),
101 MismatchedTypes(ErrString, ErrString, Box<Backtrace>),
103 AssertionFailed(ErrString, Box<Backtrace>),
105 Context(ErrString, Box<VortexError>),
107 Shared(Arc<VortexError>),
109 Arrow(arrow_schema::ArrowError, Box<Backtrace>),
111 #[cfg(feature = "flatbuffers")]
113 FlatBuffers(flatbuffers::InvalidFlatbuffer, Box<Backtrace>),
114 Fmt(fmt::Error, Box<Backtrace>),
116 Io(io::Error, Box<Backtrace>),
118 #[cfg(feature = "object_store")]
120 ObjectStore(object_store::Error, Box<Backtrace>),
121 Jiff(jiff::Error, Box<Backtrace>),
123 #[cfg(feature = "tokio")]
125 Join(tokio::task::JoinError, Box<Backtrace>),
126 TryFromInt(TryFromIntError, Box<Backtrace>),
128 Prost(Box<dyn Error + Send + Sync + 'static>, Box<Backtrace>),
130}
131
132impl VortexError {
133 pub fn with_context<T: Into<ErrString>>(self, msg: T) -> Self {
135 VortexError::Context(msg.into(), Box::new(self))
136 }
137
138 fn variant_prefix(&self) -> &'static str {
140 use VortexError::*;
141
142 match self {
143 Other(..) => "Other error: ",
144 External(..) => "External error: ",
145 OutOfBounds(..) => "Out of bounds error: ",
146 Compute(..) => "Compute error: ",
147 InvalidArgument(..) => "Invalid argument error: ",
148 Serde(..) => "Serde error: ",
149 NotImplemented(..) => "Not implemented error: ",
150 MismatchedTypes(..) => "Mismatched types error: ",
151 AssertionFailed(..) => "Assertion failed error: ",
152 Context(..) | Shared(..) => "", Arrow(..) => "Arrow error: ",
154 #[cfg(feature = "flatbuffers")]
155 FlatBuffers(..) => "Flat buffers error: ",
156 Fmt(..) => "Fmt: ",
157 Io(..) => "Io: ",
158 #[cfg(feature = "object_store")]
159 ObjectStore(..) => "Object store error: ",
160 Jiff(..) => "Jiff error: ",
161 #[cfg(feature = "tokio")]
162 Join(..) => "Tokio join error:",
163 TryFromInt(..) => "Try from int error:",
164 Prost(..) => "Prost error:",
165 }
166 }
167
168 fn backtrace(&self) -> Option<&Backtrace> {
169 use VortexError::*;
170
171 match self {
172 Other(.., bt) => Some(bt.as_ref()),
173 External(.., bt) => Some(bt.as_ref()),
174 OutOfBounds(.., bt) => Some(bt.as_ref()),
175 Compute(.., bt) => Some(bt.as_ref()),
176 InvalidArgument(.., bt) => Some(bt.as_ref()),
177 Serde(.., bt) => Some(bt.as_ref()),
178 NotImplemented(.., bt) => Some(bt.as_ref()),
179 MismatchedTypes(.., bt) => Some(bt.as_ref()),
180 AssertionFailed(.., bt) => Some(bt.as_ref()),
181 Arrow(.., bt) => Some(bt.as_ref()),
182 #[cfg(feature = "flatbuffers")]
183 FlatBuffers(.., bt) => Some(bt.as_ref()),
184 Fmt(.., bt) => Some(bt.as_ref()),
185 Io(.., bt) => Some(bt.as_ref()),
186 #[cfg(feature = "object_store")]
187 ObjectStore(.., bt) => Some(bt.as_ref()),
188 Jiff(.., bt) => Some(bt.as_ref()),
189 #[cfg(feature = "tokio")]
190 Join(.., bt) => Some(bt.as_ref()),
191 TryFromInt(.., bt) => Some(bt.as_ref()),
192 Prost(.., bt) => Some(bt.as_ref()),
193 Context(_, inner) => inner.backtrace(),
194 Shared(inner) => inner.backtrace(),
195 }
196 }
197
198 fn message(&self) -> String {
199 use VortexError::*;
200
201 match self {
202 Other(msg, _) => msg.to_string(),
203 External(err, _) => err.to_string(),
204 OutOfBounds(idx, start, stop, _) => {
205 format!("index {idx} out of bounds from {start} to {stop}")
206 }
207 Compute(msg, _) | InvalidArgument(msg, _) | Serde(msg, _) | AssertionFailed(msg, _) => {
208 format!("{msg}")
209 }
210 NotImplemented(func, by_whom, _) => {
211 format!("function {func} not implemented for {by_whom}")
212 }
213 MismatchedTypes(expected, actual, _) => {
214 format!("expected type: {expected} but instead got {actual}")
215 }
216 Context(msg, inner) => {
217 format!("{msg}:\n {inner}")
218 }
219 Shared(inner) => inner.message(),
220 Arrow(err, _) => {
221 format!("{err}")
222 }
223 #[cfg(feature = "flatbuffers")]
224 FlatBuffers(err, _) => {
225 format!("{err}")
226 }
227 Fmt(err, _) => {
228 format!("{err}")
229 }
230 Io(err, _) => {
231 format!("{err}")
232 }
233 #[cfg(feature = "object_store")]
234 ObjectStore(err, _) => {
235 format!("{err}")
236 }
237 Jiff(err, _) => {
238 format!("{err}")
239 }
240 #[cfg(feature = "tokio")]
241 Join(err, _) => {
242 format!("{err}")
243 }
244 TryFromInt(err, _) => {
245 format!("{err}")
246 }
247 Prost(err, _) => {
248 format!("{err}")
249 }
250 }
251 }
252}
253
254impl Display for VortexError {
255 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
256 write!(f, "{}", self.variant_prefix())?;
257 write!(f, "{}", self.message())?;
258 if let Some(backtrace) = self.backtrace()
259 && backtrace.status() == BacktraceStatus::Captured
260 {
261 write!(f, "\nBacktrace:\n{backtrace}")?;
262 }
263
264 Ok(())
265 }
266}
267
268impl Debug for VortexError {
269 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
270 write!(f, "{self}")
271 }
272}
273
274impl Error for VortexError {
275 fn source(&self) -> Option<&(dyn Error + 'static)> {
276 use VortexError::*;
277
278 match self {
279 External(err, _) => Some(err.as_ref()),
280 Context(_, inner) => inner.source(),
281 Shared(inner) => inner.source(),
282 Arrow(err, _) => Some(err),
283 #[cfg(feature = "flatbuffers")]
284 FlatBuffers(err, _) => Some(err),
285 Io(err, _) => Some(err),
286 #[cfg(feature = "object_store")]
287 ObjectStore(err, _) => Some(err),
288 Jiff(err, _) => Some(err),
289 #[cfg(feature = "tokio")]
290 Join(err, _) => Some(err),
291 Prost(err, _) => Some(err.as_ref()),
292 _ => None,
293 }
294 }
295}
296
297pub type VortexResult<T> = Result<T, VortexError>;
299
300pub type SharedVortexResult<T> = Result<T, Arc<VortexError>>;
302
303impl From<Arc<VortexError>> for VortexError {
304 fn from(value: Arc<VortexError>) -> Self {
305 Self::from(&value)
306 }
307}
308
309impl From<&Arc<VortexError>> for VortexError {
310 fn from(e: &Arc<VortexError>) -> Self {
311 if let VortexError::Shared(e_inner) = e.as_ref() {
312 VortexError::Shared(Arc::clone(e_inner))
314 } else {
315 VortexError::Shared(Arc::clone(e))
316 }
317 }
318}
319
320pub trait VortexExpect {
322 type Output;
324
325 fn vortex_expect(self, msg: &'static str) -> Self::Output;
334}
335
336impl<T, E> VortexExpect for Result<T, E>
337where
338 E: Into<VortexError>,
339{
340 type Output = T;
341
342 #[inline(always)]
343 fn vortex_expect(self, msg: &'static str) -> Self::Output {
344 self.map_err(|err| err.into())
345 .unwrap_or_else(|e| vortex_panic!(e.with_context(msg.to_string())))
346 }
347}
348
349impl<T> VortexExpect for Option<T> {
350 type Output = T;
351
352 #[inline(always)]
353 fn vortex_expect(self, msg: &'static str) -> Self::Output {
354 self.unwrap_or_else(|| {
355 let err = VortexError::AssertionFailed(
356 msg.to_string().into(),
357 Box::new(Backtrace::capture()),
358 );
359 vortex_panic!(err)
360 })
361 }
362}
363
364#[macro_export]
366macro_rules! vortex_err {
367 (Other: $($tts:tt)*) => {{
368 use std::backtrace::Backtrace;
369 let err_string = format!($($tts)*);
370 $crate::__private::must_use(
371 $crate::VortexError::Other(err_string.into(), Box::new(Backtrace::capture()))
372 )
373 }};
374 (AssertionFailed: $($tts:tt)*) => {{
375 use std::backtrace::Backtrace;
376 let err_string = format!($($tts)*);
377 $crate::__private::must_use(
378 $crate::VortexError::AssertionFailed(err_string.into(), Box::new(Backtrace::capture()))
379 )
380 }};
381 (IOError: $($tts:tt)*) => {{
382 use std::backtrace::Backtrace;
383 $crate::__private::must_use(
384 $crate::VortexError::IOError(err_string.into(), Box::new(Backtrace::capture()))
385 )
386 }};
387 (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
388 use std::backtrace::Backtrace;
389 $crate::__private::must_use(
390 $crate::VortexError::OutOfBounds($idx, $start, $stop, Box::new(Backtrace::capture()))
391 )
392 }};
393 (NotImplemented: $func:expr, $by_whom:expr) => {{
394 use std::backtrace::Backtrace;
395 $crate::__private::must_use(
396 $crate::VortexError::NotImplemented($func.into(), format!("{}", $by_whom).into(), Box::new(Backtrace::capture()))
397 )
398 }};
399 (MismatchedTypes: $expected:literal, $actual:expr) => {{
400 use std::backtrace::Backtrace;
401 $crate::__private::must_use(
402 $crate::VortexError::MismatchedTypes($expected.into(), $actual.to_string().into(), Box::new(Backtrace::capture()))
403 )
404 }};
405 (MismatchedTypes: $expected:expr, $actual:expr) => {{
406 use std::backtrace::Backtrace;
407 $crate::__private::must_use(
408 $crate::VortexError::MismatchedTypes($expected.to_string().into(), $actual.to_string().into(), Box::new(Backtrace::capture()))
409 )
410 }};
411 (Context: $msg:literal, $err:expr) => {{
412 $crate::__private::must_use(
413 $crate::VortexError::Context($msg.into(), Box::new($err))
414 )
415 }};
416 (External: $err:expr) => {{
417 use std::backtrace::Backtrace;
418 $crate::__private::must_use(
419 $crate::VortexError::External($err.into(), Box::new(Backtrace::capture()))
420 )
421 }};
422 ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {{
423 use std::backtrace::Backtrace;
424 $crate::__private::must_use(
425 $crate::VortexError::$variant(format!($fmt, $($arg),*).into(), Box::new(Backtrace::capture()))
426 )
427 }};
428 ($variant:ident: $err:expr $(,)?) => {
429 $crate::__private::must_use(
430 $crate::VortexError::$variant($err)
431 )
432 };
433 ($fmt:literal $(, $arg:expr)* $(,)?) => {
434 $crate::vortex_err!(Other: $fmt, $($arg),*)
435 };
436}
437
438#[macro_export]
440macro_rules! vortex_bail {
441 ($($tt:tt)+) => {
442 return Err($crate::vortex_err!($($tt)+))
443 };
444}
445
446#[macro_export]
449macro_rules! vortex_ensure {
450 ($cond:expr) => {
451 vortex_ensure!($cond, AssertionFailed: "{}", stringify!($cond));
452 };
453 ($cond:expr, $($tt:tt)*) => {
454 if !$cond {
455 $crate::vortex_bail!($($tt)*);
456 }
457 };
458}
459
460#[macro_export]
463macro_rules! vortex_ensure_eq {
464 ($left:expr, $right:expr) => {
465 $crate::vortex_ensure_eq!($left, $right, AssertionFailed: "{} != {}: {:?} != {:?}", stringify!($left), stringify!($right), $left, $right);
466 };
467 ($left:expr, $right:expr, $($tt:tt)*) => {
468 if $left != $right {
469 $crate::vortex_bail!($($tt)*);
470 }
471 };
472}
473
474#[macro_export]
477macro_rules! vortex_panic {
478 (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
479 $crate::vortex_panic!($crate::vortex_err!(OutOfBounds: $idx, $start, $stop))
480 }};
481 (NotImplemented: $func:expr, $for_whom:expr) => {{
482 $crate::vortex_panic!($crate::vortex_err!(NotImplemented: $func, $for_whom))
483 }};
484 (MismatchedTypes: $expected:literal, $actual:expr) => {{
485 $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
486 }};
487 (MismatchedTypes: $expected:expr, $actual:expr) => {{
488 $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
489 }};
490 (Context: $msg:literal, $err:expr) => {{
491 $crate::vortex_panic!($crate::vortex_err!(Context: $msg, $err))
492 }};
493 ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
494 $crate::vortex_panic!($crate::vortex_err!($variant: $fmt, $($arg),*))
495 };
496 ($err:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
497 let err: $crate::VortexError = $err;
498 panic!("{}", err.with_context(format!($fmt, $($arg),*)))
499 }};
500 ($fmt:literal $(, $arg:expr)* $(,)?) => {
501 $crate::vortex_panic!($crate::vortex_err!($fmt, $($arg),*))
502 };
503 ($err:expr) => {{
504 let err: $crate::VortexError = $err;
505 panic!("{}", err)
506 }};
507}
508
509impl From<arrow_schema::ArrowError> for VortexError {
510 fn from(value: arrow_schema::ArrowError) -> Self {
511 VortexError::Arrow(value, Box::new(Backtrace::capture()))
512 }
513}
514
515#[cfg(feature = "flatbuffers")]
516impl From<flatbuffers::InvalidFlatbuffer> for VortexError {
517 fn from(value: flatbuffers::InvalidFlatbuffer) -> Self {
518 VortexError::FlatBuffers(value, Box::new(Backtrace::capture()))
519 }
520}
521
522impl From<io::Error> for VortexError {
523 fn from(value: io::Error) -> Self {
524 VortexError::Io(value, Box::new(Backtrace::capture()))
525 }
526}
527
528#[cfg(feature = "object_store")]
529impl From<object_store::Error> for VortexError {
530 fn from(value: object_store::Error) -> Self {
531 VortexError::ObjectStore(value, Box::new(Backtrace::capture()))
532 }
533}
534
535impl From<jiff::Error> for VortexError {
536 fn from(value: jiff::Error) -> Self {
537 VortexError::Jiff(value, Box::new(Backtrace::capture()))
538 }
539}
540
541#[cfg(feature = "tokio")]
542impl From<tokio::task::JoinError> for VortexError {
543 fn from(value: tokio::task::JoinError) -> Self {
544 if value.is_panic() {
545 std::panic::resume_unwind(value.into_panic())
546 } else {
547 VortexError::Join(value, Box::new(Backtrace::capture()))
548 }
549 }
550}
551
552impl From<TryFromIntError> for VortexError {
553 fn from(value: TryFromIntError) -> Self {
554 VortexError::TryFromInt(value, Box::new(Backtrace::capture()))
555 }
556}
557
558impl From<prost::EncodeError> for VortexError {
559 fn from(value: prost::EncodeError) -> Self {
560 Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
561 }
562}
563
564impl From<prost::DecodeError> for VortexError {
565 fn from(value: prost::DecodeError) -> Self {
566 Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
567 }
568}
569
570impl From<prost::UnknownEnumValue> for VortexError {
571 fn from(value: prost::UnknownEnumValue) -> Self {
572 Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
573 }
574}
575
576#[doc(hidden)]
578pub mod __private {
579 #[doc(hidden)]
580 #[inline]
581 #[cold]
582 #[must_use]
583 pub const fn must_use(error: crate::VortexError) -> crate::VortexError {
584 error
585 }
586}