facet_macros_emit/
parsed.rs

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