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