1use std::io::{self, Error, Write};
20
21pub(super) struct ErrorEmitter {
22 namespace: String,
23 file: String,
24 lines: Vec<String>,
25}
26
27impl ErrorEmitter {
28 pub fn new(namespace: &str, file: &str, lines: Vec<String>) -> Self {
29 Self { namespace: namespace.to_string(), file: file.to_string(), lines }
30 }
31
32 fn fmt(&self, msg: String, ln: usize, col: usize) -> String {
33 let (err_msg, dbg_msg, caret) = match ln {
34 0 => (msg, "".to_string(), "".to_string()),
35 _ => {
36 let err_msg = format!("{msg} (line {ln}, column {col})");
37 let dbg_msg = format!("{}:{ln}:{col}: {}", self.file, self.lines[ln - 1]);
38 let pad = dbg_msg.split(": ").next().unwrap().len() + col + 1;
39 let caret = format!("{:width$}^", "", width = pad);
40 (err_msg, dbg_msg, caret)
41 }
42 };
43 format!("{err_msg}\n{dbg_msg}\n{caret}\n")
44 }
45
46 pub fn abort(&self, msg: &str, ln: usize, col: usize) -> Error {
47 let m = self.fmt(msg.to_string(), ln, col);
48 self.emit("error", &m);
49 Error::other(m)
50 }
51
52 pub fn warn(&self, msg: &str, ln: usize, col: usize) {
53 let m = self.fmt(msg.to_string(), ln, col);
54 self.emit("warning", &m);
55 }
56
57 pub fn emit(&self, typ: &str, msg: &str) {
58 if std::env::var("ZKAS_SILENT").is_ok() {
59 return
60 }
61
62 let stderr = io::stderr();
63 let mut handle = stderr.lock();
64
65 match typ {
66 "error" => write!(handle, "\x1b[31;1m{} error:\x1b[0m {msg}", self.namespace).unwrap(),
67
68 "warning" => {
69 write!(handle, "\x1b[33;1m{} warning:\x1b[0m {msg}", self.namespace).unwrap()
70 }
71
72 _ => unreachable!(),
73 };
74
75 handle.flush().unwrap();
76 }
77}