1 | use quote::ToTokens; |
2 | use std::cell::RefCell; |
3 | use std::fmt::Display; |
4 | use std::thread; |
5 | |
6 | /// A type to collect errors together and format them. |
7 | /// |
8 | /// Dropping this object will cause a panic. It must be consumed using `check`. |
9 | /// |
10 | /// References can be shared since this type uses run-time exclusive mut checking. |
11 | #[derive (Default)] |
12 | pub struct Ctxt { |
13 | // The contents will be set to `None` during checking. This is so that checking can be |
14 | // enforced. |
15 | errors: RefCell<Option<Vec<syn::Error>>>, |
16 | } |
17 | |
18 | impl Ctxt { |
19 | /// Create a new context object. |
20 | /// |
21 | /// This object contains no errors, but will still trigger a panic if it is not `check`ed. |
22 | pub fn new() -> Self { |
23 | Ctxt { |
24 | errors: RefCell::new(Some(Vec::new())), |
25 | } |
26 | } |
27 | |
28 | /// Add an error to the context object with a tokenenizable object. |
29 | /// |
30 | /// The object is used for spanning in error messages. |
31 | pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) { |
32 | self.errors |
33 | .borrow_mut() |
34 | .as_mut() |
35 | .unwrap() |
36 | // Curb monomorphization from generating too many identical methods. |
37 | .push(syn::Error::new_spanned(obj.into_token_stream(), msg)); |
38 | } |
39 | |
40 | /// Add one of Syn's parse errors. |
41 | pub fn syn_error(&self, err: syn::Error) { |
42 | self.errors.borrow_mut().as_mut().unwrap().push(err); |
43 | } |
44 | |
45 | /// Consume this object, producing a formatted error string if there are errors. |
46 | pub fn check(self) -> syn::Result<()> { |
47 | let mut errors = self.errors.borrow_mut().take().unwrap().into_iter(); |
48 | |
49 | let mut combined = match errors.next() { |
50 | Some(first) => first, |
51 | None => return Ok(()), |
52 | }; |
53 | |
54 | for rest in errors { |
55 | combined.combine(rest); |
56 | } |
57 | |
58 | Err(combined) |
59 | } |
60 | } |
61 | |
62 | impl Drop for Ctxt { |
63 | fn drop(&mut self) { |
64 | if !thread::panicking() && self.errors.borrow().is_some() { |
65 | panic!("forgot to check for errors" ); |
66 | } |
67 | } |
68 | } |
69 | |