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