leo_errors/emitter/
mod.rs1use crate::LeoWarning;
18
19use super::LeoError;
20
21use itertools::Itertools as _;
22use std::{cell::RefCell, fmt, rc::Rc};
23
24pub trait Emitter {
26 fn emit_err(&mut self, err: LeoError);
28
29 fn last_emitted_err_code(&self) -> Option<i32>;
31
32 fn emit_warning(&mut self, warning: LeoWarning);
34}
35
36pub struct StderrEmitter {
38 last_error_code: Option<i32>,
40}
41
42impl Emitter for StderrEmitter {
43 fn emit_err(&mut self, err: LeoError) {
44 self.last_error_code = Some(err.exit_code());
45 eprintln!("{err}");
46 }
47
48 fn last_emitted_err_code(&self) -> Option<i32> {
49 self.last_error_code
50 }
51
52 fn emit_warning(&mut self, warning: LeoWarning) {
53 eprintln!("{warning}");
54 }
55}
56
57#[derive(Debug)]
59pub struct Buffer<T>(Vec<T>);
60
61impl<T> Default for Buffer<T> {
62 fn default() -> Self {
63 Self(Vec::new())
64 }
65}
66
67impl<T> Buffer<T> {
68 pub fn push(&mut self, x: T) {
70 self.0.push(x);
71 }
72
73 pub fn into_inner(self) -> Vec<T> {
75 self.0
76 }
77
78 pub fn last_entry(&self) -> Option<&T> {
80 self.0.last()
81 }
82
83 pub fn len(&self) -> usize {
85 self.0.len()
86 }
87
88 pub fn is_empty(&self) -> bool {
90 self.0.is_empty()
91 }
92}
93
94impl<T: fmt::Display> fmt::Display for Buffer<T> {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 self.0.iter().format("").fmt(f)
97 }
98}
99
100pub type ErrBuffer = Buffer<LeoError>;
102pub type WarningBuffer = Buffer<LeoWarning>;
104
105#[derive(Default, Clone)]
107pub struct BufferEmitter(Rc<RefCell<ErrBuffer>>, Rc<RefCell<WarningBuffer>>);
108
109impl BufferEmitter {
110 pub fn new() -> Self {
112 BufferEmitter(<_>::default(), <_>::default())
113 }
114
115 pub fn extract_errs(&self) -> ErrBuffer {
117 self.0.take()
118 }
119
120 pub fn extract_warnings(&self) -> WarningBuffer {
122 self.1.take()
123 }
124}
125
126impl Emitter for BufferEmitter {
127 fn emit_err(&mut self, err: LeoError) {
128 self.0.borrow_mut().push(err);
129 }
130
131 fn last_emitted_err_code(&self) -> Option<i32> {
132 let temp = &*self.0.borrow();
133 temp.last_entry().map(|entry| entry.exit_code())
134 }
135
136 fn emit_warning(&mut self, warning: LeoWarning) {
137 self.1.borrow_mut().push(warning);
138 }
139}
140
141#[derive(Clone)]
143pub struct Handler {
144 inner: Rc<RefCell<HandlerInner>>,
145}
146
147pub struct HandlerInner {
148 err_count: usize,
150 warn_count: usize,
152 emitter: Box<dyn Emitter>,
154}
155
156impl Default for Handler {
157 fn default() -> Self {
158 Self::new(StderrEmitter { last_error_code: None })
159 }
160}
161
162impl Handler {
163 pub fn new<T: 'static + Emitter>(emitter: T) -> Self {
165 Handler {
166 inner: Rc::new(RefCell::new(HandlerInner { err_count: 0, warn_count: 0, emitter: Box::new(emitter) })),
167 }
168 }
169
170 pub fn new_with_buf() -> (Self, BufferEmitter) {
172 let buf = BufferEmitter::default();
173 let handler = Self::new(buf.clone());
174 (handler, buf)
175 }
176
177 pub fn with<T>(logic: impl for<'a> FnOnce(&'a Handler) -> Result<T, LeoError>) -> Result<T, ErrBuffer> {
180 let (handler, buf) = Handler::new_with_buf();
181 handler.extend_if_error(logic(&handler)).map_err(|_| buf.extract_errs())
182 }
183
184 fn last_emitted_err_code(&self) -> Option<i32> {
186 self.inner.borrow().emitter.last_emitted_err_code()
187 }
188
189 pub fn emit_err<E: Into<LeoError>>(&self, err: E) {
191 let mut inner = self.inner.borrow_mut();
192 inner.err_count = inner.err_count.saturating_add(1);
193 inner.emitter.emit_err(err.into());
194 }
195
196 pub fn emit_warning(&self, warning: LeoWarning) {
198 let mut inner = self.inner.borrow_mut();
199 inner.warn_count = inner.warn_count.saturating_add(1);
200 inner.emitter.emit_warning(warning);
201 }
202
203 pub fn err_count(&self) -> usize {
205 self.inner.borrow().err_count
206 }
207
208 pub fn warning_count(&self) -> usize {
210 self.inner.borrow().warn_count
211 }
212
213 pub fn had_errors(&self) -> bool {
215 self.err_count() > 0
216 }
217
218 pub fn last_err(&self) -> Result<(), Box<LeoError>> {
221 if let Some(code) = self.last_emitted_err_code() {
222 Err(Box::new(LeoError::LastErrorCode(code)))
223 } else {
224 Ok(())
225 }
226 }
227
228 #[allow(clippy::result_unit_err)]
230 pub fn extend_if_error<T>(&self, res: Result<T, LeoError>) -> Result<T, ()> {
231 match res {
232 Ok(_) if self.had_errors() => Err(()),
233 Ok(x) => Ok(x),
234 Err(e) => {
235 self.emit_err(e);
236 Err(())
237 }
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use crate::ParserError;
246 use leo_span::{Span, create_session_if_not_set_then};
247
248 #[test]
249 fn fresh_no_errors() {
250 let handler = Handler::new(BufferEmitter::new());
251 assert_eq!(handler.err_count(), 0);
252 assert!(!handler.had_errors());
253 }
254
255 #[test]
256 fn buffer_works() {
257 create_session_if_not_set_then(|_| {
258 let res: Result<(), _> = Handler::with(|h| {
259 let s = Span::default();
260 assert_eq!(h.err_count(), 0);
261 h.emit_err(ParserError::invalid_import_list(s));
262 assert_eq!(h.err_count(), 1);
263 h.emit_err(ParserError::unexpected_eof(s));
264 assert_eq!(h.err_count(), 2);
265 Err(ParserError::spread_in_array_init(s).into())
266 });
267
268 assert_eq!(res.unwrap_err().len(), 3);
269
270 let res: Result<(), _> = Handler::with(|h| {
271 let s = Span::default();
272 h.emit_err(ParserError::invalid_import_list(s));
273 h.emit_err(ParserError::unexpected_eof(s));
274 Ok(())
275 });
276 assert_eq!(res.unwrap_err().len(), 2);
277
278 Handler::with(|_| Ok(())).unwrap();
279 })
280 }
281}