snarkvm_utilities/
errors.rs1use colored::Colorize;
17use std::borrow::Borrow;
18
19#[inline]
21pub fn io_error<S: ToString>(err: S) -> std::io::Error {
22 std::io::Error::other(err.to_string())
23}
24
25#[inline]
29pub fn into_io_error<E: Into<anyhow::Error>>(err: E) -> std::io::Error {
30 let err: anyhow::Error = err.into();
31 std::io::Error::other(flatten_error(&err))
32}
33
34#[inline]
40pub fn flatten_error<E: Borrow<anyhow::Error>>(error: E) -> String {
41 let error = error.borrow();
42 let chain = error.chain().skip(1).map(|next| next.to_string()).collect::<Vec<String>>().join(" — ");
43 format!("{error}{}", format!(" — {chain}").dimmed())
44}
45
46#[track_caller]
50#[inline]
51pub fn display_error<E: Borrow<anyhow::Error>>(error: E) {
52 let error = error.borrow();
53 eprintln!("⚠️ {error}");
54 error.chain().skip(1).for_each(|cause| eprintln!(" ↳ {cause}"));
55}
56
57#[macro_export]
64macro_rules! ensure_equals {
65 ($actual:expr, $expected:expr, $message:expr $(, $format_args:tt)*) => {
66 if $actual != $expected {
67 anyhow::bail!("{}: Was {} but expected {}.", format!($message $(, $format_args)*), $actual, $expected);
68 }
69 };
70}
71
72pub trait PrettyUnwrap {
74 type Inner;
75
76 fn pretty_unwrap(self) -> Self::Inner;
78
79 fn pretty_expect<S: ToString>(self, context: S) -> Self::Inner;
81}
82
83#[track_caller]
85#[inline]
86fn pretty_panic(error: &anyhow::Error) -> ! {
87 let mut string = format!("⚠️ {error}");
88 error.chain().skip(1).for_each(|cause| string.push_str(&format!("\n ↳ {cause}")));
89 let caller = std::panic::Location::caller();
90
91 tracing::error!("[{}:{}] {string}", caller.file(), caller.line());
92 panic!("{string}");
93}
94
95impl<T> PrettyUnwrap for anyhow::Result<T> {
97 type Inner = T;
98
99 #[track_caller]
100 fn pretty_unwrap(self) -> Self::Inner {
101 match self {
102 Ok(result) => result,
103 Err(error) => {
104 pretty_panic(&error);
105 }
106 }
107 }
108
109 #[track_caller]
110 fn pretty_expect<S: ToString>(self, context: S) -> Self::Inner {
111 match self {
112 Ok(result) => result,
113 Err(error) => {
114 pretty_panic(&error.context(context.to_string()));
115 }
116 }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::{PrettyUnwrap, flatten_error, pretty_panic};
123
124 use anyhow::{Context, Result, anyhow, bail};
125 use colored::Colorize;
126
127 const ERRORS: [&str; 3] = ["Third error", "Second error", "First error"];
128
129 #[test]
130 fn test_flatten_error() {
131 let expected = format!("{}{}", ERRORS[0], format!(" — {} — {}", ERRORS[1], ERRORS[2]).dimmed());
133
134 let my_error = anyhow!(ERRORS[2]).context(ERRORS[1]).context(ERRORS[0]);
135 let result = flatten_error(&my_error);
136
137 assert_eq!(result, expected);
138 }
139
140 #[test]
141 fn chained_error_panic_format() {
142 let expected = format!("⚠️ {}\n ↳ {}\n ↳ {}", ERRORS[0], ERRORS[1], ERRORS[2]);
143
144 let result = std::panic::catch_unwind(|| {
145 let my_error = anyhow!(ERRORS[2]).context(ERRORS[1]).context(ERRORS[0]);
146 pretty_panic(&my_error);
147 })
148 .unwrap_err();
149
150 assert_eq!(*result.downcast::<String>().expect("Error was not a string"), expected);
151 }
152
153 #[test]
154 fn chained_pretty_unwrap_format() {
155 let expected = format!("⚠️ {}\n ↳ {}\n ↳ {}", ERRORS[0], ERRORS[1], ERRORS[2]);
156
157 let result = std::panic::catch_unwind(|| {
159 fn level2() -> Result<()> {
160 bail!(ERRORS[2]);
161 }
162
163 fn level1() -> Result<()> {
164 level2().with_context(|| ERRORS[1])?;
165 Ok(())
166 }
167
168 fn level0() -> Result<()> {
169 level1().with_context(|| ERRORS[0])?;
170 Ok(())
171 }
172
173 level0().pretty_unwrap();
174 })
175 .unwrap_err();
176
177 assert_eq!(*result.downcast::<String>().expect("Error was not a string"), expected);
178 }
179
180 #[test]
182 fn test_nested_with_try_vm_runtime() {
183 use crate::try_vm_runtime;
184
185 let result = std::panic::catch_unwind(|| {
186 let vm_result = try_vm_runtime!(|| {
188 panic!("VM operation failed!");
189 });
190
191 assert!(vm_result.is_err(), "try_vm_runtime should catch VM panic");
192
193 "handled_vm_error"
195 });
196
197 assert!(result.is_ok(), "Should handle VM error gracefully");
198 assert_eq!(result.unwrap(), "handled_vm_error");
199 }
200
201 #[test]
203 fn ensure_equals_with_format_string() {
204 let correct = "correct";
205 let error = || -> Result<()> {
206 ensure_equals!(1, 2, "Test value {} {correct}", "is not");
207 Ok(())
208 }()
209 .unwrap_err();
210
211 assert_eq!(error.to_string(), "Test value is not correct: Was 1 but expected 2.");
212 }
213}