leo_errors/emitter/
mod.rs1use crate::LeoWarning;
18
19use super::LeoError;
20
21use itertools::Itertools as _;
22use leo_span::Span;
23use std::{cell::RefCell, collections::HashSet, fmt, rc::Rc};
24
25pub trait Emitter {
27 fn emit_err(&mut self, err: LeoError);
29
30 fn last_emitted_err_code(&self) -> Option<i32>;
32
33 fn emit_warning(&mut self, warning: LeoWarning);
35}
36
37pub struct StderrEmitter {
39 last_error_code: Option<i32>,
41}
42
43impl Emitter for StderrEmitter {
44 fn emit_err(&mut self, err: LeoError) {
45 self.last_error_code = Some(err.exit_code());
46 eprintln!("{err}");
47 }
48
49 fn last_emitted_err_code(&self) -> Option<i32> {
50 self.last_error_code
51 }
52
53 fn emit_warning(&mut self, warning: LeoWarning) {
54 eprintln!("{warning}");
55 }
56}
57
58#[derive(Debug)]
60pub struct Buffer<T>(Vec<T>);
61
62impl<T> Default for Buffer<T> {
63 fn default() -> Self {
64 Self(Vec::new())
65 }
66}
67
68impl<T> Buffer<T> {
69 pub fn push(&mut self, x: T) {
71 self.0.push(x);
72 }
73
74 pub fn into_inner(self) -> Vec<T> {
76 self.0
77 }
78
79 pub fn last_entry(&self) -> Option<&T> {
81 self.0.last()
82 }
83
84 pub fn len(&self) -> usize {
86 self.0.len()
87 }
88
89 pub fn is_empty(&self) -> bool {
91 self.0.is_empty()
92 }
93}
94
95impl<T: fmt::Display> fmt::Display for Buffer<T> {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 self.0.iter().format("").fmt(f)
98 }
99}
100
101pub type ErrBuffer = Buffer<LeoError>;
103pub type WarningBuffer = Buffer<LeoWarning>;
105
106#[derive(Default, Clone)]
108pub struct BufferEmitter(Rc<RefCell<ErrBuffer>>, Rc<RefCell<WarningBuffer>>);
109
110impl BufferEmitter {
111 pub fn new() -> Self {
113 BufferEmitter(<_>::default(), <_>::default())
114 }
115
116 pub fn extract_errs(&self) -> ErrBuffer {
118 self.0.take()
119 }
120
121 pub fn extract_warnings(&self) -> WarningBuffer {
123 self.1.take()
124 }
125}
126
127impl Emitter for BufferEmitter {
128 fn emit_err(&mut self, err: LeoError) {
129 self.0.borrow_mut().push(err);
130 }
131
132 fn last_emitted_err_code(&self) -> Option<i32> {
133 let temp = &*self.0.borrow();
134 temp.last_entry().map(|entry| entry.exit_code())
135 }
136
137 fn emit_warning(&mut self, warning: LeoWarning) {
138 self.1.borrow_mut().push(warning);
139 }
140}
141
142#[derive(Clone)]
144pub struct Handler {
145 inner: Rc<RefCell<HandlerInner>>,
146}
147
148pub struct HandlerInner {
149 err_count: usize,
151 warn_count: usize,
153 emitter: Box<dyn Emitter>,
155 warned_spans: HashSet<(Span, String)>,
157}
158
159impl Default for Handler {
160 fn default() -> Self {
161 Self::new(StderrEmitter { last_error_code: None })
162 }
163}
164
165impl Handler {
166 pub fn new<T: 'static + Emitter>(emitter: T) -> Self {
168 Handler {
169 inner: Rc::new(RefCell::new(HandlerInner {
170 err_count: 0,
171 warn_count: 0,
172 emitter: Box::new(emitter),
173 warned_spans: HashSet::new(),
174 })),
175 }
176 }
177
178 pub fn new_with_buf() -> (Self, BufferEmitter) {
180 let buf = BufferEmitter::default();
181 let handler = Self::new(buf.clone());
182 (handler, buf)
183 }
184
185 pub fn with<T>(logic: impl for<'a> FnOnce(&'a Handler) -> Result<T, LeoError>) -> Result<T, ErrBuffer> {
188 let (handler, buf) = Handler::new_with_buf();
189 handler.extend_if_error(logic(&handler)).map_err(|_| buf.extract_errs())
190 }
191
192 fn last_emitted_err_code(&self) -> Option<i32> {
194 self.inner.borrow().emitter.last_emitted_err_code()
195 }
196
197 pub fn emit_err(&self, err: impl Into<LeoError>) {
199 let mut inner = self.inner.borrow_mut();
200 inner.err_count = inner.err_count.saturating_add(1);
201 inner.emitter.emit_err(err.into());
202 }
203
204 pub fn emit_warning(&self, warning: impl Into<LeoWarning>) {
206 let mut inner = self.inner.borrow_mut();
207 inner.warn_count = inner.warn_count.saturating_add(1);
208 inner.emitter.emit_warning(warning.into());
209 }
210
211 pub fn emit_warning_once(&self, span: Span, warning: impl Into<LeoWarning>) {
213 let mut inner = self.inner.borrow_mut();
214 let warn = warning.into();
215 if inner.warned_spans.insert((span, warn.error_code())) {
216 inner.warn_count = inner.warn_count.saturating_add(1);
217 inner.emitter.emit_warning(warn);
218 }
219 }
220
221 pub fn err_count(&self) -> usize {
223 self.inner.borrow().err_count
224 }
225
226 pub fn warning_count(&self) -> usize {
228 self.inner.borrow().warn_count
229 }
230
231 pub fn had_errors(&self) -> bool {
233 self.err_count() > 0
234 }
235
236 pub fn last_err(&self) -> Result<(), LeoError> {
239 if let Some(code) = self.last_emitted_err_code() { Err(LeoError::LastErrorCode(code)) } else { Ok(()) }
240 }
241
242 #[allow(clippy::result_unit_err)]
244 pub fn extend_if_error<T>(&self, res: Result<T, LeoError>) -> Result<T, ()> {
245 match res {
246 Ok(_) if self.had_errors() => Err(()),
247 Ok(x) => Ok(x),
248 Err(e) => {
249 self.emit_err(e);
250 Err(())
251 }
252 }
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use crate::Formatted;
260 use leo_span::{Span, create_session_if_not_set_then};
261
262 #[test]
263 fn fresh_no_errors() {
264 let handler = Handler::new(BufferEmitter::new());
265 assert_eq!(handler.err_count(), 0);
266 assert!(!handler.had_errors());
267 }
268
269 #[test]
270 fn buffer_works() {
271 create_session_if_not_set_then(|_| {
272 let res: Result<(), _> = Handler::with(|h| {
273 let s = Span::default();
274 assert_eq!(h.err_count(), 0);
275 h.emit_err(Formatted::error("TST", 0, "test error", s));
276 assert_eq!(h.err_count(), 1);
277 h.emit_err(Formatted::error("TST", 0, "test error", s));
278 assert_eq!(h.err_count(), 2);
279 Err(Formatted::error("TST", 0, "test error", s).into())
280 });
281
282 assert_eq!(res.unwrap_err().len(), 3);
283
284 let res: Result<(), _> = Handler::with(|h| {
285 let s = Span::default();
286 h.emit_err(Formatted::error("TST", 0, "test error", s));
287 h.emit_err(Formatted::error("TST", 0, "test error", s));
288 Ok(())
289 });
290 assert_eq!(res.unwrap_err().len(), 2);
291
292 Handler::with(|_| Ok(())).unwrap();
293 })
294 }
295}