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.cfg.is_some() {
73 continue;
74 }
75 if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
76 continue;
78 }
79 if !config.exclude_types.is_empty()
81 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types)
82 {
83 continue;
84 }
85 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
87 if !field.optional && matches!(field.ty, TypeRef::Duration) {
88 let cast = if config.cast_large_ints_to_i64 { " as u64" } else { "" };
89 statements.push(format!(
90 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = std::time::Duration::from_millis(__v{cast}); }}",
91 field.name
92 ));
93 continue;
94 }
95 let conversion = if optionalized && !field.optional {
96 gen_optionalized_field_to_core(&field.name, &field.ty, config, false)
99 } else {
100 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
106 };
107 let conversion = if binding_name_field != field.name {
109 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
110 } else {
111 conversion
112 };
113 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
115 statements.push(format!("__result.{} = {};", field.name, expr));
116 }
117 }
118
119 return crate::template_env::render(
120 "conversions/binding_to_core_impl",
121 minijinja::context! {
122 core_path => core_path,
123 binding_name => binding_name,
124 is_newtype => false,
125 newtype_inner_expr => "",
126 builder_mode => true,
127 uses_builder_pattern => uses_builder_pattern,
128 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
129 statements => statements,
130 fields => vec![] as Vec<String>,
131 },
132 );
133 }
134
135 let optionalized = config.optionalize_defaults && typ.has_default;
136
137 let mut fields = Vec::new();
139 let mut statements = Vec::new();
140
141 for field in &typ.fields {
142 if field.cfg.is_some() {
147 continue;
148 }
149 let references_excluded = !config.exclude_types.is_empty()
156 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types);
157 if references_excluded && typ.has_stripped_cfg_fields {
158 continue;
159 }
160 if optionalized && ((field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded) {
161 continue;
162 }
163 let field_was_optionalized = optionalized && !field.optional;
164 let conversion = if (field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded {
165 format!("{}: Default::default()", field.name)
166 } else if field_was_optionalized {
167 field_conversion_to_core_cfg(&field.name, &field.ty, false, config)
170 } else {
171 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
172 };
173 let conversion = if let Some(newtype_path) = &field.newtype_wrapper {
179 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
180 match &field.ty {
183 TypeRef::Optional(_) => format!("{}: ({expr}).map({newtype_path})", field.name),
184 TypeRef::Vec(_) => {
185 let inner_expr = if let Some(prefix) = expr.strip_suffix(".collect()") {
190 format!("{prefix}.collect::<Vec<_>>()")
191 } else {
192 expr.to_string()
193 };
194 format!(
195 "{}: ({inner_expr}).into_iter().map({newtype_path}).collect()",
196 field.name
197 )
198 }
199 _ if field.optional => format!("{}: ({expr}).map({newtype_path})", field.name),
200 _ => format!("{}: {newtype_path}({expr})", field.name),
201 }
202 } else {
203 conversion
204 }
205 } else {
206 conversion
207 };
208 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
210 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
211 if field.optional {
212 format!("{}: {}.map(Box::new)", field.name, expr)
214 } else {
215 format!("{}: Box::new({})", field.name, expr)
216 }
217 } else {
218 conversion
219 }
220 } else {
221 conversion
222 };
223 let is_opaque_arc_field = field.core_wrapper == CoreWrapper::Arc
231 && matches!(&field.ty, TypeRef::Named(n) if config
232 .opaque_types
233 .is_some_and(|opaque| opaque.contains(n.as_str())));
234 let is_opaque_no_wrapper_field = field.core_wrapper == CoreWrapper::None
238 && matches!(&field.ty, TypeRef::Named(n) if config
239 .opaque_types
240 .is_some_and(|opaque| opaque.contains(n.as_str())));
241 let conversion = if is_opaque_arc_field {
242 if field.optional {
243 format!("{}: val.{}.map(|v| v.inner)", field.name, field.name)
244 } else {
245 format!("{}: val.{}.inner", field.name, field.name)
246 }
247 } else if is_opaque_no_wrapper_field {
248 format!("{}: Default::default()", field.name)
249 } else {
250 apply_core_wrapper_to_core(
251 &conversion,
252 &field.name,
253 &field.core_wrapper,
254 &field.vec_inner_core_wrapper,
255 field.optional,
256 )
257 };
258 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
262 let conversion = if binding_name_field != field.name {
263 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
264 } else {
265 conversion
266 };
267 if optionalized {
268 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
269 if field_was_optionalized {
270 statements.push(format!(
271 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = {}; }}",
272 field.name,
273 expr.replace(&format!("val.{binding_name_field}"), "__v")
274 ));
275 } else {
276 statements.push(format!("__result.{} = {};", field.name, expr));
277 }
278 }
279 } else {
280 fields.push(conversion);
281 }
282 }
283
284 crate::template_env::render(
288 "conversions/binding_to_core_impl",
289 minijinja::context! {
290 core_path => core_path,
291 binding_name => binding_name,
292 is_newtype => false,
293 newtype_inner_expr => "",
294 builder_mode => optionalized,
295 uses_builder_pattern => uses_builder_pattern,
296 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
297 statements => statements,
298 fields => fields,
299 },
300 )
301}
302
303pub(super) fn gen_optionalized_field_to_core(
308 name: &str,
309 ty: &TypeRef,
310 config: &ConversionConfig,
311 field_is_ir_optional: bool,
312) -> String {
313 match ty {
314 TypeRef::Json if config.json_as_value => {
315 format!("{name}: val.{name}.unwrap_or_default()")
316 }
317 TypeRef::Json => {
318 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
319 }
320 TypeRef::Named(_) => {
321 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
323 }
324 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
325 format!("{name}: val.{name}.map(|v| v as f32).unwrap_or(0.0)")
326 }
327 TypeRef::Primitive(PrimitiveType::F32 | PrimitiveType::F64) => {
328 format!("{name}: val.{name}.unwrap_or(0.0)")
329 }
330 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
331 let core_ty = core_prim_str(p);
332 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
333 }
334 TypeRef::Optional(inner)
335 if config.cast_large_ints_to_i64
336 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
337 {
338 if let TypeRef::Primitive(p) = inner.as_ref() {
339 let core_ty = core_prim_str(p);
340 format!("{name}: val.{name}.map(|v| v as {core_ty})")
341 } else {
342 field_conversion_to_core(name, ty, false)
343 }
344 }
345 TypeRef::Duration if config.cast_large_ints_to_i64 => {
346 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64)).unwrap_or_default()")
347 }
348 TypeRef::Duration => {
349 format!("{name}: val.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
350 }
351 TypeRef::Path => {
352 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
353 }
354 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
355 format!("{name}: val.{name}.map(|s| std::path::PathBuf::from(s))")
357 }
358 TypeRef::Optional(_) => {
359 format!("{name}: val.{name}.map(Some)")
362 }
363 TypeRef::Char => {
365 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
366 }
367 TypeRef::Vec(inner) => match inner.as_ref() {
368 TypeRef::Json => {
369 format!(
370 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
371 )
372 }
373 TypeRef::Named(_) => {
374 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
375 }
376 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
377 let core_ty = core_prim_str(p);
378 format!(
379 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
380 )
381 }
382 _ => format!("{name}: val.{name}.unwrap_or_default()"),
383 },
384 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
385 let k_is_json = matches!(k.as_ref(), TypeRef::Json);
389 let k_expr = if k_is_json {
390 "serde_json::from_str(&k).unwrap_or_default()"
391 } else {
392 "k.into()"
393 };
394 format!(
395 "{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()"
396 )
397 }
398 TypeRef::Map(k, _v) if matches!(k.as_ref(), TypeRef::Json) => {
399 format!(
401 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (serde_json::from_str(&k).unwrap_or_default(), v)).collect()"
402 )
403 }
404 TypeRef::Map(k, v) => {
405 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
407 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
408 let val_is_string_enum = matches!(v.as_ref(), TypeRef::Named(n)
409 if config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)));
410 if field_is_ir_optional {
411 if val_is_string_enum {
413 format!(
414 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect())"
415 )
416 } else if has_named_val {
417 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
418 } else if has_named_key {
419 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
420 } else {
421 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
422 }
423 } else if val_is_string_enum {
424 format!(
425 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect()"
426 )
427 } else if has_named_val {
428 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, v.into())).collect()")
429 } else if has_named_key {
430 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k.into(), v)).collect()")
431 } else {
432 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
433 }
434 }
435 _ => {
436 format!("{name}: val.{name}.unwrap_or_default()")
438 }
439 }
440}
441
442pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
444 match ty {
445 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Unit => {
447 format!("{name}: val.{name}")
448 }
449 TypeRef::Bytes => {
454 if optional {
455 format!("{name}: val.{name}.map(|v| v.to_vec().into())")
456 } else {
457 format!("{name}: val.{name}.to_vec().into()")
458 }
459 }
460 TypeRef::Json => {
462 if optional {
463 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
464 } else {
465 format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
466 }
467 }
468 TypeRef::Char => {
470 if optional {
471 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
472 } else {
473 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
474 }
475 }
476 TypeRef::Duration => {
478 if optional {
479 format!("{name}: val.{name}.map(std::time::Duration::from_millis)")
480 } else {
481 format!("{name}: std::time::Duration::from_millis(val.{name})")
482 }
483 }
484 TypeRef::Path => {
486 if optional {
487 format!("{name}: val.{name}.map(Into::into)")
488 } else {
489 format!("{name}: val.{name}.into()")
490 }
491 }
492 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
495 format!("{name}: val.{name}")
496 }
497 TypeRef::Named(_) => {
498 if optional {
499 format!("{name}: val.{name}.map(Into::into)")
500 } else {
501 format!("{name}: val.{name}.into()")
502 }
503 }
504 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
508 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
509 "serde_json::from_str(&k).unwrap_or_default()"
510 } else {
511 "k.into()"
512 };
513 if optional {
514 format!(
515 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
516 )
517 } else {
518 format!(
519 "{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect()"
520 )
521 }
522 }
523 TypeRef::Optional(inner) => match inner.as_ref() {
525 TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
526 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
527 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
528 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
529 }
530 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
531 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
532 "serde_json::from_str(&k).unwrap_or_default()"
533 } else {
534 "k.into()"
535 };
536 format!(
537 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
538 )
539 }
540 _ => format!("{name}: val.{name}"),
541 },
542 TypeRef::Vec(inner) => match inner.as_ref() {
544 TypeRef::Json => {
545 if optional {
546 format!(
547 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
548 )
549 } else {
550 format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
551 }
552 }
553 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
555 format!("{name}: val.{name}")
556 }
557 TypeRef::Named(_) => {
558 if optional {
559 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
560 } else {
561 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
562 }
563 }
564 _ => format!("{name}: val.{name}"),
565 },
566 TypeRef::Map(k, v) => {
569 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
570 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
571 let has_json_val = matches!(v.as_ref(), TypeRef::Json);
572 let has_json_key = matches!(k.as_ref(), TypeRef::Json);
573 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)));
575 let has_vec_json_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
577 if has_json_val || has_json_key || has_named_key || has_named_val || has_vec_named_val || has_vec_json_val {
578 let k_expr = if has_json_key {
582 "serde_json::from_str(&k).unwrap_or(serde_json::Value::String(k))"
583 } else {
584 "k.into()"
585 };
586 let v_expr = if has_json_val {
587 "serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v))"
588 } else if has_named_val {
589 "v.into()"
590 } else if has_vec_named_val {
591 "v.into_iter().map(Into::into).collect()"
592 } else if has_vec_json_val {
593 "v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()"
594 } else {
595 "v"
596 };
597 if optional {
598 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
599 } else {
600 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
601 }
602 } else {
603 let is_string_map = matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String);
607 if is_string_map {
608 if optional {
609 format!(
610 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
611 )
612 } else {
613 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
614 }
615 } else {
616 if optional {
620 if has_named_val {
621 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
622 } else {
623 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
624 }
625 } else {
626 format!("{name}: val.{name}.into_iter().collect()")
627 }
628 }
629 }
630 }
631 }
632}
633
634pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
636 if optional && matches!(ty, TypeRef::Optional(_)) {
640 let inner_expr = field_conversion_to_core_cfg(name, ty, false, config);
643 if let Some(expr) = inner_expr.strip_prefix(&format!("{name}: ")) {
645 return format!("{name}: ({expr}).map(Some)");
646 }
647 return inner_expr;
648 }
649
650 if config.map_uses_jsvalue {
652 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
653 let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
654 let is_map = matches!(ty, TypeRef::Map(_, _));
655 if is_nested_vec || is_map || is_vec_json {
656 if optional {
657 return format!(
658 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
659 );
660 }
661 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
662 }
663 if let TypeRef::Optional(inner) = ty {
664 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
665 let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
666 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
667 if is_inner_nested || is_inner_map || is_inner_vec_json {
668 return format!(
669 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
670 );
671 }
672 }
673 }
674
675 if config.vec_named_to_string {
679 if let TypeRef::Vec(inner) = ty {
680 if matches!(inner.as_ref(), TypeRef::Named(_)) {
681 if optional {
682 return format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())");
683 }
684 return format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()");
685 }
686 }
687 }
688 if config.map_as_string && matches!(ty, TypeRef::Map(_, _)) {
690 return format!("{name}: Default::default()");
691 }
692 if config.map_as_string {
693 if let TypeRef::Optional(inner) = ty {
694 if matches!(inner.as_ref(), TypeRef::Map(_, _)) {
695 return format!("{name}: Default::default()");
696 }
697 }
698 }
699 if let Some(untagged_names) = config.untagged_data_enum_names {
702 let direct_named = matches!(ty, TypeRef::Named(n) if untagged_names.contains(n));
703 let optional_named = matches!(ty, TypeRef::Optional(inner)
704 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
705 let vec_named = matches!(ty, TypeRef::Vec(inner)
706 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
707 let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
708 if matches!(outer.as_ref(), TypeRef::Vec(inner)
709 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n))));
710 if direct_named {
711 if optional {
712 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
713 }
714 return format!("{name}: serde_json::from_value(val.{name}).unwrap_or_default()");
715 }
716 if optional_named {
717 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
718 }
719 if vec_named {
720 if optional {
721 return format!(
722 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
723 );
724 }
725 return format!("{name}: val.{name}.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect()");
726 }
727 if optional_vec_named {
728 return format!(
729 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
730 );
731 }
732 }
733 if config.json_to_string && matches!(ty, TypeRef::Json) {
735 return format!("{name}: Default::default()");
736 }
737 if config.json_as_value && matches!(ty, TypeRef::Json) {
739 return format!("{name}: val.{name}");
740 }
741 if config.json_as_value {
742 if let TypeRef::Optional(inner) = ty {
743 if matches!(inner.as_ref(), TypeRef::Json) {
744 return format!("{name}: val.{name}");
745 }
746 }
747 if let TypeRef::Vec(inner) = ty {
748 if matches!(inner.as_ref(), TypeRef::Json) {
749 if optional {
750 return format!("{name}: val.{name}.unwrap_or_default()");
751 }
752 return format!("{name}: val.{name}");
753 }
754 }
755 if let TypeRef::Map(_k, v) = ty {
756 if matches!(v.as_ref(), TypeRef::Json) {
757 if optional {
758 return format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())");
759 }
760 return format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v)).collect()");
761 }
762 }
763 }
764 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
766 if optional {
767 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
768 }
769 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
770 }
771 if !config.cast_large_ints_to_i64
772 && !config.cast_large_ints_to_f64
773 && !config.cast_uints_to_i32
774 && !config.cast_f32_to_f64
775 && !config.json_to_string
776 && !config.vec_named_to_string
777 && !config.map_as_string
778 && config.from_binding_skip_types.is_empty()
779 {
780 return field_conversion_to_core(name, ty, optional);
781 }
782 match ty {
784 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
785 let core_ty = core_prim_str(p);
786 if optional {
787 format!("{name}: val.{name}.map(|v| v as {core_ty})")
788 } else {
789 format!("{name}: val.{name} as {core_ty}")
790 }
791 }
792 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
794 if optional {
795 format!("{name}: val.{name}.map(|v| v as f32)")
796 } else {
797 format!("{name}: val.{name} as f32")
798 }
799 }
800 TypeRef::Duration if config.cast_large_ints_to_i64 => {
801 if optional {
802 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
803 } else {
804 format!("{name}: std::time::Duration::from_millis(val.{name} as u64)")
805 }
806 }
807 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
808 if let TypeRef::Primitive(p) = inner.as_ref() {
809 let core_ty = core_prim_str(p);
810 format!("{name}: val.{name}.map(|v| v as {core_ty})")
811 } else {
812 field_conversion_to_core(name, ty, optional)
813 }
814 }
815 TypeRef::Vec(inner)
817 if config.cast_large_ints_to_i64
818 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
819 {
820 if let TypeRef::Primitive(p) = inner.as_ref() {
821 let core_ty = core_prim_str(p);
822 if optional {
823 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
824 } else {
825 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
826 }
827 } else {
828 field_conversion_to_core(name, ty, optional)
829 }
830 }
831 TypeRef::Map(_k, v)
833 if config.cast_large_ints_to_i64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
834 {
835 if let TypeRef::Primitive(p) = v.as_ref() {
836 let core_ty = core_prim_str(p);
837 if optional {
838 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
839 } else {
840 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
841 }
842 } else {
843 field_conversion_to_core(name, ty, optional)
844 }
845 }
846 TypeRef::Vec(inner)
848 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
849 {
850 if optional {
851 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
852 } else {
853 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
854 }
855 }
856 TypeRef::Optional(inner)
858 if config.cast_f32_to_f64
859 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
860 {
861 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
862 }
863 TypeRef::Primitive(p) if config.cast_uints_to_i32 && needs_i32_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::Optional(inner)
874 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
875 {
876 if let TypeRef::Primitive(p) = inner.as_ref() {
877 let core_ty = core_prim_str(p);
878 format!("{name}: val.{name}.map(|v| v as {core_ty})")
879 } else {
880 field_conversion_to_core(name, ty, optional)
881 }
882 }
883 TypeRef::Vec(inner)
885 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
886 {
887 if let TypeRef::Primitive(p) = inner.as_ref() {
888 let core_ty = core_prim_str(p);
889 if optional {
890 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
891 } else {
892 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
893 }
894 } else {
895 field_conversion_to_core(name, ty, optional)
896 }
897 }
898 TypeRef::Primitive(p) if config.cast_large_ints_to_f64 && needs_f64_cast(p) => {
900 let core_ty = core_prim_str(p);
901 if optional {
902 format!("{name}: val.{name}.map(|v| v as {core_ty})")
903 } else {
904 format!("{name}: val.{name} as {core_ty}")
905 }
906 }
907 TypeRef::Optional(inner)
909 if config.cast_large_ints_to_f64
910 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
911 {
912 if let TypeRef::Primitive(p) = inner.as_ref() {
913 let core_ty = core_prim_str(p);
914 format!("{name}: val.{name}.map(|v| v as {core_ty})")
915 } else {
916 field_conversion_to_core(name, ty, optional)
917 }
918 }
919 TypeRef::Vec(inner)
921 if config.cast_large_ints_to_f64
922 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
923 {
924 if let TypeRef::Primitive(p) = inner.as_ref() {
925 let core_ty = core_prim_str(p);
926 if optional {
927 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
928 } else {
929 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
930 }
931 } else {
932 field_conversion_to_core(name, ty, optional)
933 }
934 }
935 TypeRef::Map(_k, v)
937 if config.cast_large_ints_to_f64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
938 {
939 if let TypeRef::Primitive(p) = v.as_ref() {
940 let core_ty = core_prim_str(p);
941 if optional {
942 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
943 } else {
944 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
945 }
946 } else {
947 field_conversion_to_core(name, ty, optional)
948 }
949 }
950 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
953 format!("{name}: Default::default()")
954 }
955 TypeRef::Optional(inner) => match inner.as_ref() {
956 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
957 format!("{name}: Default::default()")
958 }
959 _ => field_conversion_to_core(name, ty, optional),
960 },
961 _ => field_conversion_to_core(name, ty, optional),
963 }
964}
965
966pub fn apply_core_wrapper_to_core(
969 conversion: &str,
970 name: &str,
971 core_wrapper: &CoreWrapper,
972 vec_inner_core_wrapper: &CoreWrapper,
973 optional: bool,
974) -> String {
975 if *vec_inner_core_wrapper == CoreWrapper::Arc {
977 return conversion
978 .replace(
979 ".map(Into::into).collect()",
980 ".map(|v| std::sync::Arc::new(v.into())).collect()",
981 )
982 .replace(
983 "map(|v| v.into_iter().map(Into::into)",
984 "map(|v| v.into_iter().map(|v| std::sync::Arc::new(v.into()))",
985 );
986 }
987
988 match core_wrapper {
989 CoreWrapper::None => conversion.to_string(),
990 CoreWrapper::Cow => {
991 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
995 if optional {
996 format!("{name}: {expr}.map(Into::into)")
997 } else if expr == format!("val.{name}") {
998 format!("{name}: val.{name}.into()")
999 } else if expr == "Default::default()" {
1000 conversion.to_string()
1003 } else {
1004 format!("{name}: ({expr}).into()")
1005 }
1006 } else {
1007 conversion.to_string()
1008 }
1009 }
1010 CoreWrapper::Arc => {
1011 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1013 if expr == "Default::default()" {
1014 conversion.to_string()
1017 } else if optional {
1018 format!("{name}: {expr}.map(|v| std::sync::Arc::new(v))")
1019 } else {
1020 format!("{name}: std::sync::Arc::new({expr})")
1021 }
1022 } else {
1023 conversion.to_string()
1024 }
1025 }
1026 CoreWrapper::Bytes => {
1027 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1033 let already_converted_non_opt =
1034 expr == format!("val.{name}.into()") || expr == format!("val.{name}.to_vec().into()");
1035 let already_converted_opt = expr
1036 .strip_prefix(&format!("val.{name}"))
1037 .map(|s| s == ".map(Into::into)" || s == ".map(|v| v.to_vec().into())")
1038 .unwrap_or(false);
1039 if already_converted_non_opt || already_converted_opt {
1040 conversion.to_string()
1042 } else if optional {
1043 format!("{name}: {expr}.map(Into::into)")
1044 } else if expr == format!("val.{name}") {
1045 format!("{name}: val.{name}.into()")
1046 } else if expr == "Default::default()" {
1047 conversion.to_string()
1050 } else {
1051 format!("{name}: ({expr}).into()")
1052 }
1053 } else {
1054 conversion.to_string()
1055 }
1056 }
1057 CoreWrapper::ArcMutex => {
1058 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1060 if optional {
1061 format!("{name}: {expr}.map(|v| std::sync::Arc::new(std::sync::Mutex::new(v.into())))")
1062 } else if expr == format!("val.{name}") {
1063 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(val.{name}.into()))")
1064 } else {
1065 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(({expr}).into()))")
1066 }
1067 } else {
1068 conversion.to_string()
1069 }
1070 }
1071 }
1072}
1073
1074#[cfg(test)]
1075mod tests {
1076 use super::gen_from_binding_to_core;
1077 use alef_core::ir::{CoreWrapper, DefaultValue, FieldDef, TypeDef, TypeRef};
1078
1079 fn type_with_field(field: FieldDef) -> TypeDef {
1080 TypeDef {
1081 name: "ProcessConfig".to_string(),
1082 rust_path: "crate::ProcessConfig".to_string(),
1083 original_rust_path: String::new(),
1084 fields: vec![field],
1085 methods: vec![],
1086 is_opaque: false,
1087 is_clone: true,
1088 is_copy: false,
1089 doc: String::new(),
1090 cfg: None,
1091 is_trait: false,
1092 has_default: true,
1093 has_stripped_cfg_fields: false,
1094 is_return_type: false,
1095 serde_rename_all: None,
1096 has_serde: true,
1097 super_traits: vec![],
1098 }
1099 }
1100
1101 #[test]
1102 fn sanitized_cow_string_field_converts_to_core() {
1103 let field = FieldDef {
1104 name: "language".to_string(),
1105 ty: TypeRef::String,
1106 optional: false,
1107 default: None,
1108 doc: String::new(),
1109 sanitized: true,
1110 is_boxed: false,
1111 type_rust_path: None,
1112 cfg: None,
1113 typed_default: Some(DefaultValue::Empty),
1114 core_wrapper: CoreWrapper::Cow,
1115 vec_inner_core_wrapper: CoreWrapper::None,
1116 newtype_wrapper: None,
1117 serde_rename: None,
1118 };
1119
1120 let out = gen_from_binding_to_core(&type_with_field(field), "crate");
1121
1122 assert!(out.contains("language: val.language.into()"));
1123 assert!(!out.contains("language: Default::default()"));
1124 }
1125}