alef_codegen/conversions/
binding_to_core.rs1use alef_core::ir::{CoreWrapper, PrimitiveType, TypeDef, TypeRef};
2use std::fmt::Write;
3
4use super::ConversionConfig;
5use super::helpers::{core_prim_str, core_type_path, is_newtype, is_tuple_type_name, needs_i64_cast};
6
7pub fn gen_from_binding_to_core(typ: &TypeDef, core_import: &str) -> String {
10 gen_from_binding_to_core_cfg(typ, core_import, &ConversionConfig::default())
11}
12
13pub fn gen_from_binding_to_core_cfg(typ: &TypeDef, core_import: &str, config: &ConversionConfig) -> String {
15 let core_path = core_type_path(typ, core_import);
16 let binding_name = format!("{}{}", config.type_name_prefix, typ.name);
17 let mut out = String::with_capacity(256);
18 if typ.has_stripped_cfg_fields {
21 writeln!(out, "#[allow(clippy::needless_update)]").ok();
22 }
23 let uses_builder_pattern = config.option_duration_on_defaults
25 && typ.has_default
26 && typ
27 .fields
28 .iter()
29 .any(|f| !f.optional && matches!(f.ty, TypeRef::Duration));
30 if uses_builder_pattern {
31 writeln!(out, "#[allow(clippy::field_reassign_with_default)]").ok();
32 }
33 writeln!(out, "impl From<{binding_name}> for {core_path} {{").ok();
34 writeln!(out, " fn from(val: {binding_name}) -> Self {{").ok();
35
36 if is_newtype(typ) {
38 let field = &typ.fields[0];
39 let inner_expr = match &field.ty {
40 TypeRef::Named(_) => "val._0.into()".to_string(),
41 TypeRef::Path => "val._0.into()".to_string(),
42 TypeRef::Duration => "std::time::Duration::from_millis(val._0)".to_string(),
43 _ => "val._0".to_string(),
44 };
45 writeln!(out, " Self({inner_expr})").ok();
46 writeln!(out, " }}").ok();
47 write!(out, "}}").ok();
48 return out;
49 }
50
51 let has_optionalized_duration = config.option_duration_on_defaults
56 && typ.has_default
57 && typ
58 .fields
59 .iter()
60 .any(|f| !f.optional && matches!(f.ty, TypeRef::Duration));
61
62 if has_optionalized_duration {
63 writeln!(out, " let mut __result = {core_path}::default();").ok();
65 let optionalized = config.optionalize_defaults && typ.has_default;
66 for field in &typ.fields {
67 if field.sanitized {
68 continue;
70 }
71 if !config.exclude_types.is_empty()
73 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types)
74 {
75 continue;
76 }
77 if !field.optional && matches!(field.ty, TypeRef::Duration) {
79 let cast = if config.cast_large_ints_to_i64 { " as u64" } else { "" };
80 writeln!(
81 out,
82 " if let Some(__v) = val.{} {{ __result.{} = std::time::Duration::from_millis(__v{cast}); }}",
83 field.name, field.name
84 )
85 .ok();
86 continue;
87 }
88 let conversion = if optionalized && !field.optional {
89 gen_optionalized_field_to_core(&field.name, &field.ty, config)
90 } else {
91 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
92 };
93 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
95 writeln!(out, " __result.{} = {};", field.name, expr).ok();
96 }
97 }
98 writeln!(out, " __result").ok();
99 writeln!(out, " }}").ok();
100 write!(out, "}}").ok();
101 return out;
102 }
103
104 writeln!(out, " Self {{").ok();
105 let optionalized = config.optionalize_defaults && typ.has_default;
106 for field in &typ.fields {
107 let conversion = if field.sanitized
111 || (!config.exclude_types.is_empty()
112 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types))
113 {
114 format!("{}: Default::default()", field.name)
115 } else if optionalized && !field.optional {
116 gen_optionalized_field_to_core(&field.name, &field.ty, config)
119 } else {
120 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
121 };
122 let conversion = if let Some(newtype_path) = &field.newtype_wrapper {
128 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
129 match &field.ty {
132 TypeRef::Optional(_) => format!("{}: ({expr}).map({newtype_path})", field.name),
133 TypeRef::Vec(_) => {
134 format!("{}: ({expr}).into_iter().map({newtype_path}).collect()", field.name)
135 }
136 _ if field.optional => format!("{}: ({expr}).map({newtype_path})", field.name),
137 _ => format!("{}: {newtype_path}({expr})", field.name),
138 }
139 } else {
140 conversion
141 }
142 } else {
143 conversion
144 };
145 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
147 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
148 if field.optional {
149 format!("{}: {}.map(Box::new)", field.name, expr)
151 } else {
152 format!("{}: Box::new({})", field.name, expr)
153 }
154 } else {
155 conversion
156 }
157 } else {
158 conversion
159 };
160 let conversion = apply_core_wrapper_to_core(
162 &conversion,
163 &field.name,
164 &field.core_wrapper,
165 &field.vec_inner_core_wrapper,
166 field.optional,
167 );
168 writeln!(out, " {conversion},").ok();
169 }
170 if typ.has_stripped_cfg_fields {
172 writeln!(out, " ..Default::default()").ok();
173 }
174 writeln!(out, " }}").ok();
175 writeln!(out, " }}").ok();
176 write!(out, "}}").ok();
177 out
178}
179
180pub(super) fn gen_optionalized_field_to_core(name: &str, ty: &TypeRef, config: &ConversionConfig) -> String {
183 match ty {
184 TypeRef::Json => {
185 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
186 }
187 TypeRef::Named(_) => {
188 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
190 }
191 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
192 format!("{name}: val.{name}.map(|v| v as f32).unwrap_or(0.0)")
193 }
194 TypeRef::Primitive(PrimitiveType::F32 | PrimitiveType::F64) => {
195 format!("{name}: val.{name}.unwrap_or(0.0)")
196 }
197 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
198 let core_ty = core_prim_str(p);
199 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
200 }
201 TypeRef::Duration if config.cast_large_ints_to_i64 => {
202 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64)).unwrap_or_default()")
203 }
204 TypeRef::Duration => {
205 format!("{name}: val.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
206 }
207 TypeRef::Path => {
208 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
209 }
210 TypeRef::Char => {
212 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
213 }
214 TypeRef::Vec(inner) => match inner.as_ref() {
215 TypeRef::Json => {
216 format!(
217 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
218 )
219 }
220 TypeRef::Named(_) => {
221 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
222 }
223 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
224 let core_ty = core_prim_str(p);
225 format!(
226 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
227 )
228 }
229 _ => format!("{name}: val.{name}.unwrap_or_default()"),
230 },
231 TypeRef::Map(_, _) => {
232 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
234 }
235 _ => {
236 format!("{name}: val.{name}.unwrap_or_default()")
238 }
239 }
240}
241
242pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
244 match ty {
245 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Bytes | TypeRef::Unit => {
247 format!("{name}: val.{name}")
248 }
249 TypeRef::Json => {
251 if optional {
252 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
253 } else {
254 format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
255 }
256 }
257 TypeRef::Char => {
259 if optional {
260 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
261 } else {
262 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
263 }
264 }
265 TypeRef::Duration => {
267 if optional {
268 format!("{name}: val.{name}.map(std::time::Duration::from_millis)")
269 } else {
270 format!("{name}: std::time::Duration::from_millis(val.{name})")
271 }
272 }
273 TypeRef::Path => {
275 if optional {
276 format!("{name}: val.{name}.map(Into::into)")
277 } else {
278 format!("{name}: val.{name}.into()")
279 }
280 }
281 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
284 format!("{name}: val.{name}")
285 }
286 TypeRef::Named(_) => {
287 if optional {
288 format!("{name}: val.{name}.map(Into::into)")
289 } else {
290 format!("{name}: val.{name}.into()")
291 }
292 }
293 TypeRef::Optional(inner) => match inner.as_ref() {
295 TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
296 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
297 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
298 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
299 }
300 _ => format!("{name}: val.{name}"),
301 },
302 TypeRef::Vec(inner) => match inner.as_ref() {
304 TypeRef::Json => {
305 if optional {
306 format!(
307 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
308 )
309 } else {
310 format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
311 }
312 }
313 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
315 format!("{name}: val.{name}")
316 }
317 TypeRef::Named(_) => {
318 if optional {
319 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
320 } else {
321 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
322 }
323 }
324 _ => format!("{name}: val.{name}"),
325 },
326 TypeRef::Map(k, v) => {
329 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
330 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
331 let has_json_val = matches!(v.as_ref(), TypeRef::Json);
332 let has_json_key = matches!(k.as_ref(), TypeRef::Json);
333 if has_json_val || has_json_key || has_named_key || has_named_val {
334 let k_expr = if has_json_key {
335 "serde_json::from_str(&k).unwrap_or(serde_json::Value::String(k))"
336 } else if has_named_key {
337 "k.into()"
338 } else {
339 "k"
340 };
341 let v_expr = if has_json_val {
342 "serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v))"
343 } else if has_named_val {
344 "v.into()"
345 } else {
346 "v"
347 };
348 if optional {
349 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
350 } else {
351 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
352 }
353 } else {
354 if optional {
356 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
357 } else {
358 format!("{name}: val.{name}.into_iter().collect()")
359 }
360 }
361 }
362 }
363}
364
365pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
367 if config.map_uses_jsvalue {
369 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
370 let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
371 let is_map = matches!(ty, TypeRef::Map(_, _));
372 if is_nested_vec || is_map || is_vec_json {
373 if optional {
374 return format!(
375 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
376 );
377 }
378 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
379 }
380 if let TypeRef::Optional(inner) = ty {
381 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
382 let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
383 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
384 if is_inner_nested || is_inner_map || is_inner_vec_json {
385 return format!(
386 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
387 );
388 }
389 }
390 }
391
392 if config.json_to_string && matches!(ty, TypeRef::Json) {
394 return format!("{name}: Default::default()");
395 }
396 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
398 if optional {
399 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
400 }
401 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
402 }
403 if !config.cast_large_ints_to_i64 && !config.cast_f32_to_f64 && !config.json_to_string {
404 return field_conversion_to_core(name, ty, optional);
405 }
406 match ty {
408 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
409 let core_ty = core_prim_str(p);
410 if optional {
411 format!("{name}: val.{name}.map(|v| v as {core_ty})")
412 } else {
413 format!("{name}: val.{name} as {core_ty}")
414 }
415 }
416 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
418 if optional {
419 format!("{name}: val.{name}.map(|v| v as f32)")
420 } else {
421 format!("{name}: val.{name} as f32")
422 }
423 }
424 TypeRef::Duration if config.cast_large_ints_to_i64 => {
425 if optional {
426 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
427 } else {
428 format!("{name}: std::time::Duration::from_millis(val.{name} as u64)")
429 }
430 }
431 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
432 if let TypeRef::Primitive(p) = inner.as_ref() {
433 let core_ty = core_prim_str(p);
434 format!("{name}: val.{name}.map(|v| v as {core_ty})")
435 } else {
436 field_conversion_to_core(name, ty, optional)
437 }
438 }
439 TypeRef::Vec(inner)
441 if config.cast_large_ints_to_i64
442 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
443 {
444 if let TypeRef::Primitive(p) = inner.as_ref() {
445 let core_ty = core_prim_str(p);
446 if optional {
447 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
448 } else {
449 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
450 }
451 } else {
452 field_conversion_to_core(name, ty, optional)
453 }
454 }
455 TypeRef::Vec(inner)
457 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
458 {
459 if optional {
460 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
461 } else {
462 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
463 }
464 }
465 TypeRef::Optional(inner)
467 if config.cast_f32_to_f64
468 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
469 {
470 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
471 }
472 _ => field_conversion_to_core(name, ty, optional),
474 }
475}
476
477fn apply_core_wrapper_to_core(
480 conversion: &str,
481 name: &str,
482 core_wrapper: &CoreWrapper,
483 vec_inner_core_wrapper: &CoreWrapper,
484 optional: bool,
485) -> String {
486 if *vec_inner_core_wrapper == CoreWrapper::Arc {
488 return conversion
489 .replace(
490 ".map(Into::into).collect()",
491 ".map(|v| std::sync::Arc::new(v.into())).collect()",
492 )
493 .replace(
494 "map(|v| v.into_iter().map(Into::into)",
495 "map(|v| v.into_iter().map(|v| std::sync::Arc::new(v.into()))",
496 );
497 }
498
499 match core_wrapper {
500 CoreWrapper::None => conversion.to_string(),
501 CoreWrapper::Cow => {
502 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
506 if optional {
507 format!("{name}: {expr}.map(Into::into)")
508 } else if expr == format!("val.{name}") {
509 format!("{name}: val.{name}.into()")
510 } else if expr == "Default::default()" {
511 conversion.to_string()
514 } else {
515 format!("{name}: ({expr}).into()")
516 }
517 } else {
518 conversion.to_string()
519 }
520 }
521 CoreWrapper::Arc => {
522 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
524 if expr == "Default::default()" {
525 conversion.to_string()
528 } else if optional {
529 format!("{name}: {expr}.map(|v| std::sync::Arc::new(v))")
530 } else {
531 format!("{name}: std::sync::Arc::new({expr})")
532 }
533 } else {
534 conversion.to_string()
535 }
536 }
537 CoreWrapper::Bytes => {
538 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
540 if optional {
541 format!("{name}: {expr}.map(Into::into)")
542 } else if expr == format!("val.{name}") {
543 format!("{name}: val.{name}.into()")
544 } else if expr == "Default::default()" {
545 conversion.to_string()
548 } else {
549 format!("{name}: ({expr}).into()")
550 }
551 } else {
552 conversion.to_string()
553 }
554 }
555 }
556}