facet_derive/
lib.rs

1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4mod process_enum;
5mod process_struct;
6mod process_tuple_struct;
7
8use unsynn::*;
9
10keyword! {
11    KPub = "pub";
12    KStruct = "struct";
13    KEnum = "enum";
14    KDoc = "doc";
15    KRepr = "repr";
16    KCrate = "crate";
17    KConst = "const";
18    KMut = "mut";
19    KFacet = "facet";
20    KSensitive = "sensitive";
21}
22
23operator! {
24    Eq = "=";
25    Semi = ";";
26    Apostrophe = "'";
27    DoubleSemicolon = "::";
28}
29
30unsynn! {
31    enum Vis {
32        Pub(KPub),
33        PubCrate(Cons<KPub, ParenthesisGroupContaining<KCrate>>),
34    }
35
36    struct Attribute {
37        _pound: Pound,
38        body: BracketGroupContaining<AttributeInner>,
39    }
40
41    enum AttributeInner {
42        Facet(FacetAttr),
43        Doc(DocInner),
44        Repr(ReprInner),
45        Any(Vec<TokenTree>)
46    }
47
48    struct FacetAttr {
49        _facet: KFacet,
50        inner: ParenthesisGroupContaining<FacetInner>,
51    }
52
53    enum FacetInner {
54        Sensitive(KSensitive),
55        Other(Vec<TokenTree>)
56    }
57
58    struct DocInner {
59        _kw_doc: KDoc,
60        _eq: Eq,
61        value: LiteralString,
62    }
63
64    struct ReprInner {
65        _kw_repr: KRepr,
66        attr: ParenthesisGroupContaining<Ident>,
67    }
68
69    struct Struct {
70        // Skip any doc attributes by consuming them
71        attributes: Vec<Attribute>,
72        _vis: Option<Vis>,
73        _kw_struct: KStruct,
74        name: Ident,
75        body: BraceGroupContaining<CommaDelimitedVec<StructField>>,
76    }
77
78    struct Lifetime {
79        _apostrophe: Apostrophe,
80        name: Ident,
81    }
82
83    enum Expr {
84        Integer(LiteralInteger),
85    }
86
87    enum Type {
88        Path(PathType),
89        Tuple(ParenthesisGroupContaining<CommaDelimitedVec<Box<Type>>>),
90        Slice(BracketGroupContaining<Box<Type>>),
91        Bare(BareType),
92    }
93
94    struct PathType {
95        prefix: Ident,
96        _doublesemi: DoubleSemicolon,
97        rest: Box<Type>,
98    }
99
100    struct BareType {
101        name: Ident,
102        generic_params: Option<GenericParams>,
103    }
104
105    struct GenericParams {
106        _lt: Lt,
107        params: CommaDelimitedVec<Type>,
108        _gt: Gt,
109    }
110
111    enum ConstOrMut {
112        Const(KConst),
113        Mut(KMut),
114    }
115
116    struct StructField {
117        attributes: Vec<Attribute>,
118        _vis: Option<Vis>,
119        name: Ident,
120        _colon: Colon,
121        typ: Type,
122    }
123
124    struct TupleStruct {
125        // Skip any doc attributes by consuming them
126        attributes: Vec<Attribute>,
127        _vis: Option<Vis>,
128        _kw_struct: KStruct,
129        name: Ident,
130        body: ParenthesisGroupContaining<CommaDelimitedVec<TupleField>>,
131    }
132
133    struct TupleField {
134        attributes: Vec<Attribute>,
135        vis: Option<Vis>,
136        typ: Type,
137    }
138
139    struct Enum {
140        // Skip any doc attributes by consuming them
141        attributes: Vec<Attribute>,
142        _pub: Option<KPub>,
143        _kw_enum: KEnum,
144        name: Ident,
145        body: BraceGroupContaining<CommaDelimitedVec<EnumVariantLike>>,
146    }
147
148    enum EnumVariantLike {
149        Unit(UnitVariant),
150        Tuple(TupleVariant),
151        Struct(StructVariant),
152    }
153
154    struct UnitVariant {
155        attributes: Vec<Attribute>,
156        name: Ident,
157    }
158
159    struct TupleVariant {
160        // Skip any doc comments on variants
161        attributes: Vec<Attribute>,
162        name: Ident,
163        _paren: ParenthesisGroupContaining<CommaDelimitedVec<TupleField>>,
164    }
165
166    struct StructVariant {
167        // Skip any doc comments on variants
168        _doc_attributes: Vec<Attribute>,
169        name: Ident,
170        _brace: BraceGroupContaining<CommaDelimitedVec<StructField>>,
171    }
172}
173
174/// Derive the Facet trait for structs, tuple structs, and enums.
175///
176/// This uses unsynn, so it's light, but it _will_ choke on some Rust syntax because...
177/// there's a lot of Rust syntax.
178#[proc_macro_derive(Facet, attributes(facet))]
179pub fn facet_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
180    let input = TokenStream::from(input);
181    let mut i = input.to_token_iter();
182
183    // Try to parse as struct first
184    if let Ok(parsed) = i.parse::<Struct>() {
185        return process_struct::process_struct(parsed);
186    }
187    let struct_tokens_left = i.count();
188
189    // Try to parse as tuple struct
190    i = input.to_token_iter(); // Reset iterator
191    if let Ok(parsed) = i.parse::<TupleStruct>() {
192        return process_tuple_struct::process_tuple_struct(parsed);
193    }
194    let tuple_struct_tokens_left = i.count();
195
196    // Try to parse as enum
197    i = input.to_token_iter(); // Reset iterator
198    if let Ok(parsed) = i.parse::<Enum>() {
199        return process_enum::process_enum(parsed);
200    }
201    let enum_tokens_left = i.count();
202
203    let mut msg = format!(
204        "Could not parse input as struct, tuple struct, or enum: {}",
205        input
206    );
207
208    // Find which parsing left the fewest tokens
209    let min_tokens_left = struct_tokens_left
210        .min(tuple_struct_tokens_left)
211        .min(enum_tokens_left);
212
213    // Parse again for the one with fewest tokens left and show remaining tokens
214    if min_tokens_left == struct_tokens_left {
215        i = input.to_token_iter();
216        let err = i.parse::<Struct>().err();
217        msg = format!(
218            "{}\n====> Error parsing struct: {:?}\n====> Remaining tokens after struct parsing: {}",
219            msg,
220            err,
221            i.collect::<TokenStream>()
222        );
223    } else if min_tokens_left == tuple_struct_tokens_left {
224        i = input.to_token_iter();
225        let err = i.parse::<TupleStruct>().err();
226        msg = format!(
227            "{}\n====> Error parsing tuple struct: {:?}\n====> Remaining tokens after tuple struct parsing: {}",
228            msg,
229            err,
230            i.collect::<TokenStream>()
231        );
232    } else {
233        i = input.to_token_iter();
234        let err = i.parse::<Enum>().err();
235        msg = format!(
236            "{}\n====> Error parsing enum: {:?}\n====> Remaining tokens after enum parsing: {}",
237            msg,
238            err,
239            i.collect::<TokenStream>()
240        );
241    }
242
243    // If we get here, couldn't parse as struct, tuple struct, or enum
244    panic!("{msg}");
245}
246
247impl core::fmt::Display for Type {
248    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249        match self {
250            Type::Path(path) => {
251                write!(f, "{}::{}", path.prefix, path.rest)
252            }
253            Type::Tuple(tuple) => {
254                write!(f, "(")?;
255                for (i, typ) in tuple.content.0.iter().enumerate() {
256                    if i > 0 {
257                        write!(f, ", ")?;
258                    }
259                    write!(f, "{}", typ.value)?;
260                }
261                write!(f, ")")
262            }
263            Type::Slice(slice) => {
264                write!(f, "[{}]", slice.content)
265            }
266            Type::Bare(ident) => {
267                write!(f, "{}", ident.name)?;
268                if let Some(generic_params) = &ident.generic_params {
269                    write!(f, "<")?;
270                    for (i, param) in generic_params.params.0.iter().enumerate() {
271                        if i > 0 {
272                            write!(f, ", ")?;
273                        }
274                        write!(f, "{}", param.value)?;
275                    }
276                    write!(f, ">")?;
277                }
278                Ok(())
279            }
280        }
281    }
282}
283
284impl core::fmt::Display for ConstOrMut {
285    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
286        match self {
287            ConstOrMut::Const(_) => write!(f, "const"),
288            ConstOrMut::Mut(_) => write!(f, "mut"),
289        }
290    }
291}
292
293impl core::fmt::Display for Lifetime {
294    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
295        write!(f, "'{}", self.name)
296    }
297}
298
299impl core::fmt::Display for Expr {
300    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
301        match self {
302            Expr::Integer(int) => write!(f, "{}", int.value()),
303        }
304    }
305}