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