wasmer_cli/
error.rs

1//! Implements `PretyError` to print pretty errors in the CLI (when they happen)
2
3use anyhow::{Chain, Error};
4use colored::*;
5use std::fmt::{self, Debug, Write};
6#[cfg(not(any(feature = "jsc", feature = "wamr", feature = "wasmi", feature = "v8")))]
7use wasmer::RuntimeError;
8
9/// A `PrettyError` for printing `anyhow::Error` nicely.
10pub struct PrettyError {
11    error: Error,
12}
13
14impl PrettyError {
15    /// Create a new [`PrettyError`].
16    pub fn new(error: Error) -> Self {
17        PrettyError { error }
18    }
19}
20
21/// A macro that prints a warning with nice colors
22#[macro_export]
23macro_rules! warning {
24    ($($arg:tt)*) => ({
25        use colored::*;
26        eprintln!("{}: {}", "warning".yellow().bold(), format!($($arg)*));
27    })
28}
29
30#[cfg(not(any(feature = "jsc", feature = "wamr", feature = "wasmi", feature = "v8")))]
31impl PrettyError {
32    /// Process a `Result` printing any errors and exiting
33    /// the process after
34    pub fn report<T>(result: Result<T, Error>) -> ! {
35        std::process::exit(match result {
36            Ok(_t) => 0,
37            Err(error) => {
38                let runtime: Option<&RuntimeError> = error.downcast_ref();
39                let trapcode = runtime.map(|e| e.clone().to_trap());
40                eprintln!("{:?}", PrettyError { error });
41                // we don't use process:abort() here to avoid message from rust
42                // that could interfer with testing tools
43                // but still exit with the expected error code
44                match trapcode {
45                    #[cfg(target_os = "windows")]
46                    Some(_) => 3,
47                    #[cfg(not(target_os = "windows"))]
48                    Some(_) => 128 + libc::SIGABRT,
49                    _ => 1,
50                }
51            }
52        });
53    }
54}
55
56#[cfg(any(feature = "jsc", feature = "wamr", feature = "wasmi", feature = "v8"))]
57impl PrettyError {
58    /// Process a `Result` printing any errors and exiting
59    /// the process after
60    pub fn report<T>(result: Result<T, Error>) -> ! {
61        std::process::exit(match result {
62            Ok(_t) => 0,
63            Err(error) => {
64                eprintln!("{:?}", PrettyError { error });
65                // we don't use process:abort() here to avoid message from rust
66                // that could interfer with testing tools
67                // but still exit with the expected error code
68                1
69            }
70        });
71    }
72}
73
74impl Debug for PrettyError {
75    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        let error = &self.error;
77
78        if f.alternate() {
79            return Debug::fmt(&error, f);
80        }
81
82        write!(f, "{}", format!("{}: {}", "error".red(), error).bold())?;
83        // write!(f, "{}", error)?;
84
85        if let Some(cause) = error.source() {
86            // write!(f, "\n{}:", "caused by".bold().blue())?;
87            let chain = Chain::new(cause);
88            let (total_errors, _) = chain.size_hint();
89            for (n, error) in chain.enumerate() {
90                writeln!(f)?;
91                let mut indented = Indented {
92                    inner: f,
93                    number: Some(n + 1),
94                    is_last: n == total_errors - 1,
95                    started: false,
96                };
97                write!(indented, "{error}")?;
98            }
99        }
100        Ok(())
101    }
102}
103
104struct Indented<'a, D> {
105    inner: &'a mut D,
106    number: Option<usize>,
107    started: bool,
108    is_last: bool,
109}
110
111impl<T> Write for Indented<'_, T>
112where
113    T: Write,
114{
115    fn write_str(&mut self, s: &str) -> fmt::Result {
116        for (i, line) in s.split('\n').enumerate() {
117            if !self.started {
118                self.started = true;
119                match self.number {
120                    Some(number) => {
121                        if !self.is_last {
122                            write!(
123                                self.inner,
124                                "{} {: >4} ",
125                                "│".bold().blue(),
126                                format!("{number}:").dimmed()
127                            )?
128                        } else {
129                            write!(
130                                self.inner,
131                                "{}{: >2}: ",
132                                "╰─▶".bold().blue(),
133                                format!("{number}").bold().blue()
134                            )?
135                        }
136                    }
137                    None => self.inner.write_str("    ")?,
138                }
139            } else if i > 0 {
140                self.inner.write_char('\n')?;
141                if self.number.is_some() {
142                    self.inner.write_str("       ")?;
143                } else {
144                    self.inner.write_str("    ")?;
145                }
146            }
147
148            self.inner.write_str(line)?;
149        }
150
151        Ok(())
152    }
153}