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 writeln!(out, "impl From<{binding_name}> for {core_path} {{").ok();
24 writeln!(out, " fn from(val: {binding_name}) -> Self {{").ok();
25
26 if is_newtype(typ) {
28 let field = &typ.fields[0];
29 let inner_expr = match &field.ty {
30 TypeRef::Named(_) => "val._0.into()".to_string(),
31 TypeRef::Path => "val._0.into()".to_string(),
32 TypeRef::Duration => "std::time::Duration::from_millis(val._0)".to_string(),
33 _ => "val._0".to_string(),
34 };
35 writeln!(out, " Self({inner_expr})").ok();
36 writeln!(out, " }}").ok();
37 write!(out, "}}").ok();
38 return out;
39 }
40
41 let has_optionalized_duration = config.option_duration_on_defaults
46 && typ.has_default
47 && typ
48 .fields
49 .iter()
50 .any(|f| !f.optional && matches!(f.ty, TypeRef::Duration));
51
52 if has_optionalized_duration {
53 writeln!(out, " let mut __result = {core_path}::default();").ok();
55 let optionalized = config.optionalize_defaults && typ.has_default;
56 for field in &typ.fields {
57 if field.sanitized {
58 continue;
60 }
61 if !field.optional && matches!(field.ty, TypeRef::Duration) {
63 let cast = if config.cast_large_ints_to_i64 { " as u64" } else { "" };
64 writeln!(
65 out,
66 " if let Some(__v) = val.{} {{ __result.{} = std::time::Duration::from_millis(__v{cast}); }}",
67 field.name, field.name
68 )
69 .ok();
70 continue;
71 }
72 let conversion = if optionalized && !field.optional {
73 gen_optionalized_field_to_core(&field.name, &field.ty, config)
74 } else {
75 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
76 };
77 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
79 writeln!(out, " __result.{} = {};", field.name, expr).ok();
80 }
81 }
82 writeln!(out, " __result").ok();
83 writeln!(out, " }}").ok();
84 write!(out, "}}").ok();
85 return out;
86 }
87
88 writeln!(out, " Self {{").ok();
89 let optionalized = config.optionalize_defaults && typ.has_default;
90 for field in &typ.fields {
91 let conversion = if field.sanitized {
92 format!("{}: Default::default()", field.name)
93 } else if optionalized && !field.optional {
94 gen_optionalized_field_to_core(&field.name, &field.ty, config)
97 } else {
98 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
99 };
100 let conversion = if let Some(newtype_path) = &field.newtype_wrapper {
106 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
107 match &field.ty {
110 TypeRef::Optional(_) => format!("{}: ({expr}).map({newtype_path})", field.name),
111 TypeRef::Vec(_) => {
112 format!("{}: ({expr}).into_iter().map({newtype_path}).collect()", field.name)
113 }
114 _ if field.optional => format!("{}: ({expr}).map({newtype_path})", field.name),
115 _ => format!("{}: {newtype_path}({expr})", field.name),
116 }
117 } else {
118 conversion
119 }
120 } else {
121 conversion
122 };
123 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
125 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
126 if field.optional {
127 format!("{}: {}.map(Box::new)", field.name, expr)
129 } else {
130 format!("{}: Box::new({})", field.name, expr)
131 }
132 } else {
133 conversion
134 }
135 } else {
136 conversion
137 };
138 let conversion = apply_core_wrapper_to_core(
140 &conversion,
141 &field.name,
142 &field.core_wrapper,
143 &field.vec_inner_core_wrapper,
144 field.optional,
145 );
146 writeln!(out, " {conversion},").ok();
147 }
148 if typ.has_stripped_cfg_fields {
150 writeln!(out, " ..Default::default()").ok();
151 }
152 writeln!(out, " }}").ok();
153 writeln!(out, " }}").ok();
154 write!(out, "}}").ok();
155 out
156}
157
158pub(super) fn gen_optionalized_field_to_core(name: &str, ty: &TypeRef, config: &ConversionConfig) -> String {
161 match ty {
162 TypeRef::Json => {
163 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
164 }
165 TypeRef::Named(_) => {
166 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
168 }
169 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
170 format!("{name}: val.{name}.map(|v| v as f32).unwrap_or(0.0)")
171 }
172 TypeRef::Primitive(PrimitiveType::F32 | PrimitiveType::F64) => {
173 format!("{name}: val.{name}.unwrap_or(0.0)")
174 }
175 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
176 let core_ty = core_prim_str(p);
177 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
178 }
179 TypeRef::Duration if config.cast_large_ints_to_i64 => {
180 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64)).unwrap_or_default()")
181 }
182 TypeRef::Duration => {
183 format!("{name}: val.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
184 }
185 TypeRef::Path => {
186 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
187 }
188 TypeRef::Char => {
190 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
191 }
192 TypeRef::Vec(inner) => match inner.as_ref() {
193 TypeRef::Json => {
194 format!(
195 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
196 )
197 }
198 TypeRef::Named(_) => {
199 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
200 }
201 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
202 let core_ty = core_prim_str(p);
203 format!(
204 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
205 )
206 }
207 _ => format!("{name}: val.{name}.unwrap_or_default()"),
208 },
209 TypeRef::Map(_, _) => {
210 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
212 }
213 _ => {
214 format!("{name}: val.{name}.unwrap_or_default()")
216 }
217 }
218}
219
220pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
222 match ty {
223 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Bytes | TypeRef::Unit => {
225 format!("{name}: val.{name}")
226 }
227 TypeRef::Json => {
229 if optional {
230 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
231 } else {
232 format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
233 }
234 }
235 TypeRef::Char => {
237 if optional {
238 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
239 } else {
240 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
241 }
242 }
243 TypeRef::Duration => {
245 if optional {
246 format!("{name}: val.{name}.map(std::time::Duration::from_millis)")
247 } else {
248 format!("{name}: std::time::Duration::from_millis(val.{name})")
249 }
250 }
251 TypeRef::Path => {
253 if optional {
254 format!("{name}: val.{name}.map(Into::into)")
255 } else {
256 format!("{name}: val.{name}.into()")
257 }
258 }
259 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
262 format!("{name}: val.{name}")
263 }
264 TypeRef::Named(_) => {
265 if optional {
266 format!("{name}: val.{name}.map(Into::into)")
267 } else {
268 format!("{name}: val.{name}.into()")
269 }
270 }
271 TypeRef::Optional(inner) => match inner.as_ref() {
273 TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
274 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
275 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
276 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
277 }
278 _ => format!("{name}: val.{name}"),
279 },
280 TypeRef::Vec(inner) => match inner.as_ref() {
282 TypeRef::Json => {
283 if optional {
284 format!(
285 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
286 )
287 } else {
288 format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
289 }
290 }
291 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
293 format!("{name}: val.{name}")
294 }
295 TypeRef::Named(_) => {
296 if optional {
297 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
298 } else {
299 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
300 }
301 }
302 _ => format!("{name}: val.{name}"),
303 },
304 TypeRef::Map(k, v) => {
307 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
308 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
309 let has_json_val = matches!(v.as_ref(), TypeRef::Json);
310 let has_json_key = matches!(k.as_ref(), TypeRef::Json);
311 if has_json_val || has_json_key || has_named_key || has_named_val {
312 let k_expr = if has_json_key {
313 "serde_json::from_str(&k).unwrap_or(serde_json::Value::String(k))"
314 } else if has_named_key {
315 "k.into()"
316 } else {
317 "k"
318 };
319 let v_expr = if has_json_val {
320 "serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v))"
321 } else if has_named_val {
322 "v.into()"
323 } else {
324 "v"
325 };
326 if optional {
327 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
328 } else {
329 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
330 }
331 } else {
332 if optional {
334 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
335 } else {
336 format!("{name}: val.{name}.into_iter().collect()")
337 }
338 }
339 }
340 }
341}
342
343pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
345 if config.map_uses_jsvalue {
347 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
348 let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
349 let is_map = matches!(ty, TypeRef::Map(_, _));
350 if is_nested_vec || is_map || is_vec_json {
351 if optional {
352 return format!(
353 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
354 );
355 }
356 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
357 }
358 if let TypeRef::Optional(inner) = ty {
359 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
360 let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
361 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
362 if is_inner_nested || is_inner_map || is_inner_vec_json {
363 return format!(
364 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
365 );
366 }
367 }
368 }
369
370 if config.json_to_string && matches!(ty, TypeRef::Json) {
372 return format!("{name}: Default::default()");
373 }
374 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
376 if optional {
377 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
378 }
379 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
380 }
381 if !config.cast_large_ints_to_i64 && !config.cast_f32_to_f64 && !config.json_to_string {
382 return field_conversion_to_core(name, ty, optional);
383 }
384 match ty {
386 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
387 let core_ty = core_prim_str(p);
388 if optional {
389 format!("{name}: val.{name}.map(|v| v as {core_ty})")
390 } else {
391 format!("{name}: val.{name} as {core_ty}")
392 }
393 }
394 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
396 if optional {
397 format!("{name}: val.{name}.map(|v| v as f32)")
398 } else {
399 format!("{name}: val.{name} as f32")
400 }
401 }
402 TypeRef::Duration if config.cast_large_ints_to_i64 => {
403 if optional {
404 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
405 } else {
406 format!("{name}: std::time::Duration::from_millis(val.{name} as u64)")
407 }
408 }
409 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
410 if let TypeRef::Primitive(p) = inner.as_ref() {
411 let core_ty = core_prim_str(p);
412 format!("{name}: val.{name}.map(|v| v as {core_ty})")
413 } else {
414 field_conversion_to_core(name, ty, optional)
415 }
416 }
417 TypeRef::Vec(inner)
419 if config.cast_large_ints_to_i64
420 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
421 {
422 if let TypeRef::Primitive(p) = inner.as_ref() {
423 let core_ty = core_prim_str(p);
424 if optional {
425 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
426 } else {
427 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
428 }
429 } else {
430 field_conversion_to_core(name, ty, optional)
431 }
432 }
433 TypeRef::Vec(inner)
435 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
436 {
437 if optional {
438 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
439 } else {
440 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
441 }
442 }
443 TypeRef::Optional(inner)
445 if config.cast_f32_to_f64
446 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
447 {
448 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
449 }
450 _ => field_conversion_to_core(name, ty, optional),
452 }
453}
454
455fn apply_core_wrapper_to_core(
458 conversion: &str,
459 name: &str,
460 core_wrapper: &CoreWrapper,
461 vec_inner_core_wrapper: &CoreWrapper,
462 optional: bool,
463) -> String {
464 if *vec_inner_core_wrapper == CoreWrapper::Arc {
466 return conversion
467 .replace(
468 ".map(Into::into).collect()",
469 ".map(|v| std::sync::Arc::new(v.into())).collect()",
470 )
471 .replace(
472 "map(|v| v.into_iter().map(Into::into)",
473 "map(|v| v.into_iter().map(|v| std::sync::Arc::new(v.into()))",
474 );
475 }
476
477 match core_wrapper {
478 CoreWrapper::None => conversion.to_string(),
479 CoreWrapper::Cow => {
480 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
484 if optional {
485 format!("{name}: {expr}.map(Into::into)")
486 } else if expr == format!("val.{name}") {
487 format!("{name}: val.{name}.into()")
488 } else if expr == "Default::default()" {
489 conversion.to_string()
492 } else {
493 format!("{name}: ({expr}).into()")
494 }
495 } else {
496 conversion.to_string()
497 }
498 }
499 CoreWrapper::Arc => {
500 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
502 if expr == "Default::default()" {
503 conversion.to_string()
506 } else if optional {
507 format!("{name}: {expr}.map(|v| std::sync::Arc::new(v))")
508 } else {
509 format!("{name}: std::sync::Arc::new({expr})")
510 }
511 } else {
512 conversion.to_string()
513 }
514 }
515 CoreWrapper::Bytes => {
516 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
518 if optional {
519 format!("{name}: {expr}.map(Into::into)")
520 } else if expr == format!("val.{name}") {
521 format!("{name}: val.{name}.into()")
522 } else if expr == "Default::default()" {
523 conversion.to_string()
526 } else {
527 format!("{name}: ({expr}).into()")
528 }
529 } else {
530 conversion.to_string()
531 }
532 }
533 }
534}