1use ahash::{AHashMap, AHashSet};
2use alef_core::ir::{ApiSurface, EnumDef, FieldDef, PrimitiveType, TypeDef, TypeRef};
3
4use crate::conversions::ConversionConfig;
5use crate::shared::binding_fields;
6
7pub fn input_type_names(surface: &ApiSurface) -> AHashSet<String> {
20 let mut names = AHashSet::new();
21
22 for func in &surface.functions {
24 for param in &func.params {
25 collect_named_types(¶m.ty, &mut names);
26 }
27 }
28 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
30 for method in &typ.methods {
31 for param in &method.params {
32 collect_named_types(¶m.ty, &mut names);
33 }
34 }
35 }
36 for func in &surface.functions {
40 collect_named_types(&func.return_type, &mut names);
41 }
42 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
44 for method in &typ.methods {
45 collect_named_types(&method.return_type, &mut names);
46 }
47 }
48 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
53 if !typ.is_opaque && !typ.methods.is_empty() {
54 for field in binding_fields(&typ.fields) {
55 if !field.sanitized {
56 collect_named_types(&field.ty, &mut names);
57 }
58 }
59 }
60 }
61
62 for e in &surface.enums {
65 if names.contains(&e.name) {
66 for variant in &e.variants {
67 for field in &variant.fields {
68 collect_named_types(&field.ty, &mut names);
69 }
70 }
71 }
72 }
73
74 let mut changed = true;
76 while changed {
77 changed = false;
78 let snapshot: Vec<String> = names.iter().cloned().collect();
79 for name in &snapshot {
80 if let Some(typ) = surface.types.iter().find(|t| t.name == *name) {
81 for field in binding_fields(&typ.fields) {
82 let mut field_names = AHashSet::new();
83 collect_named_types(&field.ty, &mut field_names);
84 for n in field_names {
85 if names.insert(n) {
86 changed = true;
87 }
88 }
89 }
90 }
91 if let Some(e) = surface.enums.iter().find(|e| e.name == *name) {
93 for variant in &e.variants {
94 for field in &variant.fields {
95 let mut field_names = AHashSet::new();
96 collect_named_types(&field.ty, &mut field_names);
97 for n in field_names {
98 if names.insert(n) {
99 changed = true;
100 }
101 }
102 }
103 }
104 }
105 }
106 }
107
108 names
109}
110
111fn collect_named_types(ty: &TypeRef, out: &mut AHashSet<String>) {
113 match ty {
114 TypeRef::Named(name) => {
115 out.insert(name.clone());
116 }
117 TypeRef::Optional(inner) | TypeRef::Vec(inner) => collect_named_types(inner, out),
118 TypeRef::Map(k, v) => {
119 collect_named_types(k, out);
120 collect_named_types(v, out);
121 }
122 _ => {}
123 }
124}
125
126pub fn field_references_excluded_type(ty: &TypeRef, exclude_types: &[String]) -> bool {
130 match ty {
131 TypeRef::Named(name) => exclude_types.iter().any(|e| e == name),
132 TypeRef::Optional(inner) | TypeRef::Vec(inner) => field_references_excluded_type(inner, exclude_types),
133 TypeRef::Map(k, v) => {
134 field_references_excluded_type(k, exclude_types) || field_references_excluded_type(v, exclude_types)
135 }
136 _ => false,
137 }
138}
139
140pub(crate) fn needs_i64_cast(p: &PrimitiveType) -> bool {
142 matches!(p, PrimitiveType::U64 | PrimitiveType::Usize | PrimitiveType::Isize)
143}
144
145pub(crate) fn needs_i32_cast(p: &PrimitiveType) -> bool {
147 matches!(
148 p,
149 PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32 | PrimitiveType::I8 | PrimitiveType::I16
150 )
151}
152
153pub(crate) fn needs_f64_cast(p: &PrimitiveType) -> bool {
159 matches!(
160 p,
161 PrimitiveType::U64 | PrimitiveType::I64 | PrimitiveType::Usize | PrimitiveType::Isize | PrimitiveType::F32
162 )
163}
164
165pub(crate) fn core_prim_str(p: &PrimitiveType) -> &'static str {
167 match p {
168 PrimitiveType::U64 => "u64",
169 PrimitiveType::Usize => "usize",
170 PrimitiveType::Isize => "isize",
171 PrimitiveType::F32 => "f32",
172 PrimitiveType::Bool => "bool",
173 PrimitiveType::U8 => "u8",
174 PrimitiveType::U16 => "u16",
175 PrimitiveType::U32 => "u32",
176 PrimitiveType::I8 => "i8",
177 PrimitiveType::I16 => "i16",
178 PrimitiveType::I32 => "i32",
179 PrimitiveType::I64 => "i64",
180 PrimitiveType::F64 => "f64",
181 }
182}
183
184pub(crate) fn binding_prim_str(p: &PrimitiveType) -> &'static str {
186 match p {
187 PrimitiveType::U64 | PrimitiveType::Usize | PrimitiveType::Isize => "i64",
188 PrimitiveType::F32 => "f64",
189 PrimitiveType::Bool => "bool",
190 PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32 => "i32",
191 PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 => "i32",
192 PrimitiveType::I64 => "i64",
193 PrimitiveType::F64 => "f64",
194 }
195}
196
197pub fn core_to_binding_convertible_types(surface: &ApiSurface) -> AHashSet<String> {
201 let convertible_enums: AHashSet<&str> = surface
202 .enums
203 .iter()
204 .filter(|e| can_generate_enum_conversion_from_core(e))
205 .map(|e| e.name.as_str())
206 .collect();
207
208 let opaque_type_names: AHashSet<&str> = surface
209 .types
210 .iter()
211 .filter(|t| t.is_opaque)
212 .map(|t| t.name.as_str())
213 .collect();
214
215 let data_enum_names: AHashSet<&str> = surface
220 .enums
221 .iter()
222 .filter(|e| e.variants.iter().any(|v| !v.fields.is_empty()))
223 .map(|e| e.name.as_str())
224 .collect();
225
226 let (enum_paths, type_paths) = build_rust_path_maps(surface);
228
229 let mut convertible: AHashSet<String> = surface
231 .types
232 .iter()
233 .filter(|t| !t.is_opaque)
234 .map(|t| t.name.clone())
235 .collect();
236
237 let mut changed = true;
238 while changed {
239 changed = false;
240 let snapshot: Vec<String> = convertible.iter().cloned().collect();
241 let mut known: AHashSet<&str> = convertible.iter().map(|s| s.as_str()).collect();
242 known.extend(&opaque_type_names);
243 known.extend(&data_enum_names);
244 let mut to_remove = Vec::new();
245 for type_name in &snapshot {
246 if let Some(typ) = surface.types.iter().find(|t| t.name == *type_name) {
247 let ok = binding_fields(&typ.fields).all(|f| {
248 if f.sanitized {
249 true
250 } else if field_has_path_mismatch(f, &enum_paths, &type_paths) {
251 false
252 } else {
253 is_field_convertible(&f.ty, &convertible_enums, &known)
254 }
255 });
256 if !ok {
257 to_remove.push(type_name.clone());
258 }
259 }
260 }
261 for name in to_remove {
262 if convertible.remove(&name) {
263 changed = true;
264 }
265 }
266 }
267 convertible
268}
269
270pub fn convertible_types(surface: &ApiSurface) -> AHashSet<String> {
275 let convertible_enums: AHashSet<&str> = surface
277 .enums
278 .iter()
279 .filter(|e| can_generate_enum_conversion(e))
280 .map(|e| e.name.as_str())
281 .collect();
282
283 let _all_type_names: AHashSet<&str> = surface.types.iter().map(|t| t.name.as_str()).collect();
286
287 let default_type_names: AHashSet<&str> = surface
290 .types
291 .iter()
292 .filter(|t| t.has_default)
293 .map(|t| t.name.as_str())
294 .collect();
295
296 let mut convertible: AHashSet<String> = surface
300 .types
301 .iter()
302 .filter(|t| !t.is_opaque)
303 .map(|t| t.name.clone())
304 .collect();
305
306 let opaque_type_names: AHashSet<&str> = surface
309 .types
310 .iter()
311 .filter(|t| t.is_opaque)
312 .map(|t| t.name.as_str())
313 .collect();
314
315 let data_enum_names: AHashSet<&str> = surface
318 .enums
319 .iter()
320 .filter(|e| e.variants.iter().any(|v| !v.fields.is_empty()))
321 .map(|e| e.name.as_str())
322 .collect();
323
324 let (enum_paths, type_paths) = build_rust_path_maps(surface);
326
327 let mut changed = true;
332 while changed {
333 changed = false;
334 let snapshot: Vec<String> = convertible.iter().cloned().collect();
335 let mut known: AHashSet<&str> = convertible.iter().map(|s| s.as_str()).collect();
336 known.extend(&opaque_type_names);
337 known.extend(&data_enum_names);
338 let mut to_remove = Vec::new();
339 for type_name in &snapshot {
340 if let Some(typ) = surface.types.iter().find(|t| t.name == *type_name) {
341 let ok = binding_fields(&typ.fields).all(|f| {
342 if f.sanitized {
343 sanitized_field_has_default(&f.ty, &default_type_names)
344 } else if field_has_path_mismatch(f, &enum_paths, &type_paths) {
345 false
346 } else {
347 is_field_convertible(&f.ty, &convertible_enums, &known)
348 }
349 });
350 if !ok {
351 to_remove.push(type_name.clone());
352 }
353 }
354 }
355 for name in to_remove {
356 if convertible.remove(&name) {
357 changed = true;
358 }
359 }
360 }
361 convertible
362}
363
364fn sanitized_field_has_default(ty: &TypeRef, default_types: &AHashSet<&str>) -> bool {
369 match ty {
370 TypeRef::Primitive(_)
371 | TypeRef::String
372 | TypeRef::Char
373 | TypeRef::Bytes
374 | TypeRef::Path
375 | TypeRef::Unit
376 | TypeRef::Duration
377 | TypeRef::Json => true,
378 TypeRef::Optional(_) => true,
380 TypeRef::Vec(_) => true,
382 TypeRef::Map(_, _) => true,
384 TypeRef::Named(name) => {
385 if is_tuple_type_name(name) {
386 true
388 } else {
389 default_types.contains(name.as_str())
391 }
392 }
393 }
394}
395
396pub fn can_generate_conversion(typ: &TypeDef, convertible: &AHashSet<String>) -> bool {
398 convertible.contains(&typ.name)
399}
400
401pub(crate) fn is_field_convertible(
402 ty: &TypeRef,
403 convertible_enums: &AHashSet<&str>,
404 known_types: &AHashSet<&str>,
405) -> bool {
406 match ty {
407 TypeRef::Primitive(_)
408 | TypeRef::String
409 | TypeRef::Char
410 | TypeRef::Bytes
411 | TypeRef::Path
412 | TypeRef::Unit
413 | TypeRef::Duration => true,
414 TypeRef::Json => true,
415 TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_field_convertible(inner, convertible_enums, known_types),
416 TypeRef::Map(k, v) => {
417 is_field_convertible(k, convertible_enums, known_types)
418 && is_field_convertible(v, convertible_enums, known_types)
419 }
420 TypeRef::Named(name) if is_tuple_type_name(name) => true,
422 TypeRef::Named(name) => convertible_enums.contains(name.as_str()) || known_types.contains(name.as_str()),
424 }
425}
426
427fn field_has_path_mismatch(
433 field: &FieldDef,
434 enum_rust_paths: &AHashMap<&str, &str>,
435 type_rust_paths: &AHashMap<&str, &str>,
436) -> bool {
437 let name = match &field.ty {
438 TypeRef::Named(n) => n.as_str(),
439 TypeRef::Optional(inner) | TypeRef::Vec(inner) => match inner.as_ref() {
440 TypeRef::Named(n) => n.as_str(),
441 _ => return false,
442 },
443 _ => return false,
444 };
445
446 if let Some(field_path) = &field.type_rust_path {
447 if let Some(enum_path) = enum_rust_paths.get(name) {
448 if !paths_compatible(field_path, enum_path) {
449 return true;
450 }
451 }
452 if let Some(type_path) = type_rust_paths.get(name) {
453 if !paths_compatible(field_path, type_path) {
454 return true;
455 }
456 }
457 }
458 false
459}
460
461fn paths_compatible(a: &str, b: &str) -> bool {
466 if a == b {
467 return true;
468 }
469 let a_norm = a.replace('-', "_");
472 let b_norm = b.replace('-', "_");
473 if a_norm == b_norm {
474 return true;
475 }
476 if a_norm.ends_with(&b_norm) || b_norm.ends_with(&a_norm) {
478 return true;
479 }
480 let a_root = a_norm.split("::").next().unwrap_or("");
482 let b_root = b_norm.split("::").next().unwrap_or("");
483 let a_name = a_norm.rsplit("::").next().unwrap_or("");
484 let b_name = b_norm.rsplit("::").next().unwrap_or("");
485 a_root == b_root && a_name == b_name
486}
487
488fn build_rust_path_maps(surface: &ApiSurface) -> (AHashMap<&str, &str>, AHashMap<&str, &str>) {
490 let enum_paths: AHashMap<&str, &str> = surface
491 .enums
492 .iter()
493 .map(|e| (e.name.as_str(), e.rust_path.as_str()))
494 .collect();
495 let type_paths: AHashMap<&str, &str> = surface
496 .types
497 .iter()
498 .map(|t| (t.name.as_str(), t.rust_path.as_str()))
499 .collect();
500 (enum_paths, type_paths)
501}
502
503pub fn can_generate_enum_conversion(enum_def: &EnumDef) -> bool {
507 !enum_def.variants.is_empty()
508}
509
510pub fn can_generate_enum_conversion_from_core(enum_def: &EnumDef) -> bool {
513 !enum_def.variants.is_empty()
515}
516
517pub fn is_tuple_variant(fields: &[FieldDef]) -> bool {
519 !fields.is_empty()
520 && fields[0]
521 .name
522 .strip_prefix('_')
523 .is_some_and(|rest: &str| rest.chars().all(|c: char| c.is_ascii_digit()))
524}
525
526pub fn is_newtype(typ: &TypeDef) -> bool {
528 typ.fields.len() == 1 && typ.fields[0].name == "_0"
529}
530
531pub(crate) fn is_tuple_type_name(name: &str) -> bool {
534 name.starts_with('(')
535}
536
537pub fn core_type_path(typ: &TypeDef, core_import: &str) -> String {
543 core_type_path_remapped(typ, core_import, &[])
544}
545
546pub fn core_type_path_remapped(typ: &TypeDef, core_import: &str, remaps: &[(&str, &str)]) -> String {
555 let path = typ.rust_path.replace('-', "_");
560 if path.contains("::") {
561 apply_crate_remaps(&path, remaps)
562 } else {
563 format!("{core_import}::{}", typ.name)
564 }
565}
566
567pub fn apply_crate_remaps(path: &str, remaps: &[(&str, &str)]) -> String {
573 if remaps.is_empty() {
574 return path.to_string();
575 }
576 if let Some(sep) = path.find("::") {
577 let leading = &path[..sep];
578 if let Some(&(_, override_crate)) = remaps.iter().find(|(orig, _)| *orig == leading) {
579 return format!("{override_crate}{}", &path[sep..]);
580 }
581 }
582 path.to_string()
583}
584
585pub fn has_sanitized_fields(typ: &TypeDef) -> bool {
587 binding_fields(&typ.fields).any(|f| f.sanitized)
588}
589
590pub fn core_enum_path(enum_def: &EnumDef, core_import: &str) -> String {
592 core_enum_path_remapped(enum_def, core_import, &[])
593}
594
595pub fn core_enum_path_remapped(enum_def: &EnumDef, core_import: &str, remaps: &[(&str, &str)]) -> String {
598 let path = enum_def.rust_path.replace('-', "_");
599 if path.starts_with(core_import) || path.contains("::") {
600 apply_crate_remaps(&path, remaps)
602 } else {
603 format!("{core_import}::{}", enum_def.name)
605 }
606}
607
608pub fn build_type_path_map(surface: &ApiSurface, core_import: &str) -> AHashMap<String, String> {
613 let mut map = AHashMap::new();
614 for typ in surface.types.iter().filter(|typ| !typ.is_trait) {
615 let path = typ.rust_path.replace('-', "_");
616 let resolved = if path.starts_with(core_import) {
617 path
618 } else {
619 format!("{core_import}::{}", typ.name)
620 };
621 map.insert(typ.name.clone(), resolved);
622 }
623 for en in &surface.enums {
624 let path = en.rust_path.replace('-', "_");
625 let resolved = if path.starts_with(core_import) {
626 path
627 } else {
628 format!("{core_import}::{}", en.name)
629 };
630 map.insert(en.name.clone(), resolved);
631 }
632 map
633}
634
635pub fn resolve_named_path(name: &str, core_import: &str, path_map: &AHashMap<String, String>) -> String {
640 if let Some(path) = path_map.get(name) {
641 path.clone()
642 } else {
643 format!("{core_import}::{name}")
644 }
645}
646
647pub fn binding_to_core_match_arm(binding_prefix: &str, variant_name: &str, fields: &[FieldDef]) -> String {
651 binding_to_core_match_arm_ext(binding_prefix, variant_name, fields, false)
652}
653
654pub fn binding_to_core_match_arm_ext_cfg(
661 binding_prefix: &str,
662 variant_name: &str,
663 fields: &[FieldDef],
664 binding_has_data: bool,
665 config: &ConversionConfig,
666 enum_serde_untagged: bool,
667) -> String {
668 use super::binding_to_core::field_conversion_to_core_cfg;
669
670 if fields.is_empty() {
671 format!("{binding_prefix}::{variant_name} => Self::{variant_name},")
672 } else if !binding_has_data {
673 if is_tuple_variant(fields) {
675 let defaults: Vec<&str> = fields.iter().map(|_| "Default::default()").collect();
676 format!(
677 "{binding_prefix}::{variant_name} => Self::{variant_name}({}),",
678 defaults.join(", ")
679 )
680 } else {
681 let defaults: Vec<String> = fields
682 .iter()
683 .map(|f| format!("{}: Default::default()", f.name))
684 .collect();
685 format!(
686 "{binding_prefix}::{variant_name} => Self::{variant_name} {{ {} }},",
687 defaults.join(", ")
688 )
689 }
690 } else if is_tuple_variant(fields) {
691 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
692 let binding_pattern = field_names.join(", ");
693 let core_args: Vec<String> = fields
694 .iter()
695 .map(|f| {
696 let name = &f.name;
697 if f.sanitized {
699 let expr = if let TypeRef::Vec(_) = &f.ty {
700 format!("{name}.iter().filter_map(|s| serde_json::from_str(s).ok()).collect()")
703 } else {
704 format!("serde_json::from_str(&{name}).unwrap_or_default()")
705 };
706 return if f.is_boxed { format!("Box::new({expr})") } else { expr };
707 }
708 if !config.exclude_types.is_empty() && field_references_excluded_type(&f.ty, config.exclude_types) {
711 let expr = format!("serde_json::from_str(&{name}).unwrap_or_default()");
712 return if f.is_boxed { format!("Box::new({expr})") } else { expr };
713 }
714 let conv = field_conversion_to_core_cfg(name, &f.ty, f.optional, config);
718 let expr = if let Some(expr) = conv.strip_prefix(&format!("{name}: ")) {
720 let expr = expr.replace(&format!("val.{name}"), name);
721 expr.to_string()
722 } else {
723 conv
724 };
725 if f.is_boxed { format!("Box::new({expr})") } else { expr }
726 })
727 .collect();
728 let pattern_syntax = if enum_serde_untagged {
729 format!("{binding_prefix}::{variant_name}({binding_pattern})")
730 } else {
731 format!("{binding_prefix}::{variant_name} {{ {binding_pattern} }}")
732 };
733 format!("{pattern_syntax} => Self::{variant_name}({}),", core_args.join(", "))
734 } else {
735 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
736 let pattern = field_names.join(", ");
737 let core_fields: Vec<String> = fields
738 .iter()
739 .map(|f| {
740 if f.sanitized {
743 if let TypeRef::Vec(_) = &f.ty {
747 return format!(
748 "{}: {}.iter().filter_map(|s| serde_json::from_str(s).ok()).collect()",
749 f.name, f.name
750 );
751 }
752 return format!("{}: serde_json::from_str(&{}).unwrap_or_default()", f.name, f.name);
753 }
754 let conv = field_conversion_to_core_cfg(&f.name, &f.ty, f.optional, config);
758 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
760 let expr = expr.replace(&format!("val.{}", f.name), &f.name);
761 format!("{}: {}", f.name, expr)
762 } else {
763 conv
764 }
765 })
766 .collect();
767 format!(
768 "{binding_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
769 core_fields.join(", ")
770 )
771 }
772}
773
774pub fn binding_to_core_match_arm_ext(
775 binding_prefix: &str,
776 variant_name: &str,
777 fields: &[FieldDef],
778 binding_has_data: bool,
779) -> String {
780 if fields.is_empty() {
781 format!("{binding_prefix}::{variant_name} => Self::{variant_name},")
782 } else if !binding_has_data {
783 if is_tuple_variant(fields) {
785 let defaults: Vec<&str> = fields.iter().map(|_| "Default::default()").collect();
786 format!(
787 "{binding_prefix}::{variant_name} => Self::{variant_name}({}),",
788 defaults.join(", ")
789 )
790 } else {
791 let defaults: Vec<String> = fields
792 .iter()
793 .map(|f| format!("{}: Default::default()", f.name))
794 .collect();
795 format!(
796 "{binding_prefix}::{variant_name} => Self::{variant_name} {{ {} }},",
797 defaults.join(", ")
798 )
799 }
800 } else if is_tuple_variant(fields) {
801 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
803 let binding_pattern = field_names.join(", ");
804 let core_args: Vec<String> = fields
806 .iter()
807 .map(|f| {
808 let name = &f.name;
809 let expr = if matches!(&f.ty, TypeRef::Named(_)) {
810 format!("{name}.into()")
811 } else if f.sanitized {
812 format!("serde_json::from_str(&{name}).unwrap_or_default()")
813 } else {
814 name.clone()
815 };
816 if f.is_boxed { format!("Box::new({expr})") } else { expr }
817 })
818 .collect();
819 format!(
820 "{binding_prefix}::{variant_name} {{ {binding_pattern} }} => Self::{variant_name}({}),",
821 core_args.join(", ")
822 )
823 } else {
824 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
826 let pattern = field_names.join(", ");
827 let core_fields: Vec<String> = fields
828 .iter()
829 .map(|f| {
830 if matches!(&f.ty, TypeRef::Named(_)) {
831 format!("{}: {}.into()", f.name, f.name)
832 } else if f.sanitized {
833 format!("{}: serde_json::from_str(&{}).unwrap_or_default()", f.name, f.name)
837 } else {
838 format!("{0}: {0}", f.name)
839 }
840 })
841 .collect();
842 format!(
843 "{binding_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
844 core_fields.join(", ")
845 )
846 }
847}
848
849pub fn core_to_binding_match_arm(core_prefix: &str, variant_name: &str, fields: &[FieldDef]) -> String {
853 core_to_binding_match_arm_ext(core_prefix, variant_name, fields, false)
854}
855
856pub fn core_to_binding_match_arm_ext_cfg(
863 core_prefix: &str,
864 variant_name: &str,
865 fields: &[FieldDef],
866 binding_has_data: bool,
867 config: &ConversionConfig,
868 enum_serde_untagged: bool,
869) -> String {
870 use super::core_to_binding::field_conversion_from_core_cfg;
871 use ahash::AHashSet;
872
873 if fields.is_empty() {
874 format!("{core_prefix}::{variant_name} => Self::{variant_name},")
875 } else if !binding_has_data {
876 if is_tuple_variant(fields) {
878 format!("{core_prefix}::{variant_name}(..) => Self::{variant_name},")
879 } else {
880 format!("{core_prefix}::{variant_name} {{ .. }} => Self::{variant_name},")
881 }
882 } else if is_tuple_variant(fields) {
883 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
884 let core_pattern = field_names.join(", ");
885 let binding_fields: Vec<String> = fields
886 .iter()
887 .map(|f| {
888 let conv =
892 field_conversion_from_core_cfg(&f.name, &f.ty, f.optional, f.sanitized, &AHashSet::new(), config);
893 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
895 let mut expr = expr.replace(&format!("val.{}", f.name), &f.name);
896 if f.is_boxed {
898 expr = expr.replace(&format!("{}.into()", f.name), &format!("(*{}).into()", f.name));
899 }
900 if enum_serde_untagged {
901 expr
902 } else {
903 format!("{}: {}", f.name, expr)
904 }
905 } else {
906 conv
907 }
908 })
909 .collect();
910 if enum_serde_untagged {
911 format!(
912 "{core_prefix}::{variant_name}({core_pattern}) => Self::{variant_name}({}),",
913 binding_fields.join(", ")
914 )
915 } else {
916 format!(
917 "{core_prefix}::{variant_name}({core_pattern}) => Self::{variant_name} {{ {} }},",
918 binding_fields.join(", ")
919 )
920 }
921 } else {
922 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
923 let pattern = field_names.join(", ");
924 let binding_fields: Vec<String> = fields
925 .iter()
926 .map(|f| {
927 let conv =
931 field_conversion_from_core_cfg(&f.name, &f.ty, f.optional, f.sanitized, &AHashSet::new(), config);
932 if let Some(expr) = conv.strip_prefix(&format!("{}: ", f.name)) {
934 let expr = expr.replace(&format!("val.{}", f.name), &f.name);
935 format!("{}: {}", f.name, expr)
936 } else {
937 conv
938 }
939 })
940 .collect();
941 format!(
942 "{core_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
943 binding_fields.join(", ")
944 )
945 }
946}
947
948pub fn core_to_binding_match_arm_ext(
949 core_prefix: &str,
950 variant_name: &str,
951 fields: &[FieldDef],
952 binding_has_data: bool,
953) -> String {
954 if fields.is_empty() {
955 format!("{core_prefix}::{variant_name} => Self::{variant_name},")
956 } else if !binding_has_data {
957 if is_tuple_variant(fields) {
959 format!("{core_prefix}::{variant_name}(..) => Self::{variant_name},")
960 } else {
961 format!("{core_prefix}::{variant_name} {{ .. }} => Self::{variant_name},")
962 }
963 } else if is_tuple_variant(fields) {
964 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
966 let core_pattern = field_names.join(", ");
967 let binding_fields: Vec<String> = fields
969 .iter()
970 .map(|f| {
971 let name = &f.name;
972 let expr = if f.is_boxed && matches!(&f.ty, TypeRef::Named(_)) {
973 format!("(*{name}).into()")
974 } else if f.is_boxed {
975 format!("*{name}")
976 } else if matches!(&f.ty, TypeRef::Named(_)) {
977 format!("{name}.into()")
978 } else if f.sanitized {
979 format!("serde_json::to_string(&{name}).unwrap_or_default()")
980 } else {
981 name.clone()
982 };
983 format!("{name}: {expr}")
984 })
985 .collect();
986 format!(
987 "{core_prefix}::{variant_name}({core_pattern}) => Self::{variant_name} {{ {} }},",
988 binding_fields.join(", ")
989 )
990 } else {
991 let field_names: Vec<&str> = fields.iter().map(|f| f.name.as_str()).collect();
992 let pattern = field_names.join(", ");
993 let binding_fields: Vec<String> = fields
994 .iter()
995 .map(|f| {
996 if matches!(&f.ty, TypeRef::Named(_)) {
997 format!("{}: {}.into()", f.name, f.name)
998 } else if f.sanitized {
999 format!("{}: serde_json::to_string(&{}).unwrap_or_default()", f.name, f.name)
1003 } else {
1004 format!("{0}: {0}", f.name)
1005 }
1006 })
1007 .collect();
1008 format!(
1009 "{core_prefix}::{variant_name} {{ {pattern} }} => Self::{variant_name} {{ {} }},",
1010 binding_fields.join(", ")
1011 )
1012 }
1013}