alef_codegen/conversions/
core_to_binding.rs1use ahash::AHashSet;
2use alef_core::ir::{PrimitiveType, TypeDef, TypeRef};
3use std::fmt::Write;
4
5use super::ConversionConfig;
6use super::binding_to_core::field_conversion_to_core;
7use super::helpers::{binding_prim_str, core_type_path, needs_i64_cast};
8
9pub fn gen_from_core_to_binding(typ: &TypeDef, core_import: &str, opaque_types: &AHashSet<String>) -> String {
11 gen_from_core_to_binding_cfg(typ, core_import, opaque_types, &ConversionConfig::default())
12}
13
14pub fn gen_from_core_to_binding_cfg(
16 typ: &TypeDef,
17 core_import: &str,
18 opaque_types: &AHashSet<String>,
19 config: &ConversionConfig,
20) -> String {
21 let core_path = core_type_path(typ, core_import);
22 let binding_name = format!("{}{}", config.type_name_prefix, typ.name);
23 let mut out = String::with_capacity(256);
24 writeln!(out, "impl From<{core_path}> for {binding_name} {{").ok();
25 writeln!(out, " fn from(val: {core_path}) -> Self {{").ok();
26 let optionalized = config.optionalize_defaults && typ.has_default;
27 writeln!(out, " Self {{").ok();
28 for field in &typ.fields {
29 let base_conversion = field_conversion_from_core_cfg(
30 &field.name,
31 &field.ty,
32 field.optional,
33 field.sanitized,
34 opaque_types,
35 config,
36 );
37 let base_conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
39 if field.optional {
40 let src = format!("{}: val.{}.map(Into::into)", field.name, field.name);
42 let dst = format!("{}: val.{}.map(|v| (*v).into())", field.name, field.name);
43 if base_conversion == src { dst } else { base_conversion }
44 } else {
45 base_conversion.replace(&format!("val.{}", field.name), &format!("(*val.{})", field.name))
47 }
48 } else {
49 base_conversion
50 };
51 let conversion = if optionalized && !field.optional {
53 if let Some(expr) = base_conversion.strip_prefix(&format!("{}: ", field.name)) {
55 format!("{}: Some({})", field.name, expr)
56 } else {
57 base_conversion
58 }
59 } else {
60 base_conversion
61 };
62 if field.cfg.is_some() {
64 continue;
65 }
66 writeln!(out, " {conversion},").ok();
67 }
68
69 if config.include_cfg_metadata && typ.has_stripped_cfg_fields && typ.name == "ConversionResult" {
71 writeln!(out, " metadata: Some(val.metadata.into()),").ok();
72 }
73
74 writeln!(out, " }}").ok();
75 writeln!(out, " }}").ok();
76 write!(out, "}}").ok();
77 out
78}
79
80pub fn field_conversion_from_core(
83 name: &str,
84 ty: &TypeRef,
85 optional: bool,
86 sanitized: bool,
87 opaque_types: &AHashSet<String>,
88) -> String {
89 if sanitized {
92 if let TypeRef::Map(k, v) = ty {
94 if matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String) {
95 if optional {
96 return format!(
97 "{name}: val.{name}.as_ref().map(|m| m.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect())"
98 );
99 }
100 return format!(
101 "{name}: val.{name}.into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()"
102 );
103 }
104 }
105 if let TypeRef::Vec(inner) = ty {
107 if matches!(inner.as_ref(), TypeRef::String) {
108 if optional {
109 return format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| i.to_string()).collect())");
110 }
111 return format!("{name}: val.{name}.iter().map(ToString::to_string).collect()");
112 }
113 }
114 if let TypeRef::Optional(opt_inner) = ty {
116 if let TypeRef::Vec(vec_inner) = opt_inner.as_ref() {
117 if matches!(vec_inner.as_ref(), TypeRef::String) {
118 return format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| i.to_string()).collect())");
119 }
120 }
121 }
122 if matches!(ty, TypeRef::String) {
124 if optional {
125 return format!("{name}: val.{name}.as_ref().map(ToString::to_string)");
126 }
127 return format!("{name}: val.{name}.to_string()");
128 }
129 if optional {
131 return format!("{name}: val.{name}.as_ref().map(|v| format!(\"{{:?}}\", v))");
132 }
133 return format!("{name}: format!(\"{{:?}}\", val.{name})");
134 }
135 match ty {
136 TypeRef::Duration => {
138 if optional {
139 return format!("{name}: val.{name}.map(|d| d.as_secs())");
140 }
141 format!("{name}: val.{name}.as_secs()")
142 }
143 TypeRef::Path => {
145 if optional {
146 format!("{name}: val.{name}.map(|p| p.to_string_lossy().to_string())")
147 } else {
148 format!("{name}: val.{name}.to_string_lossy().to_string()")
149 }
150 }
151 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
152 format!("{name}: val.{name}.map(|p| p.to_string_lossy().to_string())")
153 }
154 TypeRef::Char => {
156 if optional {
157 format!("{name}: val.{name}.map(|c| c.to_string())")
158 } else {
159 format!("{name}: val.{name}.to_string()")
160 }
161 }
162 TypeRef::Bytes => {
164 if optional {
165 format!("{name}: val.{name}.map(|v| v.to_vec())")
166 } else {
167 format!("{name}: val.{name}.to_vec()")
168 }
169 }
170 TypeRef::Named(n) if opaque_types.contains(n.as_str()) => {
172 if optional {
173 format!("{name}: val.{name}.map(|v| {n} {{ inner: Arc::new(v) }})")
174 } else {
175 format!("{name}: {n} {{ inner: Arc::new(val.{name}) }}")
176 }
177 }
178 TypeRef::Json => {
180 if optional {
181 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
182 } else {
183 format!("{name}: val.{name}.to_string()")
184 }
185 }
186 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Json) => {
187 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
188 }
189 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json) => {
190 if optional {
191 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|i| i.to_string()).collect())")
192 } else {
193 format!("{name}: val.{name}.iter().map(ToString::to_string).collect()")
194 }
195 }
196 _ => field_conversion_to_core(name, ty, optional),
198 }
199}
200
201pub fn field_conversion_from_core_cfg(
203 name: &str,
204 ty: &TypeRef,
205 optional: bool,
206 sanitized: bool,
207 opaque_types: &AHashSet<String>,
208 config: &ConversionConfig,
209) -> String {
210 if sanitized {
213 if config.map_uses_jsvalue {
214 if let TypeRef::Map(k, v) = ty {
216 if matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String) {
217 if optional {
218 return format!(
219 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
220 );
221 }
222 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
223 }
224 }
225 if let TypeRef::Vec(inner) = ty {
227 if matches!(inner.as_ref(), TypeRef::String) {
228 if optional {
229 return format!(
230 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
231 );
232 }
233 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
234 }
235 }
236 if let TypeRef::Vec(inner) = ty {
238 if matches!(inner.as_ref(), TypeRef::Json) {
239 if optional {
240 return format!(
241 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())"
242 );
243 }
244 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
245 }
246 }
247 }
248 return field_conversion_from_core(name, ty, optional, sanitized, opaque_types);
249 }
250
251 if config.map_uses_jsvalue {
253 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
254 let is_map = matches!(ty, TypeRef::Map(_, _));
255 if is_nested_vec || is_map {
256 if optional {
257 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())");
258 }
259 return format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)");
260 }
261 if let TypeRef::Optional(inner) = ty {
262 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
263 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
264 if is_inner_nested || is_inner_map {
265 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())");
266 }
267 }
268 }
269
270 let prefix = config.type_name_prefix;
271 let is_enum_string = |n: &str| -> bool { config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)) };
272
273 match ty {
274 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
276 let cast_to = binding_prim_str(p);
277 if optional {
278 format!("{name}: val.{name}.map(|v| v as {cast_to})")
279 } else {
280 format!("{name}: val.{name} as {cast_to}")
281 }
282 }
283 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
285 if optional {
286 format!("{name}: val.{name}.map(|v| v as f64)")
287 } else {
288 format!("{name}: val.{name} as f64")
289 }
290 }
291 TypeRef::Duration if config.cast_large_ints_to_i64 => {
293 if optional {
294 format!("{name}: val.{name}.map(|d| d.as_secs() as i64)")
295 } else {
296 format!("{name}: val.{name}.as_secs() as i64")
297 }
298 }
299 TypeRef::Named(n) if opaque_types.contains(n.as_str()) && !prefix.is_empty() => {
301 let prefixed = format!("{prefix}{n}");
302 if optional {
303 format!("{name}: val.{name}.map(|v| {prefixed} {{ inner: Arc::new(v) }})")
304 } else {
305 format!("{name}: {prefixed} {{ inner: Arc::new(val.{name}) }}")
306 }
307 }
308 TypeRef::Named(n) if is_enum_string(n) => {
310 if optional {
311 format!("{name}: val.{name}.as_ref().map(|v| format!(\"{{:?}}\", v))")
312 } else {
313 format!("{name}: format!(\"{{:?}}\", val.{name})")
314 }
315 }
316 TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if is_enum_string(n)) => {
318 if optional {
319 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|x| format!(\"{{:?}}\", x)).collect())")
320 } else {
321 format!("{name}: val.{name}.iter().map(|v| format!(\"{{:?}}\", v)).collect()")
322 }
323 }
324 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(n) if is_enum_string(n))) =>
326 {
327 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|x| format!(\"{{:?}}\", x)).collect())")
328 }
329 TypeRef::Vec(inner)
331 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
332 {
333 if optional {
334 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as f64).collect())")
335 } else {
336 format!("{name}: val.{name}.iter().map(|&v| v as f64).collect()")
337 }
338 }
339 TypeRef::Optional(inner)
341 if config.cast_f32_to_f64
342 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
343 {
344 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as f64).collect())")
345 }
346 TypeRef::Optional(inner)
348 if config.cast_large_ints_to_i64
349 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
350 {
351 if let TypeRef::Primitive(p) = inner.as_ref() {
352 let cast_to = binding_prim_str(p);
353 format!("{name}: val.{name}.map(|v| v as {cast_to})")
354 } else {
355 field_conversion_from_core(name, ty, optional, sanitized, opaque_types)
356 }
357 }
358 TypeRef::Vec(inner)
360 if config.cast_large_ints_to_i64
361 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
362 {
363 if let TypeRef::Primitive(p) = inner.as_ref() {
364 let cast_to = binding_prim_str(p);
365 if optional {
366 format!("{name}: val.{name}.as_ref().map(|v| v.iter().map(|&x| x as {cast_to}).collect())")
367 } else {
368 format!("{name}: val.{name}.iter().map(|&v| v as {cast_to}).collect()")
369 }
370 } else {
371 field_conversion_from_core(name, ty, optional, sanitized, opaque_types)
372 }
373 }
374 TypeRef::Json if config.json_to_string => {
376 if optional {
377 format!("{name}: val.{name}.as_ref().map(ToString::to_string)")
378 } else {
379 format!("{name}: val.{name}.to_string()")
380 }
381 }
382 TypeRef::Json if config.map_uses_jsvalue => {
384 if optional {
385 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
386 } else {
387 format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)")
388 }
389 }
390 TypeRef::Vec(inner) if config.map_uses_jsvalue && matches!(inner.as_ref(), TypeRef::Json) => {
392 if optional {
393 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
394 } else {
395 format!("{name}: serde_wasm_bindgen::to_value(&val.{name}).unwrap_or(JsValue::NULL)")
396 }
397 }
398 TypeRef::Optional(inner)
400 if config.map_uses_jsvalue
401 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json)) =>
402 {
403 format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::to_value(v).ok())")
404 }
405 _ => field_conversion_from_core(name, ty, optional, sanitized, opaque_types),
407 }
408}