snarkvm_utilities/
errors.rs1pub fn io_error<S: ToString>(err: S) -> std::io::Error {
18 std::io::Error::other(err.to_string())
19}
20
21pub fn into_io_error<E: Into<anyhow::Error>>(err: E) -> std::io::Error {
25 let err: anyhow::Error = err.into();
26 std::io::Error::other(flatten_anyhow_error(&err))
27}
28
29#[inline]
31fn flatten_anyhow_error(error: &anyhow::Error) -> String {
32 let mut output = error.to_string();
33 for next in error.chain().skip(1) {
34 output = format!("{output} — {next}");
35 }
36 output
37}
38
39pub fn log_error(error: &anyhow::Error) {
45 tracing::error!("{}", flatten_anyhow_error(error));
46}
47
48pub fn log_warning(error: &anyhow::Error) {
54 tracing::warn!("{}", flatten_anyhow_error(error));
55}
56
57pub fn display_error(error: &anyhow::Error) {
61 eprintln!("⚠️ {error}");
62 error.chain().skip(1).for_each(|cause| eprintln!(" ↳ {cause}"));
63}
64
65#[macro_export]
72macro_rules! ensure_equals {
73 ($actual:expr, $expected:expr, $message:expr) => {
74 if $actual != $expected {
75 anyhow::bail!("{}: Was {} but expected {}.", $message, $actual, $expected);
76 }
77 };
78}
79
80pub trait PrettyUnwrap {
82 type Inner;
83
84 fn pretty_unwrap(self) -> Self::Inner;
86}
87
88#[track_caller]
90#[inline]
91fn pretty_panic(error: &anyhow::Error) -> ! {
92 let mut string = format!("⚠️ {error}");
93 error.chain().skip(1).for_each(|cause| string.push_str(&format!("\n ↳ {cause}")));
94 let caller = std::panic::Location::caller();
95
96 tracing::error!("[{}:{}] {string}", caller.file(), caller.line());
97 panic!("{string}");
98}
99
100impl<T> PrettyUnwrap for anyhow::Result<T> {
102 type Inner = T;
103
104 #[track_caller]
105 fn pretty_unwrap(self) -> Self::Inner {
106 match self {
107 Ok(result) => result,
108 Err(error) => {
109 pretty_panic(&error);
110 }
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::{PrettyUnwrap, flatten_anyhow_error, pretty_panic};
118
119 use anyhow::{Context, Result, anyhow, bail};
120
121 const ERRORS: [&str; 3] = ["Third error", "Second error", "First error"];
122
123 #[test]
124 fn flatten_error() {
125 let expected = format!("{} — {} — {}", ERRORS[0], ERRORS[1], ERRORS[2]);
126
127 let my_error = anyhow!(ERRORS[2]).context(ERRORS[1]).context(ERRORS[0]);
128 let result = flatten_anyhow_error(&my_error);
129
130 assert_eq!(result, expected);
131 }
132
133 #[test]
134 fn chained_error_panic_format() {
135 let expected = format!("⚠️ {}\n ↳ {}\n ↳ {}", ERRORS[0], ERRORS[1], ERRORS[2]);
136
137 let result = std::panic::catch_unwind(|| {
138 let my_error = anyhow!(ERRORS[2]).context(ERRORS[1]).context(ERRORS[0]);
139 pretty_panic(&my_error);
140 })
141 .unwrap_err();
142
143 assert_eq!(*result.downcast::<String>().expect("Error was not a string"), expected);
144 }
145
146 #[test]
147 fn chained_pretty_unwrap_format() {
148 let expected = format!("⚠️ {}\n ↳ {}\n ↳ {}", ERRORS[0], ERRORS[1], ERRORS[2]);
149
150 let result = std::panic::catch_unwind(|| {
152 fn level2() -> Result<()> {
153 bail!(ERRORS[2]);
154 }
155
156 fn level1() -> Result<()> {
157 level2().with_context(|| ERRORS[1])?;
158 Ok(())
159 }
160
161 fn level0() -> Result<()> {
162 level1().with_context(|| ERRORS[0])?;
163 Ok(())
164 }
165
166 level0().pretty_unwrap();
167 })
168 .unwrap_err();
169
170 assert_eq!(*result.downcast::<String>().expect("Error was not a string"), expected);
171 }
172
173 #[test]
175 fn test_nested_with_try_vm_runtime() {
176 use crate::try_vm_runtime;
177
178 let result = std::panic::catch_unwind(|| {
179 let vm_result = try_vm_runtime!(|| {
181 panic!("VM operation failed!");
182 });
183
184 assert!(vm_result.is_err(), "try_vm_runtime should catch VM panic");
185
186 "handled_vm_error"
188 });
189
190 assert!(result.is_ok(), "Should handle VM error gracefully");
191 assert_eq!(result.unwrap(), "handled_vm_error");
192 }
193}