1use colored::*;
2use std::process;
3
4#[derive(thiserror::Error, Debug)]
6pub enum Wasm4pmError {
7 #[error("IO error: {0}")]
8 Io(#[from] std::io::Error),
9
10 #[error("JSON error: {0}")]
11 Json(#[from] serde_json::Error),
12
13 #[error("Configuration error: {0}")]
14 Config(String),
15
16 #[error("Validation failed: {0}")]
17 Validation(String),
18
19 #[error("Execution error: {0}")]
20 Execution(String),
21}
22
23pub trait Report {
25 fn die(&self) -> !;
27 fn report(&self);
29}
30
31impl Report for anyhow::Error {
32 fn report(&self) {
33 eprintln!("{} {}", "error:".red().bold(), self);
34
35 let mut chain = self.chain().skip(1).peekable();
36 if chain.peek().is_some() {
37 eprintln!("\n{}", "Caused by:".yellow().bold());
38 for (i, cause) in chain.enumerate() {
39 eprintln!(" {:>2}: {}", i, cause);
40 }
41 }
42
43 if std::env::var("RUST_BACKTRACE")
45 .map(|v| v == "1")
46 .unwrap_or(false)
47 {
48 let backtrace = self.backtrace();
49 if let std::backtrace::BacktraceStatus::Captured = backtrace.status() {
50 eprintln!("\n{}", "Stack Backtrace:".cyan().bold());
51 eprintln!("{}", backtrace);
52 }
53 }
54 }
55
56 fn die(&self) -> ! {
57 self.report();
58 process::exit(1);
59 }
60}
61
62pub trait ContextExt<T, E> {
64 fn with_io_context<S: Into<String>>(self, msg: S) -> anyhow::Result<T>;
65 fn with_parse_context<S: Into<String>>(self, msg: S) -> anyhow::Result<T>;
66}
67
68impl<T, E> ContextExt<T, E> for std::result::Result<T, E>
69where
70 E: std::error::Error + Send + Sync + 'static,
71{
72 fn with_io_context<S: Into<String>>(self, msg: S) -> anyhow::Result<T> {
73 anyhow::Result::from(self).map_err(|e| {
74 let m = msg.into();
75 anyhow::anyhow!(e).context(format!("Failed IO operation: {}", m))
76 })
77 }
78
79 fn with_parse_context<S: Into<String>>(self, msg: S) -> anyhow::Result<T> {
80 anyhow::Result::from(self).map_err(|e| {
81 let m = msg.into();
82 anyhow::anyhow!(e).context(format!("Failed to parse: {}", m))
83 })
84 }
85}
86
87pub type Result<T> = anyhow::Result<T>;