1use alef_core::ir::{CoreWrapper, PrimitiveType, TypeDef, TypeRef};
2
3use super::ConversionConfig;
4use super::helpers::{
5 core_prim_str, core_type_path_remapped, is_newtype, is_tuple_type_name, needs_f64_cast, needs_i32_cast,
6 needs_i64_cast,
7};
8
9pub fn gen_from_binding_to_core(typ: &TypeDef, core_import: &str) -> String {
13 gen_from_binding_to_core_cfg(typ, core_import, &ConversionConfig::default())
14}
15
16pub fn gen_from_binding_to_core_cfg(typ: &TypeDef, core_import: &str, config: &ConversionConfig) -> String {
18 let core_path = core_type_path_remapped(typ, core_import, config.source_crate_remaps);
19 let binding_name = format!("{}{}", config.type_name_prefix, typ.name);
20
21 if is_newtype(typ) {
23 let field = &typ.fields[0];
24 let newtype_inner_expr = match &field.ty {
25 TypeRef::Named(_) => "val._0.into()".to_string(),
26 TypeRef::Path => "val._0.into()".to_string(),
27 TypeRef::Duration => "std::time::Duration::from_millis(val._0)".to_string(),
28 _ => "val._0".to_string(),
29 };
30 return crate::template_env::render(
31 "conversions/binding_to_core_impl",
32 minijinja::context! {
33 core_path => core_path,
34 binding_name => binding_name,
35 is_newtype => true,
36 newtype_inner_expr => newtype_inner_expr,
37 builder_mode => false,
38 uses_builder_pattern => false,
39 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
40 statements => vec![] as Vec<String>,
41 fields => vec![] as Vec<String>,
42 },
43 );
44 }
45
46 let uses_builder_pattern = (config.option_duration_on_defaults
48 && typ.has_default
49 && typ
50 .fields
51 .iter()
52 .any(|f| !f.optional && matches!(f.ty, TypeRef::Duration)))
53 || (config.optionalize_defaults && typ.has_default);
54
55 let has_optionalized_duration = config.option_duration_on_defaults
60 && typ.has_default
61 && typ
62 .fields
63 .iter()
64 .any(|f| !f.optional && matches!(f.ty, TypeRef::Duration));
65
66 if has_optionalized_duration {
67 let optionalized = config.optionalize_defaults && typ.has_default;
69 let mut statements = Vec::new();
70 for field in &typ.fields {
71 if field.binding_excluded {
72 continue;
73 }
74 if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
75 continue;
77 }
78 if !config.exclude_types.is_empty()
80 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types)
81 {
82 continue;
83 }
84 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
86 if !field.optional && matches!(field.ty, TypeRef::Duration) {
87 let cast = if config.cast_large_ints_to_i64 { " as u64" } else { "" };
88 statements.push(format!(
89 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = std::time::Duration::from_millis(__v{cast}); }}",
90 field.name
91 ));
92 continue;
93 }
94 let conversion = if optionalized && !field.optional {
95 gen_optionalized_field_to_core(&field.name, &field.ty, config, false)
98 } else {
99 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
105 };
106 let conversion = if binding_name_field != field.name {
108 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
109 } else {
110 conversion
111 };
112 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
114 statements.push(format!("__result.{} = {};", field.name, expr));
115 }
116 }
117
118 return crate::template_env::render(
119 "conversions/binding_to_core_impl",
120 minijinja::context! {
121 core_path => core_path,
122 binding_name => binding_name,
123 is_newtype => false,
124 newtype_inner_expr => "",
125 builder_mode => true,
126 uses_builder_pattern => uses_builder_pattern,
127 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
128 statements => statements,
129 fields => vec![] as Vec<String>,
130 },
131 );
132 }
133
134 let optionalized = config.optionalize_defaults && typ.has_default;
135
136 let mut fields = Vec::new();
138 let mut statements = Vec::new();
139
140 for field in &typ.fields {
141 if field.binding_excluded {
142 if field.cfg.is_some()
143 && !config.never_skip_cfg_field_names.contains(&field.name)
144 && (typ.has_stripped_cfg_fields || config.strip_cfg_fields_from_binding_struct)
145 {
146 continue;
147 }
148 fields.push(format!("{}: Default::default()", field.name));
149 continue;
150 }
151 let references_excluded = !config.exclude_types.is_empty()
163 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types);
164 if references_excluded && typ.has_stripped_cfg_fields {
165 continue;
166 }
167 if field.cfg.is_some()
173 && !config.never_skip_cfg_field_names.contains(&field.name)
174 && config.strip_cfg_fields_from_binding_struct
175 {
176 continue;
177 }
178 if optionalized && ((field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded) {
179 continue;
180 }
181 let field_was_optionalized = optionalized && !field.optional;
182 let conversion = if (field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded {
183 format!("{}: Default::default()", field.name)
184 } else if field_was_optionalized {
185 field_conversion_to_core_cfg(&field.name, &field.ty, false, config)
188 } else {
189 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
190 };
191 let conversion = if let Some(newtype_path) = &field.newtype_wrapper {
197 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
198 match &field.ty {
201 TypeRef::Optional(_) => format!("{}: ({expr}).map({newtype_path})", field.name),
202 TypeRef::Vec(_) => {
203 let inner_expr = if let Some(prefix) = expr.strip_suffix(".collect()") {
208 format!("{prefix}.collect::<Vec<_>>()")
209 } else {
210 expr.to_string()
211 };
212 format!(
213 "{}: ({inner_expr}).into_iter().map({newtype_path}).collect()",
214 field.name
215 )
216 }
217 _ if field.optional => format!("{}: ({expr}).map({newtype_path})", field.name),
218 _ => format!("{}: {newtype_path}({expr})", field.name),
219 }
220 } else {
221 conversion
222 }
223 } else {
224 conversion
225 };
226 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
228 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
229 if field.optional {
230 format!("{}: {}.map(Box::new)", field.name, expr)
232 } else {
233 format!("{}: Box::new({})", field.name, expr)
234 }
235 } else {
236 conversion
237 }
238 } else {
239 conversion
240 };
241 let is_opaque_arc_field = field.core_wrapper == CoreWrapper::Arc
249 && matches!(&field.ty, TypeRef::Named(n) if config
250 .opaque_types
251 .is_some_and(|opaque| opaque.contains(n.as_str())));
252 let is_opaque_no_wrapper_field = field.core_wrapper == CoreWrapper::None
256 && matches!(&field.ty, TypeRef::Named(n) if config
257 .opaque_types
258 .is_some_and(|opaque| opaque.contains(n.as_str())));
259 let conversion = if is_opaque_arc_field {
260 if field.optional {
261 format!("{}: val.{}.map(|v| v.inner)", field.name, field.name)
262 } else {
263 format!("{}: val.{}.inner", field.name, field.name)
264 }
265 } else if is_opaque_no_wrapper_field {
266 if config.trait_bridge_field_is_arc_wrapper(&field.name) {
270 if field.optional {
271 format!("{}: val.{}.map(|v| (*v.inner).clone())", field.name, field.name)
272 } else {
273 format!("{}: (*val.{}.inner).clone()", field.name, field.name)
274 }
275 } else {
276 format!("{}: Default::default()", field.name)
277 }
278 } else {
279 apply_core_wrapper_to_core(
280 &conversion,
281 &field.name,
282 &field.core_wrapper,
283 &field.vec_inner_core_wrapper,
284 field.optional,
285 )
286 };
287 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
291 let conversion = if binding_name_field != field.name {
292 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
293 } else {
294 conversion
295 };
296 if optionalized {
297 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
298 if field_was_optionalized {
299 statements.push(format!(
300 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = {}; }}",
301 field.name,
302 expr.replace(&format!("val.{binding_name_field}"), "__v")
303 ));
304 } else {
305 statements.push(format!("__result.{} = {};", field.name, expr));
306 }
307 }
308 } else {
309 fields.push(conversion);
310 }
311 }
312
313 crate::template_env::render(
317 "conversions/binding_to_core_impl",
318 minijinja::context! {
319 core_path => core_path,
320 binding_name => binding_name,
321 is_newtype => false,
322 newtype_inner_expr => "",
323 builder_mode => optionalized,
324 uses_builder_pattern => uses_builder_pattern,
325 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
326 statements => statements,
327 fields => fields,
328 },
329 )
330}
331
332pub(super) fn gen_optionalized_field_to_core(
337 name: &str,
338 ty: &TypeRef,
339 config: &ConversionConfig,
340 field_is_ir_optional: bool,
341) -> String {
342 match ty {
343 TypeRef::Json if config.json_as_value => {
344 format!("{name}: val.{name}.unwrap_or_default()")
345 }
346 TypeRef::Json => {
347 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
348 }
349 TypeRef::Named(_) => {
350 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
352 }
353 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
354 format!("{name}: val.{name}.map(|v| v as f32).unwrap_or(0.0)")
355 }
356 TypeRef::Primitive(PrimitiveType::F32 | PrimitiveType::F64) => {
357 format!("{name}: val.{name}.unwrap_or(0.0)")
358 }
359 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
360 let core_ty = core_prim_str(p);
361 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
362 }
363 TypeRef::Optional(inner)
364 if config.cast_large_ints_to_i64
365 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
366 {
367 if let TypeRef::Primitive(p) = inner.as_ref() {
368 let core_ty = core_prim_str(p);
369 format!("{name}: val.{name}.map(|v| v as {core_ty})")
370 } else {
371 field_conversion_to_core(name, ty, false)
372 }
373 }
374 TypeRef::Duration if config.cast_large_ints_to_i64 => {
375 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64)).unwrap_or_default()")
376 }
377 TypeRef::Duration => {
378 format!("{name}: val.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
379 }
380 TypeRef::Path => {
381 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
382 }
383 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
384 format!("{name}: val.{name}.map(|s| std::path::PathBuf::from(s))")
386 }
387 TypeRef::Optional(_) => {
388 format!("{name}: val.{name}.map(Some)")
391 }
392 TypeRef::Char => {
394 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
395 }
396 TypeRef::Vec(inner) => match inner.as_ref() {
397 TypeRef::Json => {
398 format!(
399 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
400 )
401 }
402 TypeRef::Named(_) => {
403 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
404 }
405 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
406 let core_ty = core_prim_str(p);
407 format!(
408 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
409 )
410 }
411 _ => format!("{name}: val.{name}.unwrap_or_default()"),
412 },
413 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
414 let k_is_json = matches!(k.as_ref(), TypeRef::Json);
418 let k_expr = if k_is_json {
419 "serde_json::from_str(&k).unwrap_or_default()"
420 } else {
421 "k.into()"
422 };
423 format!(
424 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or(serde_json::json!(v)))).collect()"
425 )
426 }
427 TypeRef::Map(k, _v) if matches!(k.as_ref(), TypeRef::Json) => {
428 format!(
430 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (serde_json::from_str(&k).unwrap_or_default(), v)).collect()"
431 )
432 }
433 TypeRef::Map(k, v) => {
434 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
436 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
437 let val_is_string_enum = matches!(v.as_ref(), TypeRef::Named(n)
438 if config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)));
439 if field_is_ir_optional {
440 if val_is_string_enum {
442 format!(
443 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect())"
444 )
445 } else if has_named_val {
446 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
447 } else if has_named_key {
448 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
449 } else {
450 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
451 }
452 } else if val_is_string_enum {
453 format!(
454 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect()"
455 )
456 } else if has_named_val {
457 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, v.into())).collect()")
458 } else if has_named_key {
459 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k.into(), v)).collect()")
460 } else {
461 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
462 }
463 }
464 _ => {
465 format!("{name}: val.{name}.unwrap_or_default()")
467 }
468 }
469}
470
471pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
473 match ty {
474 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Unit => {
476 format!("{name}: val.{name}")
477 }
478 TypeRef::Bytes => {
483 if optional {
484 format!("{name}: val.{name}.map(|v| v.to_vec().into())")
485 } else {
486 format!("{name}: val.{name}.to_vec().into()")
487 }
488 }
489 TypeRef::Json => {
491 if optional {
492 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
493 } else {
494 format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
495 }
496 }
497 TypeRef::Char => {
499 if optional {
500 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
501 } else {
502 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
503 }
504 }
505 TypeRef::Duration => {
507 if optional {
508 format!("{name}: val.{name}.map(std::time::Duration::from_millis)")
509 } else {
510 format!("{name}: std::time::Duration::from_millis(val.{name})")
511 }
512 }
513 TypeRef::Path => {
515 if optional {
516 format!("{name}: val.{name}.map(Into::into)")
517 } else {
518 format!("{name}: val.{name}.into()")
519 }
520 }
521 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
524 format!("{name}: val.{name}")
525 }
526 TypeRef::Named(_) => {
527 if optional {
528 format!("{name}: val.{name}.map(Into::into)")
529 } else {
530 format!("{name}: val.{name}.into()")
531 }
532 }
533 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
537 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
538 "serde_json::from_str(&k).unwrap_or_default()"
539 } else {
540 "k.into()"
541 };
542 if optional {
543 format!(
544 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
545 )
546 } else {
547 format!(
548 "{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect()"
549 )
550 }
551 }
552 TypeRef::Map(_k, v) if matches!(v.as_ref(), TypeRef::Bytes) => {
555 if optional {
556 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.to_vec().into())).collect())")
557 } else {
558 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v.to_vec().into())).collect()")
559 }
560 }
561 TypeRef::Optional(inner) => match inner.as_ref() {
563 TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
564 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
565 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
566 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
567 }
568 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
569 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
570 "serde_json::from_str(&k).unwrap_or_default()"
571 } else {
572 "k.into()"
573 };
574 format!(
575 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
576 )
577 }
578 _ => format!("{name}: val.{name}"),
579 },
580 TypeRef::Vec(inner) => match inner.as_ref() {
582 TypeRef::Json => {
583 if optional {
584 format!(
585 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
586 )
587 } else {
588 format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
589 }
590 }
591 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
593 format!("{name}: val.{name}")
594 }
595 TypeRef::Named(_) => {
596 if optional {
597 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
598 } else {
599 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
600 }
601 }
602 _ => format!("{name}: val.{name}"),
603 },
604 TypeRef::Map(k, v) => {
607 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
608 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
609 let has_json_val = matches!(v.as_ref(), TypeRef::Json);
610 let has_json_key = matches!(k.as_ref(), TypeRef::Json);
611 let has_vec_named_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n)));
613 let has_vec_json_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
615 if has_json_val || has_json_key || has_named_key || has_named_val || has_vec_named_val || has_vec_json_val {
616 let k_expr = if has_json_key {
620 "serde_json::from_str(&k).unwrap_or(serde_json::Value::String(k))"
621 } else {
622 "k.into()"
623 };
624 let v_expr = if has_json_val {
625 "serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v))"
626 } else if has_named_val {
627 "v.into()"
628 } else if has_vec_named_val {
629 "v.into_iter().map(Into::into).collect()"
630 } else if has_vec_json_val {
631 "v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()"
632 } else {
633 "v"
634 };
635 if optional {
636 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
637 } else {
638 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
639 }
640 } else {
641 let is_string_map = matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String);
645 if is_string_map {
646 if optional {
647 format!(
648 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
649 )
650 } else {
651 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
652 }
653 } else {
654 if optional {
658 if has_named_val {
659 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
660 } else {
661 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
662 }
663 } else {
664 format!("{name}: val.{name}.into_iter().collect()")
665 }
666 }
667 }
668 }
669 }
670}
671
672pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
674 if optional && matches!(ty, TypeRef::Optional(_)) {
678 let inner_expr = field_conversion_to_core_cfg(name, ty, false, config);
681 if let Some(expr) = inner_expr.strip_prefix(&format!("{name}: ")) {
683 return format!("{name}: ({expr}).map(Some)");
684 }
685 return inner_expr;
686 }
687
688 if config.map_uses_jsvalue {
690 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
691 let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
692 let is_map = matches!(ty, TypeRef::Map(_, _));
693 if is_nested_vec || is_map || is_vec_json {
694 if optional {
695 return format!(
696 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
697 );
698 }
699 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
700 }
701 if let TypeRef::Optional(inner) = ty {
702 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
703 let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
704 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
705 if is_inner_nested || is_inner_map || is_inner_vec_json {
706 return format!(
707 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
708 );
709 }
710 }
711 }
712
713 if config.vec_named_to_string {
717 if let TypeRef::Vec(inner) = ty {
718 if matches!(inner.as_ref(), TypeRef::Named(_)) {
719 if optional {
720 return format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())");
721 }
722 return format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()");
723 }
724 }
725 }
726 if config.map_as_string && matches!(ty, TypeRef::Map(_, _)) {
728 return format!("{name}: Default::default()");
729 }
730 if config.map_as_string {
731 if let TypeRef::Optional(inner) = ty {
732 if matches!(inner.as_ref(), TypeRef::Map(_, _)) {
733 return format!("{name}: Default::default()");
734 }
735 }
736 }
737 if config.map_uses_jsvalue {
742 if let Some(tagged_names) = config.tagged_data_enum_names {
743 let bare_named = matches!(ty, TypeRef::Named(n) if tagged_names.contains(n));
744 let optional_named = matches!(ty, TypeRef::Optional(inner)
745 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n)));
746 let vec_named = matches!(ty, TypeRef::Vec(inner)
747 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n)));
748 let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
749 if matches!(outer.as_ref(), TypeRef::Vec(inner)
750 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n))));
751 if bare_named {
752 if optional {
753 return format!(
756 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
757 );
758 }
759 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
761 }
762 if optional_named {
763 return format!(
765 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
766 );
767 }
768 if vec_named {
769 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
770 }
771 if optional_vec_named {
772 return format!(
773 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
774 );
775 }
776 }
777 }
778
779 if let Some(untagged_names) = config.untagged_data_enum_names {
782 let direct_named = matches!(ty, TypeRef::Named(n) if untagged_names.contains(n));
783 let optional_named = matches!(ty, TypeRef::Optional(inner)
784 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
785 let vec_named = matches!(ty, TypeRef::Vec(inner)
786 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
787 let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
788 if matches!(outer.as_ref(), TypeRef::Vec(inner)
789 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n))));
790 if direct_named {
791 if optional {
792 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
793 }
794 return format!("{name}: serde_json::from_value(val.{name}).unwrap_or_default()");
795 }
796 if optional_named {
797 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
798 }
799 if vec_named {
800 if optional {
801 return format!(
802 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
803 );
804 }
805 return format!("{name}: val.{name}.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect()");
806 }
807 if optional_vec_named {
808 return format!(
809 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
810 );
811 }
812 }
813 if config.json_to_string && matches!(ty, TypeRef::Json) {
815 return format!("{name}: Default::default()");
816 }
817 if config.json_as_value && matches!(ty, TypeRef::Json) {
819 return format!("{name}: val.{name}");
820 }
821 if config.json_as_value {
822 if let TypeRef::Optional(inner) = ty {
823 if matches!(inner.as_ref(), TypeRef::Json) {
824 return format!("{name}: val.{name}");
825 }
826 }
827 if let TypeRef::Vec(inner) = ty {
828 if matches!(inner.as_ref(), TypeRef::Json) {
829 if optional {
830 return format!("{name}: val.{name}.unwrap_or_default()");
831 }
832 return format!("{name}: val.{name}");
833 }
834 }
835 if let TypeRef::Map(_k, v) = ty {
836 if matches!(v.as_ref(), TypeRef::Json) {
837 if optional {
838 return format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())");
839 }
840 return format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v)).collect()");
841 }
842 }
843 }
844 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
846 if optional {
847 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
848 }
849 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
850 }
851 if !config.cast_large_ints_to_i64
852 && !config.cast_large_ints_to_f64
853 && !config.cast_uints_to_i32
854 && !config.cast_f32_to_f64
855 && !config.json_to_string
856 && !config.vec_named_to_string
857 && !config.map_as_string
858 && config.from_binding_skip_types.is_empty()
859 {
860 return field_conversion_to_core(name, ty, optional);
861 }
862 match ty {
864 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
865 let core_ty = core_prim_str(p);
866 if optional {
867 format!("{name}: val.{name}.map(|v| v as {core_ty})")
868 } else {
869 format!("{name}: val.{name} as {core_ty}")
870 }
871 }
872 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
874 if optional {
875 format!("{name}: val.{name}.map(|v| v as f32)")
876 } else {
877 format!("{name}: val.{name} as f32")
878 }
879 }
880 TypeRef::Duration if config.cast_large_ints_to_i64 => {
881 if optional {
882 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
883 } else {
884 format!("{name}: std::time::Duration::from_millis(val.{name} as u64)")
885 }
886 }
887 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
888 if let TypeRef::Primitive(p) = inner.as_ref() {
889 let core_ty = core_prim_str(p);
890 format!("{name}: val.{name}.map(|v| v as {core_ty})")
891 } else {
892 field_conversion_to_core(name, ty, optional)
893 }
894 }
895 TypeRef::Vec(inner)
897 if config.cast_large_ints_to_i64
898 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
899 {
900 if let TypeRef::Primitive(p) = inner.as_ref() {
901 let core_ty = core_prim_str(p);
902 if optional {
903 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
904 } else {
905 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
906 }
907 } else {
908 field_conversion_to_core(name, ty, optional)
909 }
910 }
911 TypeRef::Map(_k, v)
913 if config.cast_large_ints_to_i64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
914 {
915 if let TypeRef::Primitive(p) = v.as_ref() {
916 let core_ty = core_prim_str(p);
917 if optional {
918 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
919 } else {
920 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
921 }
922 } else {
923 field_conversion_to_core(name, ty, optional)
924 }
925 }
926 TypeRef::Vec(inner)
928 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
929 {
930 if optional {
931 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
932 } else {
933 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
934 }
935 }
936 TypeRef::Optional(inner)
938 if config.cast_f32_to_f64
939 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
940 {
941 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
942 }
943 TypeRef::Primitive(p) if config.cast_uints_to_i32 && needs_i32_cast(p) => {
945 let core_ty = core_prim_str(p);
946 if optional {
947 format!("{name}: val.{name}.map(|v| v as {core_ty})")
948 } else {
949 format!("{name}: val.{name} as {core_ty}")
950 }
951 }
952 TypeRef::Optional(inner)
954 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
955 {
956 if let TypeRef::Primitive(p) = inner.as_ref() {
957 let core_ty = core_prim_str(p);
958 format!("{name}: val.{name}.map(|v| v as {core_ty})")
959 } else {
960 field_conversion_to_core(name, ty, optional)
961 }
962 }
963 TypeRef::Vec(inner)
965 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
966 {
967 if let TypeRef::Primitive(p) = inner.as_ref() {
968 let core_ty = core_prim_str(p);
969 if optional {
970 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
971 } else {
972 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
973 }
974 } else {
975 field_conversion_to_core(name, ty, optional)
976 }
977 }
978 TypeRef::Primitive(p) if config.cast_large_ints_to_f64 && needs_f64_cast(p) => {
980 let core_ty = core_prim_str(p);
981 if optional {
982 format!("{name}: val.{name}.map(|v| v as {core_ty})")
983 } else {
984 format!("{name}: val.{name} as {core_ty}")
985 }
986 }
987 TypeRef::Optional(inner)
989 if config.cast_large_ints_to_f64
990 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
991 {
992 if let TypeRef::Primitive(p) = inner.as_ref() {
993 let core_ty = core_prim_str(p);
994 format!("{name}: val.{name}.map(|v| v as {core_ty})")
995 } else {
996 field_conversion_to_core(name, ty, optional)
997 }
998 }
999 TypeRef::Vec(inner)
1001 if config.cast_large_ints_to_f64
1002 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
1003 {
1004 if let TypeRef::Primitive(p) = inner.as_ref() {
1005 let core_ty = core_prim_str(p);
1006 if optional {
1007 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1008 } else {
1009 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
1010 }
1011 } else {
1012 field_conversion_to_core(name, ty, optional)
1013 }
1014 }
1015 TypeRef::Map(_k, v)
1017 if config.cast_large_ints_to_f64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
1018 {
1019 if let TypeRef::Primitive(p) = v.as_ref() {
1020 let core_ty = core_prim_str(p);
1021 if optional {
1022 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
1023 } else {
1024 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
1025 }
1026 } else {
1027 field_conversion_to_core(name, ty, optional)
1028 }
1029 }
1030 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
1033 format!("{name}: Default::default()")
1034 }
1035 TypeRef::Optional(inner) => match inner.as_ref() {
1036 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
1037 format!("{name}: Default::default()")
1038 }
1039 _ => field_conversion_to_core(name, ty, optional),
1040 },
1041 _ => field_conversion_to_core(name, ty, optional),
1043 }
1044}
1045
1046pub fn apply_core_wrapper_to_core(
1049 conversion: &str,
1050 name: &str,
1051 core_wrapper: &CoreWrapper,
1052 vec_inner_core_wrapper: &CoreWrapper,
1053 optional: bool,
1054) -> String {
1055 if *vec_inner_core_wrapper == CoreWrapper::Arc {
1057 return conversion
1058 .replace(
1059 ".map(Into::into).collect()",
1060 ".map(|v| std::sync::Arc::new(v.into())).collect()",
1061 )
1062 .replace(
1063 "map(|v| v.into_iter().map(Into::into)",
1064 "map(|v| v.into_iter().map(|v| std::sync::Arc::new(v.into()))",
1065 );
1066 }
1067
1068 match core_wrapper {
1069 CoreWrapper::None => conversion.to_string(),
1070 CoreWrapper::Cow => {
1071 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1075 if optional {
1076 format!("{name}: {expr}.map(Into::into)")
1077 } else if expr == format!("val.{name}") {
1078 format!("{name}: val.{name}.into()")
1079 } else if expr == "Default::default()" {
1080 conversion.to_string()
1083 } else {
1084 format!("{name}: ({expr}).into()")
1085 }
1086 } else {
1087 conversion.to_string()
1088 }
1089 }
1090 CoreWrapper::Arc => {
1091 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1093 if expr == "Default::default()" {
1094 conversion.to_string()
1097 } else if optional {
1098 format!("{name}: {expr}.map(|v| std::sync::Arc::new(v))")
1099 } else {
1100 format!("{name}: std::sync::Arc::new({expr})")
1101 }
1102 } else {
1103 conversion.to_string()
1104 }
1105 }
1106 CoreWrapper::Bytes => {
1107 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1113 let already_converted_non_opt =
1114 expr == format!("val.{name}.into()") || expr == format!("val.{name}.to_vec().into()");
1115 let already_converted_opt = expr
1116 .strip_prefix(&format!("val.{name}"))
1117 .map(|s| s == ".map(Into::into)" || s == ".map(|v| v.to_vec().into())")
1118 .unwrap_or(false);
1119 if already_converted_non_opt || already_converted_opt {
1120 conversion.to_string()
1122 } else if optional {
1123 format!("{name}: {expr}.map(Into::into)")
1124 } else if expr == format!("val.{name}") {
1125 format!("{name}: val.{name}.into()")
1126 } else if expr == "Default::default()" {
1127 conversion.to_string()
1130 } else {
1131 format!("{name}: ({expr}).into()")
1132 }
1133 } else {
1134 conversion.to_string()
1135 }
1136 }
1137 CoreWrapper::ArcMutex => {
1138 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1140 if optional {
1141 format!("{name}: {expr}.map(|v| std::sync::Arc::new(std::sync::Mutex::new(v.into())))")
1142 } else if expr == format!("val.{name}") {
1143 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(val.{name}.into()))")
1144 } else {
1145 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(({expr}).into()))")
1146 }
1147 } else {
1148 conversion.to_string()
1149 }
1150 }
1151 }
1152}
1153
1154#[cfg(test)]
1155mod tests {
1156 use super::gen_from_binding_to_core;
1157 use super::gen_from_binding_to_core_cfg;
1158 use crate::conversions::ConversionConfig;
1159 use ahash::AHashSet;
1160 use alef_core::ir::{CoreWrapper, DefaultValue, FieldDef, TypeDef, TypeRef};
1161
1162 fn type_with_field(field: FieldDef) -> TypeDef {
1163 TypeDef {
1164 name: "ProcessConfig".to_string(),
1165 rust_path: "crate::ProcessConfig".to_string(),
1166 original_rust_path: String::new(),
1167 fields: vec![field],
1168 methods: vec![],
1169 is_opaque: false,
1170 is_clone: true,
1171 is_copy: false,
1172 doc: String::new(),
1173 cfg: None,
1174 is_trait: false,
1175 has_default: true,
1176 has_stripped_cfg_fields: false,
1177 is_return_type: false,
1178 serde_rename_all: None,
1179 has_serde: true,
1180 super_traits: vec![],
1181 binding_excluded: false,
1182 binding_exclusion_reason: None,
1183 }
1184 }
1185
1186 #[test]
1187 fn sanitized_cow_string_field_converts_to_core() {
1188 let field = FieldDef {
1189 name: "language".to_string(),
1190 ty: TypeRef::String,
1191 optional: false,
1192 default: None,
1193 doc: String::new(),
1194 sanitized: true,
1195 is_boxed: false,
1196 type_rust_path: None,
1197 cfg: None,
1198 typed_default: Some(DefaultValue::Empty),
1199 core_wrapper: CoreWrapper::Cow,
1200 vec_inner_core_wrapper: CoreWrapper::None,
1201 newtype_wrapper: None,
1202 serde_rename: None,
1203 serde_flatten: false,
1204 binding_excluded: false,
1205 binding_exclusion_reason: None,
1206 original_type: None,
1207 };
1208
1209 let out = gen_from_binding_to_core(&type_with_field(field), "crate");
1210
1211 assert!(out.contains("language: val.language.into()"));
1212 assert!(!out.contains("language: Default::default()"));
1213 }
1214
1215 #[test]
1216 fn binding_excluded_cfg_field_is_not_emitted_into_core_literal() {
1217 let field = FieldDef {
1218 name: "di_container".to_string(),
1219 ty: TypeRef::String,
1220 optional: true,
1221 default: None,
1222 doc: String::new(),
1223 sanitized: false,
1224 is_boxed: false,
1225 type_rust_path: None,
1226 cfg: Some("feature = \"di\"".to_string()),
1227 typed_default: None,
1228 core_wrapper: CoreWrapper::None,
1229 vec_inner_core_wrapper: CoreWrapper::None,
1230 newtype_wrapper: None,
1231 serde_rename: None,
1232 serde_flatten: false,
1233 binding_excluded: true,
1234 binding_exclusion_reason: Some("internal implementation detail".to_string()),
1235 original_type: None,
1236 };
1237 let mut typ = type_with_field(field);
1238 typ.has_stripped_cfg_fields = true;
1239
1240 let out = gen_from_binding_to_core(&typ, "crate");
1241
1242 assert!(
1243 !out.contains("di_container:"),
1244 "cfg-gated binding-excluded fields may not exist in the core struct; got:\n{out}"
1245 );
1246 assert!(
1247 out.contains("..Default::default()"),
1248 "stripped cfg fields should be filled by the default update; got:\n{out}"
1249 );
1250 }
1251
1252 #[test]
1256 fn trait_bridge_arc_wrapper_field_forwards_value_not_default() {
1257 let opaque_type_name = "VisitorHandle".to_string();
1258 let mut opaque_set = AHashSet::new();
1259 opaque_set.insert(opaque_type_name.clone());
1260
1261 let field = FieldDef {
1262 name: "visitor".to_string(),
1263 ty: TypeRef::Named(opaque_type_name.clone()),
1264 optional: true,
1265 default: None,
1266 doc: String::new(),
1267 sanitized: false,
1268 is_boxed: false,
1269 type_rust_path: None,
1270 cfg: Some("feature = \"visitor\"".to_string()),
1271 typed_default: None,
1272 core_wrapper: CoreWrapper::None,
1273 vec_inner_core_wrapper: CoreWrapper::None,
1274 newtype_wrapper: None,
1275 serde_rename: None,
1276 serde_flatten: false,
1277 binding_excluded: false,
1278 binding_exclusion_reason: None,
1279 original_type: None,
1280 };
1281
1282 let never_skip = vec!["visitor".to_string()];
1283 let arc_wrapper = vec!["visitor".to_string()];
1284
1285 let config = ConversionConfig {
1286 opaque_types: Some(&opaque_set),
1287 never_skip_cfg_field_names: &never_skip,
1288 trait_bridge_arc_wrapper_field_names: &arc_wrapper,
1289 ..ConversionConfig::default()
1290 };
1291
1292 let out = gen_from_binding_to_core_cfg(&type_with_field(field), "crate", &config);
1293
1294 assert!(
1295 out.contains("val.visitor.map(|v| (*v.inner).clone())"),
1296 "expected arc-wrapper clone forwarding, got:\n{out}"
1297 );
1298 assert!(
1299 !out.contains("visitor: Default::default()"),
1300 "must not emit Default::default() for arc-wrapper trait-bridge field, got:\n{out}"
1301 );
1302 }
1303
1304 #[test]
1307 fn opaque_no_wrapper_field_without_arc_flag_emits_default() {
1308 let opaque_type_name = "OpaqueHandle".to_string();
1309 let mut opaque_set = AHashSet::new();
1310 opaque_set.insert(opaque_type_name.clone());
1311
1312 let field = FieldDef {
1313 name: "handle".to_string(),
1314 ty: TypeRef::Named(opaque_type_name.clone()),
1315 optional: false,
1316 default: None,
1317 doc: String::new(),
1318 sanitized: false,
1319 is_boxed: false,
1320 type_rust_path: None,
1321 cfg: None,
1322 typed_default: None,
1323 core_wrapper: CoreWrapper::None,
1324 vec_inner_core_wrapper: CoreWrapper::None,
1325 newtype_wrapper: None,
1326 serde_rename: None,
1327 serde_flatten: false,
1328 binding_excluded: false,
1329 binding_exclusion_reason: None,
1330 original_type: None,
1331 };
1332
1333 let config = ConversionConfig {
1334 opaque_types: Some(&opaque_set),
1335 ..ConversionConfig::default()
1337 };
1338
1339 let out = gen_from_binding_to_core_cfg(&type_with_field(field), "crate", &config);
1340
1341 assert!(
1342 out.contains("handle: Default::default()"),
1343 "expected Default::default() for non-arc-wrapper opaque field, got:\n{out}"
1344 );
1345 assert!(
1346 !out.contains("(*val.handle.inner).clone()"),
1347 "must not emit arc-clone for non-arc-wrapper opaque field, got:\n{out}"
1348 );
1349 }
1350}