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}