alef_codegen/conversions/
binding_to_core.rs1use alef_core::ir::{PrimitiveType, TypeDef, TypeRef};
2use std::fmt::Write;
3
4use super::ConversionConfig;
5use super::helpers::{core_prim_str, core_type_path, 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 writeln!(out, " Self {{").ok();
26 let optionalized = config.optionalize_defaults && typ.has_default;
27 for field in &typ.fields {
28 let conversion = if field.sanitized {
29 format!("{}: Default::default()", field.name)
30 } else if optionalized && !field.optional {
31 gen_optionalized_field_to_core(&field.name, &field.ty, config)
34 } else {
35 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
36 };
37 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
39 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
40 format!("{}: Box::new({})", field.name, expr)
41 } else {
42 conversion
43 }
44 } else {
45 conversion
46 };
47 writeln!(out, " {conversion},").ok();
48 }
49 if typ.has_stripped_cfg_fields {
51 writeln!(out, " ..Default::default()").ok();
52 }
53 writeln!(out, " }}").ok();
54 writeln!(out, " }}").ok();
55 write!(out, "}}").ok();
56 out
57}
58
59pub(super) fn gen_optionalized_field_to_core(name: &str, ty: &TypeRef, config: &ConversionConfig) -> String {
62 match ty {
63 TypeRef::Json => {
64 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
65 }
66 TypeRef::Named(_) => {
67 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
69 }
70 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
71 let core_ty = core_prim_str(p);
72 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
73 }
74 TypeRef::Duration if config.cast_large_ints_to_i64 => {
75 format!("{name}: val.{name}.map(|v| std::time::Duration::from_secs(v as u64)).unwrap_or_default()")
76 }
77 TypeRef::Duration => {
78 format!("{name}: val.{name}.map(std::time::Duration::from_secs).unwrap_or_default()")
79 }
80 TypeRef::Path => {
81 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
82 }
83 TypeRef::Char => {
85 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
86 }
87 TypeRef::Vec(inner) => match inner.as_ref() {
88 TypeRef::Json => {
89 format!(
90 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
91 )
92 }
93 TypeRef::Named(_) => {
94 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
95 }
96 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
97 let core_ty = core_prim_str(p);
98 format!(
99 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
100 )
101 }
102 _ => format!("{name}: val.{name}.unwrap_or_default()"),
103 },
104 TypeRef::Map(_, _) => {
105 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
107 }
108 _ => {
109 format!("{name}: val.{name}.unwrap_or_default()")
111 }
112 }
113}
114
115pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
117 match ty {
118 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Bytes | TypeRef::Unit => {
120 format!("{name}: val.{name}")
121 }
122 TypeRef::Json => {
124 if optional {
125 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
126 } else {
127 format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
128 }
129 }
130 TypeRef::Char => {
132 if optional {
133 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
134 } else {
135 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
136 }
137 }
138 TypeRef::Duration => {
140 if optional {
141 format!("{name}: val.{name}.map(std::time::Duration::from_secs)")
142 } else {
143 format!("{name}: std::time::Duration::from_secs(val.{name})")
144 }
145 }
146 TypeRef::Path => {
148 if optional {
149 format!("{name}: val.{name}.map(Into::into)")
150 } else {
151 format!("{name}: val.{name}.into()")
152 }
153 }
154 TypeRef::Named(_) => {
156 if optional {
157 format!("{name}: val.{name}.map(Into::into)")
158 } else {
159 format!("{name}: val.{name}.into()")
160 }
161 }
162 TypeRef::Optional(inner) => match inner.as_ref() {
164 TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
165 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
166 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
167 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
168 }
169 _ => format!("{name}: val.{name}"),
170 },
171 TypeRef::Vec(inner) => match inner.as_ref() {
173 TypeRef::Json => {
174 if optional {
175 format!(
176 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
177 )
178 } else {
179 format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
180 }
181 }
182 TypeRef::Named(_) => {
183 if optional {
184 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
185 } else {
186 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
187 }
188 }
189 _ => format!("{name}: val.{name}"),
190 },
191 TypeRef::Map(k, v) => {
195 let has_named_key = matches!(k.as_ref(), TypeRef::Named(_));
196 let has_named_val = matches!(v.as_ref(), TypeRef::Named(_));
197 if has_named_key || has_named_val {
198 let k_expr = if has_named_key { "k.into()" } else { "k" };
199 let v_expr = if has_named_val { "v.into()" } else { "v" };
200 if optional {
201 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
202 } else {
203 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
204 }
205 } else {
206 if optional {
208 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
209 } else {
210 format!("{name}: val.{name}.into_iter().collect()")
211 }
212 }
213 }
214 }
215}
216
217pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
219 if config.map_uses_jsvalue {
221 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
222 let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
223 let is_map = matches!(ty, TypeRef::Map(_, _));
224 if is_nested_vec || is_map || is_vec_json {
225 if optional {
226 return format!(
227 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
228 );
229 }
230 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
231 }
232 if let TypeRef::Optional(inner) = ty {
233 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
234 let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
235 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
236 if is_inner_nested || is_inner_map || is_inner_vec_json {
237 return format!(
238 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
239 );
240 }
241 }
242 }
243
244 if config.json_to_string && matches!(ty, TypeRef::Json) {
246 return format!("{name}: Default::default()");
247 }
248 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
250 if optional {
251 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
252 }
253 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
254 }
255 if !config.cast_large_ints_to_i64 && !config.cast_f32_to_f64 && !config.json_to_string {
256 return field_conversion_to_core(name, ty, optional);
257 }
258 match ty {
260 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
261 let core_ty = core_prim_str(p);
262 if optional {
263 format!("{name}: val.{name}.map(|v| v as {core_ty})")
264 } else {
265 format!("{name}: val.{name} as {core_ty}")
266 }
267 }
268 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
270 if optional {
271 format!("{name}: val.{name}.map(|v| v as f32)")
272 } else {
273 format!("{name}: val.{name} as f32")
274 }
275 }
276 TypeRef::Duration if config.cast_large_ints_to_i64 => {
277 if optional {
278 format!("{name}: val.{name}.map(|v| std::time::Duration::from_secs(v as u64))")
279 } else {
280 format!("{name}: std::time::Duration::from_secs(val.{name} as u64)")
281 }
282 }
283 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
284 if let TypeRef::Primitive(p) = inner.as_ref() {
285 let core_ty = core_prim_str(p);
286 format!("{name}: val.{name}.map(|v| v as {core_ty})")
287 } else {
288 field_conversion_to_core(name, ty, optional)
289 }
290 }
291 TypeRef::Vec(inner)
293 if config.cast_large_ints_to_i64
294 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
295 {
296 if let TypeRef::Primitive(p) = inner.as_ref() {
297 let core_ty = core_prim_str(p);
298 if optional {
299 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
300 } else {
301 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
302 }
303 } else {
304 field_conversion_to_core(name, ty, optional)
305 }
306 }
307 TypeRef::Vec(inner)
309 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
310 {
311 if optional {
312 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
313 } else {
314 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
315 }
316 }
317 TypeRef::Optional(inner)
319 if config.cast_f32_to_f64
320 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
321 {
322 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
323 }
324 _ => field_conversion_to_core(name, ty, optional),
326 }
327}