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 writeln!(out, "impl From<{binding_name}> for {core_path} {{").ok();
19 writeln!(out, " fn from(val: {binding_name}) -> Self {{").ok();
20 writeln!(out, " Self {{").ok();
21 let optionalized = config.optionalize_defaults && typ.has_default;
22 for field in &typ.fields {
23 let conversion = if field.sanitized {
24 format!("{}: Default::default()", field.name)
25 } else if optionalized && !field.optional {
26 gen_optionalized_field_to_core(&field.name, &field.ty, config)
29 } else {
30 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
31 };
32 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
34 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
35 format!("{}: Box::new({})", field.name, expr)
36 } else {
37 conversion
38 }
39 } else {
40 conversion
41 };
42 writeln!(out, " {conversion},").ok();
43 }
44 if typ.has_stripped_cfg_fields {
46 writeln!(out, " ..Default::default()").ok();
47 }
48 writeln!(out, " }}").ok();
49 writeln!(out, " }}").ok();
50 write!(out, "}}").ok();
51 out
52}
53
54pub(super) fn gen_optionalized_field_to_core(name: &str, ty: &TypeRef, config: &ConversionConfig) -> String {
57 match ty {
58 TypeRef::Named(_) => {
59 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
61 }
62 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
63 let core_ty = core_prim_str(p);
64 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
65 }
66 TypeRef::Duration if config.cast_large_ints_to_i64 => {
67 format!("{name}: val.{name}.map(|v| std::time::Duration::from_secs(v as u64)).unwrap_or_default()")
68 }
69 TypeRef::Duration => {
70 format!("{name}: val.{name}.map(std::time::Duration::from_secs).unwrap_or_default()")
71 }
72 TypeRef::Path => {
73 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
74 }
75 TypeRef::Char => {
77 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
78 }
79 TypeRef::Vec(inner) => match inner.as_ref() {
80 TypeRef::Named(_) => {
81 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
82 }
83 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
84 let core_ty = core_prim_str(p);
85 format!(
86 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
87 )
88 }
89 _ => format!("{name}: val.{name}.unwrap_or_default()"),
90 },
91 TypeRef::Map(_, _) => {
92 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
94 }
95 _ => {
96 format!("{name}: val.{name}.unwrap_or_default()")
98 }
99 }
100}
101
102pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
104 match ty {
105 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Bytes | TypeRef::Unit | TypeRef::Json => {
107 format!("{name}: val.{name}")
108 }
109 TypeRef::Char => {
111 if optional {
112 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
113 } else {
114 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
115 }
116 }
117 TypeRef::Duration => {
119 if optional {
120 format!("{name}: val.{name}.map(std::time::Duration::from_secs)")
121 } else {
122 format!("{name}: std::time::Duration::from_secs(val.{name})")
123 }
124 }
125 TypeRef::Path => {
127 if optional {
128 format!("{name}: val.{name}.map(Into::into)")
129 } else {
130 format!("{name}: val.{name}.into()")
131 }
132 }
133 TypeRef::Named(_) => {
135 if optional {
136 format!("{name}: val.{name}.map(Into::into)")
137 } else {
138 format!("{name}: val.{name}.into()")
139 }
140 }
141 TypeRef::Optional(inner) => match inner.as_ref() {
143 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
144 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
145 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
146 }
147 _ => format!("{name}: val.{name}"),
148 },
149 TypeRef::Vec(inner) => match inner.as_ref() {
151 TypeRef::Named(_) => {
152 if optional {
153 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
154 } else {
155 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
156 }
157 }
158 _ => format!("{name}: val.{name}"),
159 },
160 TypeRef::Map(k, v) => {
163 let has_named_key = matches!(k.as_ref(), TypeRef::Named(_));
164 let has_named_val = matches!(v.as_ref(), TypeRef::Named(_));
165 let k_expr = if has_named_key { "k.into()" } else { "k" };
166 let v_expr = if has_named_val { "v.into()" } else { "v" };
167 if optional {
168 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
169 } else {
170 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
171 }
172 }
173 }
174}
175
176pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
178 if config.map_uses_jsvalue {
180 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
181 let is_map = matches!(ty, TypeRef::Map(_, _));
182 if is_nested_vec || is_map {
183 if optional {
184 return format!(
185 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
186 );
187 }
188 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
189 }
190 if let TypeRef::Optional(inner) = ty {
191 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
192 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
193 if is_inner_nested || is_inner_map {
194 return format!(
195 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
196 );
197 }
198 }
199 }
200
201 if config.json_to_string && matches!(ty, TypeRef::Json) {
203 return format!("{name}: Default::default()");
204 }
205 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
207 if optional {
208 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
209 }
210 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
211 }
212 if !config.cast_large_ints_to_i64 && !config.cast_f32_to_f64 && !config.json_to_string {
213 return field_conversion_to_core(name, ty, optional);
214 }
215 match ty {
217 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
218 let core_ty = core_prim_str(p);
219 if optional {
220 format!("{name}: val.{name}.map(|v| v as {core_ty})")
221 } else {
222 format!("{name}: val.{name} as {core_ty}")
223 }
224 }
225 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
227 if optional {
228 format!("{name}: val.{name}.map(|v| v as f32)")
229 } else {
230 format!("{name}: val.{name} as f32")
231 }
232 }
233 TypeRef::Duration if config.cast_large_ints_to_i64 => {
234 if optional {
235 format!("{name}: val.{name}.map(|v| std::time::Duration::from_secs(v as u64))")
236 } else {
237 format!("{name}: std::time::Duration::from_secs(val.{name} as u64)")
238 }
239 }
240 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
241 if let TypeRef::Primitive(p) = inner.as_ref() {
242 let core_ty = core_prim_str(p);
243 format!("{name}: val.{name}.map(|v| v as {core_ty})")
244 } else {
245 field_conversion_to_core(name, ty, optional)
246 }
247 }
248 TypeRef::Vec(inner)
250 if config.cast_large_ints_to_i64
251 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
252 {
253 if let TypeRef::Primitive(p) = inner.as_ref() {
254 let core_ty = core_prim_str(p);
255 if optional {
256 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
257 } else {
258 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
259 }
260 } else {
261 field_conversion_to_core(name, ty, optional)
262 }
263 }
264 TypeRef::Vec(inner)
266 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
267 {
268 if optional {
269 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
270 } else {
271 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
272 }
273 }
274 TypeRef::Optional(inner)
276 if config.cast_f32_to_f64
277 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
278 {
279 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
280 }
281 _ => field_conversion_to_core(name, ty, optional),
283 }
284}