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