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