Skip to main content

wasm4pm_cli/
errors.rs

1use colored::*;
2use std::process;
3
4/// Custom error types for the wasm4pm (wpm) CLI
5#[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
23/// A trait for reporting errors to the user in a pretty format
24pub trait Report {
25    /// Prints the error to stderr and exits with a non-zero code
26    fn die(&self) -> !;
27    /// Prints the error to stderr
28    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        // Only show backtrace if RUST_BACKTRACE=1 is set
44        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
62/// Extension trait for adding common context to results
63pub 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>;