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.
new() -> Self22     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.
error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T)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.
syn_error(&self, err: syn::Error)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.
check(self) -> syn::Result<()>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 {
drop(&mut self)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