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}