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 for e in &surface.enums {
64 if names.contains(&e.name) {
65 for variant in &e.variants {
66 for field in &variant.fields {
67 collect_named_types(&field.ty, &mut names);
68 }
69 }
70 }
71 }
72
73 let mut changed = true;
75 while changed {
76 changed = false;
77 let snapshot: Vec<String> = names.iter().cloned().collect();
78 for name in &snapshot {
79 if let Some(typ) = surface.types.iter().find(|t| t.name == *name) {
80 for field in &typ.fields {
81 let mut field_names = AHashSet::new();
82 collect_named_types(&field.ty, &mut field_names);
83 for n in field_names {
84 if names.insert(n) {
85 changed = true;
86 }
87 }
88 }
89 }
90 if let Some(e) = surface.enums.iter().find(|e| e.name == *name) {
92 for variant in &e.variants {
93 for field in &variant.fields {
94 let mut field_names = AHashSet::new();
95 collect_named_types(&field.ty, &mut field_names);
96 for n in field_names {
97 if names.insert(n) {
98 changed = true;
99 }
100 }
101 }
102 }
103 }
104 }
105 }
106
107 names
108}
109
110fn collect_named_types(ty: &TypeRef, out: &mut AHashSet<String>) {
112 match ty {
113 TypeRef::Named(name) => {
114 out.insert(name.clone());
115 }
116 TypeRef::Optional(inner) | TypeRef::Vec(inner) => collect_named_types(inner, out),
117 TypeRef::Map(k, v) => {
118 collect_named_types(k, out);
119 collect_named_types(v, out);
120 }
121 _ => {}
122 }
123}
124
125pub fn field_references_excluded_type(ty: &TypeRef, exclude_types: &[String]) -> bool {
129 match ty {
130 TypeRef::Named(name) => exclude_types.iter().any(|e| e == name),
131 TypeRef::Optional(inner) | TypeRef::Vec(inner) => field_references_excluded_type(inner, exclude_types),
132 TypeRef::Map(k, v) => {
133 field_references_excluded_type(k, exclude_types) || field_references_excluded_type(v, exclude_types)
134 }
135 _ => false,
136 }
137}
138
139pub(crate) fn needs_i64_cast(p: &PrimitiveType) -> bool {
141 matches!(p, PrimitiveType::U64 | PrimitiveType::Usize | PrimitiveType::Isize)
142}
143
144pub(crate) fn needs_i32_cast(p: &PrimitiveType) -> bool {
146 matches!(
147 p,
148 PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32 | PrimitiveType::I8 | PrimitiveType::I16
149 )
150}
151
152pub(crate) fn needs_f64_cast(p: &PrimitiveType) -> bool {
158 matches!(
159 p,
160 PrimitiveType::U64 | PrimitiveType::I64 | PrimitiveType::Usize | PrimitiveType::Isize | PrimitiveType::F32
161 )
162}
163
164pub(crate) fn core_prim_str(p: &PrimitiveType) -> &'static str {
166 match p {
167 PrimitiveType::U64 => "u64",
168 PrimitiveType::Usize => "usize",
169 PrimitiveType::Isize => "isize",
170 PrimitiveType::F32 => "f32",
171 PrimitiveType::Bool => "bool",
172 PrimitiveType::U8 => "u8",
173 PrimitiveType::U16 => "u16",
174 PrimitiveType::U32 => "u32",
175 PrimitiveType::I8 => "i8",
176 PrimitiveType::I16 => "i16",
177 PrimitiveType::I32 => "i32",
178 PrimitiveType::I64 => "i64",
179 PrimitiveType::F64 => "f64",
180 }
181}
182
183pub(crate) fn binding_prim_str(p: &PrimitiveType) -> &'static str {
185 match p {
186 PrimitiveType::U64 | PrimitiveType::Usize | PrimitiveType::Isize => "i64",
187 PrimitiveType::F32 => "f64",
188 PrimitiveType::Bool => "bool",
189 PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32 => "i32",
190 PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 => "i32",
191 PrimitiveType::I64 => "i64",
192 PrimitiveType::F64 => "f64",
193 }
194}
195
196pub fn core_to_binding_convertible_types(surface: &ApiSurface) -> AHashSet<String> {
200 let convertible_enums: AHashSet<&str> = surface
201 .enums
202 .iter()
203 .filter(|e| can_generate_enum_conversion_from_core(e))
204 .map(|e| e.name.as_str())
205 .collect();
206
207 let opaque_type_names: AHashSet<&str> = surface
208 .types
209 .iter()
210 .filter(|t| t.is_opaque)
211 .map(|t| t.name.as_str())
212 .collect();
213
214 let data_enum_names: AHashSet<&str> = surface
219 .enums
220 .iter()
221 .filter(|e| e.variants.iter().any(|v| !v.fields.is_empty()))
222 .map(|e| e.name.as_str())
223 .collect();
224
225 let (enum_paths, type_paths) = build_rust_path_maps(surface);
227
228 let mut convertible: AHashSet<String> = surface
230 .types
231 .iter()
232 .filter(|t| !t.is_opaque)
233 .map(|t| t.name.clone())
234 .collect();
235
236 let mut changed = true;
237 while changed {
238 changed = false;
239 let snapshot: Vec<String> = convertible.iter().cloned().collect();
240 let mut known: AHashSet<&str> = convertible.iter().map(|s| s.as_str()).collect();
241 known.extend(&opaque_type_names);
242 known.extend(&data_enum_names);
243 let mut to_remove = Vec::new();
244 for type_name in &snapshot {
245 if let Some(typ) = surface.types.iter().find(|t| t.name == *type_name) {
246 let ok = typ.fields.iter().all(|f| {
247 if f.sanitized {
248 true
249 } else if field_has_path_mismatch(f, &enum_paths, &type_paths) {
250 false
251 } else {
252 is_field_convertible(&f.ty, &convertible_enums, &known)
253 }
254 });
255 if !ok {
256 to_remove.push(type_name.clone());
257 }
258 }
259 }
260 for name in to_remove {
261 if convertible.remove(&name) {
262 changed = true;
263 }
264 }
265 }
266 convertible
267}
268
269pub fn convertible_types(surface: &ApiSurface) -> AHashSet<String> {
274 let convertible_enums: AHashSet<&str> = surface
276 .enums
277 .iter()
278 .filter(|e| can_generate_enum_conversion(e))
279 .map(|e| e.name.as_str())
280 .collect();
281
282 let _all_type_names: AHashSet<&str> = surface.types.iter().map(|t| t.name.as_str()).collect();
285
286 let default_type_names: AHashSet<&str> = surface
289 .types
290 .iter()
291 .filter(|t| t.has_default)
292 .map(|t| t.name.as_str())
293 .collect();
294
295 let mut convertible: AHashSet<String> = surface
299 .types
300 .iter()
301 .filter(|t| !t.is_opaque)
302 .map(|t| t.name.clone())
303 .collect();
304
305 let opaque_type_names: AHashSet<&str> = surface
308 .types
309 .iter()
310 .filter(|t| t.is_opaque)
311 .map(|t| t.name.as_str())
312 .collect();
313
314 let data_enum_names: AHashSet<&str> = surface
317 .enums
318 .iter()
319 .filter(|e| e.variants.iter().any(|v| !v.fields.is_empty()))
320 .map(|e| e.name.as_str())
321 .collect();
322
323 let (enum_paths, type_paths) = build_rust_path_maps(surface);
325
326 let mut changed = true;
331 while changed {
332 changed = false;
333 let snapshot: Vec<String> = convertible.iter().cloned().collect();
334 let mut known: AHashSet<&str> = convertible.iter().map(|s| s.as_str()).collect();
335 known.extend(&opaque_type_names);
336 known.extend(&data_enum_names);
337 let mut to_remove = Vec::new();
338 for type_name in &snapshot {
339 if let Some(typ) = surface.types.iter().find(|t| t.name == *type_name) {
340 let ok = typ.fields.iter().all(|f| {
341 if f.sanitized {
342 sanitized_field_has_default(&f.ty, &default_type_names)
343 } else if field_has_path_mismatch(f, &enum_paths, &type_paths) {
344 false
345 } else {
346 is_field_convertible(&f.ty, &convertible_enums, &known)
347 }
348 });
349 if !ok {
350 to_remove.push(type_name.clone());
351 }
352 }
353 }
354 for name in to_remove {
355 if convertible.remove(&name) {
356 changed = true;
357 }
358 }
359 }
360 convertible
361}
362
363fn sanitized_field_has_default(ty: &TypeRef, default_types: &AHashSet<&str>) -> bool {
368 match ty {
369 TypeRef::Primitive(_)
370 | TypeRef::String
371 | TypeRef::Char
372 | TypeRef::Bytes
373 | TypeRef::Path
374 | TypeRef::Unit
375 | TypeRef::Duration
376 | TypeRef::Json => true,
377 TypeRef::Optional(_) => true,
379 TypeRef::Vec(_) => true,
381 TypeRef::Map(_, _) => true,
383 TypeRef::Named(name) => {
384 if is_tuple_type_name(name) {
385 true
387 } else {
388 default_types.contains(name.as_str())
390 }
391 }
392 }
393}
394
395pub fn can_generate_conversion(typ: &TypeDef, convertible: &AHashSet<String>) -> bool {
397 convertible.contains(&typ.name)
398}
399
400pub(crate) fn is_field_convertible(
401 ty: &TypeRef,
402 convertible_enums: &AHashSet<&str>,
403 known_types: &AHashSet<&str>,
404) -> bool {
405 match ty {
406 TypeRef::Primitive(_)
407 | TypeRef::String
408 | TypeRef::Char
409 | TypeRef::Bytes
410 | TypeRef::Path
411 | TypeRef::Unit
412 | TypeRef::Duration => true,
413 TypeRef::Json => true,
414 TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_field_convertible(inner, convertible_enums, known_types),
415 TypeRef::Map(k, v) => {
416 is_field_convertible(k, convertible_enums, known_types)
417 && is_field_convertible(v, convertible_enums, known_types)
418 }
419 TypeRef::Named(name) if is_tuple_type_name(name) => true,
421 TypeRef::Named(name) => convertible_enums.contains(name.as_str()) || known_types.contains(name.as_str()),
423 }
424}
425
426fn field_has_path_mismatch(
432 field: &FieldDef,
433 enum_rust_paths: &AHashMap<&str, &str>,
434 type_rust_paths: &AHashMap<&str, &str>,
435) -> bool {
436 let name = match &field.ty {
437 TypeRef::Named(n) => n.as_str(),
438 TypeRef::Optional(inner) | TypeRef::Vec(inner) => match inner.as_ref() {
439 TypeRef::Named(n) => n.as_str(),
440 _ => return false,
441 },
442 _ => return false,
443 };
444
445 if let Some(field_path) = &field.type_rust_path {
446 if let Some(enum_path) = enum_rust_paths.get(name) {
447 if !paths_compatible(field_path, enum_path) {
448 return true;
449 }
450 }
451 if let Some(type_path) = type_rust_paths.get(name) {
452 if !paths_compatible(field_path, type_path) {
453 return true;
454 }
455 }
456 }
457 false
458}
459
460fn paths_compatible(a: &str, b: &str) -> bool {
465 if a == b {
466 return true;
467 }
468 let a_norm = a.replace('-', "_");
471 let b_norm = b.replace('-', "_");
472 if a_norm == b_norm {
473 return true;
474 }
475 if a_norm.ends_with(&b_norm) || b_norm.ends_with(&a_norm) {
477 return true;
478 }
479 let a_root = a_norm.split("::").next().unwrap_or("");
481 let b_root = b_norm.split("::").next().unwrap_or("");
482 let a_name = a_norm.rsplit("::").next().unwrap_or("");
483 let b_name = b_norm.rsplit("::").next().unwrap_or("");
484 a_root == b_root && a_name == b_name
485}
486
487fn build_rust_path_maps(surface: &ApiSurface) -> (AHashMap<&str, &str>, AHashMap<&str, &str>) {
489 let enum_paths: AHashMap<&str, &str> = surface
490 .enums
491 .iter()
492 .map(|e| (e.name.as_str(), e.rust_path.as_str()))
493 .collect();
494 let type_paths: AHashMap<&str, &str> = surface
495 .types
496 .iter()
497 .map(|t| (t.name.as_str(), t.rust_path.as_str()))
498 .collect();
499 (enum_paths, type_paths)
500}
501
502pub fn can_generate_enum_conversion(enum_def: &EnumDef) -> bool {
506 !enum_def.variants.is_empty()
507}
508
509pub fn can_generate_enum_conversion_from_core(enum_def: &EnumDef) -> bool {
512 !enum_def.variants.is_empty()
514}
515
516pub fn is_tuple_variant(fields: &[FieldDef]) -> bool {
518 !fields.is_empty()
519 && fields[0]
520 .name
521 .strip_prefix('_')
522 .is_some_and(|rest: &str| rest.chars().all(|c: char| c.is_ascii_digit()))
523}
524
525pub fn is_newtype(typ: &TypeDef) -> bool {
527 typ.fields.len() == 1 && typ.fields[0].name == "_0"
528}
529
530pub(crate) fn is_tuple_type_name(name: &str) -> bool {
533 name.starts_with('(')
534}
535
536pub fn core_type_path(typ: &TypeDef, core_import: &str) -> String {
542 core_type_path_remapped(typ, core_import, &[])
543}
544
545pub fn core_type_path_remapped(typ: &TypeDef, core_import: &str, remaps: &[(&str, &str)]) -> String {
554 let path = typ.rust_path.replace('-', "_");
559 if path.contains("::") {
560 apply_crate_remaps(&path, remaps)
561 } else {
562 format!("{core_import}::{}", typ.name)
563 }
564}
565
566pub fn apply_crate_remaps(path: &str, remaps: &[(&str, &str)]) -> String {
572 if remaps.is_empty() {
573 return path.to_string();
574 }
575 if let Some(sep) = path.find("::") {
576 let leading = &path[..sep];
577 if let Some(&(_, override_crate)) = remaps.iter().find(|(orig, _)| *orig == leading) {
578 return format!("{override_crate}{}", &path[sep..]);
579 }
580 }
581 path.to_string()
582}
583
584pub fn has_sanitized_fields(typ: &TypeDef) -> bool {
586 typ.fields.iter().any(|f| f.sanitized)
587}
588
589pub fn core_enum_path(enum_def: &EnumDef, core_import: &str) -> String {
591 core_enum_path_remapped(enum_def, core_import, &[])
592}
593
594pub fn core_enum_path_remapped(enum_def: &EnumDef, core_import: &str, remaps: &[(&str, &str)]) -> String {
597 let path = enum_def.rust_path.replace('-', "_");
598 if path.starts_with(core_import) || path.contains("::") {
599 apply_crate_remaps(&path, remaps)
601 } else {
602 format!("{core_import}::{}", enum_def.name)
604 }
605}
606
607pub fn build_type_path_map(surface: &ApiSurface, core_import: &str) -> AHashMap<String, String> {
612 let mut map = AHashMap::new();
613 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
614 let path = typ.rust_path.replace('-', "_");
615 let resolved = if path.starts_with(core_import) {
616 path
617 } else {
618 format!("{core_import}::{}", typ.name)
619 };
620 map.insert(typ.name.clone(), resolved);
621 }
622 for en in &surface.enums {
623 let path = en.rust_path.replace('-', "_");
624 let resolved = if path.starts_with(core_import) {
625 path
626 } else {
627 format!("{core_import}::{}", en.name)
628 };
629 map.insert(en.name.clone(), resolved);
630 }
631 map
632}
633
634pub fn resolve_named_path(name: &str, core_import: &str, path_map: &AHashMap<String, String>) -> String {
639 if let Some(path) = path_map.get(name) {
640 path.clone()
641 } else {
642 format!("{core_import}::{name}")
643 }
644}
645
646pub fn binding_to_core_match_arm(binding_prefix: &str, variant_name: &str, fields: &[FieldDef]) -> String {
650 binding_to_core_match_arm_ext(binding_prefix, variant_name, fields, false)
651}
652
653pub fn binding_to_core_match_arm_ext_cfg(
657 binding_prefix: &str,
658 variant_name: &str,
659 fields: &[FieldDef],
660 binding_has_data: bool,
661 config: &ConversionConfig,
662) -> String {
663 use super::binding_to_core::field_conversion_to_core_cfg;
664
665 if fields.is_empty() {
666 format!("{binding_prefix}::{variant_name} => Self::{variant_name},")
667 } else if !binding_has_data {
668 if is_tuple_variant(fields) {
670 let defaults: Vec<&str> = fields.iter().map(|_| "Default::default()").collect();
671 format!(
672 "{binding_prefix}::{variant_name} => Self::{variant_name}({}),",
673 defaults.join(", ")
674 )
675 } else {
676 let defaults: Vec<String> = fields
677 .iter()
678 .map(|f| format!("{}: Default::default()", f.name))
679 .collect();
680 format!(
681 "{binding_prefix}::{variant_name} => Self::{variant_name} {{ {} }},",
682 defaults.join(", ")
683 )
684 }
685 } else if is_tuple_variant(fields) {
686 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
687 let binding_pattern = field_names.join(", ");
688 let core_args: Vec<String> = fields
689 .iter()
690 .map(|f| {
691 let name = &f.name;
692 if f.sanitized {
694 let expr = if let TypeRef::Vec(_) = &f.ty {
695 format!("{name}.iter().filter_map(|s| serde_json::from_str(s).ok()).collect()")
698 } else {
699 format!("serde_json::from_str(&{name}).unwrap_or_default()")
700 };
701 return if f.is_boxed { format!("Box::new({expr})") } else { expr };
702 }
703 if !config.exclude_types.is_empty() && field_references_excluded_type(&f.ty, config.exclude_types) {
706 let expr = format!("serde_json::from_str(&{name}).unwrap_or_default()");
707 return if f.is_boxed { format!("Box::new({expr})") } else { expr };
708 }
709 let conv = field_conversion_to_core_cfg(name, &f.ty, f.optional, config);
713 let expr = if let Some(expr) = conv.strip_prefix(&format!("{name}: ")) {
715 let expr = expr.replace(&format!("val.{name}"), name);
716 expr.to_string()
717 } else {
718 conv
719 };
720 if f.is_boxed { format!("Box::new({expr})") } else { expr }
721 })
722 .collect();
723 format!(
724 "{binding_prefix}::{variant_name} {{ {binding_pattern} }} => Self::{variant_name}({}),",
725 core_args.join(", ")
726 )
727 } else {
728 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
729 let pattern = field_names.join(", ");
730 let core_fields: Vec<String> = fields
731 .iter()
732 .map(|f| {
733 if f.sanitized {
736 if let TypeRef::Vec(_) = &f.ty {
740 return format!(
741 "{}: {}.iter().filter_map(|s| serde_json::from_str(s).ok()).collect()",
742 f.name, f.name
743 );
744 }
745 return format!("{}: serde_json::from_str(&{}).unwrap_or_default()", f.name, f.name);
746 }
747 let conv = field_conversion_to_core_cfg(&f.name, &f.ty, f.optional, config);
751 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
753 let expr = expr.replace(&format!("val.{}", f.name), &f.name);
754 format!("{}: {}", f.name, expr)
755 } else {
756 conv
757 }
758 })
759 .collect();
760 format!(
761 "{binding_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
762 core_fields.join(", ")
763 )
764 }
765}
766
767pub fn binding_to_core_match_arm_ext(
768 binding_prefix: &str,
769 variant_name: &str,
770 fields: &[FieldDef],
771 binding_has_data: bool,
772) -> String {
773 if fields.is_empty() {
774 format!("{binding_prefix}::{variant_name} => Self::{variant_name},")
775 } else if !binding_has_data {
776 if is_tuple_variant(fields) {
778 let defaults: Vec<&str> = fields.iter().map(|_| "Default::default()").collect();
779 format!(
780 "{binding_prefix}::{variant_name} => Self::{variant_name}({}),",
781 defaults.join(", ")
782 )
783 } else {
784 let defaults: Vec<String> = fields
785 .iter()
786 .map(|f| format!("{}: Default::default()", f.name))
787 .collect();
788 format!(
789 "{binding_prefix}::{variant_name} => Self::{variant_name} {{ {} }},",
790 defaults.join(", ")
791 )
792 }
793 } else if is_tuple_variant(fields) {
794 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
796 let binding_pattern = field_names.join(", ");
797 let core_args: Vec<String> = fields
799 .iter()
800 .map(|f| {
801 let name = &f.name;
802 let expr = if matches!(&f.ty, TypeRef::Named(_)) {
803 format!("{name}.into()")
804 } else if f.sanitized {
805 format!("serde_json::from_str(&{name}).unwrap_or_default()")
806 } else {
807 name.clone()
808 };
809 if f.is_boxed { format!("Box::new({expr})") } else { expr }
810 })
811 .collect();
812 format!(
813 "{binding_prefix}::{variant_name} {{ {binding_pattern} }} => Self::{variant_name}({}),",
814 core_args.join(", ")
815 )
816 } else {
817 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
819 let pattern = field_names.join(", ");
820 let core_fields: Vec<String> = fields
821 .iter()
822 .map(|f| {
823 if matches!(&f.ty, TypeRef::Named(_)) {
824 format!("{}: {}.into()", f.name, f.name)
825 } else if f.sanitized {
826 format!("{}: serde_json::from_str(&{}).unwrap_or_default()", f.name, f.name)
830 } else {
831 format!("{0}: {0}", f.name)
832 }
833 })
834 .collect();
835 format!(
836 "{binding_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
837 core_fields.join(", ")
838 )
839 }
840}
841
842pub fn core_to_binding_match_arm(core_prefix: &str, variant_name: &str, fields: &[FieldDef]) -> String {
846 core_to_binding_match_arm_ext(core_prefix, variant_name, fields, false)
847}
848
849pub fn core_to_binding_match_arm_ext_cfg(
853 core_prefix: &str,
854 variant_name: &str,
855 fields: &[FieldDef],
856 binding_has_data: bool,
857 config: &ConversionConfig,
858) -> String {
859 use super::core_to_binding::field_conversion_from_core_cfg;
860 use ahash::AHashSet;
861
862 if fields.is_empty() {
863 format!("{core_prefix}::{variant_name} => Self::{variant_name},")
864 } else if !binding_has_data {
865 if is_tuple_variant(fields) {
867 format!("{core_prefix}::{variant_name}(..) => Self::{variant_name},")
868 } else {
869 format!("{core_prefix}::{variant_name} {{ .. }} => Self::{variant_name},")
870 }
871 } else if is_tuple_variant(fields) {
872 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
873 let core_pattern = field_names.join(", ");
874 let binding_fields: Vec<String> = fields
875 .iter()
876 .map(|f| {
877 let conv =
881 field_conversion_from_core_cfg(&f.name, &f.ty, f.optional, f.sanitized, &AHashSet::new(), config);
882 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
884 let mut expr = expr.replace(&format!("val.{}", f.name), &f.name);
885 if f.is_boxed {
887 expr = expr.replace(&format!("{}.into()", f.name), &format!("(*{}).into()", f.name));
888 }
889 format!("{}: {}", f.name, expr)
890 } else {
891 conv
892 }
893 })
894 .collect();
895 format!(
896 "{core_prefix}::{variant_name}({core_pattern}) => Self::{variant_name} {{ {} }},",
897 binding_fields.join(", ")
898 )
899 } else {
900 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
901 let pattern = field_names.join(", ");
902 let binding_fields: Vec<String> = fields
903 .iter()
904 .map(|f| {
905 let conv =
909 field_conversion_from_core_cfg(&f.name, &f.ty, f.optional, f.sanitized, &AHashSet::new(), config);
910 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
912 let expr = expr.replace(&format!("val.{}", f.name), &f.name);
913 format!("{}: {}", f.name, expr)
914 } else {
915 conv
916 }
917 })
918 .collect();
919 format!(
920 "{core_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
921 binding_fields.join(", ")
922 )
923 }
924}
925
926pub fn core_to_binding_match_arm_ext(
927 core_prefix: &str,
928 variant_name: &str,
929 fields: &[FieldDef],
930 binding_has_data: bool,
931) -> String {
932 if fields.is_empty() {
933 format!("{core_prefix}::{variant_name} => Self::{variant_name},")
934 } else if !binding_has_data {
935 if is_tuple_variant(fields) {
937 format!("{core_prefix}::{variant_name}(..) => Self::{variant_name},")
938 } else {
939 format!("{core_prefix}::{variant_name} {{ .. }} => Self::{variant_name},")
940 }
941 } else if is_tuple_variant(fields) {
942 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
944 let core_pattern = field_names.join(", ");
945 let binding_fields: Vec<String> = fields
947 .iter()
948 .map(|f| {
949 let name = &f.name;
950 let expr = if f.is_boxed && matches!(&f.ty, TypeRef::Named(_)) {
951 format!("(*{name}).into()")
952 } else if f.is_boxed {
953 format!("*{name}")
954 } else if matches!(&f.ty, TypeRef::Named(_)) {
955 format!("{name}.into()")
956 } else if f.sanitized {
957 format!("serde_json::to_string(&{name}).unwrap_or_default()")
958 } else {
959 name.clone()
960 };
961 format!("{name}: {expr}")
962 })
963 .collect();
964 format!(
965 "{core_prefix}::{variant_name}({core_pattern}) => Self::{variant_name} {{ {} }},",
966 binding_fields.join(", ")
967 )
968 } else {
969 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
970 let pattern = field_names.join(", ");
971 let binding_fields: Vec<String> = fields
972 .iter()
973 .map(|f| {
974 if matches!(&f.ty, TypeRef::Named(_)) {
975 format!("{}: {}.into()", f.name, f.name)
976 } else if f.sanitized {
977 format!("{}: serde_json::to_string(&{}).unwrap_or_default()", f.name, f.name)
981 } else {
982 format!("{0}: {0}", f.name)
983 }
984 })
985 .collect();
986 format!(
987 "{core_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
988 binding_fields.join(", ")
989 )
990 }
991}