serde_derive/internals/
ctxt.rs

1use quote::ToTokens;
2use std::cell::RefCell;
3use std::fmt::Display;
4use 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)]
12pub 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
18impl 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
62impl 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}