facet_macro_types/
lib.rs

1//! # Facet Macro Types
2//!
3//! Unsynn grammar and type definitions for the facet macro ecosystem.
4//!
5//! This crate provides the foundational parsing infrastructure used by facet's
6//! derive macros. It includes:
7//!
8//! - Unsynn grammar definitions for parsing Rust struct and enum definitions
9//! - RenameRule for field/variant name transformations
10//! - Helper types like LifetimeName for code generation
11
12#![warn(missing_docs)]
13#![allow(uncommon_codepoints)]
14
15// ============================================================================
16// RE-EXPORTS FROM UNSYNN
17// ============================================================================
18
19pub use unsynn::*;
20
21// ============================================================================
22// KEYWORDS AND OPERATORS
23// ============================================================================
24
25keyword! {
26    /// The "pub" keyword.
27    pub KPub = "pub";
28    /// The "struct" keyword.
29    pub KStruct = "struct";
30    /// The "enum" keyword.
31    pub KEnum = "enum";
32    /// The "doc" keyword.
33    pub KDoc = "doc";
34    /// The "repr" keyword.
35    pub KRepr = "repr";
36    /// The "crate" keyword.
37    pub KCrate = "crate";
38    /// The "in" keyword.
39    pub KIn = "in";
40    /// The "const" keyword.
41    pub KConst = "const";
42    /// The "where" keyword.
43    pub KWhere = "where";
44    /// The "mut" keyword.
45    pub KMut = "mut";
46    /// The "facet" keyword.
47    pub KFacet = "facet";
48}
49
50operator! {
51    /// Represents the '=' operator.
52    pub Eq = "=";
53    /// Represents the ';' operator.
54    pub Semi = ";";
55    /// Represents the apostrophe '\'' operator.
56    pub Apostrophe = "'";
57    /// Represents the double semicolon '::' operator.
58    pub DoubleSemicolon = "::";
59}
60
61// ============================================================================
62// HELPER TYPES
63// ============================================================================
64
65/// Parses tokens and groups until `C` is found on the current token tree level.
66pub type VerbatimUntil<C> = Many<Cons<Except<C>, AngleTokenTree>>;
67
68/// Represents a module path, consisting of an optional path separator followed by
69/// a path-separator-delimited sequence of identifiers.
70pub type ModPath = Cons<Option<PathSep>, PathSepDelimited<Ident>>;
71
72/// Represents type bounds, consisting of a colon followed by tokens until
73/// a comma, equals sign, or closing angle bracket is encountered.
74pub type Bounds = Cons<Colon, VerbatimUntil<Either<Comma, Eq, Gt>>>;
75
76// ============================================================================
77// UNSYNN GRAMMAR
78// ============================================================================
79
80unsynn! {
81    /// Parses either a `TokenTree` or `<...>` grouping (which is not a [`Group`] as far as proc-macros
82    /// are concerned).
83    #[derive(Clone)]
84    pub struct AngleTokenTree(
85        #[allow(clippy::type_complexity)] // look,
86        pub Either<Cons<Lt, Vec<Cons<Except<Gt>, AngleTokenTree>>, Gt>, TokenTree>,
87    );
88
89    /// Represents an algebraic data type (ADT) declaration, which can be either a struct or enum.
90    pub enum AdtDecl {
91        /// A struct ADT variant.
92        Struct(Struct),
93        /// An enum ADT variant.
94        Enum(Enum),
95    }
96
97    /// Represents visibility modifiers for items.
98    pub enum Vis {
99        /// `pub(in? crate::foo::bar)`/`pub(in? ::foo::bar)`
100        PubIn(Cons<KPub, ParenthesisGroupContaining<Cons<Option<KIn>, ModPath>>>),
101        /// Public visibility, indicated by the "pub" keyword.
102        Pub(KPub),
103    }
104
105    /// Represents an attribute annotation on a field, typically in the form `#[attr]`.
106    pub struct Attribute {
107        /// The pound sign preceding the attribute.
108        pub _pound: Pound,
109        /// The content of the attribute enclosed in square brackets.
110        pub body: BracketGroupContaining<AttributeInner>,
111    }
112
113    /// Represents the inner content of an attribute annotation.
114    pub enum AttributeInner {
115        /// A facet attribute that can contain specialized metadata.
116        Facet(FacetAttr),
117        /// A documentation attribute typically used for generating documentation.
118        Doc(DocInner),
119        /// A representation attribute that specifies how data should be laid out.
120        Repr(ReprInner),
121        /// Any other attribute represented as a sequence of token trees.
122        Any(Vec<TokenTree>),
123    }
124
125    /// Represents a facet attribute that can contain specialized metadata.
126    pub struct FacetAttr {
127        /// The keyword for the facet attribute.
128        pub _facet: KFacet,
129        /// The inner content of the facet attribute.
130        pub inner: ParenthesisGroupContaining<CommaDelimitedVec<FacetInner>>,
131    }
132
133    /// Represents the inner content of a facet attribute.
134    ///
135    /// All attributes are now parsed uniformly - either namespaced (`kdl::child`)
136    /// or simple (`sensitive`, `rename = "foo"`). The grammar system determines
137    /// what's valid, not hardcoded keywords.
138    pub enum FacetInner {
139        /// A namespaced attribute like `kdl::child` or `args::short = 'v'`
140        Namespaced(NamespacedAttr),
141        /// A non-namespaced (builtin) attribute like `sensitive` or `rename = "foo"`
142        Simple(SimpleAttr),
143    }
144
145    /// A namespaced attribute like `kdl::child` or `args::short = 'v'`
146    pub struct NamespacedAttr {
147        /// The namespace (e.g., "kdl", "args")
148        pub ns: Ident,
149        /// The path separator ::
150        pub _sep: PathSep,
151        /// The key (e.g., "child", "short")
152        pub key: Ident,
153        /// Optional arguments - either in parentheses like `(args)` or with equals like `= value`
154        pub args: Option<AttrArgs>,
155    }
156
157    /// A simple (builtin) attribute like `sensitive` or `rename = "foo"`
158    pub struct SimpleAttr {
159        /// The key (e.g., "sensitive", "rename")
160        pub key: Ident,
161        /// Optional arguments - either in parentheses like `(args)` or with equals like `= value`
162        pub args: Option<AttrArgs>,
163    }
164
165    /// Arguments for attributes - either parenthesized `(args)` or with equals `= value`
166    pub enum AttrArgs {
167        /// Parenthesized arguments like `(auto_increment)`
168        Parens(ParenthesisGroupContaining<Vec<TokenTree>>),
169        /// Equals-style arguments like `= 'v'`
170        Equals(AttrEqualsArgs),
171    }
172
173    /// Equals-style arguments like `= 'v'` or `= "value"`
174    pub struct AttrEqualsArgs {
175        /// The equals sign
176        pub _eq: Eq,
177        /// The value (tokens until comma or end)
178        pub value: VerbatimUntil<Comma>,
179    }
180
181    /// Represents documentation for an item.
182    pub struct DocInner {
183        /// The "doc" keyword.
184        pub _kw_doc: KDoc,
185        /// The equality operator.
186        pub _eq: Eq,
187        /// The documentation content as a literal string.
188        pub value: LiteralString,
189    }
190
191    /// Represents the inner content of a `repr` attribute, typically used for specifying
192    /// memory layout or representation hints.
193    pub struct ReprInner {
194        /// The "repr" keyword.
195        pub _kw_repr: KRepr,
196        /// The representation attributes enclosed in parentheses.
197        pub attr: ParenthesisGroupContaining<CommaDelimitedVec<Ident>>,
198    }
199
200    /// Represents a struct definition.
201    pub struct Struct {
202        /// Attributes applied to the struct.
203        pub attributes: Vec<Attribute>,
204        /// The visibility modifier of the struct (e.g., `pub`).
205        pub _vis: Option<Vis>,
206        /// The "struct" keyword.
207        pub _kw_struct: KStruct,
208        /// The name of the struct.
209        pub name: Ident,
210        /// Generic parameters for the struct, if any.
211        pub generics: Option<GenericParams>,
212        /// The variant of struct (unit, tuple, or regular struct with named fields).
213        pub kind: StructKind,
214    }
215
216    /// Represents the generic parameters of a struct or enum definition, enclosed in angle brackets.
217    /// e.g., `<'a, T: Trait, const N: usize>`.
218    pub struct GenericParams {
219        /// The opening angle bracket `<`.
220        pub _lt: Lt,
221        /// The comma-delimited list of generic parameters.
222        pub params: CommaDelimitedVec<GenericParam>,
223        /// The closing angle bracket `>`.
224        pub _gt: Gt,
225    }
226
227    /// Represents a single generic parameter within a `GenericParams` list.
228    pub enum GenericParam {
229        /// A lifetime parameter, e.g., `'a` or `'a: 'b + 'c`.
230        Lifetime {
231            /// The lifetime identifier (e.g., `'a`).
232            name: Lifetime,
233            /// Optional lifetime bounds (e.g., `: 'b + 'c`).
234            bounds: Option<Cons<Colon, VerbatimUntil<Either<Comma, Gt>>>>,
235        },
236        /// A const generic parameter, e.g., `const N: usize = 10`.
237        Const {
238            /// The `const` keyword.
239            _const: KConst,
240            /// The name of the const parameter (e.g., `N`).
241            name: Ident,
242            /// The colon separating the name and type.
243            _colon: Colon,
244            /// The type of the const parameter (e.g., `usize`).
245            typ: VerbatimUntil<Either<Comma, Gt, Eq>>,
246            /// An optional default value (e.g., `= 10`).
247            default: Option<Cons<Eq, VerbatimUntil<Either<Comma, Gt>>>>,
248        },
249        /// A type parameter, e.g., `T: Trait = DefaultType`.
250        Type {
251            /// The name of the type parameter (e.g., `T`).
252            name: Ident,
253            /// Optional type bounds (e.g., `: Trait`).
254            bounds: Option<Bounds>,
255            /// An optional default type (e.g., `= DefaultType`).
256            default: Option<Cons<Eq, VerbatimUntil<Either<Comma, Gt>>>>,
257        },
258    }
259
260    /// Represents a `where` clause attached to a definition.
261    /// e.g., `where T: Trait, 'a: 'b`.
262    #[derive(Clone)]
263    pub struct WhereClauses {
264        /// The `where` keyword.
265        pub _kw_where: KWhere,
266        /// The comma-delimited list of where clause predicates.
267        pub clauses: CommaDelimitedVec<WhereClause>,
268    }
269
270    /// Represents a single predicate within a `where` clause.
271    /// e.g., `T: Trait` or `'a: 'b`.
272    #[derive(Clone)]
273    pub struct WhereClause {
274        /// The type or lifetime being constrained (e.g., `T` or `'a`).
275        /// We specifically required a single colon, not 2 because of `<A as B>::Thingy`
276        pub _pred: VerbatimUntil<Cons<Colon, Except<Colon>>>,
277        /// The colon separating the constrained item and its bounds.
278        pub _colon: Colon,
279        /// The bounds applied to the type or lifetime (e.g., `Trait` or `'b`).
280        pub bounds: VerbatimUntil<Either<Comma, Semicolon, BraceGroup>>,
281    }
282
283    /// Represents the kind of a struct definition.
284    pub enum StructKind {
285        /// A regular struct with named fields, e.g., `struct Foo { bar: u32 }`.
286        Struct {
287            /// Optional where clauses.
288            clauses: Option<WhereClauses>,
289            /// The fields enclosed in braces `{}`.
290            fields: BraceGroupContaining<CommaDelimitedVec<StructField>>,
291        },
292        /// A tuple struct, e.g., `struct Foo(u32, String);`.
293        TupleStruct {
294            /// The fields enclosed in parentheses `()`.
295            fields: ParenthesisGroupContaining<CommaDelimitedVec<TupleField>>,
296            /// Optional where clauses.
297            clauses: Option<WhereClauses>,
298            /// The trailing semicolon `;`.
299            semi: Semi,
300        },
301        /// A unit struct, e.g., `struct Foo;`.
302        UnitStruct {
303            /// Optional where clauses.
304            clauses: Option<WhereClauses>,
305            /// The trailing semicolon `;`.
306            semi: Semi,
307        },
308    }
309
310    /// Represents a lifetime annotation, like `'a`.
311    pub struct Lifetime {
312        /// The apostrophe `'` starting the lifetime.
313        pub _apostrophe: PunctJoint<'\''>,
314        /// The identifier name of the lifetime (e.g., `a`).
315        pub name: Ident,
316    }
317
318    /// Represents a simple expression, currently only integer literals.
319    /// Used potentially for const generic default values.
320    pub enum Expr {
321        /// An integer literal expression.
322        Integer(LiteralInteger),
323    }
324
325    /// Represents either the `const` or `mut` keyword, often used with pointers.
326    pub enum ConstOrMut {
327        /// The `const` keyword.
328        Const(KConst),
329        /// The `mut` keyword.
330        Mut(KMut),
331    }
332
333    /// Represents a field within a regular struct definition.
334    /// e.g., `pub name: String`.
335    pub struct StructField {
336        /// Attributes applied to the field (e.g., `#[doc = "..."]`).
337        pub attributes: Vec<Attribute>,
338        /// Optional visibility modifier (e.g., `pub`).
339        pub _vis: Option<Vis>,
340        /// The name of the field.
341        pub name: Ident,
342        /// The colon separating the name and type.
343        pub _colon: Colon,
344        /// The type of the field.
345        pub typ: VerbatimUntil<Comma>,
346    }
347
348    /// Represents a field within a tuple struct definition.
349    /// e.g., `pub String`.
350    pub struct TupleField {
351        /// Attributes applied to the field (e.g., `#[doc = "..."]`).
352        pub attributes: Vec<Attribute>,
353        /// Optional visibility modifier (e.g., `pub`).
354        pub vis: Option<Vis>,
355        /// The type of the field.
356        pub typ: VerbatimUntil<Comma>,
357    }
358
359    /// Represents an enum definition.
360    /// e.g., `#[repr(u8)] pub enum MyEnum<T> where T: Clone { Variant1, Variant2(T) }`.
361    pub struct Enum {
362        /// Attributes applied to the enum (e.g., `#[repr(...)]`).
363        pub attributes: Vec<Attribute>,
364        /// Optional visibility modifier (e.g., `pub`, `pub(crate)`, etc.).
365        pub _vis: Option<Vis>,
366        /// The `enum` keyword.
367        pub _kw_enum: KEnum,
368        /// The name of the enum.
369        pub name: Ident,
370        /// Optional generic parameters.
371        pub generics: Option<GenericParams>,
372        /// Optional where clauses.
373        pub clauses: Option<WhereClauses>,
374        /// The enum variants enclosed in braces `{}`.
375        pub body: BraceGroupContaining<CommaDelimitedVec<EnumVariantLike>>,
376    }
377
378    /// Represents a variant of an enum, including the optional discriminant value
379    pub struct EnumVariantLike {
380        /// The actual variant
381        pub variant: EnumVariantData,
382        /// The optional discriminant value
383        pub discriminant: Option<Cons<Eq, VerbatimUntil<Comma>>>
384    }
385
386    /// Represents the different kinds of variants an enum can have.
387    pub enum EnumVariantData {
388        /// A tuple-like variant, e.g., `Variant(u32, String)`.
389        Tuple(TupleVariant),
390        /// A struct-like variant, e.g., `Variant { field1: u32, field2: String }`.
391        Struct(StructEnumVariant),
392        /// A unit-like variant, e.g., `Variant`.
393        Unit(UnitVariant),
394    }
395
396    /// Represents a unit-like enum variant.
397    /// e.g., `MyVariant`.
398    pub struct UnitVariant {
399        /// Attributes applied to the variant.
400        pub attributes: Vec<Attribute>,
401        /// The name of the variant.
402        pub name: Ident,
403    }
404
405    /// Represents a tuple-like enum variant.
406    /// e.g., `MyVariant(u32, String)`.
407    pub struct TupleVariant {
408        /// Attributes applied to the variant.
409        pub attributes: Vec<Attribute>,
410        /// The name of the variant.
411        pub name: Ident,
412        /// The fields enclosed in parentheses `()`.
413        pub fields: ParenthesisGroupContaining<CommaDelimitedVec<TupleField>>,
414    }
415
416    /// Represents a struct-like enum variant.
417    /// e.g., `MyVariant { field1: u32, field2: String }`.
418    pub struct StructEnumVariant {
419        /// Attributes applied to the variant.
420        pub attributes: Vec<Attribute>,
421        /// The name of the variant.
422        pub name: Ident,
423        /// The fields enclosed in braces `{}`.
424        pub fields: BraceGroupContaining<CommaDelimitedVec<StructField>>,
425    }
426
427    /// A lifetime or a tokentree, used to gather lifetimes in type definitions
428    pub enum LifetimeOrTt {
429        /// A lifetime annotation.
430        Lifetime(Lifetime),
431        /// A single token tree.
432        TokenTree(TokenTree),
433    }
434}
435
436// ============================================================================
437// DISPLAY IMPLEMENTATIONS
438// ============================================================================
439
440impl core::fmt::Display for AngleTokenTree {
441    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
442        match &self.0 {
443            Either::First(it) => {
444                write!(f, "<")?;
445                for it in it.second.iter() {
446                    write!(f, "{}", it.second)?;
447                }
448                write!(f, ">")?;
449            }
450            Either::Second(it) => write!(f, "{it}")?,
451            Either::Third(Invalid) => unreachable!(),
452            Either::Fourth(Invalid) => unreachable!(),
453        };
454        Ok(())
455    }
456}
457
458/// Display the verbatim tokens until the given token.
459pub struct VerbatimDisplay<'a, C>(
460    /// The verbatim tokens to display
461    pub &'a VerbatimUntil<C>,
462);
463
464impl<C> core::fmt::Display for VerbatimDisplay<'_, C> {
465    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
466        for tt in self.0.iter() {
467            write!(f, "{}", tt.value.second)?;
468        }
469        Ok(())
470    }
471}
472
473impl core::fmt::Display for ConstOrMut {
474    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
475        match self {
476            ConstOrMut::Const(_) => write!(f, "const"),
477            ConstOrMut::Mut(_) => write!(f, "mut"),
478        }
479    }
480}
481
482impl core::fmt::Display for Lifetime {
483    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
484        write!(f, "'{}", self.name)
485    }
486}
487
488impl core::fmt::Display for WhereClauses {
489    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490        write!(f, "where ")?;
491        for clause in self.clauses.iter() {
492            write!(f, "{},", clause.value)?;
493        }
494        Ok(())
495    }
496}
497
498impl core::fmt::Display for WhereClause {
499    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500        write!(
501            f,
502            "{}: {}",
503            VerbatimDisplay(&self._pred),
504            VerbatimDisplay(&self.bounds)
505        )
506    }
507}
508
509impl core::fmt::Display for Expr {
510    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
511        match self {
512            Expr::Integer(int) => write!(f, "{}", int.value()),
513        }
514    }
515}
516
517impl Struct {
518    /// Returns an iterator over the `FacetInner` content of `#[facet(...)]` attributes
519    /// applied to this struct.
520    pub fn facet_attributes(&self) -> impl Iterator<Item = &FacetInner> {
521        self.attributes
522            .iter()
523            .filter_map(|attr| match &attr.body.content {
524                AttributeInner::Facet(f) => Some(f.inner.content.as_slice()),
525                _ => None,
526            })
527            .flatten()
528            .map(|d| &d.value)
529    }
530}
531
532// ============================================================================
533// HELPER TYPES FOR CODE GENERATION
534// ============================================================================
535
536/// Helper type for lifetime name formatting
537#[derive(Clone)]
538pub struct LifetimeName(
539    /// The identifier for the lifetime
540    pub Ident,
541);
542
543impl quote::ToTokens for LifetimeName {
544    fn to_tokens(&self, tokens: &mut TokenStream) {
545        use proc_macro2::{Punct, Spacing, TokenTree};
546        let punct = TokenTree::Punct(Punct::new('\'', Spacing::Joint));
547        let name = &self.0;
548        tokens.extend(quote::quote! {
549            #punct #name
550        });
551    }
552}
553
554// ============================================================================
555// RENAME RULE
556// ============================================================================
557
558mod renamerule;
559pub use renamerule::*;
560
561#[cfg(test)]
562mod tests {
563    use super::*;
564    use quote::quote;
565
566    #[test]
567    fn test_struct_with_field_doc_comments() {
568        let input = quote! {
569            #[derive(Facet)]
570            pub struct User {
571                #[doc = " The user's unique identifier"]
572                pub id: u64,
573            }
574        };
575
576        let mut it = input.to_token_iter();
577        let parsed = it.parse::<Struct>().expect("Failed to parse struct");
578
579        // Check that we parsed the struct correctly
580        assert_eq!(parsed.name.to_string(), "User");
581
582        // Extract fields from the struct
583        if let StructKind::Struct { fields, .. } = &parsed.kind {
584            assert_eq!(fields.content.len(), 1);
585
586            // Check first field (id)
587            let id_field = &fields.content[0].value;
588            assert_eq!(id_field.name.to_string(), "id");
589
590            // Extract doc comments from id field
591            let mut doc_found = false;
592            for attr in &id_field.attributes {
593                match &attr.body.content {
594                    AttributeInner::Doc(doc_inner) => {
595                        // This should work with LiteralString
596                        assert_eq!(doc_inner.value, " The user's unique identifier");
597                        doc_found = true;
598                    }
599                    _ => {
600                        // Skip non-doc attributes
601                    }
602                }
603            }
604            assert!(doc_found, "Should have found a doc comment");
605        } else {
606            panic!("Expected a regular struct with named fields");
607        }
608    }
609}