Skip to main content

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