facet_derive_emit/parsed.rs
1use crate::{BoundedGenericParams, RenameRule};
2use facet_derive_parse::{Ident, ReprInner, ToTokens, TokenStream};
3use quote::quote;
4
5/// For struct fields, they can either be identifiers (`my_struct.foo`)
6/// or literals (`my_struct.2`) — for tuple structs.
7#[derive(Clone)]
8pub enum IdentOrLiteral {
9 Ident(Ident),
10 Literal(usize),
11}
12
13impl quote::ToTokens for IdentOrLiteral {
14 fn to_tokens(&self, tokens: &mut TokenStream) {
15 match self {
16 IdentOrLiteral::Ident(ident) => tokens.extend(quote::quote! { #ident }),
17 IdentOrLiteral::Literal(lit) => {
18 let unsuffixed = facet_derive_parse::Literal::usize_unsuffixed(*lit);
19 tokens.extend(quote! { #unsuffixed })
20 }
21 }
22 }
23}
24
25/// All the supported facet attributes, e.g. `#[facet(sensitive)]` `#[facet(rename_all)]`, etc.
26///
27/// Stands for `parsed facet attr`
28#[derive(Clone)]
29pub enum PFacetAttr {
30 /// Valid in field
31 /// `#[facet(sensitive)]` — must be censored in debug outputs
32 Sensitive,
33
34 /// Valid in container
35 /// `#[facet(opaque)]` — the inner field does not have to implement
36 /// `Facet`
37 Opaque,
38
39 /// Valid in container
40 /// `#[facet(transparent)]` — applied on things like `NonZero<T>`, `Utf8PathBuf`,
41 /// etc. — when you're doing the newtype pattern. `de/ser` is forwarded.
42 Transparent,
43
44 /// Valid in field
45 /// `#[facet(flatten)]` — flattens a field's contents
46 /// into the parent structure.
47 Flatten,
48
49 /// Valid in field
50 /// `#[facet(child)]` — marks a field as child node in a hierarchy
51 Child,
52
53 /// Valid in container
54 /// `#[facet(invariants = "Self::invariants_func")]` — returns a bool, is called
55 /// when doing `Wip::build`
56 Invariants { expr: TokenStream },
57
58 /// Valid in container
59 /// `#[facet(deny_unknown_fields)]`
60 DenyUnknownFields,
61
62 /// Valid in field
63 /// `#[facet(default = expr)]` — when deserializing and missing, use `fn_name` to provide a default value
64 DefaultEquals { expr: TokenStream },
65
66 /// Valid in field
67 /// `#[facet(default)]` — when deserializing and missing, use the field's value from
68 /// the container's `Default::default()`
69 Default,
70
71 /// Valid in field, enum variant, container
72 /// An arbitrary/unknown string, like,
73 /// `#[facet(bleh)]`
74 Arbitrary { content: String },
75
76 /// Valid in container
77 /// `#[facet(rename_all = "rule")]` — rename all fields following a rule
78 RenameAll { rule: RenameRule },
79
80 /// Valid in field, enum variant, or container
81 /// `#[facet(skip_serializing)]` — skip serializing this field. Like serde.
82 SkipSerializing,
83
84 /// Valid in field, enum variant, or container
85 /// `#[facet(skip_serializing_if = "func")]` — skip serializing if the function returns true.
86 SkipSerializingIf { expr: TokenStream },
87
88 /// Valid in container
89 /// `#[facet(type_tag = "com.example.MyType")]` — identify type by tag and serialize with this tag
90 TypeTag { content: String },
91}
92
93impl PFacetAttr {
94 /// Parse a `FacetAttr` attribute into a `PFacetAttr`.
95 /// Pushes to `dest` for each parsed attribute.
96 pub fn parse(
97 facet_attr: &facet_derive_parse::FacetAttr,
98 display_name: &mut String,
99 dest: &mut Vec<PFacetAttr>,
100 ) {
101 use facet_derive_parse::FacetInner;
102
103 for attr in facet_attr.inner.content.0.iter().map(|d| &d.value) {
104 match attr {
105 FacetInner::Sensitive(_) => dest.push(PFacetAttr::Sensitive),
106 FacetInner::Opaque(_) => dest.push(PFacetAttr::Opaque),
107 FacetInner::Flatten(_) => dest.push(PFacetAttr::Flatten),
108 FacetInner::Child(_) => dest.push(PFacetAttr::Child),
109 FacetInner::Transparent(_) => dest.push(PFacetAttr::Transparent),
110
111 FacetInner::Invariants(invariant) => {
112 let expr = invariant.expr.to_token_stream();
113 dest.push(PFacetAttr::Invariants { expr });
114 }
115 FacetInner::DenyUnknownFields(_) => dest.push(PFacetAttr::DenyUnknownFields),
116 FacetInner::DefaultEquals(default_equals) => dest.push(PFacetAttr::DefaultEquals {
117 expr: default_equals.expr.to_token_stream(),
118 }),
119 FacetInner::Default(_) => dest.push(PFacetAttr::Default),
120 FacetInner::Rename(rename) => {
121 *display_name = rename.value.as_str().to_string();
122 }
123 FacetInner::RenameAll(rename_all) => {
124 let rule_str = rename_all.value.as_str();
125 if let Some(rule) = RenameRule::from_str(rule_str) {
126 dest.push(PFacetAttr::RenameAll { rule });
127 } else {
128 panic!("Unknown #[facet(rename_all = ...)] rule: {}", rule_str);
129 }
130 }
131 FacetInner::Arbitrary(tt) => {
132 dest.push(PFacetAttr::Arbitrary {
133 content: tt.tokens_to_string(),
134 });
135 }
136 FacetInner::SkipSerializing(_) => {
137 dest.push(PFacetAttr::SkipSerializing);
138 }
139 FacetInner::SkipSerializingIf(skip_if) => {
140 dest.push(PFacetAttr::SkipSerializingIf {
141 expr: skip_if.expr.to_token_stream(),
142 });
143 }
144 FacetInner::TypeTag(type_tag) => {
145 dest.push(PFacetAttr::TypeTag {
146 content: type_tag.expr.as_str().to_string(),
147 });
148 }
149 }
150 }
151 }
152}
153
154/// Parsed attr
155pub enum PAttr {
156 /// A single line of doc comments
157 /// `#[doc = "Some doc"], or `/// Some doc`, same thing
158 Doc { line: String },
159
160 /// A representation attribute
161 Repr { repr: PRepr },
162
163 /// A facet attribute
164 Facet { name: String },
165}
166
167/// A parsed name, which includes the raw name and the
168/// effective name.
169///
170/// Examples:
171///
172/// raw = "foo_bar", no rename rule, effective = "foo_bar"
173/// raw = "foo_bar", #[facet(rename = "kiki")], effective = "kiki"
174/// raw = "foo_bar", #[facet(rename_all = camelCase)], effective = "fooBar"
175/// raw = "r#type", no rename rule, effective = "type"
176///
177#[derive(Clone)]
178pub struct PName {
179 /// The raw identifier, as we found it in the source code. It might
180 /// be _actually_ raw, as in "r#keyword".
181 pub raw: IdentOrLiteral,
182
183 /// The name after applying rename rules, which might not be a valid identifier in Rust.
184 /// It could be a number. It could be a kebab-case thing.
185 pub effective: String,
186}
187
188impl PName {
189 /// Constructs a new `PName` with the given raw name, an optional container-level rename rule,
190 /// an optional field-level rename rule, and a raw identifier.
191 ///
192 /// Precedence:
193 /// - If field_rename_rule is Some, use it on raw for effective name
194 /// - Else if container_rename_rule is Some, use it on raw for effective name
195 /// - Else, strip raw ("r#" if present) for effective name
196 pub fn new(container_rename_rule: Option<RenameRule>, raw: IdentOrLiteral) -> Self {
197 // Remove Rust's raw identifier prefix, e.g. r#type -> type
198 let norm_raw_str = match &raw {
199 IdentOrLiteral::Ident(ident) => ident
200 .tokens_to_string()
201 .trim_start_matches("r#")
202 .to_string(),
203 IdentOrLiteral::Literal(l) => l.to_string(),
204 };
205
206 let effective = if let Some(container_rule) = container_rename_rule {
207 container_rule.apply(&norm_raw_str)
208 } else {
209 norm_raw_str // Use the normalized string (without r#)
210 };
211
212 Self {
213 raw: raw.clone(), // Keep the original raw identifier
214 effective,
215 }
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
220pub enum PRepr {
221 Transparent,
222 Rust(Option<PrimitiveRepr>),
223 C(Option<PrimitiveRepr>),
224}
225
226impl PRepr {
227 /// Parse a `&str` (for example a value coming from #[repr(...)] attribute)
228 /// into a `PRepr` variant.
229 pub fn parse(s: &ReprInner) -> Option<Self> {
230 enum ReprKind {
231 Rust,
232 C,
233 }
234
235 let items = s.attr.content.0.as_slice();
236 let mut repr_kind: Option<ReprKind> = None;
237 let mut primitive_repr: Option<PrimitiveRepr> = None;
238 let mut is_transparent = false;
239
240 for token_delimited in items {
241 let token_str = token_delimited.value.to_string();
242 match token_str.as_str() {
243 "C" | "c" => {
244 if repr_kind.is_some() && !matches!(repr_kind, Some(ReprKind::C)) {
245 panic!(
246 "Conflicting repr kinds found in #[repr(...)]. Cannot mix C/c and Rust/rust."
247 );
248 }
249 if is_transparent {
250 panic!(
251 "Conflicting repr kinds found in #[repr(...)]. Cannot mix C/c and transparent."
252 );
253 }
254 // If primitive is already set, and kind is not already C, ensure kind becomes C.
255 // Example: #[repr(u8, C)] is valid.
256 repr_kind = Some(ReprKind::C);
257 }
258 "Rust" | "rust" => {
259 if repr_kind.is_some() && !matches!(repr_kind, Some(ReprKind::Rust)) {
260 panic!(
261 "Conflicting repr kinds found in #[repr(...)]. Cannot mix Rust/rust and C/c."
262 );
263 }
264 if is_transparent {
265 panic!(
266 "Conflicting repr kinds found in #[repr(...)]. Cannot mix Rust/rust and transparent."
267 );
268 }
269 // If primitive is already set, and kind is not already Rust, ensure kind becomes Rust.
270 // Example: #[repr(i32, Rust)] is valid.
271 repr_kind = Some(ReprKind::Rust);
272 }
273 "transparent" => {
274 if repr_kind.is_some() || primitive_repr.is_some() {
275 panic!(
276 "Conflicting repr kinds found in #[repr(...)]. Cannot mix transparent with C/c, Rust/rust, or primitive types."
277 );
278 }
279 // Allow duplicate "transparent", although weird.
280 is_transparent = true;
281 }
282 prim_str @ ("u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32"
283 | "i64" | "i128" | "usize" | "isize") => {
284 let current_prim = match prim_str {
285 "u8" => PrimitiveRepr::U8,
286 "u16" => PrimitiveRepr::U16,
287 "u32" => PrimitiveRepr::U32,
288 "u64" => PrimitiveRepr::U64,
289 "u128" => PrimitiveRepr::U128,
290 "i8" => PrimitiveRepr::I8,
291 "i16" => PrimitiveRepr::I16,
292 "i32" => PrimitiveRepr::I32,
293 "i64" => PrimitiveRepr::I64,
294 "i128" => PrimitiveRepr::I128,
295 "usize" => PrimitiveRepr::Usize,
296 "isize" => PrimitiveRepr::Isize,
297 _ => unreachable!(), // Already matched by outer pattern
298 };
299 if is_transparent {
300 panic!(
301 "Conflicting repr kinds found in #[repr(...)]. Cannot mix primitive types and transparent."
302 );
303 }
304 if primitive_repr.is_some() {
305 panic!("Multiple primitive types specified in #[repr(...)].");
306 }
307 primitive_repr = Some(current_prim);
308 }
309 unknown => {
310 // Standard #[repr] only allows specific identifiers.
311 panic!(
312 "Unknown token '{}' in #[repr(...)]. Only C, Rust, transparent, or primitive integer types allowed.",
313 unknown
314 );
315 }
316 }
317 }
318
319 // Final construction
320 if is_transparent {
321 if repr_kind.is_some() || primitive_repr.is_some() {
322 // This check should be redundant due to checks inside the loop, but added for safety.
323 panic!("Internal error: transparent repr mixed with other kinds after parsing.");
324 }
325 Some(PRepr::Transparent)
326 } else {
327 // Default to Rust if only a primitive type is provided (e.g., #[repr(u8)]) or if nothing is specified.
328 // If C/c or Rust/rust was specified, use that.
329 let final_kind = repr_kind.unwrap_or(ReprKind::Rust);
330 match final_kind {
331 ReprKind::Rust => Some(PRepr::Rust(primitive_repr)),
332 ReprKind::C => Some(PRepr::C(primitive_repr)),
333 }
334 }
335 }
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
339pub enum PrimitiveRepr {
340 U8,
341 U16,
342 U32,
343 U64,
344 U128,
345 I8,
346 I16,
347 I32,
348 I64,
349 I128,
350 Isize,
351 Usize,
352}
353
354impl PrimitiveRepr {
355 pub fn type_name(&self) -> TokenStream {
356 match self {
357 PrimitiveRepr::U8 => quote! { u8 },
358 PrimitiveRepr::U16 => quote! { u16 },
359 PrimitiveRepr::U32 => quote! { u32 },
360 PrimitiveRepr::U64 => quote! { u64 },
361 PrimitiveRepr::U128 => quote! { u128 },
362 PrimitiveRepr::I8 => quote! { i8 },
363 PrimitiveRepr::I16 => quote! { i16 },
364 PrimitiveRepr::I32 => quote! { i32 },
365 PrimitiveRepr::I64 => quote! { i64 },
366 PrimitiveRepr::I128 => quote! { i128 },
367 PrimitiveRepr::Isize => quote! { isize },
368 PrimitiveRepr::Usize => quote! { usize },
369 }
370 }
371}
372
373/// Parsed attributes
374#[derive(Clone)]
375pub struct PAttrs {
376 /// An array of doc lines
377 pub doc: Vec<TokenStream>,
378
379 /// Facet attributes specifically
380 pub facet: Vec<PFacetAttr>,
381
382 /// Representation of the facet
383 pub repr: PRepr,
384
385 /// rename_all rule (if any)
386 pub rename_all: Option<RenameRule>,
387}
388
389impl PAttrs {
390 fn parse(attrs: &[facet_derive_parse::Attribute], display_name: &mut String) -> Self {
391 let mut doc_lines: Vec<TokenStream> = Vec::new();
392 let mut facet_attrs: Vec<PFacetAttr> = Vec::new();
393 let mut repr: Option<PRepr> = None;
394 let mut rename_all: Option<RenameRule> = None;
395
396 for attr in attrs {
397 match &attr.body.content {
398 facet_derive_parse::AttributeInner::Doc(doc_attr) => {
399 // Handle doc comments
400 doc_lines.push(doc_attr.value.to_token_stream());
401 }
402 facet_derive_parse::AttributeInner::Repr(repr_attr) => {
403 if repr.is_some() {
404 panic!("Multiple #[repr] attributes found");
405 }
406
407 // Parse repr attribute, e.g. #[repr(C)], #[repr(transparent)], #[repr(u8)]
408 // repr_attr.attr.content is a Vec<Delimited<Ident, Operator<','>>>
409 // which represents something like ["C"], or ["u8"], or ["transparent"]
410 //
411 // We should parse each possible repr kind. But usually there's only one item.
412 //
413 // We'll take the first one and parse it, ignoring the rest.
414 repr = match PRepr::parse(repr_attr) {
415 Some(parsed) => Some(parsed),
416 None => {
417 panic!(
418 "Unknown #[repr] attribute: {}",
419 repr_attr.tokens_to_string()
420 );
421 }
422 };
423 }
424 facet_derive_parse::AttributeInner::Facet(facet_attr) => {
425 PFacetAttr::parse(facet_attr, display_name, &mut facet_attrs);
426 }
427 _ => {
428 // Ignore unknown AttributeInner types
429 }
430 }
431 }
432
433 for attr in &facet_attrs {
434 if let PFacetAttr::RenameAll { rule } = attr {
435 rename_all = Some(*rule);
436 }
437 }
438
439 Self {
440 doc: doc_lines,
441 facet: facet_attrs,
442 repr: repr.unwrap_or(PRepr::Rust(None)),
443 rename_all,
444 }
445 }
446
447 pub(crate) fn is_transparent(&self) -> bool {
448 self.facet
449 .iter()
450 .any(|attr| matches!(attr, PFacetAttr::Transparent))
451 }
452
453 pub(crate) fn type_tag(&self) -> Option<&str> {
454 for attr in &self.facet {
455 if let PFacetAttr::TypeTag { content } = attr {
456 return Some(content);
457 }
458 }
459 None
460 }
461}
462
463/// Parsed container
464pub struct PContainer {
465 /// Name of the container (could be a struct, an enum variant, etc.)
466 pub name: Ident,
467
468 /// Attributes of the container
469 pub attrs: PAttrs,
470
471 /// Generic parameters of the container
472 pub bgp: BoundedGenericParams,
473}
474
475/// Parse struct
476pub struct PStruct {
477 /// Container information
478 pub container: PContainer,
479
480 /// Kind of struct
481 pub kind: PStructKind,
482}
483
484/// Parsed enum (given attributes etc.)
485pub struct PEnum {
486 /// Container information
487 pub container: PContainer,
488 /// The variants of the enum, in parsed form
489 pub variants: Vec<PVariant>,
490 /// The representation (repr) for the enum (e.g., C, u8, etc.)
491 pub repr: PRepr,
492}
493
494impl PEnum {
495 /// Parse a `facet_derive_parse::Enum` into a `PEnum`.
496 pub fn parse(e: &facet_derive_parse::Enum) -> Self {
497 let mut container_display_name = e.name.to_string();
498
499 // Parse container-level attributes
500 let attrs = PAttrs::parse(&e.attributes, &mut container_display_name);
501
502 // Get the container-level rename_all rule
503 let container_rename_all_rule = attrs.rename_all;
504
505 // Build PContainer
506 let container = PContainer {
507 name: e.name.clone(),
508 attrs,
509 bgp: BoundedGenericParams::parse(e.generics.as_ref()),
510 };
511
512 // Parse variants, passing the container's rename_all rule
513 let variants = e
514 .body
515 .content
516 .0
517 .iter()
518 .map(|delim| PVariant::parse(&delim.value, container_rename_all_rule))
519 .collect();
520
521 // Get the repr attribute if present, or default to Rust(None)
522 let mut repr = None;
523 for attr in &e.attributes {
524 if let facet_derive_parse::AttributeInner::Repr(repr_attr) = &attr.body.content {
525 // Parse repr attribute, will panic if invalid, just like struct repr parser
526 repr = match PRepr::parse(repr_attr) {
527 Some(parsed) => Some(parsed),
528 None => panic!(
529 "Unknown #[repr] attribute: {}",
530 repr_attr.tokens_to_string()
531 ),
532 };
533 break; // Only use the first #[repr] attribute
534 }
535 }
536 // Default to Rust(None) if not present, to match previous behavior, but enums will typically require repr(C) or a primitive in process_enum
537 let repr = repr.unwrap_or(PRepr::Rust(None));
538
539 PEnum {
540 container,
541 variants,
542 repr,
543 }
544 }
545}
546
547/// Parsed field
548#[derive(Clone)]
549pub struct PStructField {
550 /// The field's name (with rename rules applied)
551 pub name: PName,
552
553 /// The field's type
554 pub ty: TokenStream,
555
556 /// The field's offset (can be an expression, like `offset_of!(self, field)`)
557 pub offset: TokenStream,
558
559 /// The field's attributes
560 pub attrs: PAttrs,
561}
562
563impl PStructField {
564 /// Parse a named struct field (usual struct).
565 pub(crate) fn from_struct_field(
566 f: &facet_derive_parse::StructField,
567 rename_all_rule: Option<RenameRule>,
568 ) -> Self {
569 use facet_derive_parse::ToTokens;
570 Self::parse(
571 &f.attributes,
572 IdentOrLiteral::Ident(f.name.clone()),
573 f.typ.to_token_stream(),
574 rename_all_rule,
575 )
576 }
577
578 /// Parse a tuple (unnamed) field for tuple structs or enum tuple variants.
579 /// The index is converted to an identifier like `_0`, `_1`, etc.
580 pub(crate) fn from_enum_field(
581 attrs: &[facet_derive_parse::Attribute],
582 idx: usize,
583 typ: &facet_derive_parse::VerbatimUntil<facet_derive_parse::Comma>,
584 rename_all_rule: Option<RenameRule>,
585 ) -> Self {
586 use facet_derive_parse::ToTokens;
587 // Create an Ident from the index, using `_` prefix convention for tuple fields
588 let ty = typ.to_token_stream(); // Convert to TokenStream
589 Self::parse(attrs, IdentOrLiteral::Literal(idx), ty, rename_all_rule)
590 }
591
592 /// Central parse function used by both `from_struct_field` and `from_enum_field`.
593 fn parse(
594 attrs: &[facet_derive_parse::Attribute],
595 name: IdentOrLiteral,
596 ty: TokenStream,
597 rename_all_rule: Option<RenameRule>,
598 ) -> Self {
599 let initial_display_name = quote::ToTokens::to_token_stream(&name).tokens_to_string();
600 let mut display_name = initial_display_name.clone();
601
602 // Parse attributes for the field
603 let attrs = PAttrs::parse(attrs, &mut display_name);
604
605 // Name resolution:
606 // Precedence:
607 // 1. Field-level #[facet(rename = "...")]
608 // 2. rename_all_rule argument (container-level rename_all, passed in)
609 // 3. Raw field name (after stripping "r#")
610 let raw = name.clone();
611
612 let p_name = if display_name != initial_display_name {
613 // If #[facet(rename = "...")] is present, use it directly as the effective name.
614 // Preserve the span of the original identifier.
615 PName {
616 raw: raw.clone(),
617 effective: display_name,
618 }
619 } else {
620 // Use PName::new logic with container_rename_rule as the rename_all_rule argument.
621 // PName::new handles the case where rename_all_rule is None.
622 PName::new(rename_all_rule, raw)
623 };
624
625 // Field type as TokenStream (already provided as argument)
626 let ty = ty.clone();
627
628 // Offset string -- we don't know the offset here in generic parsing, so just default to empty
629 let offset = quote! {};
630
631 PStructField {
632 name: p_name,
633 ty,
634 offset,
635 attrs,
636 }
637 }
638}
639/// Parsed struct kind, modeled after `StructKind`.
640pub enum PStructKind {
641 /// A regular struct with named fields.
642 Struct { fields: Vec<PStructField> },
643 /// A tuple struct.
644 TupleStruct { fields: Vec<PStructField> },
645 /// A unit struct.
646 UnitStruct,
647}
648
649impl PStructKind {
650 /// Parse a `facet_derive_parse::StructKind` into a `PStructKind`.
651 /// Passes rename_all_rule through to all PStructField parsing.
652 pub fn parse(
653 kind: &facet_derive_parse::StructKind,
654 rename_all_rule: Option<RenameRule>,
655 ) -> Self {
656 match kind {
657 facet_derive_parse::StructKind::Struct { clauses: _, fields } => {
658 let parsed_fields = fields
659 .content
660 .0
661 .iter()
662 .map(|delim| PStructField::from_struct_field(&delim.value, rename_all_rule))
663 .collect();
664 PStructKind::Struct {
665 fields: parsed_fields,
666 }
667 }
668 facet_derive_parse::StructKind::TupleStruct {
669 fields,
670 clauses: _,
671 semi: _,
672 } => {
673 let parsed_fields = fields
674 .content
675 .0
676 .iter()
677 .enumerate()
678 .map(|(idx, delim)| {
679 PStructField::from_enum_field(
680 &delim.value.attributes,
681 idx,
682 &delim.value.typ,
683 rename_all_rule,
684 )
685 })
686 .collect();
687 PStructKind::TupleStruct {
688 fields: parsed_fields,
689 }
690 }
691 facet_derive_parse::StructKind::UnitStruct {
692 clauses: _,
693 semi: _,
694 } => PStructKind::UnitStruct,
695 }
696 }
697}
698
699impl PStruct {
700 pub fn parse(s: &facet_derive_parse::Struct) -> Self {
701 // Create a mutable string to pass to PAttrs::parse.
702 // While #[facet(rename = "...")] isn't typically used directly on the struct
703 // definition itself in the same way as fields, the parse function expects
704 // a mutable string to potentially modify if such an attribute is found.
705 // We initialize it with the struct's name, although its value isn't
706 // directly used for the container's name after parsing attributes.
707 let mut container_display_name = s.name.to_string();
708
709 // Parse top-level (container) attributes for the struct.
710 let attrs = PAttrs::parse(&s.attributes, &mut container_display_name);
711
712 // Extract the rename_all rule *after* parsing all attributes.
713 let rename_all_rule = attrs.rename_all;
714
715 // Build PContainer from struct's name and attributes.
716 let container = PContainer {
717 name: s.name.clone(),
718 attrs, // Use the parsed attributes (which includes rename_all implicitly)
719 bgp: BoundedGenericParams::parse(s.generics.as_ref()),
720 };
721
722 // Pass the container's rename_all rule (extracted above) as argument to PStructKind::parse
723 let kind = PStructKind::parse(&s.kind, rename_all_rule);
724
725 PStruct { container, kind }
726 }
727}
728
729/// Parsed enum variant kind
730pub enum PVariantKind {
731 /// Unit variant, e.g., `Variant`.
732 Unit,
733 /// Tuple variant, e.g., `Variant(u32, String)`.
734 Tuple { fields: Vec<PStructField> },
735 /// Struct variant, e.g., `Variant { field1: u32, field2: String }`.
736 Struct { fields: Vec<PStructField> },
737}
738
739/// Parsed enum variant
740pub struct PVariant {
741 /// Name of the variant (with rename rules applied)
742 pub name: PName,
743 /// Attributes of the variant
744 pub attrs: PAttrs,
745 /// Kind of the variant (unit, tuple, or struct)
746 pub kind: PVariantKind,
747 /// Optional explicit discriminant (`= literal`)
748 pub discriminant: Option<TokenStream>,
749}
750
751impl PVariant {
752 /// Parses an `EnumVariantLike` from `facet_derive_parse` into a `PVariant`.
753 ///
754 /// Requires the container-level `rename_all` rule to correctly determine the
755 /// effective name of the variant itself. The variant's own `rename_all` rule
756 /// (if present) will be stored in `attrs.rename_all` and used for its fields.
757 fn parse(
758 var_like: &facet_derive_parse::EnumVariantLike,
759 container_rename_all_rule: Option<RenameRule>,
760 ) -> Self {
761 use facet_derive_parse::{EnumVariantData, StructEnumVariant, TupleVariant, UnitVariant};
762
763 let (raw_name_ident, attributes) = match &var_like.variant {
764 // Fix: Changed var_like.value.variant to var_like.variant
765 EnumVariantData::Unit(UnitVariant { name, attributes })
766 | EnumVariantData::Tuple(TupleVariant {
767 name, attributes, ..
768 })
769 | EnumVariantData::Struct(StructEnumVariant {
770 name, attributes, ..
771 }) => (name, attributes),
772 };
773
774 let initial_display_name = raw_name_ident.to_string();
775 let mut display_name = initial_display_name.clone();
776
777 // Parse variant attributes, potentially modifying display_name if #[facet(rename=...)] is found
778 let attrs = PAttrs::parse(attributes.as_slice(), &mut display_name); // Fix: Pass attributes as a slice
779
780 // Determine the variant's effective name
781 let name = if display_name != initial_display_name {
782 // #[facet(rename=...)] was present on the variant
783 PName {
784 raw: IdentOrLiteral::Ident(raw_name_ident.clone()),
785 effective: display_name,
786 }
787 } else {
788 // Use container's rename_all rule if no variant-specific rename found
789 PName::new(
790 container_rename_all_rule,
791 IdentOrLiteral::Ident(raw_name_ident.clone()),
792 )
793 };
794
795 // Extract the variant's own rename_all rule to apply to its fields
796 let variant_field_rename_rule = attrs.rename_all;
797
798 // Parse the variant kind and its fields
799 let kind = match &var_like.variant {
800 // Fix: Changed var_like.value.variant to var_like.variant
801 EnumVariantData::Unit(_) => PVariantKind::Unit,
802 EnumVariantData::Tuple(TupleVariant { fields, .. }) => {
803 let parsed_fields = fields
804 .content
805 .0
806 .iter()
807 .enumerate()
808 .map(|(idx, delim)| {
809 PStructField::from_enum_field(
810 &delim.value.attributes,
811 idx,
812 &delim.value.typ,
813 variant_field_rename_rule, // Use variant's rule for its fields
814 )
815 })
816 .collect();
817 PVariantKind::Tuple {
818 fields: parsed_fields,
819 }
820 }
821 EnumVariantData::Struct(StructEnumVariant { fields, .. }) => {
822 let parsed_fields = fields
823 .content
824 .0
825 .iter()
826 .map(|delim| {
827 PStructField::from_struct_field(
828 &delim.value,
829 variant_field_rename_rule, // Use variant's rule for its fields
830 )
831 })
832 .collect();
833 PVariantKind::Struct {
834 fields: parsed_fields,
835 }
836 }
837 };
838
839 // Extract the discriminant literal if present
840 let discriminant = var_like
841 .discriminant
842 .as_ref()
843 .map(|d| d.second.to_token_stream());
844
845 PVariant {
846 name,
847 attrs,
848 kind,
849 discriminant,
850 }
851 }
852}