matchmaker_partial_macros/
lib.rs1use proc_macro::TokenStream;
2use quote::{ToTokens, format_ident, quote};
3use std::collections::HashSet;
4use syn::{
5 Fields, GenericArgument, ItemStruct, LitStr, Meta, Path, PathArguments, Token, Type,
6 parse::Parse, parse_macro_input, spanned::Spanned,
7};
8
9#[proc_macro_attribute]
10pub fn partial(attr: TokenStream, item: TokenStream) -> TokenStream {
11 let mut input = parse_macro_input!(item as ItemStruct);
12
13 if !cfg!(feature = "partial") {
14 input.attrs.retain(|attr| !attr.path().is_ident("partial"));
15 if let Fields::Named(fields) = &mut input.fields {
16 for field in &mut fields.named {
17 field.attrs.retain(|attr| !attr.path().is_ident("partial"));
18 }
19 }
20 return quote!(#input).into();
21 }
22
23 let name = &input.ident;
24 let partial_name = format_ident!("Partial{}", name);
25
26 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
27 let vis = &input.vis;
28
29 let mut struct_recurse = false;
30 let mut struct_unwrap = false;
31 let mut generate_path_setter = false;
32 let mut enable_merge = false; let mut manual_derives: Option<proc_macro2::TokenStream> = None;
34 let mut manual_attrs: Vec<proc_macro2::TokenStream> = Vec::new();
35 let mut has_manual_attrs = false;
36 let mut no_field_mirror = false;
37
38 if !attr.is_empty() {
40 let parser = syn::parse::Parser::parse2(
41 |input: syn::parse::ParseStream| {
42 while !input.is_empty() {
43 let path: Path = input.parse()?;
44 if path.is_ident("recurse") {
45 struct_recurse = true;
46 } else if path.is_ident("unwrap") {
47 struct_unwrap = true;
48 } else if path.is_ident("path") {
49 generate_path_setter = true;
50 } else if path.is_ident("merge") {
51 enable_merge = true; } else if path.is_ident("derive") {
53 if input.peek(syn::token::Paren) {
55 let content;
56 syn::parenthesized!(content in input);
57 let paths = content.parse_terminated(Path::parse, Token![,])?;
58 manual_derives = Some(quote! { #[derive(#paths)] });
59 } else {
60 manual_derives = Some(quote! {});
62 }
63 } else if path.is_ident("attr") {
64 has_manual_attrs = true;
65 if input.peek(syn::token::Paren) {
66 let content;
67 syn::parenthesized!(content in input);
68 let inner: Meta = content.parse()?;
69 if inner.path().is_ident("clear") {
70 no_field_mirror = true;
71 } else {
72 manual_attrs.push(quote! { #[#inner] });
73 }
74 }
75 } else {
76 return Err(syn::Error::new(
78 path.span(),
79 format!("unknown partial attribute: {}", path.to_token_stream()),
80 ));
81 }
82
83 if input.peek(Token![,]) {
84 input.parse::<Token![,]>()?;
85 }
86 }
87 Ok(())
88 },
89 attr.into(),
90 );
91
92 if let Err(e) = parser {
93 return e.to_compile_error().into();
94 }
95 }
96
97 let mut final_attrs = Vec::new();
99 let mut has_default = false;
100
101 if let Some(manual) = manual_derives {
102 let manual_str = manual.to_token_stream().to_string();
103 if manual_str.contains("Default") {
104 has_default = true;
105 }
106 final_attrs.push(manual);
107 } else {
108 for attr in &input.attrs {
109 if attr.path().is_ident("derive") {
110 let tokens = attr.to_token_stream();
111 if tokens.to_string().contains("Default") {
112 has_default = true;
113 }
114 final_attrs.push(tokens);
115 }
116 }
117 }
118
119 if !has_default {
120 final_attrs.push(quote! { #[derive(Default)] });
121 }
122
123 if has_manual_attrs {
124 final_attrs.extend(manual_attrs);
125 } else {
126 for attr in &input.attrs {
127 if !attr.path().is_ident("derive") {
128 final_attrs.push(attr.to_token_stream());
129 }
130 }
131 }
132
133 let fields = match &mut input.fields {
135 Fields::Named(fields) => &mut fields.named,
136 _ => panic!("Partial only supports structs with named fields"),
137 };
138
139 let mut partial_field_defs = Vec::new();
140 let mut apply_field_stmts = Vec::new();
141 let mut merge_field_stmts = Vec::new();
142 let mut clear_field_stmts = Vec::new();
143 let mut set_field_arms = Vec::new();
144 let mut flattened_field_targets = Vec::new();
145 let mut used_idents = HashSet::new();
146
147 for field in fields.iter_mut() {
148 let field_name = &field.ident;
149 let field_vis = &field.vis;
150 let field_ty = &field.ty;
151
152 let is_opt = is_option(field_ty);
153 let mut skip_field = false;
154 let mut field_recurse = false;
155 let mut recurse_override: Option<Option<proc_macro2::TokenStream>> = None;
156 let mut field_unwrap = struct_unwrap;
157 let mut field_set: Option<String> = None;
158 let mut field_attrs_for_mirror = Vec::new();
159 let mut field_errors = Vec::new();
160 let mut custom_deserializer: Option<Path> = None;
162 let mut field_aliases = Vec::new();
163 let mut is_flattened = false;
164
165 field.attrs.retain(|attr| {
166 if attr.path().is_ident("partial") {
168 let res = attr.parse_nested_meta(|meta| {
169 if meta.path.is_ident("skip") {
170 skip_field = true;
171 } else if meta.path.is_ident("unwrap") {
172 field_unwrap = true;
173 } else if meta.path.is_ident("set") {
174 let s: LitStr = meta.value()?.parse()?;
175 field_set = Some(s.value());
176 } else if meta.path.is_ident("alias") {
177 let s: LitStr = meta.value()?.parse()?;
178 field_aliases.push(s.value());
179 } else if meta.path.is_ident("flatten") {
180 is_flattened = true;
181 } else if meta.path.is_ident("recurse") {
182 if let Ok(value) = meta.value() {
183 let s: LitStr = value.parse().unwrap();
184 if s.value().is_empty() {
185 recurse_override = Some(None);
186 } else {
187 let ty: Type = s.parse().unwrap();
188 recurse_override = Some(Some(quote! { #ty }));
189 }
190 } else {
191 field_recurse = true;
193 }
194 } else if meta.path.is_ident("attr") {
195 field_attrs_for_mirror.clear();
196 if meta.input.peek(syn::token::Paren) {
197 let content;
198 syn::parenthesized!(content in meta.input);
199 while !content.is_empty() {
200 let inner_meta: Meta = content.parse()?;
201 field_attrs_for_mirror.push(quote! { #[#inner_meta] });
202 if content.peek(Token![,]) {
203 content.parse::<Token![,]>()?;
204 }
205 }
206 }
207 } else {
208 return Err(meta.error(format!(
209 "unknown partial attribute: {}",
210 meta.path.to_token_stream()
211 )));
212 }
213 Ok(())
214 });
215
216 if let Err(e) = res {
217 field_errors.push(e);
218 }
219 return false; }
221
222 if attr.path().is_ident("serde") {
226 let should_recurse =
228 (struct_recurse || field_recurse || recurse_override.is_some())
229 && !matches!(recurse_override, Some(None));
230 let is_same_type = !should_recurse && (field_unwrap == !is_opt);
231
232 let mut drop_attr = false;
233 let _ = attr.parse_nested_meta(|meta| {
234 if meta.path.is_ident("deserialize_with") {
235 if let Ok(value) = meta.value()
236 && let Ok(s) = value.parse::<LitStr>()
237 {
238 custom_deserializer = s.parse::<Path>().ok();
239 if !is_same_type {
240 drop_attr = true;
241 }
242 }
243 } else if meta.path.is_ident("with") {
244 if let Ok(value) = meta.value()
245 && let Ok(s) = value.parse::<LitStr>()
246 && let Ok(mut p) = s.parse::<Path>()
247 {
248 p.segments.push(format_ident!("deserialize").into());
249 custom_deserializer = Some(p);
250 if !is_same_type {
251 drop_attr = true;
252 }
253 } else if meta.path.is_ident("serialize_with") && !is_same_type {
254 drop_attr = true;
255 }
256 } else if meta.path.is_ident("alias") {
257 if let Ok(value) = meta.value()
258 && let Ok(s) = value.parse::<LitStr>()
259 {
260 field_aliases.push(s.value());
261 }
262 } else if meta.path.is_ident("flatten") {
263 is_flattened = true;
264 }
265 Ok(())
266 });
267
268 if drop_attr {
269 return false; }
271 }
272
273 if !no_field_mirror {
276 field_attrs_for_mirror.push(attr.to_token_stream());
277 }
278 true
279 });
280
281 if let Some(err) = field_errors.first() {
282 return err.to_compile_error().into();
283 }
284
285 if skip_field {
286 continue;
287 }
288
289 if let Some(ref s) = field_set
290 && s == "sequence"
291 && recurse_override.as_ref().is_some_and(|s| s.is_some())
292 {
293 return syn::Error::new(
294 field.span(),
295 "cannot use 'recurse' and 'set = \"sequence\"' on the same field",
296 )
297 .to_compile_error()
298 .into();
299 }
300
301 let inner_ty = if is_opt {
302 extract_inner_type_from_option(field_ty)
303 } else {
304 field_ty
305 };
306
307 let coll_info = get_collection_info(inner_ty);
308
309 let should_recurse = (struct_recurse || field_recurse || recurse_override.is_some())
312 && !matches!(recurse_override, Some(None));
313
314 let current_field_ty: proc_macro2::TokenStream;
315 let mut is_recursive_field = false;
316
317 if let Some((kind, inners)) = coll_info {
318 let element_ty = inners
319 .last()
320 .expect("Collection must have at least one inner type");
321 let partial_element_ty = if should_recurse {
322 is_recursive_field = true;
323 if let Some(Some(ref overridden)) = recurse_override {
324 overridden.clone()
325 } else if let Type::Path(tp) = element_ty {
326 let mut p_path = tp.path.clone();
327 if let Some(seg) = p_path.segments.last_mut() {
328 seg.ident = format_ident!("Partial{}", seg.ident);
329 quote! { #p_path }
330 } else {
331 quote! { #element_ty }
332 }
333 } else {
334 quote! { #element_ty }
335 }
336 } else {
337 quote! { #element_ty }
338 };
339
340 let coll_ident = match kind {
341 CollectionKind::Vec => quote! { Vec },
342 CollectionKind::HashSet => quote! { HashSet },
343 CollectionKind::BTreeSet => quote! { BTreeSet },
344 CollectionKind::HashMap => quote! { HashMap },
345 CollectionKind::BTreeMap => quote! { BTreeMap },
346 };
347
348 let partial_coll_ty = if inners.len() == 2 {
349 let key_ty = inners[0];
350 quote! { #coll_ident<#key_ty, #partial_element_ty> }
351 } else {
352 quote! { #coll_ident<#partial_element_ty> }
353 };
354
355 current_field_ty = if field_unwrap {
356 partial_coll_ty.clone()
357 } else {
358 quote! { Option<#partial_coll_ty> }
359 };
360
361 let target_expr = if is_opt {
363 quote! { self.#field_name.get_or_insert_with(Default::default) }
364 } else {
365 quote! { self.#field_name }
366 };
367
368 let apply_stmt = if is_recursive_field {
369 let element_apply = match kind {
370 CollectionKind::Vec | CollectionKind::HashSet | CollectionKind::BTreeSet => {
371 let push_method = if kind == CollectionKind::Vec {
372 quote! { push }
373 } else {
374 quote! { insert }
375 };
376 if !field_unwrap {
377 if kind == CollectionKind::Vec {
378 quote! {
379 let mut p_it = p.into_iter();
380 for target in #target_expr.iter_mut() {
381 if let Some(p_item) = p_it.next() {
382 matchmaker_partial::Apply::apply(target, p_item);
383 } else {
384 break;
385 }
386 }
387 for p_item in p_it {
388 let mut t = <#element_ty as Default>::default();
389 matchmaker_partial::Apply::apply(&mut t, p_item);
390 #target_expr.push(t);
391 }
392 }
393 } else {
394 quote! {
395 for p_item in p {
396 let mut t = <#element_ty as Default>::default();
397 matchmaker_partial::Apply::apply(&mut t, p_item);
398 #target_expr.insert(t);
399 }
400 }
401 }
402 } else {
403 quote! {
404 for p_item in partial.#field_name {
405 let mut t = <#element_ty as Default>::default();
406 matchmaker_partial::Apply::apply(&mut t, p_item);
407 #target_expr.#push_method(t);
408 }
409 }
410 }
411 }
412 CollectionKind::HashMap | CollectionKind::BTreeMap => {
413 if !field_unwrap {
414 quote! {
415 for (k, p_v) in p {
416 if let Some(v) = #target_expr.get_mut(&k) {
417 matchmaker_partial::Apply::apply(v, p_v);
418 } else {
419 let mut v = <#element_ty as Default>::default();
420 matchmaker_partial::Apply::apply(&mut v, p_v);
421 #target_expr.insert(k, v);
422 }
423 }
424 }
425 } else {
426 quote! {
427 for (k, p_v) in partial.#field_name {
428 if let Some(v) = #target_expr.get_mut(&k) {
429 matchmaker_partial::Apply::apply(v, p_v);
430 } else {
431 let mut v = <#element_ty as Default>::default();
432 matchmaker_partial::Apply::apply(&mut v, p_v);
433 #target_expr.insert(k, v);
434 }
435 }
436 }
437 }
438 }
439 };
440
441 if !field_unwrap {
442 quote! { if let Some(p) = partial.#field_name { #element_apply } }
443 } else {
444 element_apply
445 }
446 } else if !field_unwrap {
447 let val = if is_opt {
448 quote! { Some(p) }
449 } else {
450 quote! { p }
451 };
452 quote! { if let Some(p) = partial.#field_name { self.#field_name = #val; } }
453 } else if kind == CollectionKind::HashMap || kind == CollectionKind::BTreeMap {
454 quote! {
455 for (k, v) in partial.#field_name {
456 #target_expr.insert(k, v);
457 }
458 }
459 } else {
460 quote! { #target_expr.extend(partial.#field_name.into_iter()); }
461 };
462 apply_field_stmts.push(apply_stmt);
463
464 if !field_unwrap {
466 merge_field_stmts.push(quote! {
467 if let Some(other_coll) = other.#field_name {
468 self.#field_name.get_or_insert_with(Default::default).extend(other_coll.into_iter());
469 }
470 });
471 clear_field_stmts.push(quote! { self.#field_name = None; });
472 } else {
473 merge_field_stmts
474 .push(quote! { self.#field_name.extend(other.#field_name.into_iter()); });
475 clear_field_stmts.push(quote! { self.#field_name.clear(); });
476 }
477
478 if let Some(field_ident) = &field.ident {
480 let field_name_str = field_ident.to_string();
481 let field_name_str = field_name_str.strip_prefix("r#").unwrap_or(&field_name_str);
482
483 let is_sequence = field_set.as_deref() == Some("sequence");
484 let is_set_recurse = field_set.as_deref() == Some("recurse");
485 let set_logic = if is_sequence {
486 let assignment = if !field_unwrap {
487 quote! { self.#field_ident = Some(deserialized); }
488 } else {
489 quote! { self.#field_ident.extend(deserialized); }
490 };
491 quote! {
492 let deserialized: #partial_coll_ty = matchmaker_partial::deserialize(val)?;
493 #assignment
494 }
495 } else {
496 let target = if !field_unwrap {
497 quote! { self.#field_ident.get_or_insert_with(Default::default) }
498 } else {
499 quote! { self.#field_ident }
500 };
501
502 let set_full_coll_logic = if !field_unwrap {
503 quote! { self.#field_ident = Some(new_map); }
504 } else {
505 quote! { #target.extend(new_map.into_iter()); }
506 };
507
508 let p_element_ty = if let Type::Path(tp) = element_ty {
509 let mut p_path = tp.path.clone();
510 if let Some(seg) = p_path.segments.last_mut() {
511 seg.ident = format_ident!("Partial{}", seg.ident);
512 quote! { #p_path }
513 } else {
514 quote! { #element_ty }
515 }
516 } else {
517 quote! { #element_ty }
518 };
519
520 if inners.len() == 2 {
521 let key_ty = inners[0];
522 let val_ty = if should_recurse {
523 quote! { #partial_element_ty }
524 } else {
525 quote! { #element_ty }
526 };
527
528 let descent_logic = if should_recurse || is_set_recurse {
529 let set_item_logic = if should_recurse {
530 quote! { matchmaker_partial::Set::set(item, rest, val)?; }
531 } else {
532 quote! {
533 let mut p_item = #p_element_ty::default();
534 matchmaker_partial::Set::set(&mut p_item, rest, val)?;
535 *item = matchmaker_partial::from(p_item);
536 }
537 };
538
539 quote! {
540 if rest.is_empty() {
541 let mut combined = vec![key_str.clone()];
542 combined.extend_from_slice(val);
543 let (key, value): (#key_ty, #val_ty) = matchmaker_partial::deserialize(&combined)?;
544 let _ = #target.insert(key, value);
545 } else {
546 let key: #key_ty = matchmaker_partial::deserialize(&[key_str.clone()])?;
547 let item = #target.entry(key).or_insert_with(Default::default);
548 #set_item_logic
549 }
550 }
551 } else {
552 quote! {
553 if rest.is_empty() {
554 let mut combined = vec![key_str.clone()];
555 combined.extend_from_slice(val);
556 let (key, value): (#key_ty, #val_ty) = matchmaker_partial::deserialize(&combined)?;
557 let _ = #target.insert(key, value);
558 } else {
559 return Err(matchmaker_partial::PartialSetError::ExtraPaths(rest.to_vec()));
560 }
561 }
562 };
563
564 quote! {
565 if let Some((key_str, rest)) = tail.split_first() {
566 #descent_logic
567 } else {
568 let new_map: #partial_coll_ty = matchmaker_partial::deserialize(val)?;
569 #set_full_coll_logic
570 }
571 }
572 } else {
573 let push_method = match kind {
574 CollectionKind::Vec => quote! { push },
575 _ => quote! { insert },
576 };
577 let item_ty = if should_recurse {
578 quote! { #partial_element_ty }
579 } else {
580 quote! { #element_ty }
581 };
582 if is_set_recurse {
583 if should_recurse {
584 quote! {
585 let mut item = #item_ty::default();
586 if tail.is_empty() {
587 item = matchmaker_partial::deserialize(val)?;
588 } else {
589 matchmaker_partial::Set::set(&mut item, tail, val)?;
590 }
591 #target.#push_method(item);
592 }
593 } else {
594 quote! {
595 if tail.is_empty() {
596 let item: #item_ty = matchmaker_partial::deserialize(val)?;
597 #target.#push_method(item);
598 } else {
599 let mut p_item = #p_element_ty::default();
600 matchmaker_partial::Set::set(&mut p_item, tail, val)?;
601 let item: #item_ty = matchmaker_partial::from(p_item);
602 #target.#push_method(item);
603 }
604 }
605 }
606 } else {
607 quote! {
608 if let Some((_, _)) = tail.split_first() {
609 return Err(matchmaker_partial::PartialSetError::ExtraPaths(tail.to_vec()));
610 }
611 let item: #item_ty = matchmaker_partial::deserialize(val)?;
612 #target.#push_method(item);
613 }
614 }
615 }
616 };
617
618 set_field_arms.push(quote! {
619 #field_name_str #(| #field_aliases)* => {
620 #set_logic
621 Ok(())
622 }
623 });
624 }
625 } else {
626 current_field_ty = if should_recurse {
628 is_recursive_field = true;
629 let p_ty = if let Some(Some(ref overridden)) = recurse_override {
630 overridden.clone()
631 } else if let Type::Path(ty_path) = inner_ty {
632 let mut p_path = ty_path.path.clone();
633 if let Some(seg) = p_path.segments.last_mut() {
634 seg.ident = format_ident!("Partial{}", seg.ident);
635 quote! { #p_path }
636 } else {
637 quote! { #inner_ty }
638 }
639 } else {
640 quote! { #inner_ty }
641 };
642
643 if field_unwrap {
644 p_ty
645 } else if is_opt {
646 quote! { Option<#p_ty> }
647 } else {
648 p_ty
649 }
650 } else if field_unwrap {
651 quote! { #inner_ty }
652 } else if is_opt {
653 quote! { #field_ty }
654 } else {
655 quote! { Option<#field_ty> }
656 };
657
658 if is_recursive_field {
659 if !field_unwrap && is_opt {
660 apply_field_stmts.push(quote! {
661 if let Some(p) = partial.#field_name {
662 if let Some(ref mut v) = self.#field_name {
663 matchmaker_partial::Apply::apply(v, p);
664 } else {
665 self.#field_name = Some(matchmaker_partial::from(p));
666 }
667 }
668 });
669 merge_field_stmts.push(quote! {
670 match (&mut self.#field_name, other.#field_name) {
671 (Some(s), Some(o)) => matchmaker_partial::Merge::merge(s, o),
672 (t @ None, Some(o)) => *t = Some(o),
673 _ => {}
674 }
675 });
676 clear_field_stmts.push(quote! { self.#field_name = None; });
677 } else if field_unwrap && is_opt {
678 apply_field_stmts.push(quote! {
680 if let Some(ref mut v) = self.#field_name {
681 matchmaker_partial::Apply::apply(v, partial.#field_name);
682 } else {
683 self.#field_name = Some(matchmaker_partial::from(partial.#field_name));
684 }
685 });
686 merge_field_stmts.push(quote! { matchmaker_partial::Merge::merge(&mut self.#field_name, other.#field_name); });
687 clear_field_stmts
688 .push(quote! { matchmaker_partial::Merge::clear(&mut self.#field_name); });
689 } else {
690 apply_field_stmts.push(quote! { matchmaker_partial::Apply::apply(&mut self.#field_name, partial.#field_name); });
691 merge_field_stmts.push(quote! { matchmaker_partial::Merge::merge(&mut self.#field_name, other.#field_name); });
692 clear_field_stmts
693 .push(quote! { matchmaker_partial::Merge::clear(&mut self.#field_name); });
694 }
695
696 if let Some(field_ident) = &field.ident {
697 let field_name_str = field_ident.to_string();
698 let field_name_str =
699 field_name_str.strip_prefix("r#").unwrap_or(&field_name_str);
700
701 let set_target = if is_opt {
702 quote! { self.#field_ident.get_or_insert_with(Default::default) }
703 } else {
704 quote! { &mut self.#field_ident }
705 };
706
707 if is_flattened {
708 flattened_field_targets.push(set_target);
709 } else {
710 set_field_arms.push(quote! {
711 #field_name_str #(| #field_aliases)* => {
712 if tail.is_empty() {
713 return Err(matchmaker_partial::PartialSetError::EarlyEnd(head.clone()));
714 }
715 matchmaker_partial::Set::set(#set_target, tail, val)
716 }
717 });
718 }
719 }
720 } else {
721 if field_unwrap {
722 if is_opt {
723 apply_field_stmts
724 .push(quote! { self.#field_name = Some(partial.#field_name); });
725 } else {
726 apply_field_stmts.push(quote! { self.#field_name = partial.#field_name; });
727 }
728 } else if !is_opt {
729 apply_field_stmts.push(
730 quote! { if let Some(v) = partial.#field_name { self.#field_name = v; } },
731 );
732 } else {
733 apply_field_stmts.push(
734 quote! { if let Some(v) = partial.#field_name { self.#field_name = Some(v); } },
735 );
736 }
737 merge_field_stmts.push(
738 quote! { if other.#field_name.is_some() { self.#field_name = other.#field_name; } },
739 );
740 clear_field_stmts.push(quote! { self.#field_name = None; });
741
742 if let Some(field_ident) = &field.ident {
743 let field_name_str = field_ident.to_string();
744 let field_name_str =
745 field_name_str.strip_prefix("r#").unwrap_or(&field_name_str);
746
747 let set_logic = if let Some(custom_func) = custom_deserializer {
749 let assignment = if is_opt {
753 quote! { self.#field_name = result; }
754 } else {
755 quote! { self.#field_name = Some(result); }
756 };
757
758 quote! {
759 let mut deserializer = matchmaker_partial::SimpleDeserializer::from_slice(val);
760 let result = #custom_func(&mut deserializer)?;
761 #assignment
762 }
763 } else {
764 let inner_ty = extract_inner_type_from_option(field_ty);
767 quote! {
768 let deserialized = matchmaker_partial::deserialize::<#inner_ty>(val)?;
769 self.#field_name = Some(deserialized);
770 }
771 };
772
773 set_field_arms.push(quote! {
774 #field_name_str #(| #field_aliases)* => {
775 if !tail.is_empty() {
776 return Err(matchmaker_partial::PartialSetError::ExtraPaths(tail.to_vec()));
777 }
778 #set_logic
779 Ok(())
780 }
781 });
782 }
783 }
784 }
785
786 find_idents_in_tokens(current_field_ty.clone(), &mut used_idents);
787 partial_field_defs
788 .push(quote! { #(#field_attrs_for_mirror)* #field_vis #field_name: #current_field_ty });
789 }
790
791 let mut partial_generics = input.generics.clone();
793 partial_generics.params = partial_generics
794 .params
795 .into_iter()
796 .filter(|param| match param {
797 syn::GenericParam::Type(t) => used_idents.contains(&t.ident),
798 syn::GenericParam::Lifetime(l) => used_idents.contains(&l.lifetime.ident),
799 syn::GenericParam::Const(c) => used_idents.contains(&c.ident),
800 })
801 .collect();
802
803 let (p_impl_generics, p_ty_generics, p_where_clause) = partial_generics.split_for_impl();
804
805 let path_setter_impl = if generate_path_setter {
807 quote! {
808 impl #p_impl_generics matchmaker_partial::Set for #partial_name #p_ty_generics #p_where_clause {
809 fn set(&mut self, path: &[String], val: &[String]) -> Result<(), matchmaker_partial::PartialSetError> {
810 let (head, tail) = path.split_first().ok_or_else(|| {
811 matchmaker_partial::PartialSetError::EarlyEnd("root".to_string())
812 })?;
813
814 match head.as_str() {
815 #(#set_field_arms)*
816 _ => {
817 #(
818 match matchmaker_partial::Set::set(#flattened_field_targets, path, val) {
819 Err(matchmaker_partial::PartialSetError::Missing(_)) => {}
820 x => return x,
821 }
822 )*
823 Err(matchmaker_partial::PartialSetError::Missing(head.clone()))
824 }
825 }
826 }
827 }
828 }
829 } else {
830 quote! {}
831 };
832
833 let merge_impl = if enable_merge {
835 quote! {
836 impl #p_impl_generics matchmaker_partial::Merge for #partial_name #p_ty_generics #p_where_clause {
837 fn merge(&mut self, other: Self) {
838 #(#merge_field_stmts)*
839 }
840
841 fn clear(&mut self) {
842 #(#clear_field_stmts)*
843 }
844 }
845 }
846 } else {
847 quote! {}
848 };
849
850 let expanded = quote! {
851 #input
852
853 #(#final_attrs)*
854 #vis struct #partial_name #p_ty_generics #p_where_clause {
855 #(#partial_field_defs),*
856 }
857
858 impl #impl_generics matchmaker_partial::Apply for #name #ty_generics #where_clause {
859 type Partial = #partial_name #p_ty_generics;
860 fn apply(&mut self, partial: Self::Partial) {
861 #(#apply_field_stmts)*
862 }
863 }
864
865 #merge_impl
866
867 #path_setter_impl
868 };
869
870 TokenStream::from(expanded)
871}
872
873fn is_option(ty: &Type) -> bool {
874 if let Type::Path(tp) = ty {
875 tp.path.segments.last().is_some_and(|s| s.ident == "Option")
876 } else {
877 false
878 }
879}
880
881#[derive(PartialEq, Clone, Copy)]
882enum CollectionKind {
883 Vec,
884 HashSet,
885 BTreeSet,
886 HashMap,
887 BTreeMap,
888}
889
890fn get_collection_info(ty: &Type) -> Option<(CollectionKind, Vec<&Type>)> {
891 if let Type::Path(tp) = ty {
892 let last_seg = tp.path.segments.last()?;
893 let kind = if last_seg.ident == "Vec" {
894 CollectionKind::Vec
895 } else if last_seg.ident == "HashSet" {
896 CollectionKind::HashSet
897 } else if last_seg.ident == "BTreeSet" {
898 CollectionKind::BTreeSet
899 } else if last_seg.ident == "HashMap" {
900 CollectionKind::HashMap
901 } else if last_seg.ident == "BTreeMap" {
902 CollectionKind::BTreeMap
903 } else {
904 return None;
905 };
906
907 let mut inner_types = Vec::new();
908 if let PathArguments::AngleBracketed(args) = &last_seg.arguments {
909 for arg in &args.args {
910 if let GenericArgument::Type(inner_ty) = arg {
911 inner_types.push(inner_ty);
912 }
913 }
914 }
915 Some((kind, inner_types))
916 } else {
917 None
918 }
919}
920
921fn extract_inner_type_from_option(ty: &Type) -> &Type {
923 if let Type::Path(tp) = ty
924 && let Some(last_seg) = tp.path.segments.last()
925 && last_seg.ident == "Option"
926 && let PathArguments::AngleBracketed(args) = &last_seg.arguments
927 && let Some(GenericArgument::Type(inner)) = args.args.first()
928 {
929 return inner;
930 }
931 ty
932}
933
934fn find_idents_in_tokens(tokens: proc_macro2::TokenStream, set: &mut HashSet<proc_macro2::Ident>) {
935 for token in tokens {
936 match token {
937 proc_macro2::TokenTree::Ident(id) => {
938 set.insert(id);
939 }
940 proc_macro2::TokenTree::Group(g) => find_idents_in_tokens(g.stream(), set),
941 _ => {}
942 }
943 }
944}