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