sk_core/
errors.rs

1pub use std::backtrace::Backtrace;
2use std::ops::Deref;
3
4pub use anyhow::{
5    anyhow,
6    bail,
7    ensure,
8};
9pub use paste::paste;
10pub use regex::{
11    Regex,
12    RegexBuilder,
13};
14pub use thiserror::Error;
15
16pub type EmptyResult = anyhow::Result<()>;
17
18pub const BUILD_DIR: &str = "/.build/";
19pub const RUSTC_DIR: &str = "/rustc/";
20pub const GLIBC: &str = "glibc";
21
22// This is sortof a stupid hack, because anyhow::Error doesn't derive from
23// std::error::Error, but the reconcile functions require you to return a
24// result that derives from std::error::Error.  So we just wrap the anyhow,
25// and then implement deref for it so we can get back to the underlying error
26// wherever we actually care.
27#[derive(Debug, Error)]
28#[error(transparent)]
29pub struct AnyhowError(#[from] anyhow::Error);
30
31impl Deref for AnyhowError {
32    type Target = anyhow::Error;
33
34    fn deref(&self) -> &Self::Target {
35        &self.0
36    }
37}
38
39// This macro creates an enum which derives from thiserror::Error, and also
40// creates constructor functions in snake case for each of the enum variants
41#[macro_export]
42macro_rules! err_impl {
43    (@hidden $errtype:ident, $item:ident, String) => {
44        paste! {
45            pub(crate) fn [<$item:snake>](in_: &str) -> anyhow::Error {
46                anyhow!{$errtype::$item(in_.into())}
47            }
48        }
49    };
50
51    (@hidden $errtype:ident, $item:ident, $($dtype:tt)::+) => {
52        paste! {
53            pub(crate) fn [<$item:snake>](in_: &$($dtype)::+) -> anyhow::Error {
54                anyhow!{$errtype::$item(in_.clone())}
55            }
56        }
57    };
58
59    ($errtype:ident,
60        $(#[$errinfo:meta] $item:ident($($dtype:tt)::+),)+
61    ) => {
62        #[derive(Debug, Error)]
63        pub(crate) enum $errtype {
64            $(#[$errinfo] $item($($dtype)::+)),+
65        }
66
67        impl $errtype {
68            $(err_impl! {@hidden $errtype, $item, $($dtype)::+})+
69        }
70    };
71}
72
73// This unholy mess prunes down a 70-plus tokio-laden backtrace into _just_ the
74// bits of the backtrace that are relevant to our code.  It's _heavily_ modified from
75// https://github.com/rust-lang/rust/issues/79676#issuecomment-1502670961.
76//
77// It's also reasonably expensive to call, which I more-or-less justify since it should
78// only be called in exceptional circumstances, but if it's getting called regularly it
79// potentially maybe could bog things down?
80//
81// It's also also probably fairly brittle, so there's a non-zero chance that important
82// stack frames will get pruned.
83#[macro_export]
84macro_rules! skerr {
85    (@hidden $err:ident, $msg:literal, $($args:expr),*) => {
86        let bt = $err.backtrace().to_string();
87        let re = RegexBuilder::new(r"^\s+\d+(?s:.*?)(\s+at\s+.*:\d+)$")
88            .multi_line(true)
89            .build()
90            .unwrap();
91        let mut skipped_frames = 0;
92        let mut filtered_bt = re.find_iter(&bt).fold(String::new(), |mut acc, frame| {
93            let frame = frame.as_str();
94            if frame.contains(BUILD_DIR) || frame.contains(RUSTC_DIR) || frame.contains (GLIBC) {
95                skipped_frames += 1;
96            } else if !frame.is_empty() {
97                if skipped_frames == 1 {
98                    acc += &format!("      -- <skipped 1 frame> --\n");
99                } else if skipped_frames > 1 {
100                    acc += &format!("      -- <skipped {skipped_frames} frames> --\n");
101                }
102                acc += &format!("{frame}\n");
103                skipped_frames = 0;
104            }
105            acc
106        });
107
108        if skipped_frames == 1 {
109            filtered_bt += &format!("      -- <skipped 1 frame> --");
110        } else if skipped_frames > 1 {
111            filtered_bt += &format!("      -- <skipped {skipped_frames} frames> --");
112        }
113        error!(concat!($msg, "\n\n{}\n\nPartial Stack Trace:\n\n{}\n\n") $(, $args)*, $err, filtered_bt);
114    };
115
116    ($err:ident, $msg:literal) => {
117        skerr! {@hidden $err, $msg, };
118    };
119
120    ($err:ident, $msg:literal, $($args:expr),*) => {
121        skerr! {@hidden $err, $msg, $($args),*};
122    };
123}
124
125pub use {
126    err_impl,
127    skerr,
128};