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}