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 let base_conversion = field_conversion_from_core_cfg(
47 &field.name,
48 &field.ty,
49 field.optional,
50 field.sanitized,
51 opaque_types,
52 config,
53 );
54 let base_conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
56 if field.optional {
57 let src = format!("{}: val.{}.map(Into::into)", field.name, field.name);
59 let dst = format!("{}: val.{}.map(|v| (*v).into())", field.name, field.name);
60 if base_conversion == src { dst } else { base_conversion }
61 } else {
62 base_conversion.replace(&format!("val.{}", field.name), &format!("(*val.{})", field.name))
64 }
65 } else {
66 base_conversion
67 };
68 let base_conversion = if field.newtype_wrapper.is_some() {
74 match &field.ty {
75 TypeRef::Optional(_) => {
76 base_conversion.replace(
78 &format!("val.{}", field.name),
79 &format!("val.{}.map(|v| v.0)", field.name),
80 )
81 }
82 TypeRef::Vec(_) => {
83 base_conversion.replace(
85 &format!("val.{}", field.name),
86 &format!("val.{}.iter().map(|v| v.0).collect::<Vec<_>>()", field.name),
87 )
88 }
89 _ if field.optional => base_conversion.replace(
92 &format!("val.{}", field.name),
93 &format!("val.{}.map(|v| v.0)", field.name),
94 ),
95 _ => {
96 base_conversion.replace(&format!("val.{}", field.name), &format!("val.{}.0", field.name))
98 }
99 }
100 } else {
101 base_conversion
102 };
103 let needs_some_wrap = (optionalized && !field.optional)
106 || (config.option_duration_on_defaults
107 && typ.has_default
108 && !field.optional
109 && matches!(field.ty, TypeRef::Duration));
110 let conversion = if needs_some_wrap {
111 if let Some(expr) = base_conversion.strip_prefix(&format!("{}: ", field.name)) {
113 format!("{}: Some({})", field.name, expr)
114 } else {
115 base_conversion
116 }
117 } else {
118 base_conversion
119 };
120 let conversion = apply_core_wrapper_from_core(
122 &conversion,
123 &field.name,
124 &field.core_wrapper,
125 &field.vec_inner_core_wrapper,
126 field.optional,
127 );
128 if field.cfg.is_some() {
130 continue;
131 }
132 writeln!(out, " {conversion},").ok();
133 }
134
135 writeln!(out, " }}").ok();
136 writeln!(out, " }}").ok();
137 write!(out, "}}").ok();
138 out
139}
140
141pub fn field_conversion_from_core(
144 name: &str,
145 ty: &TypeRef,
146 optional: bool,
147 sanitized: bool,
148 opaque_types: &AHashSet<String>,
149) -> String {
150 if sanitized {
153 if let TypeRef::Map(k, v) = ty {
155 if matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String) {
156 if optional {
157 return format!(
158 "{name}: val.{name}.as_ref().map(|m| m.iter().map(|(k, v)| (format!(\"{{:?}}\", k), format!(\"{{:?}}\", v))).collect())"
159 );
160 }
161 return format!(
162 "{name}: val.{name}.into_iter().map(|(k, v)| (format!(\"{{:?}}\", k), format!(\"{{:?}}\", v))).collect()"
163 );
164 }
165 }
166 if let TypeRef::Vec(inner) = ty {
168 if matches!(inner.as_ref(), TypeRef::String) {
169 if optional {
170 return format!(
171 "{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| format!(\"{{:?}}\", i)).collect())"
172 );
173 }
174 return format!("{name}: val.{name}.iter().map(|i| format!(\"{{:?}}\", i)).collect()");
175 }
176 }
177 if let TypeRef::Optional(opt_inner) = ty {
180 if let TypeRef::Vec(vec_inner) = opt_inner.as_ref() {
181 if matches!(vec_inner.as_ref(), TypeRef::String) {
182 return format!(
183 "{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| format!(\"{{:?}}\", i)).collect())"
184 );
185 }
186 }
187 }
188 if matches!(ty, TypeRef::String) {
191 if optional {
192 return format!("{name}: val.{name}.as_ref().map(|v| format!(\"{{:?}}\", v))");
193 }
194 return format!("{name}: format!(\"{{:?}}\", val.{name})");
195 }
196 if optional {
198 return format!("{name}: val.{name}.as_ref().map(|v| format!(\"{{:?}}\", v))");
199 }
200 return format!("{name}: format!(\"{{:?}}\", val.{name})");
201 }
202 match ty {
203 TypeRef::Duration => {
205 if optional {
206 return format!("{name}: val.{name}.map(|d| d.as_millis() as u64)");
207 }
208 format!("{name}: val.{name}.as_millis() as u64")
209 }
210 TypeRef::Path => {
212 if optional {
213 format!("{name}: val.{name}.map(|p| p.to_string_lossy().to_string())")
214 } else {
215 format!("{name}: val.{name}.to_string_lossy().to_string()")
216 }
217 }
218 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
219 format!("{name}: val.{name}.map(|p| p.to_string_lossy().to_string())")
220 }
221 TypeRef::Char => {
223 if optional {
224 format!("{name}: val.{name}.map(|c| c.to_string())")
225 } else {
226 format!("{name}: val.{name}.to_string()")
227 }
228 }
229 TypeRef::Bytes => {
231 if optional {
232 format!("{name}: val.{name}.map(|v| v.to_vec())")
233 } else {
234 format!("{name}: val.{name}.to_vec()")
235 }
236 }
237 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
239 if optional {
240 format!("{name}: val.{name}.map(|v| {n} {{ inner: Arc::new(v) }})")
241 } else {
242 format!("{name}: {n} {{ inner: Arc::new(val.{name}) }}")
243 }
244 }
245 TypeRef::Json => {
247 if optional {
248 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
249 } else {
250 format!("{name}: val.{name}.to_string()")
251 }
252 }
253 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Json) => {
254 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
255 }
256 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json) => {
257 if optional {
258 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| i.to_string()).collect())")
259 } else {
260 format!("{name}: val.{name}.iter().map(ToString::to_string).collect()")
261 }
262 }
263 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
265 let k_is_json = matches!(k.as_ref(), TypeRef::Json);
266 let k_expr = if k_is_json { "k.to_string()" } else { "k" };
267 if optional {
268 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, v.to_string())).collect())")
269 } else {
270 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, v.to_string())).collect()")
271 }
272 }
273 TypeRef::Map(k, _v) if matches!(k.as_ref(), TypeRef::Json) => {
275 if optional {
276 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.to_string(), v)).collect())")
277 } else {
278 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.to_string(), v)).collect()")
279 }
280 }
281 _ => field_conversion_to_core(name, ty, optional),
283 }
284}
285
286pub fn field_conversion_from_core_cfg(
288 name: &str,
289 ty: &TypeRef,
290 optional: bool,
291 sanitized: bool,
292 opaque_types: &AHashSet<String>,
293 config: &ConversionConfig,
294) -> String {
295 if sanitized {
298 if config.map_uses_jsvalue {
299 if let TypeRef::Map(k, v) = ty {
301 if matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String) {
302 if optional {
303 return format!(
304 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
305 );
306 }
307 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
308 }
309 }
310 if let TypeRef::Vec(inner) = ty {
312 if matches!(inner.as_ref(), TypeRef::String) {
313 if optional {
314 return format!(
315 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
316 );
317 }
318 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
319 }
320 }
321 if let TypeRef::Vec(inner) = ty {
323 if matches!(inner.as_ref(), TypeRef::Json) {
324 if optional {
325 return format!(
326 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
327 );
328 }
329 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
330 }
331 }
332 }
333 return field_conversion_from_core(name, ty, optional, sanitized, opaque_types);
334 }
335
336 if config.map_uses_jsvalue {
338 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
339 let is_map = matches!(ty, TypeRef::Map(_, _));
340 if is_nested_vec || is_map {
341 if optional {
342 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())");
343 }
344 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
345 }
346 if let TypeRef::Optional(inner) = ty {
347 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
348 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
349 if is_inner_nested || is_inner_map {
350 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())");
351 }
352 }
353 }
354
355 let prefix = config.type_name_prefix;
356 let is_enum_string = |n: &str| -> bool { config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)) };
357
358 match ty {
359 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
361 let cast_to = binding_prim_str(p);
362 if optional {
363 format!("{name}: val.{name}.map(|v| v as {cast_to})")
364 } else {
365 format!("{name}: val.{name} as {cast_to}")
366 }
367 }
368 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
370 if optional {
371 format!("{name}: val.{name}.map(|v| v as f64)")
372 } else {
373 format!("{name}: val.{name} as f64")
374 }
375 }
376 TypeRef::Duration if config.cast_large_ints_to_i64 => {
378 if optional {
379 format!("{name}: val.{name}.map(|d| d.as_millis() as u64 as i64)")
380 } else {
381 format!("{name}: val.{name}.as_millis() as u64 as i64")
382 }
383 }
384 TypeRef::Named(n) if opaque_types.contains(n.as_str()) && !prefix.is_empty() => {
386 let prefixed = format!("{prefix}{n}");
387 if optional {
388 format!("{name}: val.{name}.map(|v| {prefixed} {{ inner: Arc::new(v) }})")
389 } else {
390 format!("{name}: {prefixed} {{ inner: Arc::new(val.{name}) }}")
391 }
392 }
393 TypeRef::Named(n) if is_enum_string(n) => {
395 if optional {
396 format!("{name}: val.{name}.as_ref().map(|v| format!(\"{{:?}}\", v))")
397 } else {
398 format!("{name}: format!(\"{{:?}}\", val.{name})")
399 }
400 }
401 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if is_enum_string(n)) => {
403 if optional {
404 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|x| format!(\"{{:?}}\", x)).collect())")
405 } else {
406 format!("{name}: val.{name}.iter().map(|v| format!(\"{{:?}}\", v)).collect()")
407 }
408 }
409 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(n) if is_enum_string(n))) =>
411 {
412 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|x| format!(\"{{:?}}\", x)).collect())")
413 }
414 TypeRef::Vec(inner)
416 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
417 {
418 if optional {
419 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as f64).collect())")
420 } else {
421 format!("{name}: val.{name}.iter().map(|&v| v as f64).collect()")
422 }
423 }
424 TypeRef::Optional(inner)
426 if config.cast_f32_to_f64
427 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
428 {
429 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as f64).collect())")
430 }
431 TypeRef::Optional(inner)
433 if config.cast_large_ints_to_i64
434 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
435 {
436 if let TypeRef::Primitive(p) = inner.as_ref() {
437 let cast_to = binding_prim_str(p);
438 format!("{name}: val.{name}.map(|v| v as {cast_to})")
439 } else {
440 field_conversion_from_core(name, ty, optional, sanitized, opaque_types)
441 }
442 }
443 TypeRef::Vec(inner)
445 if config.cast_large_ints_to_i64
446 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
447 {
448 if let TypeRef::Primitive(p) = inner.as_ref() {
449 let cast_to = binding_prim_str(p);
450 if optional {
451 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as {cast_to}).collect())")
452 } else {
453 format!("{name}: val.{name}.iter().map(|&v| v as {cast_to}).collect()")
454 }
455 } else {
456 field_conversion_from_core(name, ty, optional, sanitized, opaque_types)
457 }
458 }
459 TypeRef::Json if config.json_to_string => {
461 if optional {
462 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
463 } else {
464 format!("{name}: val.{name}.to_string()")
465 }
466 }
467 TypeRef::Json if config.map_uses_jsvalue => {
469 if optional {
470 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
471 } else {
472 format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)")
473 }
474 }
475 TypeRef::Vec(inner) if config.map_uses_jsvalue && matches!(inner.as_ref(), TypeRef::Json) => {
477 if optional {
478 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
479 } else {
480 format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)")
481 }
482 }
483 TypeRef::Optional(inner)
485 if config.map_uses_jsvalue
486 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json)) =>
487 {
488 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
489 }
490 _ => field_conversion_from_core(name, ty, optional, sanitized, opaque_types),
492 }
493}
494
495fn apply_core_wrapper_from_core(
498 conversion: &str,
499 name: &str,
500 core_wrapper: &CoreWrapper,
501 vec_inner_core_wrapper: &CoreWrapper,
502 optional: bool,
503) -> String {
504 if *vec_inner_core_wrapper == CoreWrapper::Arc {
506 return conversion
507 .replace(".map(Into::into).collect()", ".map(|v| (*v).clone().into()).collect()")
508 .replace(
509 "map(|v| v.into_iter().map(Into::into)",
510 "map(|v| v.into_iter().map(|v| (*v).clone().into())",
511 );
512 }
513
514 match core_wrapper {
515 CoreWrapper::None => conversion.to_string(),
516 CoreWrapper::Cow => {
517 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
522 if optional {
523 conversion.to_string()
525 } else if expr == format!("val.{name}") {
526 format!("{name}: val.{name}.into_owned()")
527 } else {
528 conversion.to_string()
529 }
530 } else {
531 conversion.to_string()
532 }
533 }
534 CoreWrapper::Arc => {
535 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
537 if optional {
538 format!("{name}: {expr}.map(|v| (*v).clone().into())")
539 } else {
540 let unwrapped = expr.replace(&format!("val.{name}"), &format!("(*val.{name}).clone()"));
541 format!("{name}: {unwrapped}")
542 }
543 } else {
544 conversion.to_string()
545 }
546 }
547 CoreWrapper::Bytes => {
548 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
550 if optional {
551 format!("{name}: {expr}.map(|v| v.to_vec())")
552 } else if expr == format!("val.{name}") {
553 format!("{name}: val.{name}.to_vec()")
554 } else {
555 conversion.to_string()
556 }
557 } else {
558 conversion.to_string()
559 }
560 }
561 }
562}