1use crate::{Attribute, AttributeInner, FacetInner, IParse, Ident, ToTokenIter, TokenStream};
20use quote::quote;
21
22#[derive(Debug, Clone)]
27pub enum PluginRef {
28 Simple(String),
30 Path {
32 crate_name: String,
34 plugin_name: String,
36 },
37}
38
39impl PluginRef {
40 pub fn crate_path(&self) -> TokenStream {
42 match self {
43 PluginRef::Simple(name) => {
44 let snake_case = to_snake_case(name);
45 let crate_name = format!("facet_{snake_case}");
46 let crate_ident = quote::format_ident!("{}", crate_name);
47 quote! { ::#crate_ident }
48 }
49 PluginRef::Path { crate_name, .. } => {
50 let crate_ident = quote::format_ident!("{}", crate_name);
51 quote! { ::#crate_ident }
52 }
53 }
54 }
55}
56
57pub fn extract_derive_plugins(attrs: &[Attribute]) -> Vec<PluginRef> {
63 let mut plugins = Vec::new();
64
65 for attr in attrs {
66 if let AttributeInner::Facet(facet_attr) = &attr.body.content {
67 for inner in facet_attr.inner.content.iter().map(|d| &d.value) {
68 if let FacetInner::Simple(simple) = inner
69 && simple.key == "derive"
70 {
71 if let Some(ref args) = simple.args {
73 match args {
74 crate::AttrArgs::Parens(parens) => {
75 plugins.extend(parse_plugin_list(&parens.content));
77 }
78 crate::AttrArgs::Equals(_) => {
79 }
81 }
82 }
83 }
84 }
85 }
86 }
87
88 plugins
89}
90
91fn parse_plugin_list(tokens: &[crate::TokenTree]) -> Vec<PluginRef> {
93 let mut plugins = Vec::new();
94 let mut iter = tokens.iter().cloned().peekable();
95
96 while iter.peek().is_some() {
97 let mut item_tokens = Vec::new();
99 while let Some(tt) = iter.peek() {
100 if let proc_macro2::TokenTree::Punct(p) = tt
101 && p.as_char() == ','
102 {
103 iter.next(); break;
105 }
106 item_tokens.push(iter.next().unwrap());
107 }
108
109 if let Some(plugin_ref) = parse_plugin_ref(&item_tokens) {
111 plugins.push(plugin_ref);
112 }
113 }
114
115 plugins
116}
117
118fn parse_plugin_ref(tokens: &[proc_macro2::TokenTree]) -> Option<PluginRef> {
120 if tokens.is_empty() {
121 return None;
122 }
123
124 let has_path_sep = tokens.windows(2).any(|w| {
126 matches!((&w[0], &w[1]),
127 (proc_macro2::TokenTree::Punct(p1), proc_macro2::TokenTree::Punct(p2))
128 if p1.as_char() == ':' && p2.as_char() == ':')
129 });
130
131 if has_path_sep {
132 let mut iter = tokens.iter();
135
136 let crate_name = match iter.next() {
138 Some(proc_macro2::TokenTree::Ident(id)) => id.to_string(),
139 _ => return None,
140 };
141
142 match (iter.next(), iter.next()) {
144 (Some(proc_macro2::TokenTree::Punct(p1)), Some(proc_macro2::TokenTree::Punct(p2)))
145 if p1.as_char() == ':' && p2.as_char() == ':' => {}
146 _ => return None,
147 }
148
149 let plugin_name = match iter.next() {
151 Some(proc_macro2::TokenTree::Ident(id)) => id.to_string(),
152 _ => return None,
153 };
154
155 Some(PluginRef::Path {
156 crate_name,
157 plugin_name,
158 })
159 } else {
160 match tokens.first() {
162 Some(proc_macro2::TokenTree::Ident(id)) => Some(PluginRef::Simple(id.to_string())),
163 _ => None,
164 }
165 }
166}
167
168pub fn plugin_to_crate_path(plugin_name: &str) -> TokenStream {
173 let snake_case = to_snake_case(plugin_name);
175 let crate_name = format!("facet_{snake_case}");
176 let crate_ident = quote::format_ident!("{}", crate_name);
177 quote! { ::#crate_ident }
178}
179
180fn to_snake_case(s: &str) -> String {
182 let mut result = String::new();
183 for (i, c) in s.chars().enumerate() {
184 if c.is_uppercase() {
185 if i > 0 {
186 result.push('_');
187 }
188 result.push(c.to_ascii_lowercase());
189 } else {
190 result.push(c);
191 }
192 }
193 result
194}
195
196fn strip_derive_attrs(tokens: TokenStream) -> TokenStream {
207 let mut result = TokenStream::new();
208 let mut iter = tokens.into_iter().peekable();
209
210 while let Some(tt) = iter.next() {
211 if let proc_macro2::TokenTree::Punct(p) = &tt
213 && p.as_char() == '#'
214 && let Some(proc_macro2::TokenTree::Group(g)) = iter.peek()
215 && g.delimiter() == proc_macro2::Delimiter::Bracket
216 {
217 let inner = g.stream();
219 if is_plugin_attr(&inner) {
220 iter.next(); continue;
223 }
224 }
225 result.extend(std::iter::once(tt));
226 }
227
228 result
229}
230
231fn is_plugin_attr(inner: &TokenStream) -> bool {
239 let mut iter = inner.clone().into_iter();
240
241 if let Some(proc_macro2::TokenTree::Ident(id)) = iter.next() {
243 if id != "facet" {
244 return false;
245 }
246 } else {
247 return false;
248 }
249
250 if let Some(proc_macro2::TokenTree::Group(g)) = iter.next() {
252 if g.delimiter() != proc_macro2::Delimiter::Parenthesis {
253 return false;
254 }
255
256 let content = g.stream();
257 let mut content_iter = content.into_iter();
258
259 if let Some(proc_macro2::TokenTree::Ident(id)) = content_iter.next() {
261 let first = id.to_string();
262
263 if first == "derive" {
265 return true;
266 }
267
268 if let Some(proc_macro2::TokenTree::Punct(p)) = content_iter.next()
270 && p.as_char() == ':'
271 && let Some(proc_macro2::TokenTree::Punct(p2)) = content_iter.next()
272 && p2.as_char() == ':'
273 {
274 return true;
276 }
277 }
278 }
279
280 false
281}
282
283#[deprecated(note = "use is_plugin_attr instead")]
285#[allow(dead_code)]
286fn is_facet_derive_attr(inner: &TokenStream) -> bool {
287 is_plugin_attr(inner)
288}
289
290pub fn generate_plugin_chain(
295 input_tokens: &TokenStream,
296 plugins: &[PluginRef],
297 facet_crate: &TokenStream,
298) -> Option<TokenStream> {
299 if plugins.is_empty() {
300 return None;
301 }
302
303 let plugin_paths: Vec<TokenStream> = plugins
306 .iter()
307 .map(|p| {
308 let crate_path = p.crate_path();
309 quote! { #crate_path::__facet_invoke }
310 })
311 .collect();
312
313 let first = &plugin_paths[0];
314 let rest: Vec<_> = plugin_paths[1..].iter().collect();
315
316 let remaining = if rest.is_empty() {
317 quote! {}
318 } else {
319 quote! { #(#rest),* }
320 };
321
322 Some(quote! {
323 #first! {
324 @tokens { #input_tokens }
325 @remaining { #remaining }
326 @plugins { }
327 @facet_crate { #facet_crate }
328 }
329 })
330}
331
332pub fn facet_finalize(input: TokenStream) -> TokenStream {
339 let mut iter = input.to_token_iter();
345
346 let mut tokens: Option<TokenStream> = None;
347 let mut plugins_section: Option<TokenStream> = None;
348 let mut facet_crate: Option<TokenStream> = None;
349
350 while let Ok(section) = iter.parse::<FinalizeSection>() {
352 match section.marker.name.to_string().as_str() {
353 "tokens" => {
354 tokens = Some(section.content.content);
355 }
356 "plugins" => {
357 plugins_section = Some(section.content.content);
358 }
359 "facet_crate" => {
360 facet_crate = Some(section.content.content);
361 }
362 other => {
363 let msg = format!("unknown section in __facet_finalize: @{other}");
364 return quote! { compile_error!(#msg); };
365 }
366 }
367 }
368
369 let tokens = match tokens {
370 Some(t) => t,
371 None => {
372 return quote! { compile_error!("__facet_finalize: missing @tokens section"); };
373 }
374 };
375
376 let facet_crate = facet_crate.unwrap_or_else(|| quote! { ::facet });
377
378 let filtered_tokens = strip_derive_attrs(tokens.clone());
380
381 let mut type_iter = filtered_tokens.clone().to_token_iter();
383 let facet_impl = match type_iter.parse::<crate::Cons<crate::AdtDecl, crate::EndOfStream>>() {
384 Ok(it) => match it.first {
385 crate::AdtDecl::Struct(parsed) => crate::process_struct::process_struct(parsed),
386 crate::AdtDecl::Enum(parsed) => crate::process_enum::process_enum(parsed),
387 },
388 Err(err) => {
389 let msg = format!("__facet_finalize: could not parse type: {err}");
390 return quote! { compile_error!(#msg); };
391 }
392 };
393
394 let plugin_impls = if let Some(plugins_tokens) = plugins_section {
396 extract_plugin_templates(plugins_tokens, &filtered_tokens, &facet_crate)
398 } else {
399 vec![]
400 };
401
402 quote! {
403 #facet_impl
404 #(#plugin_impls)*
405 }
406}
407
408struct PluginTemplate {
410 #[allow(dead_code)] name: String,
412 template: TokenStream,
413}
414
415fn extract_plugin_templates(
417 plugins_tokens: TokenStream,
418 type_tokens: &TokenStream,
419 facet_crate: &TokenStream,
420) -> Vec<TokenStream> {
421 let plugins = parse_plugin_sections(plugins_tokens);
423
424 let parsed_type = match facet_macro_parse::parse_type(type_tokens.clone()) {
426 Ok(ty) => ty,
427 Err(e) => {
428 let msg = format!("failed to parse type for plugin templates: {e}");
429 return vec![quote! { compile_error!(#msg); }];
430 }
431 };
432
433 plugins
435 .into_iter()
436 .map(|plugin| evaluate_template(plugin.template, &parsed_type, facet_crate))
437 .collect()
438}
439
440fn parse_plugin_sections(tokens: TokenStream) -> Vec<PluginTemplate> {
442 let mut plugins = Vec::new();
443 let mut iter = tokens.into_iter().peekable();
444
445 while let Some(tt) = iter.next() {
446 if let proc_macro2::TokenTree::Punct(p) = &tt
448 && p.as_char() == '@'
449 {
450 if let Some(proc_macro2::TokenTree::Ident(id)) = iter.peek()
452 && *id == "plugin"
453 {
454 iter.next(); if let Some(proc_macro2::TokenTree::Group(g)) = iter.next()
458 && g.delimiter() == proc_macro2::Delimiter::Brace
459 && let Some(plugin) = parse_plugin_content(g.stream())
460 {
461 plugins.push(plugin);
462 }
463 }
464 }
465 }
466
467 plugins
468}
469
470fn parse_plugin_content(tokens: TokenStream) -> Option<PluginTemplate> {
472 let mut name: Option<String> = None;
473 let mut template: Option<TokenStream> = None;
474 let mut iter = tokens.into_iter().peekable();
475
476 while let Some(tt) = iter.next() {
477 if let proc_macro2::TokenTree::Punct(p) = &tt
478 && p.as_char() == '@'
479 && let Some(proc_macro2::TokenTree::Ident(id)) = iter.peek()
480 {
481 let key = id.to_string();
482 iter.next(); if let Some(proc_macro2::TokenTree::Group(g)) = iter.next()
486 && g.delimiter() == proc_macro2::Delimiter::Brace
487 {
488 match key.as_str() {
489 "name" => {
490 let content = g.stream().into_iter().collect::<Vec<_>>();
492 if let Some(proc_macro2::TokenTree::Literal(lit)) = content.first() {
493 let s = lit.to_string();
494 name = Some(s.trim_matches('"').to_string());
495 }
496 }
497 "template" => {
498 template = Some(g.stream());
499 }
500 _ => {}
501 }
502 }
503 }
504 }
505
506 match (name, template) {
507 (Some(n), Some(t)) => Some(PluginTemplate {
508 name: n,
509 template: t,
510 }),
511 _ => None,
512 }
513}
514
515fn evaluate_template(
517 template: TokenStream,
518 parsed_type: &facet_macro_parse::PType,
519 _facet_crate: &TokenStream,
520) -> TokenStream {
521 let mut ctx = EvalContext::new(parsed_type);
522 evaluate_with_context(template, &mut ctx)
523}
524
525struct EvalContext<'a> {
535 parsed_type: &'a facet_macro_parse::PType,
537
538 stack: Vec<ContextFrame<'a>>,
540}
541
542enum ContextFrame<'a> {
544 Variant {
546 variant: &'a facet_macro_parse::PVariant,
547 },
548
549 Field {
551 field: &'a facet_macro_parse::PStructField,
552 index: usize,
554 },
555
556 Attr {
558 attr: &'a facet_macro_parse::PFacetAttr,
560 },
561}
562
563impl<'a> EvalContext<'a> {
564 fn new(parsed_type: &'a facet_macro_parse::PType) -> Self {
565 Self {
566 parsed_type,
567 stack: Vec::new(),
568 }
569 }
570
571 fn push(&mut self, frame: ContextFrame<'a>) {
572 self.stack.push(frame);
573 }
574
575 fn pop(&mut self) {
576 self.stack.pop();
577 }
578
579 fn current_variant(&self) -> Option<&'a facet_macro_parse::PVariant> {
581 self.stack.iter().rev().find_map(|f| match f {
582 ContextFrame::Variant { variant } => Some(*variant),
583 _ => None,
584 })
585 }
586
587 fn current_field(&self) -> Option<(&'a facet_macro_parse::PStructField, usize)> {
589 self.stack.iter().rev().find_map(|f| match f {
590 ContextFrame::Field { field, index } => Some((*field, *index)),
591 _ => None,
592 })
593 }
594
595 fn current_attr(&self) -> Option<&'a facet_macro_parse::PFacetAttr> {
597 self.stack.iter().rev().find_map(|f| match f {
598 ContextFrame::Attr { attr } => Some(*attr),
599 _ => None,
600 })
601 }
602
603 fn current_fields(&self) -> Option<&'a [facet_macro_parse::PStructField]> {
605 if let Some(variant) = self.current_variant() {
607 return match &variant.kind {
608 facet_macro_parse::PVariantKind::Tuple { fields } => Some(fields),
609 facet_macro_parse::PVariantKind::Struct { fields } => Some(fields),
610 facet_macro_parse::PVariantKind::Unit => None,
611 };
612 }
613
614 if let facet_macro_parse::PType::Struct(s) = self.parsed_type {
616 return match &s.kind {
617 facet_macro_parse::PStructKind::Struct { fields } => Some(fields),
618 facet_macro_parse::PStructKind::TupleStruct { fields } => Some(fields),
619 facet_macro_parse::PStructKind::UnitStruct => None,
620 };
621 }
622
623 None
624 }
625
626 fn current_attrs(&self) -> &'a facet_macro_parse::PAttrs {
628 if let Some((field, _)) = self.current_field() {
630 return &field.attrs;
631 }
632
633 if let Some(variant) = self.current_variant() {
635 return &variant.attrs;
636 }
637
638 match self.parsed_type {
640 facet_macro_parse::PType::Struct(s) => &s.container.attrs,
641 facet_macro_parse::PType::Enum(e) => &e.container.attrs,
642 }
643 }
644}
645
646struct AttrQuery {
652 ns: String,
653 key: String,
654}
655
656impl AttrQuery {
657 fn parse(tokens: TokenStream) -> Option<Self> {
659 let mut iter = tokens.into_iter();
660
661 let ns = match iter.next() {
663 Some(proc_macro2::TokenTree::Ident(id)) => id.to_string(),
664 _ => return None,
665 };
666
667 match (iter.next(), iter.next()) {
669 (Some(proc_macro2::TokenTree::Punct(p1)), Some(proc_macro2::TokenTree::Punct(p2)))
670 if p1.as_char() == ':' && p2.as_char() == ':' => {}
671 _ => return None,
672 }
673
674 let key = match iter.next() {
676 Some(proc_macro2::TokenTree::Ident(id)) => id.to_string(),
677 _ => return None,
678 };
679
680 Some(AttrQuery { ns, key })
681 }
682
683 fn matches(&self, attr: &facet_macro_parse::PFacetAttr) -> bool {
685 if let Some(ref ns) = attr.ns {
686 *ns == self.ns && attr.key == self.key
687 } else {
688 false
689 }
690 }
691
692 fn find_in<'a>(
694 &self,
695 attrs: &'a [facet_macro_parse::PFacetAttr],
696 ) -> Option<&'a facet_macro_parse::PFacetAttr> {
697 attrs.iter().find(|a| self.matches(a))
698 }
699}
700
701fn evaluate_with_context(template: TokenStream, ctx: &mut EvalContext<'_>) -> TokenStream {
707 let mut output = TokenStream::new();
708 let mut iter = template.into_iter().peekable();
709
710 while let Some(tt) = iter.next() {
711 match &tt {
712 proc_macro2::TokenTree::Punct(p) if p.as_char() == '@' => {
713 handle_directive(&mut iter, ctx, &mut output);
714 }
715 proc_macro2::TokenTree::Group(g) => {
716 let inner = evaluate_with_context(g.stream(), ctx);
718 let new_group = proc_macro2::Group::new(g.delimiter(), inner);
719 output.extend(std::iter::once(proc_macro2::TokenTree::Group(new_group)));
720 }
721 _ => {
722 output.extend(std::iter::once(tt));
723 }
724 }
725 }
726
727 output
728}
729
730fn handle_directive(
732 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
733 ctx: &mut EvalContext<'_>,
734 output: &mut TokenStream,
735) {
736 let Some(next) = iter.next() else {
737 output.extend(quote! { @ });
739 return;
740 };
741
742 let proc_macro2::TokenTree::Ident(directive_ident) = &next else {
743 output.extend(quote! { @ });
745 output.extend(std::iter::once(next));
746 return;
747 };
748
749 let directive = directive_ident.to_string();
750
751 match directive.as_str() {
752 "Self" => emit_self_type(ctx, output),
754
755 "for_variant" => handle_for_variant(iter, ctx, output),
757 "for_field" => handle_for_field(iter, ctx, output),
758
759 "if_attr" => handle_if_attr(iter, ctx, output),
761 "if_field_attr" => handle_if_field_attr(iter, ctx, output),
762 "if_any_field_attr" => handle_if_any_field_attr(iter, ctx, output),
763 "if_struct" => handle_if_struct(iter, ctx, output),
764 "if_enum" => handle_if_enum(iter, ctx, output),
765 "if_unit_variant" => handle_if_unit_variant(iter, ctx, output),
766 "if_tuple_variant" => handle_if_tuple_variant(iter, ctx, output),
767 "if_struct_variant" => handle_if_struct_variant(iter, ctx, output),
768
769 "variant_name" => emit_variant_name(ctx, output),
771 "variant_pattern" => emit_variant_pattern(ctx, output),
772 "variant_pattern_only" => handle_variant_pattern_only(iter, ctx, output),
773 "field_name" => emit_field_name(ctx, output),
774 "field_type" => emit_field_type(ctx, output),
775 "field_expr" => emit_field_expr(ctx, output),
776 "attr_args" => emit_attr_args(ctx, output),
777 "doc" => emit_doc(ctx, output),
778
779 "field_default_expr" => emit_field_default_expr(ctx, output),
781 "variant_default_construction" => emit_variant_default_construction(ctx, output),
782
783 "format_doc_comment" => emit_format_doc_comment(ctx, output),
785
786 _ => {
788 output.extend(quote! { @ });
790 output.extend(std::iter::once(next.clone()));
791 }
792 }
793}
794
795fn emit_self_type(ctx: &EvalContext<'_>, output: &mut TokenStream) {
800 let name = ctx.parsed_type.name();
801 output.extend(quote! { #name });
802}
803
804fn handle_for_variant(
810 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
811 ctx: &mut EvalContext<'_>,
812 output: &mut TokenStream,
813) {
814 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
815 return; };
817
818 let body = body_group.stream();
819
820 let facet_macro_parse::PType::Enum(e) = ctx.parsed_type else {
822 return;
823 };
824
825 for variant in &e.variants {
826 ctx.push(ContextFrame::Variant { variant });
827 let expanded = evaluate_with_context(body.clone(), ctx);
828 output.extend(expanded);
829 ctx.pop();
830 }
831}
832
833fn handle_for_field(
835 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
836 ctx: &mut EvalContext<'_>,
837 output: &mut TokenStream,
838) {
839 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
840 return;
841 };
842
843 let body = body_group.stream();
844
845 let Some(fields) = ctx.current_fields() else {
846 return;
847 };
848
849 let fields: Vec<_> = fields.iter().enumerate().collect();
851
852 for (index, field) in fields {
853 ctx.push(ContextFrame::Field { field, index });
854 let expanded = evaluate_with_context(body.clone(), ctx);
855 output.extend(expanded);
856 ctx.pop();
857 }
858}
859
860fn handle_if_attr(
866 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
867 ctx: &mut EvalContext<'_>,
868 output: &mut TokenStream,
869) {
870 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
872 return;
873 };
874
875 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
877 return;
878 };
879
880 let Some(query) = AttrQuery::parse(query_group.stream()) else {
881 return;
882 };
883
884 let attrs = ctx.current_attrs();
885
886 if let Some(matched_attr) = query.find_in(&attrs.facet) {
887 ctx.push(ContextFrame::Attr { attr: matched_attr });
888 let expanded = evaluate_with_context(body_group.stream(), ctx);
889 output.extend(expanded);
890 ctx.pop();
891 }
892}
893
894fn handle_if_field_attr(
899 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
900 ctx: &mut EvalContext<'_>,
901 output: &mut TokenStream,
902) {
903 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
904 return;
905 };
906
907 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
908 return;
909 };
910
911 let Some(query) = AttrQuery::parse(query_group.stream()) else {
912 return;
913 };
914
915 let Some(fields) = ctx.current_fields() else {
916 return;
917 };
918
919 let fields: Vec<_> = fields.iter().enumerate().collect();
921
922 for (index, field) in fields {
924 if let Some(matched_attr) = query.find_in(&field.attrs.facet) {
925 ctx.push(ContextFrame::Field { field, index });
926 ctx.push(ContextFrame::Attr { attr: matched_attr });
927 let expanded = evaluate_with_context(body_group.stream(), ctx);
928 output.extend(expanded);
929 ctx.pop(); ctx.pop(); return; }
933 }
934}
935
936fn handle_if_any_field_attr(
942 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
943 ctx: &mut EvalContext<'_>,
944 output: &mut TokenStream,
945) {
946 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
947 return;
948 };
949
950 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
951 return;
952 };
953
954 let Some(query) = AttrQuery::parse(query_group.stream()) else {
955 return;
956 };
957
958 let Some(fields) = ctx.current_fields() else {
959 return;
960 };
961
962 let has_any = fields
964 .iter()
965 .any(|f| query.find_in(&f.attrs.facet).is_some());
966
967 if has_any {
968 let expanded = evaluate_with_context(body_group.stream(), ctx);
969 output.extend(expanded);
970 }
971}
972
973fn handle_if_struct(
975 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
976 ctx: &mut EvalContext<'_>,
977 output: &mut TokenStream,
978) {
979 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
980 return;
981 };
982
983 if matches!(ctx.parsed_type, facet_macro_parse::PType::Struct(_)) {
984 let expanded = evaluate_with_context(body_group.stream(), ctx);
985 output.extend(expanded);
986 }
987}
988
989fn handle_if_enum(
991 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
992 ctx: &mut EvalContext<'_>,
993 output: &mut TokenStream,
994) {
995 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
996 return;
997 };
998
999 if matches!(ctx.parsed_type, facet_macro_parse::PType::Enum(_)) {
1000 let expanded = evaluate_with_context(body_group.stream(), ctx);
1001 output.extend(expanded);
1002 }
1003}
1004
1005fn handle_if_unit_variant(
1007 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1008 ctx: &mut EvalContext<'_>,
1009 output: &mut TokenStream,
1010) {
1011 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1012 return;
1013 };
1014
1015 let Some(variant) = ctx.current_variant() else {
1016 return;
1017 };
1018
1019 if matches!(variant.kind, facet_macro_parse::PVariantKind::Unit) {
1020 let expanded = evaluate_with_context(body_group.stream(), ctx);
1021 output.extend(expanded);
1022 }
1023}
1024
1025fn handle_if_tuple_variant(
1027 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1028 ctx: &mut EvalContext<'_>,
1029 output: &mut TokenStream,
1030) {
1031 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1032 return;
1033 };
1034
1035 let Some(variant) = ctx.current_variant() else {
1036 return;
1037 };
1038
1039 if matches!(variant.kind, facet_macro_parse::PVariantKind::Tuple { .. }) {
1040 let expanded = evaluate_with_context(body_group.stream(), ctx);
1041 output.extend(expanded);
1042 }
1043}
1044
1045fn handle_if_struct_variant(
1047 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1048 ctx: &mut EvalContext<'_>,
1049 output: &mut TokenStream,
1050) {
1051 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1052 return;
1053 };
1054
1055 let Some(variant) = ctx.current_variant() else {
1056 return;
1057 };
1058
1059 if matches!(variant.kind, facet_macro_parse::PVariantKind::Struct { .. }) {
1060 let expanded = evaluate_with_context(body_group.stream(), ctx);
1061 output.extend(expanded);
1062 }
1063}
1064
1065fn emit_variant_name(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1070 if let Some(variant) = ctx.current_variant()
1071 && let facet_macro_parse::IdentOrLiteral::Ident(name) = &variant.name.raw
1072 {
1073 output.extend(quote! { #name });
1074 }
1075}
1076
1077fn emit_variant_pattern(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1078 let Some(variant) = ctx.current_variant() else {
1079 return;
1080 };
1081
1082 use facet_macro_parse::{IdentOrLiteral, PVariantKind};
1083
1084 match &variant.kind {
1085 PVariantKind::Unit => {
1086 }
1088 PVariantKind::Tuple { fields } => {
1089 let names: Vec<_> = (0..fields.len())
1092 .map(|i| quote::format_ident!("v{}", i))
1093 .collect();
1094 output.extend(quote! { ( #(#names),* ) });
1095 }
1096 PVariantKind::Struct { fields } => {
1097 let names: Vec<_> = fields
1098 .iter()
1099 .filter_map(|f| {
1100 if let IdentOrLiteral::Ident(id) = &f.name.raw {
1101 Some(id.clone())
1102 } else {
1103 None
1104 }
1105 })
1106 .collect();
1107 output.extend(quote! { { #(#names),* } });
1108 }
1109 }
1110}
1111
1112fn handle_variant_pattern_only(
1114 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1115 ctx: &EvalContext<'_>,
1116 output: &mut TokenStream,
1117) {
1118 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
1119 return;
1120 };
1121
1122 let Some(query) = AttrQuery::parse(query_group.stream()) else {
1123 return;
1124 };
1125
1126 let Some(variant) = ctx.current_variant() else {
1127 return;
1128 };
1129
1130 use facet_macro_parse::{IdentOrLiteral, PVariantKind};
1131
1132 match &variant.kind {
1133 PVariantKind::Unit => {
1134 }
1136 PVariantKind::Tuple { fields } => {
1137 let patterns: Vec<_> = fields
1138 .iter()
1139 .enumerate()
1140 .map(|(i, f)| {
1141 if query.find_in(&f.attrs.facet).is_some() {
1142 let name = quote::format_ident!("v{}", i);
1143 quote! { #name }
1144 } else {
1145 quote! { _ }
1146 }
1147 })
1148 .collect();
1149 output.extend(quote! { ( #(#patterns),* ) });
1150 }
1151 PVariantKind::Struct { fields } => {
1152 let bindings: Vec<_> = fields
1153 .iter()
1154 .filter_map(|f| {
1155 if let IdentOrLiteral::Ident(id) = &f.name.raw {
1156 if query.find_in(&f.attrs.facet).is_some() {
1157 Some(quote! { #id })
1158 } else {
1159 Some(quote! { #id: _ })
1160 }
1161 } else {
1162 None
1163 }
1164 })
1165 .collect();
1166 output.extend(quote! { { #(#bindings),* } });
1167 }
1168 }
1169}
1170
1171fn emit_field_name(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1172 let Some((field, index)) = ctx.current_field() else {
1173 return;
1174 };
1175
1176 use facet_macro_parse::IdentOrLiteral;
1177
1178 match &field.name.raw {
1179 IdentOrLiteral::Ident(id) => {
1180 output.extend(quote! { #id });
1181 }
1182 IdentOrLiteral::Literal(_) => {
1183 let name = quote::format_ident!("v{}", index);
1185 output.extend(quote! { #name });
1186 }
1187 }
1188}
1189
1190fn emit_field_type(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1191 if let Some((field, _)) = ctx.current_field() {
1192 let ty = &field.ty;
1193 output.extend(quote! { #ty });
1194 }
1195}
1196
1197fn emit_field_expr(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1198 emit_field_name(ctx, output);
1200}
1201
1202fn emit_attr_args(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1203 if let Some(attr) = ctx.current_attr() {
1204 let args = &attr.args;
1205 output.extend(args.clone());
1206 }
1207}
1208
1209fn emit_doc(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1210 let attrs = ctx.current_attrs();
1211 let doc = attrs.doc.join(" ").trim().to_string();
1212 if !doc.is_empty() {
1213 output.extend(quote! { #doc });
1214 }
1215}
1216
1217fn emit_field_default_expr(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1228 let Some((field, _)) = ctx.current_field() else {
1229 return;
1230 };
1231
1232 let value_query = AttrQuery {
1234 ns: "default".to_string(),
1235 key: "value".to_string(),
1236 };
1237
1238 if let Some(attr) = value_query.find_in(&field.attrs.facet) {
1239 let args = &attr.args;
1241 output.extend(quote! { (#args).into() });
1242 return;
1243 }
1244
1245 let func_query = AttrQuery {
1247 ns: "default".to_string(),
1248 key: "func".to_string(),
1249 };
1250
1251 if let Some(attr) = func_query.find_in(&field.attrs.facet) {
1252 let func_path = parse_func_path(&attr.args);
1254 output.extend(quote! { #func_path() });
1255 return;
1256 }
1257
1258 output.extend(quote! { ::core::default::Default::default() });
1260}
1261
1262fn emit_variant_default_construction(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1268 use facet_macro_parse::{IdentOrLiteral, PVariantKind};
1269
1270 let Some(variant) = ctx.current_variant() else {
1271 return;
1272 };
1273
1274 match &variant.kind {
1275 PVariantKind::Unit => {
1276 }
1278 PVariantKind::Tuple { fields } => {
1279 let defaults: Vec<_> = fields.iter().map(field_default_tokens).collect();
1280 output.extend(quote! { ( #(#defaults),* ) });
1281 }
1282 PVariantKind::Struct { fields } => {
1283 let field_inits: Vec<_> = fields
1284 .iter()
1285 .filter_map(|f| {
1286 if let IdentOrLiteral::Ident(name) = &f.name.raw {
1287 let default_expr = field_default_tokens(f);
1288 Some(quote! { #name: #default_expr })
1289 } else {
1290 None
1291 }
1292 })
1293 .collect();
1294 output.extend(quote! { { #(#field_inits),* } });
1295 }
1296 }
1297}
1298
1299fn field_default_tokens(field: &facet_macro_parse::PStructField) -> TokenStream {
1301 let value_query = AttrQuery {
1303 ns: "default".to_string(),
1304 key: "value".to_string(),
1305 };
1306
1307 if let Some(attr) = value_query.find_in(&field.attrs.facet) {
1308 let args = &attr.args;
1309 return quote! { (#args).into() };
1310 }
1311
1312 let func_query = AttrQuery {
1314 ns: "default".to_string(),
1315 key: "func".to_string(),
1316 };
1317
1318 if let Some(attr) = func_query.find_in(&field.attrs.facet) {
1319 let func_path = parse_func_path(&attr.args);
1320 return quote! { #func_path() };
1321 }
1322
1323 quote! { ::core::default::Default::default() }
1325}
1326
1327fn parse_func_path(args: &TokenStream) -> TokenStream {
1331 let args_str = args.to_string();
1333 let trimmed = args_str.trim();
1334
1335 if trimmed.starts_with('"') && trimmed.ends_with('"') {
1337 let path_str = &trimmed[1..trimmed.len() - 1];
1338
1339 let segments: Vec<_> = path_str.split("::").collect();
1341
1342 if segments.len() == 1 {
1343 let ident = quote::format_ident!("{}", segments[0]);
1345 quote! { #ident }
1346 } else {
1347 let idents: Vec<_> = segments
1349 .iter()
1350 .map(|s| quote::format_ident!("{}", s))
1351 .collect();
1352 quote! { #(#idents)::* }
1353 }
1354 } else {
1355 args.clone()
1357 }
1358}
1359
1360fn emit_format_doc_comment(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1366 use facet_macro_parse::PVariantKind;
1367
1368 let Some(variant) = ctx.current_variant() else {
1369 return;
1370 };
1371
1372 let doc = variant.attrs.doc.join(" ").trim().to_string();
1373 let format_str = if doc.is_empty() {
1374 variant.name.effective.clone()
1375 } else {
1376 doc
1377 };
1378
1379 match &variant.kind {
1381 PVariantKind::Unit => {
1382 output.extend(quote! { #format_str });
1383 }
1384 PVariantKind::Tuple { fields } => {
1385 if format_str.contains("{0}") {
1386 let field_names: Vec<_> = (0..fields.len())
1388 .map(|i| quote::format_ident!("v{}", i))
1389 .collect();
1390 output.extend(quote! { #format_str, #(#field_names),* });
1391 } else {
1392 output.extend(quote! { #format_str });
1393 }
1394 }
1395 PVariantKind::Struct { fields: _ } => {
1396 output.extend(quote! { #format_str });
1398 }
1399 }
1400}
1401
1402crate::unsynn! {
1404 struct FinalizeSectionMarker {
1406 _at: crate::At,
1407 name: Ident,
1408 }
1409
1410 struct FinalizeSection {
1412 marker: FinalizeSectionMarker,
1413 content: crate::BraceGroupContaining<TokenStream>,
1414 }
1415}
1416
1417#[cfg(test)]
1418mod tests {
1419 use super::*;
1420 use crate::IParse;
1421 use quote::quote;
1422
1423 #[test]
1424 fn test_to_snake_case() {
1425 assert_eq!(to_snake_case("Error"), "error");
1426 assert_eq!(to_snake_case("Display"), "display");
1427 assert_eq!(to_snake_case("PartialEq"), "partial_eq");
1428 assert_eq!(to_snake_case("FromStr"), "from_str");
1429 }
1430
1431 #[test]
1432 fn test_extract_derive_plugins() {
1433 let input = quote! {
1434 #[derive(Facet, Debug)]
1435 #[facet(derive(Error))]
1436 #[repr(u8)]
1437 pub enum MyError {
1438 Disconnect(u32),
1439 }
1440 };
1441
1442 let mut iter = input.to_token_iter();
1443 let parsed = iter.parse::<crate::Enum>().expect("Failed to parse enum");
1444
1445 let plugins = extract_derive_plugins(&parsed.attributes);
1446 assert_eq!(plugins.len(), 1);
1447 assert!(matches!(&plugins[0], PluginRef::Simple(name) if name == "Error"));
1448 }
1449
1450 #[test]
1451 fn test_extract_multiple_plugins() {
1452 let input = quote! {
1453 #[facet(derive(Error, Display))]
1454 pub enum MyError {
1455 Unknown,
1456 }
1457 };
1458
1459 let mut iter = input.to_token_iter();
1460 let parsed = iter.parse::<crate::Enum>().expect("Failed to parse enum");
1461
1462 let plugins = extract_derive_plugins(&parsed.attributes);
1463 assert_eq!(plugins.len(), 2);
1464 assert!(matches!(&plugins[0], PluginRef::Simple(name) if name == "Error"));
1465 assert!(matches!(&plugins[1], PluginRef::Simple(name) if name == "Display"));
1466 }
1467
1468 #[test]
1469 fn test_extract_path_plugins() {
1470 let input = quote! {
1471 #[facet(derive(Error, facet_miette::Diagnostic))]
1472 pub enum MyError {
1473 Unknown,
1474 }
1475 };
1476
1477 let mut iter = input.to_token_iter();
1478 let parsed = iter.parse::<crate::Enum>().expect("Failed to parse enum");
1479
1480 let plugins = extract_derive_plugins(&parsed.attributes);
1481 assert_eq!(plugins.len(), 2);
1482 assert!(matches!(&plugins[0], PluginRef::Simple(name) if name == "Error"));
1483 assert!(
1484 matches!(&plugins[1], PluginRef::Path { crate_name, plugin_name } if crate_name == "facet_miette" && plugin_name == "Diagnostic")
1485 );
1486 }
1487
1488 #[test]
1489 fn test_plugin_ref_crate_path() {
1490 let simple = PluginRef::Simple("Error".to_string());
1491 assert_eq!(simple.crate_path().to_string(), ":: facet_error");
1492
1493 let path = PluginRef::Path {
1494 crate_name: "facet_miette".to_string(),
1495 plugin_name: "Diagnostic".to_string(),
1496 };
1497 assert_eq!(path.crate_path().to_string(), ":: facet_miette");
1498 }
1499}