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
766 "variant_name" => emit_variant_name(ctx, output),
768 "variant_pattern" => emit_variant_pattern(ctx, output),
769 "field_name" => emit_field_name(ctx, output),
770 "field_type" => emit_field_type(ctx, output),
771 "field_expr" => emit_field_expr(ctx, output),
772 "attr_args" => emit_attr_args(ctx, output),
773 "doc" => emit_doc(ctx, output),
774
775 "field_default_expr" => emit_field_default_expr(ctx, output),
777 "variant_default_construction" => emit_variant_default_construction(ctx, output),
778
779 "format_doc_comment" => emit_format_doc_comment(ctx, output),
781 "if_has_source_field" => handle_legacy_if_has_source_field(iter, ctx, output),
782 "if_has_from_field" => handle_legacy_if_has_from_field(iter, ctx, output),
783 "source_pattern" => emit_legacy_source_pattern(ctx, output),
784 "source_expr" => emit_legacy_source_expr(output),
785 "from_field_type" => emit_legacy_from_field_type(ctx, output),
786
787 _ => {
789 output.extend(quote! { @ });
791 output.extend(std::iter::once(next.clone()));
792 }
793 }
794}
795
796fn emit_self_type(ctx: &EvalContext<'_>, output: &mut TokenStream) {
801 let name = ctx.parsed_type.name();
802 output.extend(quote! { #name });
803}
804
805fn handle_for_variant(
811 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
812 ctx: &mut EvalContext<'_>,
813 output: &mut TokenStream,
814) {
815 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
816 return; };
818
819 let body = body_group.stream();
820
821 let facet_macro_parse::PType::Enum(e) = ctx.parsed_type else {
823 return;
824 };
825
826 for variant in &e.variants {
827 ctx.push(ContextFrame::Variant { variant });
828 let expanded = evaluate_with_context(body.clone(), ctx);
829 output.extend(expanded);
830 ctx.pop();
831 }
832}
833
834fn handle_for_field(
836 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
837 ctx: &mut EvalContext<'_>,
838 output: &mut TokenStream,
839) {
840 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
841 return;
842 };
843
844 let body = body_group.stream();
845
846 let Some(fields) = ctx.current_fields() else {
847 return;
848 };
849
850 let fields: Vec<_> = fields.iter().enumerate().collect();
852
853 for (index, field) in fields {
854 ctx.push(ContextFrame::Field { field, index });
855 let expanded = evaluate_with_context(body.clone(), ctx);
856 output.extend(expanded);
857 ctx.pop();
858 }
859}
860
861fn handle_if_attr(
867 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
868 ctx: &mut EvalContext<'_>,
869 output: &mut TokenStream,
870) {
871 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
873 return;
874 };
875
876 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
878 return;
879 };
880
881 let Some(query) = AttrQuery::parse(query_group.stream()) else {
882 return;
883 };
884
885 let attrs = ctx.current_attrs();
886
887 if let Some(matched_attr) = query.find_in(&attrs.facet) {
888 ctx.push(ContextFrame::Attr { attr: matched_attr });
889 let expanded = evaluate_with_context(body_group.stream(), ctx);
890 output.extend(expanded);
891 ctx.pop();
892 }
893}
894
895fn handle_if_field_attr(
900 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
901 ctx: &mut EvalContext<'_>,
902 output: &mut TokenStream,
903) {
904 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
905 return;
906 };
907
908 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
909 return;
910 };
911
912 let Some(query) = AttrQuery::parse(query_group.stream()) else {
913 return;
914 };
915
916 let Some(fields) = ctx.current_fields() else {
917 return;
918 };
919
920 let fields: Vec<_> = fields.iter().enumerate().collect();
922
923 for (index, field) in fields {
925 if let Some(matched_attr) = query.find_in(&field.attrs.facet) {
926 ctx.push(ContextFrame::Field { field, index });
927 ctx.push(ContextFrame::Attr { attr: matched_attr });
928 let expanded = evaluate_with_context(body_group.stream(), ctx);
929 output.extend(expanded);
930 ctx.pop(); ctx.pop(); return; }
934 }
935}
936
937fn handle_if_any_field_attr(
943 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
944 ctx: &mut EvalContext<'_>,
945 output: &mut TokenStream,
946) {
947 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
948 return;
949 };
950
951 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
952 return;
953 };
954
955 let Some(query) = AttrQuery::parse(query_group.stream()) else {
956 return;
957 };
958
959 let Some(fields) = ctx.current_fields() else {
960 return;
961 };
962
963 let has_any = fields
965 .iter()
966 .any(|f| query.find_in(&f.attrs.facet).is_some());
967
968 if has_any {
969 let expanded = evaluate_with_context(body_group.stream(), ctx);
970 output.extend(expanded);
971 }
972}
973
974fn handle_if_struct(
976 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
977 ctx: &mut EvalContext<'_>,
978 output: &mut TokenStream,
979) {
980 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
981 return;
982 };
983
984 if matches!(ctx.parsed_type, facet_macro_parse::PType::Struct(_)) {
985 let expanded = evaluate_with_context(body_group.stream(), ctx);
986 output.extend(expanded);
987 }
988}
989
990fn handle_if_enum(
992 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
993 ctx: &mut EvalContext<'_>,
994 output: &mut TokenStream,
995) {
996 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
997 return;
998 };
999
1000 if matches!(ctx.parsed_type, facet_macro_parse::PType::Enum(_)) {
1001 let expanded = evaluate_with_context(body_group.stream(), ctx);
1002 output.extend(expanded);
1003 }
1004}
1005
1006fn emit_variant_name(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1011 if let Some(variant) = ctx.current_variant()
1012 && let facet_macro_parse::IdentOrLiteral::Ident(name) = &variant.name.raw
1013 {
1014 output.extend(quote! { #name });
1015 }
1016}
1017
1018fn emit_variant_pattern(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1019 let Some(variant) = ctx.current_variant() else {
1020 return;
1021 };
1022
1023 use facet_macro_parse::{IdentOrLiteral, PVariantKind};
1024
1025 match &variant.kind {
1026 PVariantKind::Unit => {
1027 }
1029 PVariantKind::Tuple { fields } => {
1030 let names: Vec<_> = (0..fields.len())
1033 .map(|i| quote::format_ident!("v{}", i))
1034 .collect();
1035 output.extend(quote! { ( #(#names),* ) });
1036 }
1037 PVariantKind::Struct { fields } => {
1038 let names: Vec<_> = fields
1039 .iter()
1040 .filter_map(|f| {
1041 if let IdentOrLiteral::Ident(id) = &f.name.raw {
1042 Some(id.clone())
1043 } else {
1044 None
1045 }
1046 })
1047 .collect();
1048 output.extend(quote! { { #(#names),* } });
1049 }
1050 }
1051}
1052
1053fn emit_field_name(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1054 let Some((field, index)) = ctx.current_field() else {
1055 return;
1056 };
1057
1058 use facet_macro_parse::IdentOrLiteral;
1059
1060 match &field.name.raw {
1061 IdentOrLiteral::Ident(id) => {
1062 output.extend(quote! { #id });
1063 }
1064 IdentOrLiteral::Literal(_) => {
1065 let name = quote::format_ident!("v{}", index);
1067 output.extend(quote! { #name });
1068 }
1069 }
1070}
1071
1072fn emit_field_type(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1073 if let Some((field, _)) = ctx.current_field() {
1074 let ty = &field.ty;
1075 output.extend(quote! { #ty });
1076 }
1077}
1078
1079fn emit_field_expr(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1080 emit_field_name(ctx, output);
1082}
1083
1084fn emit_attr_args(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1085 if let Some(attr) = ctx.current_attr() {
1086 let args = &attr.args;
1087 output.extend(args.clone());
1088 }
1089}
1090
1091fn emit_doc(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1092 let attrs = ctx.current_attrs();
1093 let doc = attrs.doc.join(" ").trim().to_string();
1094 if !doc.is_empty() {
1095 output.extend(quote! { #doc });
1096 }
1097}
1098
1099fn emit_field_default_expr(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1110 let Some((field, _)) = ctx.current_field() else {
1111 return;
1112 };
1113
1114 let value_query = AttrQuery {
1116 ns: "default".to_string(),
1117 key: "value".to_string(),
1118 };
1119
1120 if let Some(attr) = value_query.find_in(&field.attrs.facet) {
1121 let args = &attr.args;
1123 output.extend(quote! { (#args).into() });
1124 return;
1125 }
1126
1127 let func_query = AttrQuery {
1129 ns: "default".to_string(),
1130 key: "func".to_string(),
1131 };
1132
1133 if let Some(attr) = func_query.find_in(&field.attrs.facet) {
1134 let func_path = parse_func_path(&attr.args);
1136 output.extend(quote! { #func_path() });
1137 return;
1138 }
1139
1140 output.extend(quote! { ::core::default::Default::default() });
1142}
1143
1144fn emit_variant_default_construction(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1150 use facet_macro_parse::{IdentOrLiteral, PVariantKind};
1151
1152 let Some(variant) = ctx.current_variant() else {
1153 return;
1154 };
1155
1156 match &variant.kind {
1157 PVariantKind::Unit => {
1158 }
1160 PVariantKind::Tuple { fields } => {
1161 let defaults: Vec<_> = fields.iter().map(field_default_tokens).collect();
1162 output.extend(quote! { ( #(#defaults),* ) });
1163 }
1164 PVariantKind::Struct { fields } => {
1165 let field_inits: Vec<_> = fields
1166 .iter()
1167 .filter_map(|f| {
1168 if let IdentOrLiteral::Ident(name) = &f.name.raw {
1169 let default_expr = field_default_tokens(f);
1170 Some(quote! { #name: #default_expr })
1171 } else {
1172 None
1173 }
1174 })
1175 .collect();
1176 output.extend(quote! { { #(#field_inits),* } });
1177 }
1178 }
1179}
1180
1181fn field_default_tokens(field: &facet_macro_parse::PStructField) -> TokenStream {
1183 let value_query = AttrQuery {
1185 ns: "default".to_string(),
1186 key: "value".to_string(),
1187 };
1188
1189 if let Some(attr) = value_query.find_in(&field.attrs.facet) {
1190 let args = &attr.args;
1191 return quote! { (#args).into() };
1192 }
1193
1194 let func_query = AttrQuery {
1196 ns: "default".to_string(),
1197 key: "func".to_string(),
1198 };
1199
1200 if let Some(attr) = func_query.find_in(&field.attrs.facet) {
1201 let func_path = parse_func_path(&attr.args);
1202 return quote! { #func_path() };
1203 }
1204
1205 quote! { ::core::default::Default::default() }
1207}
1208
1209fn parse_func_path(args: &TokenStream) -> TokenStream {
1213 let args_str = args.to_string();
1215 let trimmed = args_str.trim();
1216
1217 if trimmed.starts_with('"') && trimmed.ends_with('"') {
1219 let path_str = &trimmed[1..trimmed.len() - 1];
1220
1221 let segments: Vec<_> = path_str.split("::").collect();
1223
1224 if segments.len() == 1 {
1225 let ident = quote::format_ident!("{}", segments[0]);
1227 quote! { #ident }
1228 } else {
1229 let idents: Vec<_> = segments
1231 .iter()
1232 .map(|s| quote::format_ident!("{}", s))
1233 .collect();
1234 quote! { #(#idents)::* }
1235 }
1236 } else {
1237 args.clone()
1239 }
1240}
1241
1242fn emit_format_doc_comment(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1248 use facet_macro_parse::PVariantKind;
1249
1250 let Some(variant) = ctx.current_variant() else {
1251 return;
1252 };
1253
1254 let doc = variant.attrs.doc.join(" ").trim().to_string();
1255 let format_str = if doc.is_empty() {
1256 variant.name.effective.clone()
1257 } else {
1258 doc
1259 };
1260
1261 match &variant.kind {
1263 PVariantKind::Unit => {
1264 output.extend(quote! { #format_str });
1265 }
1266 PVariantKind::Tuple { fields } => {
1267 if format_str.contains("{0}") {
1268 let field_names: Vec<_> = (0..fields.len())
1270 .map(|i| quote::format_ident!("v{}", i))
1271 .collect();
1272 output.extend(quote! { #format_str, #(#field_names),* });
1273 } else {
1274 output.extend(quote! { #format_str });
1275 }
1276 }
1277 PVariantKind::Struct { fields: _ } => {
1278 output.extend(quote! { #format_str });
1280 }
1281 }
1282}
1283
1284fn handle_legacy_if_has_source_field(
1286 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1287 ctx: &mut EvalContext<'_>,
1288 output: &mut TokenStream,
1289) {
1290 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1291 return;
1292 };
1293
1294 let Some(variant) = ctx.current_variant() else {
1295 return;
1296 };
1297
1298 let has_source = variant.attrs.facet.iter().any(|attr| {
1300 if let Some(ns) = &attr.ns {
1301 *ns == "error" && (attr.key == "source" || attr.key == "from")
1302 } else {
1303 false
1304 }
1305 });
1306
1307 let is_single_tuple = matches!(&variant.kind, facet_macro_parse::PVariantKind::Tuple { fields } if fields.len() == 1);
1309
1310 if has_source && is_single_tuple {
1311 let expanded = evaluate_with_context(body_group.stream(), ctx);
1312 output.extend(expanded);
1313 }
1314}
1315
1316fn handle_legacy_if_has_from_field(
1318 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1319 ctx: &mut EvalContext<'_>,
1320 output: &mut TokenStream,
1321) {
1322 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1323 return;
1324 };
1325
1326 let Some(variant) = ctx.current_variant() else {
1327 return;
1328 };
1329
1330 let has_from = variant.attrs.facet.iter().any(|attr| {
1332 if let Some(ns) = &attr.ns {
1333 *ns == "error" && attr.key == "from"
1334 } else {
1335 false
1336 }
1337 });
1338
1339 let is_single_tuple = matches!(&variant.kind, facet_macro_parse::PVariantKind::Tuple { fields } if fields.len() == 1);
1341
1342 if has_from && is_single_tuple {
1343 let expanded = evaluate_with_context(body_group.stream(), ctx);
1344 output.extend(expanded);
1345 }
1346}
1347
1348fn emit_legacy_source_pattern(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1350 use facet_macro_parse::PVariantKind;
1351
1352 let Some(variant) = ctx.current_variant() else {
1353 return;
1354 };
1355
1356 match &variant.kind {
1357 PVariantKind::Tuple { .. } => {
1358 output.extend(quote! { (e) });
1359 }
1360 PVariantKind::Struct { fields } => {
1361 for field in fields {
1363 if field.attrs.facet.iter().any(|attr| {
1364 if let Some(ns) = &attr.ns {
1365 *ns == "error" && (attr.key == "source" || attr.key == "from")
1366 } else {
1367 false
1368 }
1369 }) && let facet_macro_parse::IdentOrLiteral::Ident(name) = &field.name.raw
1370 {
1371 output.extend(quote! { { #name, .. } });
1372 return;
1373 }
1374 }
1375 output.extend(quote! { { .. } });
1376 }
1377 _ => {}
1378 }
1379}
1380
1381fn emit_legacy_source_expr(output: &mut TokenStream) {
1383 output.extend(quote! { e });
1384}
1385
1386fn emit_legacy_from_field_type(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1388 use facet_macro_parse::PVariantKind;
1389
1390 let Some(variant) = ctx.current_variant() else {
1391 return;
1392 };
1393
1394 if let PVariantKind::Tuple { fields } = &variant.kind
1395 && let Some(field) = fields.first()
1396 {
1397 let ty = &field.ty;
1398 output.extend(quote! { #ty });
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}