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 {
300 if config.map_uses_jsvalue {
301 if let TypeRef::Map(k, v) = ty {
303 if matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String) {
304 if optional {
305 return format!(
306 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
307 );
308 }
309 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
310 }
311 }
312 if let TypeRef::Vec(inner) = ty {
314 if matches!(inner.as_ref(), TypeRef::Json) {
315 if optional {
316 return format!(
317 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
318 );
319 }
320 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
321 }
322 }
323 }
324 return field_conversion_from_core(name, ty, optional, sanitized, opaque_types);
325 }
326
327 if config.map_uses_jsvalue {
329 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
330 let is_map = matches!(ty, TypeRef::Map(_, _));
331 if is_nested_vec || is_map {
332 if optional {
333 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())");
334 }
335 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
336 }
337 if let TypeRef::Optional(inner) = ty {
338 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
339 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
340 if is_inner_nested || is_inner_map {
341 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())");
342 }
343 }
344 }
345
346 let prefix = config.type_name_prefix;
347 let is_enum_string = |n: &str| -> bool { config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)) };
348
349 match ty {
350 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
352 let cast_to = binding_prim_str(p);
353 if optional {
354 format!("{name}: val.{name}.map(|v| v as {cast_to})")
355 } else {
356 format!("{name}: val.{name} as {cast_to}")
357 }
358 }
359 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
361 if optional {
362 format!("{name}: val.{name}.map(|v| v as f64)")
363 } else {
364 format!("{name}: val.{name} as f64")
365 }
366 }
367 TypeRef::Duration if config.cast_large_ints_to_i64 => {
369 if optional {
370 format!("{name}: val.{name}.map(|d| d.as_millis() as u64 as i64)")
371 } else {
372 format!("{name}: val.{name}.as_millis() as u64 as i64")
373 }
374 }
375 TypeRef::Named(n) if opaque_types.contains(n.as_str()) && !prefix.is_empty() => {
377 let prefixed = format!("{prefix}{n}");
378 if optional {
379 format!("{name}: val.{name}.map(|v| {prefixed} {{ inner: Arc::new(v) }})")
380 } else {
381 format!("{name}: {prefixed} {{ inner: Arc::new(val.{name}) }}")
382 }
383 }
384 TypeRef::Named(n) if is_enum_string(n) => {
386 if optional {
387 format!("{name}: val.{name}.as_ref().map(|v| format!(\"{{:?}}\", v))")
388 } else {
389 format!("{name}: format!(\"{{:?}}\", val.{name})")
390 }
391 }
392 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if is_enum_string(n)) => {
394 if optional {
395 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|x| format!(\"{{:?}}\", x)).collect())")
396 } else {
397 format!("{name}: val.{name}.iter().map(|v| format!(\"{{:?}}\", v)).collect()")
398 }
399 }
400 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(n) if is_enum_string(n))) =>
402 {
403 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|x| format!(\"{{:?}}\", x)).collect())")
404 }
405 TypeRef::Vec(inner)
407 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
408 {
409 if optional {
410 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as f64).collect())")
411 } else {
412 format!("{name}: val.{name}.iter().map(|&v| v as f64).collect()")
413 }
414 }
415 TypeRef::Optional(inner)
417 if config.cast_f32_to_f64
418 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
419 {
420 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as f64).collect())")
421 }
422 TypeRef::Optional(inner)
424 if config.cast_large_ints_to_i64
425 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
426 {
427 if let TypeRef::Primitive(p) = inner.as_ref() {
428 let cast_to = binding_prim_str(p);
429 format!("{name}: val.{name}.map(|v| v as {cast_to})")
430 } else {
431 field_conversion_from_core(name, ty, optional, sanitized, opaque_types)
432 }
433 }
434 TypeRef::Vec(inner)
436 if config.cast_large_ints_to_i64
437 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
438 {
439 if let TypeRef::Primitive(p) = inner.as_ref() {
440 let cast_to = binding_prim_str(p);
441 if optional {
442 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as {cast_to}).collect())")
443 } else {
444 format!("{name}: val.{name}.iter().map(|&v| v as {cast_to}).collect()")
445 }
446 } else {
447 field_conversion_from_core(name, ty, optional, sanitized, opaque_types)
448 }
449 }
450 TypeRef::Json if config.json_to_string => {
452 if optional {
453 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
454 } else {
455 format!("{name}: val.{name}.to_string()")
456 }
457 }
458 TypeRef::Json if config.map_uses_jsvalue => {
460 if optional {
461 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
462 } else {
463 format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)")
464 }
465 }
466 TypeRef::Vec(inner) if config.map_uses_jsvalue && matches!(inner.as_ref(), TypeRef::Json) => {
468 if optional {
469 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
470 } else {
471 format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)")
472 }
473 }
474 TypeRef::Optional(inner)
476 if config.map_uses_jsvalue
477 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json)) =>
478 {
479 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
480 }
481 _ => field_conversion_from_core(name, ty, optional, sanitized, opaque_types),
483 }
484}
485
486fn apply_core_wrapper_from_core(
489 conversion: &str,
490 name: &str,
491 core_wrapper: &CoreWrapper,
492 vec_inner_core_wrapper: &CoreWrapper,
493 optional: bool,
494) -> String {
495 if *vec_inner_core_wrapper == CoreWrapper::Arc {
497 return conversion
498 .replace(".map(Into::into).collect()", ".map(|v| (*v).clone().into()).collect()")
499 .replace(
500 "map(|v| v.into_iter().map(Into::into)",
501 "map(|v| v.into_iter().map(|v| (*v).clone().into())",
502 );
503 }
504
505 match core_wrapper {
506 CoreWrapper::None => conversion.to_string(),
507 CoreWrapper::Cow => {
508 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
513 if optional {
514 conversion.to_string()
516 } else if expr == format!("val.{name}") {
517 format!("{name}: val.{name}.into_owned()")
518 } else {
519 conversion.to_string()
520 }
521 } else {
522 conversion.to_string()
523 }
524 }
525 CoreWrapper::Arc => {
526 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
528 if optional {
529 format!("{name}: {expr}.map(|v| (*v).clone().into())")
530 } else {
531 let unwrapped = expr.replace(&format!("val.{name}"), &format!("(*val.{name}).clone()"));
532 format!("{name}: {unwrapped}")
533 }
534 } else {
535 conversion.to_string()
536 }
537 }
538 CoreWrapper::Bytes => {
539 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
541 if optional {
542 format!("{name}: {expr}.map(|v| v.to_vec())")
543 } else if expr == format!("val.{name}") {
544 format!("{name}: val.{name}.to_vec()")
545 } else {
546 conversion.to_string()
547 }
548 } else {
549 conversion.to_string()
550 }
551 }
552 }
553}