musli_macros/internals/
ctxt.rs

1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::fmt::{self, Write};
4use std::rc::Rc;
5
6use proc_macro2::Span;
7
8use super::attr::{ModeIdent, ModeKind};
9use super::build::Field;
10use super::ATTR;
11
12struct Inner {
13    b1: String,
14    modes: HashMap<ModeKind, ModeIdent>,
15    errors: Vec<syn::Error>,
16    #[cfg(not(feature = "verbose"))]
17    names: HashMap<String, usize>,
18    #[cfg(not(feature = "verbose"))]
19    types: usize,
20}
21
22pub(crate) struct Ctxt {
23    inner: RefCell<Inner>,
24}
25
26impl Ctxt {
27    /// Construct a new handling context.
28    pub(crate) fn new() -> Self {
29        Self {
30            inner: RefCell::new(Inner {
31                b1: String::new(),
32                modes: HashMap::new(),
33                errors: Vec::new(),
34                #[cfg(not(feature = "verbose"))]
35                names: HashMap::new(),
36                #[cfg(not(feature = "verbose"))]
37                types: 0,
38            }),
39        }
40    }
41
42    /// Emit diagnostics for a transparent encode / decode that failed because
43    /// the wrong number of fields existed.
44    pub(crate) fn transparent_diagnostics(&self, span: Span, fields: &[Rc<Field>]) {
45        if fields.is_empty() {
46            self.error_span(
47                span,
48                format_args!("#[{ATTR}(transparent)] types must have a single unskipped field"),
49            );
50        } else {
51            self.error_span(
52                span,
53                format_args!(
54                    "#[{ATTR}(transparent)] can only be used on types which have a single field",
55                ),
56            );
57        }
58    }
59
60    /// Register a new mode.
61    pub(crate) fn register_mode(&self, mode: ModeIdent) {
62        self.inner
63            .borrow_mut()
64            .modes
65            .insert(mode.kind.clone(), mode);
66    }
67
68    /// Test if context contains errors.
69    pub(crate) fn has_errors(&self) -> bool {
70        !self.inner.borrow().errors.is_empty()
71    }
72
73    /// Report an error with a span.
74    pub(crate) fn error_span<T>(&self, span: Span, message: T)
75    where
76        T: fmt::Display,
77    {
78        self.inner
79            .borrow_mut()
80            .errors
81            .push(syn::Error::new(span, message));
82    }
83
84    /// Error reported directly by syn.
85    pub(crate) fn syn_error(&self, error: syn::Error) {
86        self.inner.borrow_mut().errors.push(error);
87    }
88
89    /// Access interior errors.
90    pub(crate) fn into_errors(self) -> Vec<syn::Error> {
91        std::mem::take(&mut self.inner.borrow_mut().errors)
92    }
93
94    /// Get all extra modes specified.
95    pub(crate) fn modes(&self) -> Vec<ModeIdent> {
96        self.inner.borrow().modes.values().cloned().collect()
97    }
98
99    pub(crate) fn reset(&self) {
100        #[cfg(not(feature = "verbose"))]
101        {
102            let mut inner = self.inner.borrow_mut();
103            inner.names.clear();
104            inner.types = 0;
105        }
106    }
107
108    /// Build a lifetime.
109    #[allow(unused)]
110    pub(crate) fn lifetime(&self, name: &str) -> syn::Lifetime {
111        self.with_string("'", name, "", |s| syn::Lifetime::new(s, Span::call_site()))
112    }
113
114    /// Build an identifier with the given name, escaped so it's harder to conflict with.
115    pub(crate) fn ident(&self, name: &str) -> syn::Ident {
116        self.ident_with_span(name, Span::call_site(), "")
117    }
118
119    /// Build an identifier with the given name, escaped so it's harder to conflict with.
120    pub(crate) fn ident_with_span(&self, name: &str, span: Span, extra: &str) -> syn::Ident {
121        self.with_string("", name, extra, |s| syn::Ident::new(s, span))
122    }
123
124    fn with_string<F, O>(&self, prefix: &str, name: &str, suffix: &str, f: F) -> O
125    where
126        F: FnOnce(&str) -> O,
127    {
128        let mut inner = self.inner.borrow_mut();
129
130        #[cfg(not(feature = "verbose"))]
131        {
132            let index = if let Some(index) = inner.names.get(name) {
133                *index
134            } else {
135                let index = inner.names.len();
136                inner.names.insert(name.to_owned(), index);
137                index
138            };
139
140            _ = write!(inner.b1, "{prefix}_{index}{suffix}");
141        }
142
143        #[cfg(feature = "verbose")]
144        {
145            let name = name.strip_prefix('_').unwrap_or(name);
146            _ = write!(inner.b1, "{prefix}_{name}{suffix}");
147        }
148
149        let ident = f(&inner.b1);
150        inner.b1.clear();
151        ident
152    }
153
154    /// Build a type identifier with a span.
155    pub(crate) fn type_with_span<N>(
156        &self,
157        #[cfg_attr(not(feature = "verbose"), allow(unused))] name: N,
158        span: Span,
159    ) -> syn::Ident
160    where
161        N: fmt::Display,
162    {
163        let mut inner = self.inner.borrow_mut();
164
165        #[cfg(not(feature = "verbose"))]
166        {
167            let index = inner.types;
168            inner.types += 1;
169            _ = write!(inner.b1, "T{index}");
170        }
171
172        #[cfg(feature = "verbose")]
173        {
174            _ = write!(inner.b1, "{name}");
175        }
176
177        let ident = syn::Ident::new(&inner.b1, span);
178        inner.b1.clear();
179        ident
180    }
181}