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}