Skip to main content

vortex_error/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4#![deny(missing_docs)]
5
6//! This crate defines error & result types for Vortex.
7//! It also contains a variety of useful macros for error handling.
8
9use 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/// A string that can be used as an error message.
26#[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/// The top-level error type for Vortex.
85#[non_exhaustive]
86pub enum VortexError {
87    /// A catch-all error variant
88    Other(ErrString, Box<Backtrace>),
89    /// A wrapped external error
90    External(Box<dyn Error + Send + Sync + 'static>, Box<Backtrace>),
91    /// An index is out of bounds.
92    OutOfBounds(usize, usize, usize, Box<Backtrace>),
93    /// An error occurred while executing a compute kernel.
94    Compute(ErrString, Box<Backtrace>),
95    /// An invalid argument was provided.
96    InvalidArgument(ErrString, Box<Backtrace>),
97    /// An error occurred while serializing or deserializing.
98    Serde(ErrString, Box<Backtrace>),
99    /// An unimplemented function was called.
100    NotImplemented(ErrString, ErrString, Box<Backtrace>),
101    /// A type mismatch occurred.
102    MismatchedTypes(ErrString, ErrString, Box<Backtrace>),
103    /// An assertion failed.
104    AssertionFailed(ErrString, Box<Backtrace>),
105    /// A wrapper for other errors, carrying additional context.
106    Context(ErrString, Box<VortexError>),
107    /// A wrapper for shared errors that require cloning.
108    Shared(Arc<VortexError>),
109    /// A wrapper for errors from the Arrow library.
110    Arrow(arrow_schema::ArrowError, Box<Backtrace>),
111    /// A wrapper for errors from the FlatBuffers library.
112    #[cfg(feature = "flatbuffers")]
113    FlatBuffers(flatbuffers::InvalidFlatbuffer, Box<Backtrace>),
114    /// A wrapper for formatting errors.
115    Fmt(fmt::Error, Box<Backtrace>),
116    /// A wrapper for IO errors.
117    Io(io::Error, Box<Backtrace>),
118    /// A wrapper for errors from the Object Store library.
119    #[cfg(feature = "object_store")]
120    ObjectStore(object_store::Error, Box<Backtrace>),
121    /// A wrapper for errors from the Jiff library.
122    Jiff(jiff::Error, Box<Backtrace>),
123    /// A wrapper for Tokio join error.
124    #[cfg(feature = "tokio")]
125    Join(tokio::task::JoinError, Box<Backtrace>),
126    /// Wrap errors for fallible integer casting.
127    TryFromInt(TryFromIntError, Box<Backtrace>),
128    /// Wrap protobuf-related errors
129    Prost(Box<dyn Error + Send + Sync + 'static>, Box<Backtrace>),
130}
131
132impl VortexError {
133    /// Adds additional context to an error.
134    pub fn with_context<T: Into<ErrString>>(self, msg: T) -> Self {
135        VortexError::Context(msg.into(), Box::new(self))
136    }
137
138    /// Error prefix by variant
139    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(..) => "", // basically delegate to the underlying one
153            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
297/// A type alias for Results that return VortexErrors as their error type.
298pub type VortexResult<T> = Result<T, VortexError>;
299
300/// A vortex result that can be shared or cloned.
301pub 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            // don't re-wrap
313            VortexError::Shared(Arc::clone(e_inner))
314        } else {
315            VortexError::Shared(Arc::clone(e))
316        }
317    }
318}
319
320/// A trait for expect-ing a VortexResult or an Option.
321pub trait VortexExpect {
322    /// The type of the value being expected.
323    type Output;
324
325    /// Returns the value of the result if it is Ok, otherwise panics with the error.
326    /// Should be called only in contexts where the error condition represents a bug (programmer error).
327    ///
328    /// # `&'static` message lifetime
329    ///
330    /// The panic string argument should be a string literal, hence the `&'static` lifetime. If
331    /// you'd like to panic with a dynamic format string, consider using `unwrap_or_else` combined
332    /// with the `vortex_panic!` macro instead.
333    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    #[allow(clippy::inline_always)]
343    #[inline(always)]
344    fn vortex_expect(self, msg: &'static str) -> Self::Output {
345        match self {
346            Ok(value) => value,
347            Err(err) => __private::result_expect_failed(err, msg),
348        }
349    }
350}
351
352impl<T> VortexExpect for Option<T> {
353    type Output = T;
354
355    #[allow(clippy::inline_always)]
356    #[inline(always)]
357    fn vortex_expect(self, msg: &'static str) -> Self::Output {
358        match self {
359            Some(value) => value,
360            None => __private::option_expect_failed(msg),
361        }
362    }
363}
364
365/// A convenient macro for creating a VortexError.
366#[macro_export]
367macro_rules! vortex_err {
368    (Other: $($tts:tt)*) => {
369        $crate::__private::fmt_err($crate::VortexError::Other, &format_args!($($tts)*))
370    };
371    (AssertionFailed: $($tts:tt)*) => {
372        $crate::__private::fmt_err($crate::VortexError::AssertionFailed, &format_args!($($tts)*))
373    };
374    (IOError: $($tts:tt)*) => {{
375        use std::backtrace::Backtrace;
376        $crate::__private::must_use(
377            $crate::VortexError::IOError(err_string.into(), Box::new(Backtrace::capture()))
378        )
379    }};
380    (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {
381        $crate::__private::out_of_bounds($idx, $start, $stop)
382    };
383    (NotImplemented: $func:expr, $by_whom:expr) => {
384        $crate::__private::not_implemented($func, &format_args!("{}", $by_whom))
385    };
386    (MismatchedTypes: $expected:expr, $actual:expr) => {
387        $crate::__private::mismatched_types(&$expected, &$actual)
388    };
389    (Context: $msg:literal, $err:expr) => {
390        $crate::__private::context($msg, $err)
391    };
392    (External: $err:expr) => {
393        $crate::__private::external($err)
394    };
395    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
396        $crate::__private::fmt_err($crate::VortexError::$variant, &format_args!($fmt $(, $arg)*))
397    };
398    ($variant:ident: $err:expr $(,)?) => {
399        $crate::__private::must_use(
400            $crate::VortexError::$variant($err)
401        )
402    };
403    ($fmt:literal $(, $arg:expr)* $(,)?) => {
404        $crate::vortex_err!(Other: $fmt, $($arg),*)
405    };
406}
407
408/// A convenience macro for returning a VortexError.
409#[macro_export]
410macro_rules! vortex_bail {
411    ($($tt:tt)+) => {
412        return Err($crate::vortex_err!($($tt)+))
413    };
414}
415
416/// A macro that mirrors `assert!` but instead of panicking on a failed condition,
417/// it will immediately return an erroneous `VortexResult` to the calling context.
418#[macro_export]
419macro_rules! vortex_ensure {
420    ($cond:expr) => {
421        $crate::vortex_ensure!($cond, AssertionFailed: "{}", stringify!($cond));
422    };
423    ($cond:expr, $($tt:tt)*) => {
424        if !$cond {
425            $crate::vortex_bail!($($tt)*);
426        }
427    };
428}
429
430/// A macro that mirrors `assert_eq!` but instead of panicking when `left != right`, it will
431/// immediately return an erroneous `VortexResult` to the calling context.
432///
433/// Both values must implement [`PartialEq`] and [`Display`], and each is
434/// evaluated exactly once. Use [`vortex_ensure!`] for values that do not implement `Display`.
435///
436/// By default this returns an [`AssertionFailed`](VortexError::AssertionFailed) error that
437/// shows both expressions and their values, so most callers do not need a message:
438///
439/// ```
440/// # use vortex_error::{VortexResult, vortex_ensure_eq};
441/// fn check(len: usize) -> VortexResult<()> {
442///     vortex_ensure_eq!(len, 2);
443///     Ok(())
444/// }
445///
446/// let err = check(3).unwrap_err().to_string();
447/// assert!(err.starts_with("Assertion failed error: `len == 2`\n  left: 3\n right: 2"));
448/// ```
449///
450/// A custom message, optionally prefixed with an error variant, replaces the expressions. The
451/// values are still appended, so the message does not need to repeat them:
452///
453/// ```
454/// # use vortex_error::{VortexResult, vortex_ensure_eq};
455/// fn check(len: usize) -> VortexResult<()> {
456///     vortex_ensure_eq!(len, 2, InvalidArgument: "map entries must have {} fields", 2);
457///     Ok(())
458/// }
459///
460/// let err = check(3).unwrap_err().to_string();
461/// assert!(err.starts_with(
462///     "Invalid argument error: map entries must have 2 fields\n  left: 3\n right: 2"
463/// ));
464/// ```
465#[macro_export]
466macro_rules! vortex_ensure_eq {
467    ($left:expr, $right:expr $(,)?) => {
468        match (&$left, &$right) {
469            (left_val, right_val) => {
470                if *left_val != *right_val {
471                    $crate::vortex_bail!(
472                        AssertionFailed: "`{} == {}`\n  left: {}\n right: {}",
473                        stringify!($left),
474                        stringify!($right),
475                        left_val,
476                        right_val
477                    );
478                }
479            }
480        }
481    };
482    ($left:expr, $right:expr, $variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
483        match (&$left, &$right) {
484            (left_val, right_val) => {
485                if *left_val != *right_val {
486                    $crate::vortex_bail!(
487                        $variant: "{}\n  left: {}\n right: {}",
488                        format_args!($fmt $(, $arg)*),
489                        left_val,
490                        right_val
491                    );
492                }
493            }
494        }
495    };
496    ($left:expr, $right:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {
497        $crate::vortex_ensure_eq!($left, $right, Other: $fmt $(, $arg)*)
498    };
499}
500
501/// A convenient macro for panicking with a VortexError in the presence of a programmer error
502/// (e.g., an invariant has been violated).
503#[macro_export]
504macro_rules! vortex_panic {
505    (OutOfBounds: $idx:expr, $start:expr, $stop:expr) => {{
506        $crate::vortex_panic!($crate::vortex_err!(OutOfBounds: $idx, $start, $stop))
507    }};
508    (NotImplemented: $func:expr, $for_whom:expr) => {{
509        $crate::vortex_panic!($crate::vortex_err!(NotImplemented: $func, $for_whom))
510    }};
511    (MismatchedTypes: $expected:literal, $actual:expr) => {{
512        $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
513    }};
514    (MismatchedTypes: $expected:expr, $actual:expr) => {{
515        $crate::vortex_panic!($crate::vortex_err!(MismatchedTypes: $expected, $actual))
516    }};
517    (Context: $msg:literal, $err:expr) => {{
518        $crate::vortex_panic!($crate::vortex_err!(Context: $msg, $err))
519    }};
520    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
521        $crate::vortex_panic!($crate::vortex_err!($variant: $fmt, $($arg),*))
522    };
523    ($err:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {
524        $crate::__private::panic_err_ctx($err, &format_args!($fmt $(, $arg)*))
525    };
526    ($fmt:literal $(, $arg:expr)* $(,)?) => {
527        $crate::__private::panic_fmt(&format_args!($fmt $(, $arg)*))
528    };
529    ($err:expr) => {
530        $crate::__private::panic_err($err)
531    };
532}
533
534impl From<arrow_schema::ArrowError> for VortexError {
535    fn from(value: arrow_schema::ArrowError) -> Self {
536        VortexError::Arrow(value, Box::new(Backtrace::capture()))
537    }
538}
539
540#[cfg(feature = "flatbuffers")]
541impl From<flatbuffers::InvalidFlatbuffer> for VortexError {
542    fn from(value: flatbuffers::InvalidFlatbuffer) -> Self {
543        VortexError::FlatBuffers(value, Box::new(Backtrace::capture()))
544    }
545}
546
547impl From<io::Error> for VortexError {
548    fn from(value: io::Error) -> Self {
549        VortexError::Io(value, Box::new(Backtrace::capture()))
550    }
551}
552
553#[cfg(feature = "object_store")]
554impl From<object_store::Error> for VortexError {
555    fn from(value: object_store::Error) -> Self {
556        VortexError::ObjectStore(value, Box::new(Backtrace::capture()))
557    }
558}
559
560impl From<jiff::Error> for VortexError {
561    fn from(value: jiff::Error) -> Self {
562        VortexError::Jiff(value, Box::new(Backtrace::capture()))
563    }
564}
565
566#[cfg(feature = "tokio")]
567impl From<tokio::task::JoinError> for VortexError {
568    fn from(value: tokio::task::JoinError) -> Self {
569        if value.is_panic() {
570            std::panic::resume_unwind(value.into_panic())
571        } else {
572            VortexError::Join(value, Box::new(Backtrace::capture()))
573        }
574    }
575}
576
577impl From<TryFromIntError> for VortexError {
578    fn from(value: TryFromIntError) -> Self {
579        VortexError::TryFromInt(value, Box::new(Backtrace::capture()))
580    }
581}
582
583impl From<prost::EncodeError> for VortexError {
584    fn from(value: prost::EncodeError) -> Self {
585        Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
586    }
587}
588
589impl From<prost::DecodeError> for VortexError {
590    fn from(value: prost::DecodeError) -> Self {
591        Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
592    }
593}
594
595impl From<prost::UnknownEnumValue> for VortexError {
596    fn from(value: prost::UnknownEnumValue) -> Self {
597        Self::Prost(Box::new(value), Box::new(Backtrace::capture()))
598    }
599}
600
601/// We want our error handlers to be fast in the happy path, so we inline them.
602/// However, if we inline them as is, the call site gets the cold branch with
603/// panic! macros which gets expanded into backtrace collection and throwing.
604/// LLVM inliner/vectoriser in turn doesn't vectorize some code.
605/// So we inline only the happy path, the error path gets to a cold handler which
606/// is never inlined and thus doesn't prevent vectorization.
607#[doc(hidden)]
608#[expect(
609    clippy::panic,
610    reason = "cold handlers for vortex_panic!/vortex_expect intentionally panic"
611)]
612pub mod __private {
613    use std::backtrace::Backtrace;
614    use std::error::Error;
615    use std::fmt;
616    use std::fmt::Arguments;
617
618    use crate::ErrString;
619    use crate::VortexError;
620
621    #[doc(hidden)]
622    #[inline]
623    #[cold]
624    #[must_use]
625    pub const fn must_use(error: VortexError) -> VortexError {
626        error
627    }
628
629    #[doc(hidden)]
630    #[cold]
631    #[inline(never)]
632    #[must_use]
633    pub fn fmt_err(
634        variant: fn(ErrString, Box<Backtrace>) -> VortexError,
635        args: &Arguments<'_>,
636    ) -> VortexError {
637        variant(args.to_string().into(), Box::new(Backtrace::capture()))
638    }
639
640    #[doc(hidden)]
641    #[cold]
642    #[inline(never)]
643    #[must_use]
644    pub fn out_of_bounds(idx: usize, start: usize, stop: usize) -> VortexError {
645        VortexError::OutOfBounds(idx, start, stop, Box::new(Backtrace::capture()))
646    }
647
648    #[doc(hidden)]
649    #[cold]
650    #[inline(never)]
651    #[must_use]
652    pub fn not_implemented(func: impl Into<ErrString>, by_whom: &Arguments<'_>) -> VortexError {
653        VortexError::NotImplemented(
654            func.into(),
655            by_whom.to_string().into(),
656            Box::new(Backtrace::capture()),
657        )
658    }
659
660    #[doc(hidden)]
661    #[cold]
662    #[inline(never)]
663    #[must_use]
664    pub fn mismatched_types(expected: &dyn fmt::Display, actual: &dyn fmt::Display) -> VortexError {
665        VortexError::MismatchedTypes(
666            expected.to_string().into(),
667            actual.to_string().into(),
668            Box::new(Backtrace::capture()),
669        )
670    }
671
672    #[doc(hidden)]
673    #[cold]
674    #[inline(never)]
675    #[must_use]
676    pub fn context(msg: &'static str, err: VortexError) -> VortexError {
677        VortexError::Context(msg.into(), Box::new(err))
678    }
679
680    #[doc(hidden)]
681    #[cold]
682    #[inline(never)]
683    #[must_use]
684    pub fn external(err: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> VortexError {
685        VortexError::External(err.into(), Box::new(Backtrace::capture()))
686    }
687
688    #[doc(hidden)]
689    #[cold]
690    #[inline(never)]
691    pub fn panic_err(err: VortexError) -> ! {
692        panic!("{err}")
693    }
694
695    #[doc(hidden)]
696    #[cold]
697    #[inline(never)]
698    pub fn panic_fmt(args: &Arguments<'_>) -> ! {
699        panic!(
700            "{}",
701            VortexError::Other(args.to_string().into(), Box::new(Backtrace::capture()))
702        )
703    }
704
705    #[doc(hidden)]
706    #[cold]
707    #[inline(never)]
708    pub fn panic_err_ctx(err: VortexError, args: &Arguments<'_>) -> ! {
709        panic!("{}", err.with_context(args.to_string()))
710    }
711
712    #[doc(hidden)]
713    #[cold]
714    #[inline(never)]
715    pub fn result_expect_failed(err: impl Into<VortexError>, msg: &'static str) -> ! {
716        panic!("{}", err.into().with_context(msg))
717    }
718
719    #[doc(hidden)]
720    #[cold]
721    #[inline(never)]
722    pub fn option_expect_failed(msg: &'static str) -> ! {
723        panic!(
724            "{}",
725            VortexError::AssertionFailed(msg.into(), Box::new(Backtrace::capture()))
726        )
727    }
728}