1use ahash::AHashSet;
2use alef_core::ir::{CoreWrapper, PrimitiveType, TypeDef, TypeRef};
3use std::fmt::Write;
4
5use super::ConversionConfig;
6use super::binding_to_core::field_conversion_to_core;
7use super::helpers::is_newtype;
8use super::helpers::{binding_prim_str, core_type_path, needs_i64_cast};
9
10pub fn gen_from_core_to_binding(typ: &TypeDef, core_import: &str, opaque_types: &AHashSet<String>) -> String {
12 gen_from_core_to_binding_cfg(typ, core_import, opaque_types, &ConversionConfig::default())
13}
14
15pub fn gen_from_core_to_binding_cfg(
17 typ: &TypeDef,
18 core_import: &str,
19 opaque_types: &AHashSet<String>,
20 config: &ConversionConfig,
21) -> String {
22 let core_path = core_type_path(typ, core_import);
23 let binding_name = format!("{}{}", config.type_name_prefix, typ.name);
24 let mut out = String::with_capacity(256);
25 writeln!(out, "impl From<{core_path}> for {binding_name} {{").ok();
26 writeln!(out, " fn from(val: {core_path}) -> Self {{").ok();
27
28 if is_newtype(typ) {
30 let field = &typ.fields[0];
31 let inner_expr = match &field.ty {
32 TypeRef::Named(_) => "val.0.into()".to_string(),
33 TypeRef::Path => "val.0.to_string_lossy().to_string()".to_string(),
34 TypeRef::Duration => "val.0.as_millis() as u64".to_string(),
35 _ => "val.0".to_string(),
36 };
37 writeln!(out, " Self {{ _0: {inner_expr} }}").ok();
38 writeln!(out, " }}").ok();
39 write!(out, "}}").ok();
40 return out;
41 }
42
43 let optionalized = config.optionalize_defaults && typ.has_default;
44 writeln!(out, " Self {{").ok();
45 for field in &typ.fields {
46 if !config.exclude_types.is_empty()
48 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types)
49 {
50 continue;
51 }
52 let base_conversion = field_conversion_from_core_cfg(
53 &field.name,
54 &field.ty,
55 field.optional,
56 field.sanitized,
57 opaque_types,
58 config,
59 );
60 let base_conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
62 if field.optional {
63 let src = format!("{}: val.{}.map(Into::into)", field.name, field.name);
65 let dst = format!("{}: val.{}.map(|v| (*v).into())", field.name, field.name);
66 if base_conversion == src { dst } else { base_conversion }
67 } else {
68 base_conversion.replace(&format!("val.{}", field.name), &format!("(*val.{})", field.name))
70 }
71 } else {
72 base_conversion
73 };
74 let base_conversion = if field.newtype_wrapper.is_some() {
80 match &field.ty {
81 TypeRef::Optional(_) => {
82 base_conversion.replace(
84 &format!("val.{}", field.name),
85 &format!("val.{}.map(|v| v.0)", field.name),
86 )
87 }
88 TypeRef::Vec(_) => {
89 base_conversion.replace(
91 &format!("val.{}", field.name),
92 &format!("val.{}.iter().map(|v| v.0).collect::<Vec<_>>()", field.name),
93 )
94 }
95 _ if field.optional => base_conversion.replace(
98 &format!("val.{}", field.name),
99 &format!("val.{}.map(|v| v.0)", field.name),
100 ),
101 _ => {
102 base_conversion.replace(&format!("val.{}", field.name), &format!("val.{}.0", field.name))
104 }
105 }
106 } else {
107 base_conversion
108 };
109 let needs_some_wrap = (optionalized && !field.optional)
112 || (config.option_duration_on_defaults
113 && typ.has_default
114 && !field.optional
115 && matches!(field.ty, TypeRef::Duration));
116 let conversion = if needs_some_wrap {
117 if let Some(expr) = base_conversion.strip_prefix(&format!("{}: ", field.name)) {
119 format!("{}: Some({})", field.name, expr)
120 } else {
121 base_conversion
122 }
123 } else {
124 base_conversion
125 };
126 let conversion = apply_core_wrapper_from_core(
128 &conversion,
129 &field.name,
130 &field.core_wrapper,
131 &field.vec_inner_core_wrapper,
132 field.optional,
133 );
134 if field.cfg.is_some() {
136 continue;
137 }
138 writeln!(out, " {conversion},").ok();
139 }
140
141 writeln!(out, " }}").ok();
142 writeln!(out, " }}").ok();
143 write!(out, "}}").ok();
144 out
145}
146
147pub fn field_conversion_from_core(
150 name: &str,
151 ty: &TypeRef,
152 optional: bool,
153 sanitized: bool,
154 opaque_types: &AHashSet<String>,
155) -> String {
156 if sanitized {
159 if let TypeRef::Map(k, v) = ty {
161 if matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String) {
162 if optional {
163 return format!(
164 "{name}: val.{name}.as_ref().map(|m| m.iter().map(|(k, v)| (format!(\"{{:?}}\", k), format!(\"{{:?}}\", v))).collect())"
165 );
166 }
167 return format!(
168 "{name}: val.{name}.into_iter().map(|(k, v)| (format!(\"{{:?}}\", k), format!(\"{{:?}}\", v))).collect()"
169 );
170 }
171 }
172 if let TypeRef::Vec(inner) = ty {
174 if matches!(inner.as_ref(), TypeRef::String) {
175 if optional {
176 return format!(
177 "{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| format!(\"{{:?}}\", i)).collect())"
178 );
179 }
180 return format!("{name}: val.{name}.iter().map(|i| format!(\"{{:?}}\", i)).collect()");
181 }
182 }
183 if let TypeRef::Optional(opt_inner) = ty {
186 if let TypeRef::Vec(vec_inner) = opt_inner.as_ref() {
187 if matches!(vec_inner.as_ref(), TypeRef::String) {
188 return format!(
189 "{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| format!(\"{{:?}}\", i)).collect())"
190 );
191 }
192 }
193 }
194 if matches!(ty, TypeRef::String) {
197 if optional {
198 return format!("{name}: val.{name}.as_ref().map(|v| format!(\"{{:?}}\", v))");
199 }
200 return format!("{name}: format!(\"{{:?}}\", val.{name})");
201 }
202 if optional {
204 return format!("{name}: val.{name}.as_ref().map(|v| format!(\"{{:?}}\", v))");
205 }
206 return format!("{name}: format!(\"{{:?}}\", val.{name})");
207 }
208 match ty {
209 TypeRef::Duration => {
211 if optional {
212 return format!("{name}: val.{name}.map(|d| d.as_millis() as u64)");
213 }
214 format!("{name}: val.{name}.as_millis() as u64")
215 }
216 TypeRef::Path => {
218 if optional {
219 format!("{name}: val.{name}.map(|p| p.to_string_lossy().to_string())")
220 } else {
221 format!("{name}: val.{name}.to_string_lossy().to_string()")
222 }
223 }
224 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
225 format!("{name}: val.{name}.map(|p| p.to_string_lossy().to_string())")
226 }
227 TypeRef::Char => {
229 if optional {
230 format!("{name}: val.{name}.map(|c| c.to_string())")
231 } else {
232 format!("{name}: val.{name}.to_string()")
233 }
234 }
235 TypeRef::Bytes => {
237 if optional {
238 format!("{name}: val.{name}.map(|v| v.to_vec())")
239 } else {
240 format!("{name}: val.{name}.to_vec()")
241 }
242 }
243 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
245 if optional {
246 format!("{name}: val.{name}.map(|v| {n} {{ inner: Arc::new(v) }})")
247 } else {
248 format!("{name}: {n} {{ inner: Arc::new(val.{name}) }}")
249 }
250 }
251 TypeRef::Json => {
253 if optional {
254 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
255 } else {
256 format!("{name}: val.{name}.to_string()")
257 }
258 }
259 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Json) => {
260 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
261 }
262 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json) => {
263 if optional {
264 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| i.to_string()).collect())")
265 } else {
266 format!("{name}: val.{name}.iter().map(ToString::to_string).collect()")
267 }
268 }
269 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
271 let k_is_json = matches!(k.as_ref(), TypeRef::Json);
272 let k_expr = if k_is_json { "k.to_string()" } else { "k" };
273 if optional {
274 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, v.to_string())).collect())")
275 } else {
276 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, v.to_string())).collect()")
277 }
278 }
279 TypeRef::Map(k, _v) if matches!(k.as_ref(), TypeRef::Json) => {
281 if optional {
282 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.to_string(), v)).collect())")
283 } else {
284 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.to_string(), v)).collect()")
285 }
286 }
287 _ => field_conversion_to_core(name, ty, optional),
289 }
290}
291
292pub fn field_conversion_from_core_cfg(
294 name: &str,
295 ty: &TypeRef,
296 optional: bool,
297 sanitized: bool,
298 opaque_types: &AHashSet<String>,
299 config: &ConversionConfig,
300) -> String {
301 if sanitized {
306 if config.map_uses_jsvalue {
307 if let TypeRef::Map(k, v) = ty {
309 if matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String) {
310 if optional {
311 return format!(
312 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
313 );
314 }
315 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
316 }
317 }
318 if let TypeRef::Vec(inner) = ty {
320 if matches!(inner.as_ref(), TypeRef::Json) {
321 if optional {
322 return format!(
323 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
324 );
325 }
326 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
327 }
328 }
329 }
330 return field_conversion_from_core(name, ty, optional, sanitized, opaque_types);
331 }
332
333 if config.map_uses_jsvalue {
335 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
336 let is_map = matches!(ty, TypeRef::Map(_, _));
337 if is_nested_vec || is_map {
338 if optional {
339 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())");
340 }
341 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
342 }
343 if let TypeRef::Optional(inner) = ty {
344 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
345 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
346 if is_inner_nested || is_inner_map {
347 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())");
348 }
349 }
350 }
351
352 let prefix = config.type_name_prefix;
353 let is_enum_string = |n: &str| -> bool { config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)) };
354
355 match ty {
356 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
358 let cast_to = binding_prim_str(p);
359 if optional {
360 format!("{name}: val.{name}.map(|v| v as {cast_to})")
361 } else {
362 format!("{name}: val.{name} as {cast_to}")
363 }
364 }
365 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
367 if optional {
368 format!("{name}: val.{name}.map(|v| v as f64)")
369 } else {
370 format!("{name}: val.{name} as f64")
371 }
372 }
373 TypeRef::Duration if config.cast_large_ints_to_i64 => {
375 if optional {
376 format!("{name}: val.{name}.map(|d| d.as_millis() as u64 as i64)")
377 } else {
378 format!("{name}: val.{name}.as_millis() as u64 as i64")
379 }
380 }
381 TypeRef::Named(n) if opaque_types.contains(n.as_str()) && !prefix.is_empty() => {
383 let prefixed = format!("{prefix}{n}");
384 if optional {
385 format!("{name}: val.{name}.map(|v| {prefixed} {{ inner: Arc::new(v) }})")
386 } else {
387 format!("{name}: {prefixed} {{ inner: Arc::new(val.{name}) }}")
388 }
389 }
390 TypeRef::Named(n) if is_enum_string(n) => {
392 if optional {
395 format!(
396 "{name}: val.{name}.as_ref().map(|v| serde_json::to_value(v).ok().and_then(|s| s.as_str().map(String::from)).unwrap_or_default())"
397 )
398 } else {
399 format!(
400 "{name}: serde_json::to_value(val.{name}).ok().and_then(|s| s.as_str().map(String::from)).unwrap_or_default()"
401 )
402 }
403 }
404 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if is_enum_string(n)) => {
406 if optional {
407 format!(
408 "{name}: val.{name}.as_ref().map(|v| v.iter().map(|x| serde_json::to_value(x).ok().and_then(|s| s.as_str().map(String::from)).unwrap_or_default()).collect())"
409 )
410 } else {
411 format!(
412 "{name}: val.{name}.iter().map(|v| serde_json::to_value(v).ok().and_then(|s| s.as_str().map(String::from)).unwrap_or_default()).collect()"
413 )
414 }
415 }
416 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(n) if is_enum_string(n))) =>
418 {
419 format!(
420 "{name}: val.{name}.as_ref().map(|v| v.iter().map(|x| serde_json::to_value(x).ok().and_then(|s| s.as_str().map(String::from)).unwrap_or_default()).collect())"
421 )
422 }
423 TypeRef::Vec(inner)
425 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
426 {
427 if optional {
428 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as f64).collect())")
429 } else {
430 format!("{name}: val.{name}.iter().map(|&v| v as f64).collect()")
431 }
432 }
433 TypeRef::Optional(inner)
435 if config.cast_f32_to_f64
436 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
437 {
438 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as f64).collect())")
439 }
440 TypeRef::Optional(inner)
442 if config.cast_large_ints_to_i64
443 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
444 {
445 if let TypeRef::Primitive(p) = inner.as_ref() {
446 let cast_to = binding_prim_str(p);
447 format!("{name}: val.{name}.map(|v| v as {cast_to})")
448 } else {
449 field_conversion_from_core(name, ty, optional, sanitized, opaque_types)
450 }
451 }
452 TypeRef::Vec(inner)
454 if config.cast_large_ints_to_i64
455 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
456 {
457 if let TypeRef::Primitive(p) = inner.as_ref() {
458 let cast_to = binding_prim_str(p);
459 if optional {
460 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as {cast_to}).collect())")
461 } else {
462 format!("{name}: val.{name}.iter().map(|&v| v as {cast_to}).collect()")
463 }
464 } else {
465 field_conversion_from_core(name, ty, optional, sanitized, opaque_types)
466 }
467 }
468 TypeRef::Json if config.json_to_string => {
470 if optional {
471 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
472 } else {
473 format!("{name}: val.{name}.to_string()")
474 }
475 }
476 TypeRef::Json if config.map_uses_jsvalue => {
478 if optional {
479 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
480 } else {
481 format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)")
482 }
483 }
484 TypeRef::Vec(inner) if config.map_uses_jsvalue && matches!(inner.as_ref(), TypeRef::Json) => {
486 if optional {
487 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
488 } else {
489 format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)")
490 }
491 }
492 TypeRef::Optional(inner)
494 if config.map_uses_jsvalue
495 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json)) =>
496 {
497 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
498 }
499 _ => field_conversion_from_core(name, ty, optional, sanitized, opaque_types),
501 }
502}
503
504fn apply_core_wrapper_from_core(
507 conversion: &str,
508 name: &str,
509 core_wrapper: &CoreWrapper,
510 vec_inner_core_wrapper: &CoreWrapper,
511 optional: bool,
512) -> String {
513 if *vec_inner_core_wrapper == CoreWrapper::Arc {
515 return conversion
516 .replace(".map(Into::into).collect()", ".map(|v| (*v).clone().into()).collect()")
517 .replace(
518 "map(|v| v.into_iter().map(Into::into)",
519 "map(|v| v.into_iter().map(|v| (*v).clone().into())",
520 );
521 }
522
523 match core_wrapper {
524 CoreWrapper::None => conversion.to_string(),
525 CoreWrapper::Cow => {
526 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
531 if optional {
532 conversion.to_string()
534 } else if expr == format!("val.{name}") {
535 format!("{name}: val.{name}.into_owned()")
536 } else {
537 conversion.to_string()
538 }
539 } else {
540 conversion.to_string()
541 }
542 }
543 CoreWrapper::Arc => {
544 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
546 if optional {
547 format!("{name}: {expr}.map(|v| (*v).clone().into())")
548 } else {
549 let unwrapped = expr.replace(&format!("val.{name}"), &format!("(*val.{name}).clone()"));
550 format!("{name}: {unwrapped}")
551 }
552 } else {
553 conversion.to_string()
554 }
555 }
556 CoreWrapper::Bytes => {
557 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
559 if optional {
560 format!("{name}: {expr}.map(|v| v.to_vec())")
561 } else if expr == format!("val.{name}") {
562 format!("{name}: val.{name}.to_vec()")
563 } else {
564 conversion.to_string()
565 }
566 } else {
567 conversion.to_string()
568 }
569 }
570 }
571}