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 {
211 let mut result = TokenStream::new();
212 let mut iter = tokens.into_iter().peekable();
213
214 while let Some(tt) = iter.next() {
215 if let proc_macro2::TokenTree::Punct(p) = &tt
217 && p.as_char() == '#'
218 && let Some(proc_macro2::TokenTree::Group(g)) = iter.peek()
219 && g.delimiter() == proc_macro2::Delimiter::Bracket
220 {
221 let inner = g.stream();
223 if let Some(filtered) = strip_plugin_items_from_facet_attr(&inner) {
224 if filtered.is_empty() {
225 iter.next(); continue;
228 } else {
229 result.extend(std::iter::once(tt));
231 iter.next(); result.extend(std::iter::once(proc_macro2::TokenTree::Group(
233 proc_macro2::Group::new(proc_macro2::Delimiter::Bracket, filtered),
234 )));
235 continue;
236 }
237 }
238 }
239 result.extend(std::iter::once(tt));
240 }
241
242 result
243}
244
245fn strip_plugin_items_from_facet_attr(inner: &TokenStream) -> Option<TokenStream> {
251 let mut iter = inner.clone().into_iter().peekable();
252
253 let facet_ident = match iter.next() {
255 Some(proc_macro2::TokenTree::Ident(id)) if id == "facet" => id,
256 _ => return None,
257 };
258
259 let group = match iter.next() {
261 Some(proc_macro2::TokenTree::Group(g))
262 if g.delimiter() == proc_macro2::Delimiter::Parenthesis =>
263 {
264 g
265 }
266 _ => return None,
267 };
268
269 let filtered_content = strip_plugin_items_from_content(group.stream());
271
272 let mut result = TokenStream::new();
274 result.extend(std::iter::once(proc_macro2::TokenTree::Ident(facet_ident)));
275 result.extend(std::iter::once(proc_macro2::TokenTree::Group(
276 proc_macro2::Group::new(proc_macro2::Delimiter::Parenthesis, filtered_content),
277 )));
278
279 Some(result)
280}
281
282fn strip_plugin_items_from_content(content: TokenStream) -> TokenStream {
288 let mut items: Vec<TokenStream> = Vec::new();
289
290 let mut current_item = TokenStream::new();
292 let tokens: Vec<proc_macro2::TokenTree> = content.into_iter().collect();
293
294 for tt in &tokens {
295 if let proc_macro2::TokenTree::Punct(p) = tt
297 && p.as_char() == ','
298 {
299 if !current_item.is_empty() && !is_plugin_item(¤t_item) {
301 items.push(current_item);
302 }
303 current_item = TokenStream::new();
304 continue;
305 }
306
307 current_item.extend(std::iter::once(tt.clone()));
308 }
309
310 if !current_item.is_empty() && !is_plugin_item(¤t_item) {
312 items.push(current_item);
313 }
314
315 let mut result = TokenStream::new();
317 for (idx, item) in items.iter().enumerate() {
318 if idx > 0 {
319 result.extend(std::iter::once(proc_macro2::TokenTree::Punct(
320 proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
321 )));
322 }
323 result.extend(item.clone());
324 }
325
326 result
327}
328
329fn is_plugin_item(item: &TokenStream) -> bool {
339 let mut iter = item.clone().into_iter();
340
341 if let Some(proc_macro2::TokenTree::Ident(id)) = iter.next() {
342 let name = id.to_string();
343
344 if name == "derive" {
346 return true;
347 }
348 }
349
350 false
351}
352
353pub fn generate_plugin_chain(
358 input_tokens: &TokenStream,
359 plugins: &[PluginRef],
360 facet_crate: &TokenStream,
361) -> Option<TokenStream> {
362 if plugins.is_empty() {
363 return None;
364 }
365
366 let plugin_paths: Vec<TokenStream> = plugins
369 .iter()
370 .map(|p| {
371 let crate_path = p.crate_path();
372 quote! { #crate_path::__facet_invoke }
373 })
374 .collect();
375
376 let first = &plugin_paths[0];
377 let rest: Vec<_> = plugin_paths[1..].iter().collect();
378
379 let remaining = if rest.is_empty() {
380 quote! {}
381 } else {
382 quote! { #(#rest),* }
383 };
384
385 Some(quote! {
386 #first! {
387 @tokens { #input_tokens }
388 @remaining { #remaining }
389 @plugins { }
390 @facet_crate { #facet_crate }
391 }
392 })
393}
394
395pub fn facet_finalize(input: TokenStream) -> TokenStream {
402 let mut iter = input.to_token_iter();
408
409 let mut tokens: Option<TokenStream> = None;
410 let mut plugins_section: Option<TokenStream> = None;
411 let mut facet_crate: Option<TokenStream> = None;
412
413 while let Ok(section) = iter.parse::<FinalizeSection>() {
415 match section.marker.name.to_string().as_str() {
416 "tokens" => {
417 tokens = Some(section.content.content);
418 }
419 "plugins" => {
420 plugins_section = Some(section.content.content);
421 }
422 "facet_crate" => {
423 facet_crate = Some(section.content.content);
424 }
425 other => {
426 let msg = format!("unknown section in __facet_finalize: @{other}");
427 return quote! { compile_error!(#msg); };
428 }
429 }
430 }
431
432 let tokens = match tokens {
433 Some(t) => t,
434 None => {
435 return quote! { compile_error!("__facet_finalize: missing @tokens section"); };
436 }
437 };
438
439 let facet_crate = facet_crate.unwrap_or_else(|| quote! { ::facet });
440
441 let filtered_tokens = strip_derive_attrs(tokens.clone());
443
444 let mut type_iter = filtered_tokens.clone().to_token_iter();
446 let facet_impl = match type_iter.parse::<crate::Cons<crate::AdtDecl, crate::EndOfStream>>() {
447 Ok(it) => match it.first {
448 crate::AdtDecl::Struct(parsed) => crate::process_struct::process_struct(parsed),
449 crate::AdtDecl::Enum(parsed) => crate::process_enum::process_enum(parsed),
450 },
451 Err(err) => {
452 let msg = format!("__facet_finalize: could not parse type: {err}");
453 return quote! { compile_error!(#msg); };
454 }
455 };
456
457 let plugin_impls = if let Some(plugins_tokens) = plugins_section {
459 extract_plugin_templates(plugins_tokens, &filtered_tokens, &facet_crate)
461 } else {
462 vec![]
463 };
464
465 quote! {
466 #facet_impl
467 #(#plugin_impls)*
468 }
469}
470
471struct PluginTemplate {
473 #[allow(dead_code)] name: String,
475 template: TokenStream,
476}
477
478fn extract_plugin_templates(
480 plugins_tokens: TokenStream,
481 type_tokens: &TokenStream,
482 facet_crate: &TokenStream,
483) -> Vec<TokenStream> {
484 let plugins = parse_plugin_sections(plugins_tokens);
486
487 let parsed_type = match facet_macro_parse::parse_type(type_tokens.clone()) {
489 Ok(ty) => ty,
490 Err(e) => {
491 let msg = format!("failed to parse type for plugin templates: {e}");
492 return vec![quote! { compile_error!(#msg); }];
493 }
494 };
495
496 plugins
498 .into_iter()
499 .map(|plugin| evaluate_template(plugin.template, &parsed_type, facet_crate))
500 .collect()
501}
502
503fn parse_plugin_sections(tokens: TokenStream) -> Vec<PluginTemplate> {
505 let mut plugins = Vec::new();
506 let mut iter = tokens.into_iter().peekable();
507
508 while let Some(tt) = iter.next() {
509 if let proc_macro2::TokenTree::Punct(p) = &tt
511 && p.as_char() == '@'
512 {
513 if let Some(proc_macro2::TokenTree::Ident(id)) = iter.peek()
515 && *id == "plugin"
516 {
517 iter.next(); if let Some(proc_macro2::TokenTree::Group(g)) = iter.next()
521 && g.delimiter() == proc_macro2::Delimiter::Brace
522 && let Some(plugin) = parse_plugin_content(g.stream())
523 {
524 plugins.push(plugin);
525 }
526 }
527 }
528 }
529
530 plugins
531}
532
533fn parse_plugin_content(tokens: TokenStream) -> Option<PluginTemplate> {
535 let mut name: Option<String> = None;
536 let mut template: Option<TokenStream> = None;
537 let mut iter = tokens.into_iter().peekable();
538
539 while let Some(tt) = iter.next() {
540 if let proc_macro2::TokenTree::Punct(p) = &tt
541 && p.as_char() == '@'
542 && let Some(proc_macro2::TokenTree::Ident(id)) = iter.peek()
543 {
544 let key = id.to_string();
545 iter.next(); if let Some(proc_macro2::TokenTree::Group(g)) = iter.next()
549 && g.delimiter() == proc_macro2::Delimiter::Brace
550 {
551 match key.as_str() {
552 "name" => {
553 let content = g.stream().into_iter().collect::<Vec<_>>();
555 if let Some(proc_macro2::TokenTree::Literal(lit)) = content.first() {
556 let s = lit.to_string();
557 name = Some(s.trim_matches('"').to_string());
558 }
559 }
560 "template" => {
561 template = Some(g.stream());
562 }
563 _ => {}
564 }
565 }
566 }
567 }
568
569 match (name, template) {
570 (Some(n), Some(t)) => Some(PluginTemplate {
571 name: n,
572 template: t,
573 }),
574 _ => None,
575 }
576}
577
578fn evaluate_template(
580 template: TokenStream,
581 parsed_type: &facet_macro_parse::PType,
582 _facet_crate: &TokenStream,
583) -> TokenStream {
584 let mut ctx = EvalContext::new(parsed_type);
585 evaluate_with_context(template, &mut ctx)
586}
587
588struct EvalContext<'a> {
598 parsed_type: &'a facet_macro_parse::PType,
600
601 stack: Vec<ContextFrame<'a>>,
603}
604
605enum ContextFrame<'a> {
607 Variant {
609 variant: &'a facet_macro_parse::PVariant,
610 },
611
612 Field {
614 field: &'a facet_macro_parse::PStructField,
615 index: usize,
617 },
618
619 Attr {
621 attr: &'a facet_macro_parse::PFacetAttr,
623 },
624}
625
626impl<'a> EvalContext<'a> {
627 const fn new(parsed_type: &'a facet_macro_parse::PType) -> Self {
628 Self {
629 parsed_type,
630 stack: Vec::new(),
631 }
632 }
633
634 fn push(&mut self, frame: ContextFrame<'a>) {
635 self.stack.push(frame);
636 }
637
638 fn pop(&mut self) {
639 self.stack.pop();
640 }
641
642 fn current_variant(&self) -> Option<&'a facet_macro_parse::PVariant> {
644 self.stack.iter().rev().find_map(|f| match f {
645 ContextFrame::Variant { variant } => Some(*variant),
646 _ => None,
647 })
648 }
649
650 fn current_field(&self) -> Option<(&'a facet_macro_parse::PStructField, usize)> {
652 self.stack.iter().rev().find_map(|f| match f {
653 ContextFrame::Field { field, index } => Some((*field, *index)),
654 _ => None,
655 })
656 }
657
658 fn current_attr(&self) -> Option<&'a facet_macro_parse::PFacetAttr> {
660 self.stack.iter().rev().find_map(|f| match f {
661 ContextFrame::Attr { attr } => Some(*attr),
662 _ => None,
663 })
664 }
665
666 fn current_fields(&self) -> Option<&'a [facet_macro_parse::PStructField]> {
668 if let Some(variant) = self.current_variant() {
670 return match &variant.kind {
671 facet_macro_parse::PVariantKind::Tuple { fields } => Some(fields),
672 facet_macro_parse::PVariantKind::Struct { fields } => Some(fields),
673 facet_macro_parse::PVariantKind::Unit => None,
674 };
675 }
676
677 if let facet_macro_parse::PType::Struct(s) = self.parsed_type {
679 return match &s.kind {
680 facet_macro_parse::PStructKind::Struct { fields } => Some(fields),
681 facet_macro_parse::PStructKind::TupleStruct { fields } => Some(fields),
682 facet_macro_parse::PStructKind::UnitStruct => None,
683 };
684 }
685
686 None
687 }
688
689 fn current_attrs(&self) -> &'a facet_macro_parse::PAttrs {
691 if let Some((field, _)) = self.current_field() {
693 return &field.attrs;
694 }
695
696 if let Some(variant) = self.current_variant() {
698 return &variant.attrs;
699 }
700
701 match self.parsed_type {
703 facet_macro_parse::PType::Struct(s) => &s.container.attrs,
704 facet_macro_parse::PType::Enum(e) => &e.container.attrs,
705 }
706 }
707}
708
709struct AttrQuery {
715 ns: String,
716 key: String,
717}
718
719impl AttrQuery {
720 fn parse(tokens: TokenStream) -> Option<Self> {
722 let mut iter = tokens.into_iter();
723
724 let ns = match iter.next() {
726 Some(proc_macro2::TokenTree::Ident(id)) => id.to_string(),
727 _ => return None,
728 };
729
730 match (iter.next(), iter.next()) {
732 (Some(proc_macro2::TokenTree::Punct(p1)), Some(proc_macro2::TokenTree::Punct(p2)))
733 if p1.as_char() == ':' && p2.as_char() == ':' => {}
734 _ => return None,
735 }
736
737 let key = match iter.next() {
739 Some(proc_macro2::TokenTree::Ident(id)) => id.to_string(),
740 _ => return None,
741 };
742
743 Some(AttrQuery { ns, key })
744 }
745
746 fn matches(&self, attr: &facet_macro_parse::PFacetAttr) -> bool {
748 if let Some(ref ns) = attr.ns {
749 *ns == self.ns && attr.key == self.key
750 } else {
751 false
752 }
753 }
754
755 fn find_in<'a>(
757 &self,
758 attrs: &'a [facet_macro_parse::PFacetAttr],
759 ) -> Option<&'a facet_macro_parse::PFacetAttr> {
760 attrs.iter().find(|a| self.matches(a))
761 }
762}
763
764fn evaluate_with_context(template: TokenStream, ctx: &mut EvalContext<'_>) -> TokenStream {
770 let mut output = TokenStream::new();
771 let mut iter = template.into_iter().peekable();
772
773 while let Some(tt) = iter.next() {
774 match &tt {
775 proc_macro2::TokenTree::Punct(p) if p.as_char() == '@' => {
776 handle_directive(&mut iter, ctx, &mut output);
777 }
778 proc_macro2::TokenTree::Group(g) => {
779 let inner = evaluate_with_context(g.stream(), ctx);
781 let new_group = proc_macro2::Group::new(g.delimiter(), inner);
782 output.extend(std::iter::once(proc_macro2::TokenTree::Group(new_group)));
783 }
784 _ => {
785 output.extend(std::iter::once(tt));
786 }
787 }
788 }
789
790 output
791}
792
793fn handle_directive(
795 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
796 ctx: &mut EvalContext<'_>,
797 output: &mut TokenStream,
798) {
799 let Some(next) = iter.next() else {
800 output.extend(quote! { @ });
802 return;
803 };
804
805 let proc_macro2::TokenTree::Ident(directive_ident) = &next else {
806 output.extend(quote! { @ });
808 output.extend(std::iter::once(next));
809 return;
810 };
811
812 let directive = directive_ident.to_string();
813
814 match directive.as_str() {
815 "Self" => emit_self_type(ctx, output),
817
818 "for_variant" => handle_for_variant(iter, ctx, output),
820 "for_field" => handle_for_field(iter, ctx, output),
821
822 "if_attr" => handle_if_attr(iter, ctx, output),
824 "if_field_attr" => handle_if_field_attr(iter, ctx, output),
825 "if_any_field_attr" => handle_if_any_field_attr(iter, ctx, output),
826 "if_struct" => handle_if_struct(iter, ctx, output),
827 "if_enum" => handle_if_enum(iter, ctx, output),
828 "if_unit_variant" => handle_if_unit_variant(iter, ctx, output),
829 "if_tuple_variant" => handle_if_tuple_variant(iter, ctx, output),
830 "if_struct_variant" => handle_if_struct_variant(iter, ctx, output),
831
832 "variant_name" => emit_variant_name(ctx, output),
834 "variant_pattern" => emit_variant_pattern(ctx, output),
835 "variant_pattern_only" => handle_variant_pattern_only(iter, ctx, output),
836 "field_name" => emit_field_name(ctx, output),
837 "field_type" => emit_field_type(ctx, output),
838 "field_expr" => emit_field_expr(ctx, output),
839 "attr_args" => emit_attr_args(ctx, output),
840 "doc" => emit_doc(ctx, output),
841
842 "field_default_expr" => emit_field_default_expr(ctx, output),
844 "variant_default_construction" => emit_variant_default_construction(ctx, output),
845
846 "format_doc_comment" => emit_format_doc_comment(ctx, output),
848
849 _ => {
851 output.extend(quote! { @ });
853 output.extend(std::iter::once(next.clone()));
854 }
855 }
856}
857
858fn emit_self_type(ctx: &EvalContext<'_>, output: &mut TokenStream) {
863 let name = ctx.parsed_type.name();
864 output.extend(quote! { #name });
865}
866
867fn handle_for_variant(
873 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
874 ctx: &mut EvalContext<'_>,
875 output: &mut TokenStream,
876) {
877 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
878 return; };
880
881 let body = body_group.stream();
882
883 let facet_macro_parse::PType::Enum(e) = ctx.parsed_type else {
885 return;
886 };
887
888 for variant in &e.variants {
889 ctx.push(ContextFrame::Variant { variant });
890 let expanded = evaluate_with_context(body.clone(), ctx);
891 output.extend(expanded);
892 ctx.pop();
893 }
894}
895
896fn handle_for_field(
898 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
899 ctx: &mut EvalContext<'_>,
900 output: &mut TokenStream,
901) {
902 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
903 return;
904 };
905
906 let body = body_group.stream();
907
908 let Some(fields) = ctx.current_fields() else {
909 return;
910 };
911
912 let fields: Vec<_> = fields.iter().enumerate().collect();
914
915 for (index, field) in fields {
916 ctx.push(ContextFrame::Field { field, index });
917 let expanded = evaluate_with_context(body.clone(), ctx);
918 output.extend(expanded);
919 ctx.pop();
920 }
921}
922
923fn handle_if_attr(
929 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
930 ctx: &mut EvalContext<'_>,
931 output: &mut TokenStream,
932) {
933 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
935 return;
936 };
937
938 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
940 return;
941 };
942
943 let Some(query) = AttrQuery::parse(query_group.stream()) else {
944 return;
945 };
946
947 let attrs = ctx.current_attrs();
948
949 if let Some(matched_attr) = query.find_in(&attrs.facet) {
950 ctx.push(ContextFrame::Attr { attr: matched_attr });
951 let expanded = evaluate_with_context(body_group.stream(), ctx);
952 output.extend(expanded);
953 ctx.pop();
954 }
955}
956
957fn handle_if_field_attr(
962 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
963 ctx: &mut EvalContext<'_>,
964 output: &mut TokenStream,
965) {
966 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
967 return;
968 };
969
970 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
971 return;
972 };
973
974 let Some(query) = AttrQuery::parse(query_group.stream()) else {
975 return;
976 };
977
978 let Some(fields) = ctx.current_fields() else {
979 return;
980 };
981
982 let fields: Vec<_> = fields.iter().enumerate().collect();
984
985 for (index, field) in fields {
987 if let Some(matched_attr) = query.find_in(&field.attrs.facet) {
988 ctx.push(ContextFrame::Field { field, index });
989 ctx.push(ContextFrame::Attr { attr: matched_attr });
990 let expanded = evaluate_with_context(body_group.stream(), ctx);
991 output.extend(expanded);
992 ctx.pop(); ctx.pop(); return; }
996 }
997}
998
999fn handle_if_any_field_attr(
1005 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1006 ctx: &mut EvalContext<'_>,
1007 output: &mut TokenStream,
1008) {
1009 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
1010 return;
1011 };
1012
1013 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1014 return;
1015 };
1016
1017 let Some(query) = AttrQuery::parse(query_group.stream()) else {
1018 return;
1019 };
1020
1021 let Some(fields) = ctx.current_fields() else {
1022 return;
1023 };
1024
1025 let has_any = fields
1027 .iter()
1028 .any(|f| query.find_in(&f.attrs.facet).is_some());
1029
1030 if has_any {
1031 let expanded = evaluate_with_context(body_group.stream(), ctx);
1032 output.extend(expanded);
1033 }
1034}
1035
1036fn handle_if_struct(
1038 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1039 ctx: &mut EvalContext<'_>,
1040 output: &mut TokenStream,
1041) {
1042 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1043 return;
1044 };
1045
1046 if matches!(ctx.parsed_type, facet_macro_parse::PType::Struct(_)) {
1047 let expanded = evaluate_with_context(body_group.stream(), ctx);
1048 output.extend(expanded);
1049 }
1050}
1051
1052fn handle_if_enum(
1054 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1055 ctx: &mut EvalContext<'_>,
1056 output: &mut TokenStream,
1057) {
1058 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1059 return;
1060 };
1061
1062 if matches!(ctx.parsed_type, facet_macro_parse::PType::Enum(_)) {
1063 let expanded = evaluate_with_context(body_group.stream(), ctx);
1064 output.extend(expanded);
1065 }
1066}
1067
1068fn handle_if_unit_variant(
1070 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1071 ctx: &mut EvalContext<'_>,
1072 output: &mut TokenStream,
1073) {
1074 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1075 return;
1076 };
1077
1078 let Some(variant) = ctx.current_variant() else {
1079 return;
1080 };
1081
1082 if matches!(variant.kind, facet_macro_parse::PVariantKind::Unit) {
1083 let expanded = evaluate_with_context(body_group.stream(), ctx);
1084 output.extend(expanded);
1085 }
1086}
1087
1088fn handle_if_tuple_variant(
1090 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1091 ctx: &mut EvalContext<'_>,
1092 output: &mut TokenStream,
1093) {
1094 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1095 return;
1096 };
1097
1098 let Some(variant) = ctx.current_variant() else {
1099 return;
1100 };
1101
1102 if matches!(variant.kind, facet_macro_parse::PVariantKind::Tuple { .. }) {
1103 let expanded = evaluate_with_context(body_group.stream(), ctx);
1104 output.extend(expanded);
1105 }
1106}
1107
1108fn handle_if_struct_variant(
1110 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1111 ctx: &mut EvalContext<'_>,
1112 output: &mut TokenStream,
1113) {
1114 let Some(proc_macro2::TokenTree::Group(body_group)) = iter.next() else {
1115 return;
1116 };
1117
1118 let Some(variant) = ctx.current_variant() else {
1119 return;
1120 };
1121
1122 if matches!(variant.kind, facet_macro_parse::PVariantKind::Struct { .. }) {
1123 let expanded = evaluate_with_context(body_group.stream(), ctx);
1124 output.extend(expanded);
1125 }
1126}
1127
1128fn emit_variant_name(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1133 if let Some(variant) = ctx.current_variant()
1134 && let facet_macro_parse::IdentOrLiteral::Ident(name) = &variant.name.raw
1135 {
1136 output.extend(quote! { #name });
1137 }
1138}
1139
1140fn emit_variant_pattern(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1141 let Some(variant) = ctx.current_variant() else {
1142 return;
1143 };
1144
1145 use facet_macro_parse::{IdentOrLiteral, PVariantKind};
1146
1147 match &variant.kind {
1148 PVariantKind::Unit => {
1149 }
1151 PVariantKind::Tuple { fields } => {
1152 let names: Vec<_> = (0..fields.len())
1155 .map(|i| quote::format_ident!("v{}", i))
1156 .collect();
1157 output.extend(quote! { ( #(#names),* ) });
1158 }
1159 PVariantKind::Struct { fields } => {
1160 let names: Vec<_> = fields
1161 .iter()
1162 .filter_map(|f| {
1163 if let IdentOrLiteral::Ident(id) = &f.name.raw {
1164 Some(id.clone())
1165 } else {
1166 None
1167 }
1168 })
1169 .collect();
1170 output.extend(quote! { { #(#names),* } });
1171 }
1172 }
1173}
1174
1175fn handle_variant_pattern_only(
1177 iter: &mut std::iter::Peekable<proc_macro2::token_stream::IntoIter>,
1178 ctx: &EvalContext<'_>,
1179 output: &mut TokenStream,
1180) {
1181 let Some(proc_macro2::TokenTree::Group(query_group)) = iter.next() else {
1182 return;
1183 };
1184
1185 let Some(query) = AttrQuery::parse(query_group.stream()) else {
1186 return;
1187 };
1188
1189 let Some(variant) = ctx.current_variant() else {
1190 return;
1191 };
1192
1193 use facet_macro_parse::{IdentOrLiteral, PVariantKind};
1194
1195 match &variant.kind {
1196 PVariantKind::Unit => {
1197 }
1199 PVariantKind::Tuple { fields } => {
1200 let patterns: Vec<_> = fields
1201 .iter()
1202 .enumerate()
1203 .map(|(i, f)| {
1204 if query.find_in(&f.attrs.facet).is_some() {
1205 let name = quote::format_ident!("v{}", i);
1206 quote! { #name }
1207 } else {
1208 quote! { _ }
1209 }
1210 })
1211 .collect();
1212 output.extend(quote! { ( #(#patterns),* ) });
1213 }
1214 PVariantKind::Struct { fields } => {
1215 let bindings: Vec<_> = fields
1216 .iter()
1217 .filter_map(|f| {
1218 if let IdentOrLiteral::Ident(id) = &f.name.raw {
1219 if query.find_in(&f.attrs.facet).is_some() {
1220 Some(quote! { #id })
1221 } else {
1222 Some(quote! { #id: _ })
1223 }
1224 } else {
1225 None
1226 }
1227 })
1228 .collect();
1229 output.extend(quote! { { #(#bindings),* } });
1230 }
1231 }
1232}
1233
1234fn emit_field_name(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1235 let Some((field, index)) = ctx.current_field() else {
1236 return;
1237 };
1238
1239 use facet_macro_parse::IdentOrLiteral;
1240
1241 match &field.name.raw {
1242 IdentOrLiteral::Ident(id) => {
1243 output.extend(quote! { #id });
1244 }
1245 IdentOrLiteral::Literal(_) => {
1246 let name = quote::format_ident!("v{}", index);
1248 output.extend(quote! { #name });
1249 }
1250 }
1251}
1252
1253fn emit_field_type(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1254 if let Some((field, _)) = ctx.current_field() {
1255 let ty = &field.ty;
1256 output.extend(quote! { #ty });
1257 }
1258}
1259
1260fn emit_field_expr(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1261 emit_field_name(ctx, output);
1263}
1264
1265fn emit_attr_args(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1266 if let Some(attr) = ctx.current_attr() {
1267 let args = &attr.args;
1268 output.extend(args.clone());
1269 }
1270}
1271
1272fn emit_doc(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1273 let attrs = ctx.current_attrs();
1274 let doc = attrs.doc.join(" ").trim().to_string();
1275 if !doc.is_empty() {
1276 output.extend(quote! { #doc });
1277 }
1278}
1279
1280fn emit_field_default_expr(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1293 let Some((field, _)) = ctx.current_field() else {
1294 return;
1295 };
1296
1297 if let Some(attr) = field
1299 .attrs
1300 .facet
1301 .iter()
1302 .find(|a| a.ns.is_none() && a.key == "default")
1303 {
1304 let args = &attr.args;
1305 if args.is_empty() {
1306 output.extend(quote! { ::core::default::Default::default() });
1308 } else {
1309 output.extend(quote! { #args });
1311 }
1312 return;
1313 }
1314
1315 output.extend(quote! { ::core::default::Default::default() });
1317}
1318
1319fn emit_variant_default_construction(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1325 use facet_macro_parse::{IdentOrLiteral, PVariantKind};
1326
1327 let Some(variant) = ctx.current_variant() else {
1328 return;
1329 };
1330
1331 match &variant.kind {
1332 PVariantKind::Unit => {
1333 }
1335 PVariantKind::Tuple { fields } => {
1336 let defaults: Vec<_> = fields.iter().map(field_default_tokens).collect();
1337 output.extend(quote! { ( #(#defaults),* ) });
1338 }
1339 PVariantKind::Struct { fields } => {
1340 let field_inits: Vec<_> = fields
1341 .iter()
1342 .filter_map(|f| {
1343 if let IdentOrLiteral::Ident(name) = &f.name.raw {
1344 let default_expr = field_default_tokens(f);
1345 Some(quote! { #name: #default_expr })
1346 } else {
1347 None
1348 }
1349 })
1350 .collect();
1351 output.extend(quote! { { #(#field_inits),* } });
1352 }
1353 }
1354}
1355
1356fn field_default_tokens(field: &facet_macro_parse::PStructField) -> TokenStream {
1358 if let Some(attr) = field
1360 .attrs
1361 .facet
1362 .iter()
1363 .find(|a| a.ns.is_none() && a.key == "default")
1364 {
1365 let args = &attr.args;
1366 if args.is_empty() {
1367 return quote! { ::core::default::Default::default() };
1369 } else {
1370 return quote! { #args };
1372 }
1373 }
1374
1375 quote! { ::core::default::Default::default() }
1377}
1378
1379fn emit_format_doc_comment(ctx: &EvalContext<'_>, output: &mut TokenStream) {
1385 use facet_macro_parse::PVariantKind;
1386
1387 let Some(variant) = ctx.current_variant() else {
1388 return;
1389 };
1390
1391 let doc = variant.attrs.doc.join(" ").trim().to_string();
1392 let format_str = if doc.is_empty() {
1393 variant.name.original.clone()
1394 } else {
1395 doc
1396 };
1397
1398 match &variant.kind {
1400 PVariantKind::Unit => {
1401 output.extend(quote! { #format_str });
1402 }
1403 PVariantKind::Tuple { fields } => {
1404 if format_str.contains("{0}") {
1405 let field_names: Vec<_> = (0..fields.len())
1407 .map(|i| quote::format_ident!("v{}", i))
1408 .collect();
1409 output.extend(quote! { #format_str, #(#field_names),* });
1410 } else {
1411 output.extend(quote! { #format_str });
1412 }
1413 }
1414 PVariantKind::Struct { fields: _ } => {
1415 output.extend(quote! { #format_str });
1417 }
1418 }
1419}
1420
1421crate::unsynn! {
1423 struct FinalizeSectionMarker {
1425 _at: crate::At,
1426 name: Ident,
1427 }
1428
1429 struct FinalizeSection {
1431 marker: FinalizeSectionMarker,
1432 content: crate::BraceGroupContaining<TokenStream>,
1433 }
1434}
1435
1436#[cfg(test)]
1437mod tests {
1438 use super::*;
1439 use crate::IParse;
1440 use quote::quote;
1441
1442 #[test]
1443 fn test_to_snake_case() {
1444 assert_eq!(to_snake_case("Error"), "error");
1445 assert_eq!(to_snake_case("Display"), "display");
1446 assert_eq!(to_snake_case("PartialEq"), "partial_eq");
1447 assert_eq!(to_snake_case("FromStr"), "from_str");
1448 }
1449
1450 #[test]
1451 fn test_extract_derive_plugins() {
1452 let input = quote! {
1453 #[derive(Facet, Debug)]
1454 #[facet(derive(Error))]
1455 #[repr(u8)]
1456 pub enum MyError {
1457 Disconnect(u32),
1458 }
1459 };
1460
1461 let mut iter = input.to_token_iter();
1462 let parsed = iter.parse::<crate::Enum>().expect("Failed to parse enum");
1463
1464 let plugins = extract_derive_plugins(&parsed.attributes);
1465 assert_eq!(plugins.len(), 1);
1466 assert!(matches!(&plugins[0], PluginRef::Simple(name) if name == "Error"));
1467 }
1468
1469 #[test]
1470 fn test_extract_multiple_plugins() {
1471 let input = quote! {
1472 #[facet(derive(Error, Display))]
1473 pub enum MyError {
1474 Unknown,
1475 }
1476 };
1477
1478 let mut iter = input.to_token_iter();
1479 let parsed = iter.parse::<crate::Enum>().expect("Failed to parse enum");
1480
1481 let plugins = extract_derive_plugins(&parsed.attributes);
1482 assert_eq!(plugins.len(), 2);
1483 assert!(matches!(&plugins[0], PluginRef::Simple(name) if name == "Error"));
1484 assert!(matches!(&plugins[1], PluginRef::Simple(name) if name == "Display"));
1485 }
1486
1487 #[test]
1488 fn test_extract_path_plugins() {
1489 let input = quote! {
1490 #[facet(derive(Error, facet_default::Default))]
1491 pub enum MyError {
1492 Unknown,
1493 }
1494 };
1495
1496 let mut iter = input.to_token_iter();
1497 let parsed = iter.parse::<crate::Enum>().expect("Failed to parse enum");
1498
1499 let plugins = extract_derive_plugins(&parsed.attributes);
1500 assert_eq!(plugins.len(), 2);
1501 assert!(matches!(&plugins[0], PluginRef::Simple(name) if name == "Error"));
1502 assert!(
1503 matches!(&plugins[1], PluginRef::Path { crate_name, plugin_name } if crate_name == "facet_default" && plugin_name == "Default")
1504 );
1505 }
1506
1507 #[test]
1508 fn test_plugin_ref_crate_path() {
1509 let simple = PluginRef::Simple("Error".to_string());
1510 assert_eq!(simple.crate_path().to_string(), ":: facet_error");
1511
1512 let path = PluginRef::Path {
1513 crate_name: "facet_default".to_string(),
1514 plugin_name: "Default".to_string(),
1515 };
1516 assert_eq!(path.crate_path().to_string(), ":: facet_default");
1517 }
1518
1519 #[test]
1521 fn test_extract_derive_plugins_combined_attrs() {
1522 let input = quote! {
1524 #[derive(Debug, Facet)]
1525 #[facet(rename_all = "kebab-case", derive(Default))]
1526 struct PreCommitConfig {
1527 generate_readmes: bool,
1528 }
1529 };
1530
1531 let mut iter = input.to_token_iter();
1532 let parsed = iter
1533 .parse::<crate::Struct>()
1534 .expect("Failed to parse struct");
1535
1536 let plugins = extract_derive_plugins(&parsed.attributes);
1537 assert_eq!(
1538 plugins.len(),
1539 1,
1540 "should extract derive(Default) even when combined with other attrs"
1541 );
1542 assert!(matches!(&plugins[0], PluginRef::Simple(name) if name == "Default"));
1543 }
1544
1545 #[test]
1547 fn test_strip_derive_attrs_combined() {
1548 let input = quote! {
1550 #[derive(Debug, Facet)]
1551 #[facet(rename_all = "kebab-case", derive(Default))]
1552 struct PreCommitConfig {
1553 generate_readmes: bool,
1554 }
1555 };
1556
1557 let stripped = strip_derive_attrs(input);
1558 let stripped_str = stripped.to_string();
1559
1560 assert!(
1562 stripped_str.contains("derive"),
1563 "should keep #[derive(Debug, Facet)]"
1564 );
1565
1566 assert!(
1568 stripped_str.contains("rename_all"),
1569 "should keep rename_all attribute"
1570 );
1571
1572 assert!(
1576 !stripped_str.contains("facet (rename_all = \"kebab-case\" , derive (Default))"),
1577 "should strip derive(Default) from combined attribute"
1578 );
1579 }
1580
1581 #[test]
1583 fn test_strip_derive_attrs_only_derive() {
1584 let input = quote! {
1585 #[facet(derive(Default))]
1586 struct Foo {}
1587 };
1588
1589 let stripped = strip_derive_attrs(input);
1590 let stripped_str = stripped.to_string();
1591
1592 assert!(
1595 !stripped_str.contains("derive (Default)"),
1596 "derive(Default) should be stripped"
1597 );
1598 }
1599}