1use ahash::{AHashMap, AHashSet};
2use alef_core::ir::{ApiSurface, EnumDef, FieldDef, PrimitiveType, TypeDef, TypeRef};
3
4use crate::conversions::ConversionConfig;
5
6pub fn input_type_names(surface: &ApiSurface) -> AHashSet<String> {
19 let mut names = AHashSet::new();
20
21 for func in &surface.functions {
23 for param in &func.params {
24 collect_named_types(¶m.ty, &mut names);
25 }
26 }
27 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
29 for method in &typ.methods {
30 for param in &method.params {
31 collect_named_types(¶m.ty, &mut names);
32 }
33 }
34 }
35 for func in &surface.functions {
39 collect_named_types(&func.return_type, &mut names);
40 }
41 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
43 for method in &typ.methods {
44 collect_named_types(&method.return_type, &mut names);
45 }
46 }
47 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
52 if !typ.is_opaque && !typ.methods.is_empty() {
53 for field in &typ.fields {
54 if !field.sanitized {
55 collect_named_types(&field.ty, &mut names);
56 }
57 }
58 }
59 }
60
61 let mut changed = true;
63 while changed {
64 changed = false;
65 let snapshot: Vec<String> = names.iter().cloned().collect();
66 for name in &snapshot {
67 if let Some(typ) = surface.types.iter().find(|t| t.name == *name) {
68 for field in &typ.fields {
69 let mut field_names = AHashSet::new();
70 collect_named_types(&field.ty, &mut field_names);
71 for n in field_names {
72 if names.insert(n) {
73 changed = true;
74 }
75 }
76 }
77 }
78 }
79 }
80
81 names
82}
83
84fn collect_named_types(ty: &TypeRef, out: &mut AHashSet<String>) {
86 match ty {
87 TypeRef::Named(name) => {
88 out.insert(name.clone());
89 }
90 TypeRef::Optional(inner) | TypeRef::Vec(inner) => collect_named_types(inner, out),
91 TypeRef::Map(k, v) => {
92 collect_named_types(k, out);
93 collect_named_types(v, out);
94 }
95 _ => {}
96 }
97}
98
99pub fn field_references_excluded_type(ty: &TypeRef, exclude_types: &[String]) -> bool {
103 match ty {
104 TypeRef::Named(name) => exclude_types.iter().any(|e| e == name),
105 TypeRef::Optional(inner) | TypeRef::Vec(inner) => field_references_excluded_type(inner, exclude_types),
106 TypeRef::Map(k, v) => {
107 field_references_excluded_type(k, exclude_types) || field_references_excluded_type(v, exclude_types)
108 }
109 _ => false,
110 }
111}
112
113pub(crate) fn needs_i64_cast(p: &PrimitiveType) -> bool {
115 matches!(p, PrimitiveType::U64 | PrimitiveType::Usize | PrimitiveType::Isize)
116}
117
118pub(crate) fn core_prim_str(p: &PrimitiveType) -> &'static str {
120 match p {
121 PrimitiveType::U64 => "u64",
122 PrimitiveType::Usize => "usize",
123 PrimitiveType::Isize => "isize",
124 PrimitiveType::F32 => "f32",
125 PrimitiveType::Bool => "bool",
126 PrimitiveType::U8 => "u8",
127 PrimitiveType::U16 => "u16",
128 PrimitiveType::U32 => "u32",
129 PrimitiveType::I8 => "i8",
130 PrimitiveType::I16 => "i16",
131 PrimitiveType::I32 => "i32",
132 PrimitiveType::I64 => "i64",
133 PrimitiveType::F64 => "f64",
134 }
135}
136
137pub(crate) fn binding_prim_str(p: &PrimitiveType) -> &'static str {
139 match p {
140 PrimitiveType::U64 | PrimitiveType::Usize | PrimitiveType::Isize => "i64",
141 PrimitiveType::F32 => "f64",
142 PrimitiveType::Bool => "bool",
143 PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32 => "i32",
144 PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 => "i32",
145 PrimitiveType::I64 => "i64",
146 PrimitiveType::F64 => "f64",
147 }
148}
149
150pub fn core_to_binding_convertible_types(surface: &ApiSurface) -> AHashSet<String> {
154 let convertible_enums: AHashSet<&str> = surface
155 .enums
156 .iter()
157 .filter(|e| can_generate_enum_conversion_from_core(e))
158 .map(|e| e.name.as_str())
159 .collect();
160
161 let opaque_type_names: AHashSet<&str> = surface
162 .types
163 .iter()
164 .filter(|t| t.is_opaque)
165 .map(|t| t.name.as_str())
166 .collect();
167
168 let (enum_paths, type_paths) = build_rust_path_maps(surface);
170
171 let mut convertible: AHashSet<String> = surface
173 .types
174 .iter()
175 .filter(|t| !t.is_opaque)
176 .map(|t| t.name.clone())
177 .collect();
178
179 let mut changed = true;
180 while changed {
181 changed = false;
182 let snapshot: Vec<String> = convertible.iter().cloned().collect();
183 let mut known: AHashSet<&str> = convertible.iter().map(|s| s.as_str()).collect();
184 known.extend(&opaque_type_names);
185 let mut to_remove = Vec::new();
186 for type_name in &snapshot {
187 if let Some(typ) = surface.types.iter().find(|t| t.name == *type_name) {
188 let ok = typ.fields.iter().all(|f| {
189 if f.sanitized {
190 true
191 } else if field_has_path_mismatch(f, &enum_paths, &type_paths) {
192 false
193 } else {
194 is_field_convertible(&f.ty, &convertible_enums, &known)
195 }
196 });
197 if !ok {
198 to_remove.push(type_name.clone());
199 }
200 }
201 }
202 for name in to_remove {
203 if convertible.remove(&name) {
204 changed = true;
205 }
206 }
207 }
208 convertible
209}
210
211pub fn convertible_types(surface: &ApiSurface) -> AHashSet<String> {
216 let convertible_enums: AHashSet<&str> = surface
218 .enums
219 .iter()
220 .filter(|e| can_generate_enum_conversion(e))
221 .map(|e| e.name.as_str())
222 .collect();
223
224 let _all_type_names: AHashSet<&str> = surface.types.iter().map(|t| t.name.as_str()).collect();
227
228 let default_type_names: AHashSet<&str> = surface
231 .types
232 .iter()
233 .filter(|t| t.has_default)
234 .map(|t| t.name.as_str())
235 .collect();
236
237 let mut convertible: AHashSet<String> = surface
241 .types
242 .iter()
243 .filter(|t| !t.is_opaque)
244 .map(|t| t.name.clone())
245 .collect();
246
247 let opaque_type_names: AHashSet<&str> = surface
250 .types
251 .iter()
252 .filter(|t| t.is_opaque)
253 .map(|t| t.name.as_str())
254 .collect();
255
256 let (enum_paths, type_paths) = build_rust_path_maps(surface);
258
259 let mut changed = true;
264 while changed {
265 changed = false;
266 let snapshot: Vec<String> = convertible.iter().cloned().collect();
267 let mut known: AHashSet<&str> = convertible.iter().map(|s| s.as_str()).collect();
268 known.extend(&opaque_type_names);
269 let mut to_remove = Vec::new();
270 for type_name in &snapshot {
271 if let Some(typ) = surface.types.iter().find(|t| t.name == *type_name) {
272 let ok = typ.fields.iter().all(|f| {
273 if f.sanitized {
274 sanitized_field_has_default(&f.ty, &default_type_names)
275 } else if field_has_path_mismatch(f, &enum_paths, &type_paths) {
276 false
277 } else {
278 is_field_convertible(&f.ty, &convertible_enums, &known)
279 }
280 });
281 if !ok {
282 to_remove.push(type_name.clone());
283 }
284 }
285 }
286 for name in to_remove {
287 if convertible.remove(&name) {
288 changed = true;
289 }
290 }
291 }
292 convertible
293}
294
295fn sanitized_field_has_default(ty: &TypeRef, default_types: &AHashSet<&str>) -> bool {
300 match ty {
301 TypeRef::Primitive(_)
302 | TypeRef::String
303 | TypeRef::Char
304 | TypeRef::Bytes
305 | TypeRef::Path
306 | TypeRef::Unit
307 | TypeRef::Duration
308 | TypeRef::Json => true,
309 TypeRef::Optional(_) => true,
311 TypeRef::Vec(_) => true,
313 TypeRef::Map(_, _) => true,
315 TypeRef::Named(name) => {
316 if is_tuple_type_name(name) {
317 true
319 } else {
320 default_types.contains(name.as_str())
322 }
323 }
324 }
325}
326
327pub fn can_generate_conversion(typ: &TypeDef, convertible: &AHashSet<String>) -> bool {
329 convertible.contains(&typ.name)
330}
331
332pub(crate) fn is_field_convertible(
333 ty: &TypeRef,
334 convertible_enums: &AHashSet<&str>,
335 known_types: &AHashSet<&str>,
336) -> bool {
337 match ty {
338 TypeRef::Primitive(_)
339 | TypeRef::String
340 | TypeRef::Char
341 | TypeRef::Bytes
342 | TypeRef::Path
343 | TypeRef::Unit
344 | TypeRef::Duration => true,
345 TypeRef::Json => true,
346 TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_field_convertible(inner, convertible_enums, known_types),
347 TypeRef::Map(k, v) => {
348 is_field_convertible(k, convertible_enums, known_types)
349 && is_field_convertible(v, convertible_enums, known_types)
350 }
351 TypeRef::Named(name) if is_tuple_type_name(name) => true,
353 TypeRef::Named(name) => convertible_enums.contains(name.as_str()) || known_types.contains(name.as_str()),
355 }
356}
357
358fn field_has_path_mismatch(
364 field: &FieldDef,
365 enum_rust_paths: &AHashMap<&str, &str>,
366 type_rust_paths: &AHashMap<&str, &str>,
367) -> bool {
368 let name = match &field.ty {
369 TypeRef::Named(n) => n.as_str(),
370 TypeRef::Optional(inner) | TypeRef::Vec(inner) => match inner.as_ref() {
371 TypeRef::Named(n) => n.as_str(),
372 _ => return false,
373 },
374 _ => return false,
375 };
376
377 if let Some(field_path) = &field.type_rust_path {
378 if let Some(enum_path) = enum_rust_paths.get(name) {
379 if !paths_compatible(field_path, enum_path) {
380 return true;
381 }
382 }
383 if let Some(type_path) = type_rust_paths.get(name) {
384 if !paths_compatible(field_path, type_path) {
385 return true;
386 }
387 }
388 }
389 false
390}
391
392fn paths_compatible(a: &str, b: &str) -> bool {
397 if a == b {
398 return true;
399 }
400 let a_norm = a.replace('-', "_");
403 let b_norm = b.replace('-', "_");
404 if a_norm == b_norm {
405 return true;
406 }
407 if a_norm.ends_with(&b_norm) || b_norm.ends_with(&a_norm) {
409 return true;
410 }
411 let a_root = a_norm.split("::").next().unwrap_or("");
413 let b_root = b_norm.split("::").next().unwrap_or("");
414 let a_name = a_norm.rsplit("::").next().unwrap_or("");
415 let b_name = b_norm.rsplit("::").next().unwrap_or("");
416 a_root == b_root && a_name == b_name
417}
418
419fn build_rust_path_maps(surface: &ApiSurface) -> (AHashMap<&str, &str>, AHashMap<&str, &str>) {
421 let enum_paths: AHashMap<&str, &str> = surface
422 .enums
423 .iter()
424 .map(|e| (e.name.as_str(), e.rust_path.as_str()))
425 .collect();
426 let type_paths: AHashMap<&str, &str> = surface
427 .types
428 .iter()
429 .map(|t| (t.name.as_str(), t.rust_path.as_str()))
430 .collect();
431 (enum_paths, type_paths)
432}
433
434pub fn can_generate_enum_conversion(enum_def: &EnumDef) -> bool {
438 !enum_def.variants.is_empty()
439}
440
441pub fn can_generate_enum_conversion_from_core(enum_def: &EnumDef) -> bool {
444 !enum_def.variants.is_empty()
446}
447
448pub fn is_tuple_variant(fields: &[FieldDef]) -> bool {
450 !fields.is_empty()
451 && fields[0]
452 .name
453 .strip_prefix('_')
454 .is_some_and(|rest: &str| rest.chars().all(|c: char| c.is_ascii_digit()))
455}
456
457pub fn is_newtype(typ: &TypeDef) -> bool {
459 typ.fields.len() == 1 && typ.fields[0].name == "_0"
460}
461
462pub(crate) fn is_tuple_type_name(name: &str) -> bool {
465 name.starts_with('(')
466}
467
468pub fn core_type_path(typ: &TypeDef, core_import: &str) -> String {
470 let path = typ.rust_path.replace('-', "_");
473 if path.starts_with(core_import) {
475 path
476 } else {
477 format!("{core_import}::{}", typ.name)
479 }
480}
481
482pub fn has_sanitized_fields(typ: &TypeDef) -> bool {
484 typ.fields.iter().any(|f| f.sanitized)
485}
486
487pub fn core_enum_path(enum_def: &EnumDef, core_import: &str) -> String {
489 let path = enum_def.rust_path.replace('-', "_");
490 if path.starts_with(core_import) {
491 path
492 } else {
493 format!("{core_import}::{}", enum_def.name)
494 }
495}
496
497pub fn build_type_path_map(surface: &ApiSurface, core_import: &str) -> AHashMap<String, String> {
502 let mut map = AHashMap::new();
503 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
504 let path = typ.rust_path.replace('-', "_");
505 let resolved = if path.starts_with(core_import) {
506 path
507 } else {
508 format!("{core_import}::{}", typ.name)
509 };
510 map.insert(typ.name.clone(), resolved);
511 }
512 for en in &surface.enums {
513 let path = en.rust_path.replace('-', "_");
514 let resolved = if path.starts_with(core_import) {
515 path
516 } else {
517 format!("{core_import}::{}", en.name)
518 };
519 map.insert(en.name.clone(), resolved);
520 }
521 map
522}
523
524pub fn resolve_named_path(name: &str, core_import: &str, path_map: &AHashMap<String, String>) -> String {
529 if let Some(path) = path_map.get(name) {
530 path.clone()
531 } else {
532 format!("{core_import}::{name}")
533 }
534}
535
536pub fn binding_to_core_match_arm(binding_prefix: &str, variant_name: &str, fields: &[FieldDef]) -> String {
540 binding_to_core_match_arm_ext(binding_prefix, variant_name, fields, false)
541}
542
543pub fn binding_to_core_match_arm_ext_cfg(
547 binding_prefix: &str,
548 variant_name: &str,
549 fields: &[FieldDef],
550 binding_has_data: bool,
551 config: &ConversionConfig,
552) -> String {
553 use super::binding_to_core::field_conversion_to_core_cfg;
554
555 if fields.is_empty() {
556 format!("{binding_prefix}::{variant_name} => Self::{variant_name},")
557 } else if !binding_has_data {
558 if is_tuple_variant(fields) {
560 let defaults: Vec<&str> = fields.iter().map(|_| "Default::default()").collect();
561 format!(
562 "{binding_prefix}::{variant_name} => Self::{variant_name}({}),",
563 defaults.join(", ")
564 )
565 } else {
566 let defaults: Vec<String> = fields
567 .iter()
568 .map(|f| format!("{}: Default::default()", f.name))
569 .collect();
570 format!(
571 "{binding_prefix}::{variant_name} => Self::{variant_name} {{ {} }},",
572 defaults.join(", ")
573 )
574 }
575 } else if is_tuple_variant(fields) {
576 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
577 let binding_pattern = field_names.join(", ");
578 let core_args: Vec<String> = fields
579 .iter()
580 .map(|f| {
581 let conv = field_conversion_to_core_cfg(&f.name, &f.ty, f.optional, config);
585 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
587 let expr = expr.replace(&format!("val.{}", f.name), &f.name);
588 expr.to_string()
589 } else {
590 conv
591 }
592 })
593 .collect();
594 format!(
595 "{binding_prefix}::{variant_name} {{ {binding_pattern} }} => Self::{variant_name}({}),",
596 core_args.join(", ")
597 )
598 } else {
599 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
600 let pattern = field_names.join(", ");
601 let core_fields: Vec<String> = fields
602 .iter()
603 .map(|f| {
604 let conv = field_conversion_to_core_cfg(&f.name, &f.ty, f.optional, config);
608 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
610 let expr = expr.replace(&format!("val.{}", f.name), &f.name);
611 format!("{}: {}", f.name, expr)
612 } else {
613 conv
614 }
615 })
616 .collect();
617 format!(
618 "{binding_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
619 core_fields.join(", ")
620 )
621 }
622}
623
624pub fn binding_to_core_match_arm_ext(
625 binding_prefix: &str,
626 variant_name: &str,
627 fields: &[FieldDef],
628 binding_has_data: bool,
629) -> String {
630 if fields.is_empty() {
631 format!("{binding_prefix}::{variant_name} => Self::{variant_name},")
632 } else if !binding_has_data {
633 if is_tuple_variant(fields) {
635 let defaults: Vec<&str> = fields.iter().map(|_| "Default::default()").collect();
636 format!(
637 "{binding_prefix}::{variant_name} => Self::{variant_name}({}),",
638 defaults.join(", ")
639 )
640 } else {
641 let defaults: Vec<String> = fields
642 .iter()
643 .map(|f| format!("{}: Default::default()", f.name))
644 .collect();
645 format!(
646 "{binding_prefix}::{variant_name} => Self::{variant_name} {{ {} }},",
647 defaults.join(", ")
648 )
649 }
650 } else if is_tuple_variant(fields) {
651 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
653 let binding_pattern = field_names.join(", ");
654 let core_args: Vec<String> = fields
656 .iter()
657 .map(|f| {
658 let name = &f.name;
659 let expr = if matches!(&f.ty, TypeRef::Named(_)) {
660 format!("{name}.into()")
661 } else if f.sanitized {
662 format!("serde_json::from_str(&{name}).unwrap_or_default()")
663 } else {
664 name.clone()
665 };
666 if f.is_boxed { format!("Box::new({expr})") } else { expr }
667 })
668 .collect();
669 format!(
670 "{binding_prefix}::{variant_name} {{ {binding_pattern} }} => Self::{variant_name}({}),",
671 core_args.join(", ")
672 )
673 } else {
674 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
676 let pattern = field_names.join(", ");
677 let core_fields: Vec<String> = fields
678 .iter()
679 .map(|f| {
680 if matches!(&f.ty, TypeRef::Named(_)) {
681 format!("{}: {}.into()", f.name, f.name)
682 } else if f.sanitized {
683 format!("{}: serde_json::from_str(&{}).unwrap_or_default()", f.name, f.name)
687 } else {
688 format!("{0}: {0}", f.name)
689 }
690 })
691 .collect();
692 format!(
693 "{binding_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
694 core_fields.join(", ")
695 )
696 }
697}
698
699pub fn core_to_binding_match_arm(core_prefix: &str, variant_name: &str, fields: &[FieldDef]) -> String {
703 core_to_binding_match_arm_ext(core_prefix, variant_name, fields, false)
704}
705
706pub fn core_to_binding_match_arm_ext_cfg(
710 core_prefix: &str,
711 variant_name: &str,
712 fields: &[FieldDef],
713 binding_has_data: bool,
714 config: &ConversionConfig,
715) -> String {
716 use super::core_to_binding::field_conversion_from_core_cfg;
717 use ahash::AHashSet;
718
719 if fields.is_empty() {
720 format!("{core_prefix}::{variant_name} => Self::{variant_name},")
721 } else if !binding_has_data {
722 if is_tuple_variant(fields) {
724 format!("{core_prefix}::{variant_name}(..) => Self::{variant_name},")
725 } else {
726 format!("{core_prefix}::{variant_name} {{ .. }} => Self::{variant_name},")
727 }
728 } else if is_tuple_variant(fields) {
729 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
730 let core_pattern = field_names.join(", ");
731 let binding_fields: Vec<String> = fields
732 .iter()
733 .map(|f| {
734 let conv =
738 field_conversion_from_core_cfg(&f.name, &f.ty, f.optional, f.sanitized, &AHashSet::new(), config);
739 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
741 let mut expr = expr.replace(&format!("val.{}", f.name), &f.name);
742 if f.is_boxed {
744 expr = expr.replace(&format!("{}.into()", f.name), &format!("(*{}).into()", f.name));
745 }
746 format!("{}: {}", f.name, expr)
747 } else {
748 conv
749 }
750 })
751 .collect();
752 format!(
753 "{core_prefix}::{variant_name}({core_pattern}) => Self::{variant_name} {{ {} }},",
754 binding_fields.join(", ")
755 )
756 } else {
757 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
758 let pattern = field_names.join(", ");
759 let binding_fields: Vec<String> = fields
760 .iter()
761 .map(|f| {
762 let conv =
766 field_conversion_from_core_cfg(&f.name, &f.ty, f.optional, f.sanitized, &AHashSet::new(), config);
767 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
769 let expr = expr.replace(&format!("val.{}", f.name), &f.name);
770 format!("{}: {}", f.name, expr)
771 } else {
772 conv
773 }
774 })
775 .collect();
776 format!(
777 "{core_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
778 binding_fields.join(", ")
779 )
780 }
781}
782
783pub fn core_to_binding_match_arm_ext(
784 core_prefix: &str,
785 variant_name: &str,
786 fields: &[FieldDef],
787 binding_has_data: bool,
788) -> String {
789 if fields.is_empty() {
790 format!("{core_prefix}::{variant_name} => Self::{variant_name},")
791 } else if !binding_has_data {
792 if is_tuple_variant(fields) {
794 format!("{core_prefix}::{variant_name}(..) => Self::{variant_name},")
795 } else {
796 format!("{core_prefix}::{variant_name} {{ .. }} => Self::{variant_name},")
797 }
798 } else if is_tuple_variant(fields) {
799 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
801 let core_pattern = field_names.join(", ");
802 let binding_fields: Vec<String> = fields
804 .iter()
805 .map(|f| {
806 let name = &f.name;
807 let expr = if f.is_boxed && matches!(&f.ty, TypeRef::Named(_)) {
808 format!("(*{name}).into()")
809 } else if f.is_boxed {
810 format!("*{name}")
811 } else if matches!(&f.ty, TypeRef::Named(_)) {
812 format!("{name}.into()")
813 } else if f.sanitized {
814 format!("serde_json::to_string(&{name}).unwrap_or_default()")
815 } else {
816 name.clone()
817 };
818 format!("{name}: {expr}")
819 })
820 .collect();
821 format!(
822 "{core_prefix}::{variant_name}({core_pattern}) => Self::{variant_name} {{ {} }},",
823 binding_fields.join(", ")
824 )
825 } else {
826 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
827 let pattern = field_names.join(", ");
828 let binding_fields: Vec<String> = fields
829 .iter()
830 .map(|f| {
831 if matches!(&f.ty, TypeRef::Named(_)) {
832 format!("{}: {}.into()", f.name, f.name)
833 } else if f.sanitized {
834 format!("{}: serde_json::to_string(&{}).unwrap_or_default()", f.name, f.name)
838 } else {
839 format!("{0}: {0}", f.name)
840 }
841 })
842 .collect();
843 format!(
844 "{core_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
845 binding_fields.join(", ")
846 )
847 }
848}