1use ahash::AHashSet;
2use alef_core::ir::{DefaultValue, FieldDef, MethodDef, ParamDef, PrimitiveType, ReceiverKind, TypeRef};
3use std::collections::HashMap;
4
5pub fn is_promoted_optional(params: &[ParamDef], idx: usize) -> bool {
9 if params[idx].optional {
10 return false; }
12 params[..idx].iter().any(|p| p.optional)
14}
15
16pub fn can_auto_delegate_function(func: &alef_core::ir::FunctionDef, opaque_types: &AHashSet<String>) -> bool {
20 !func.sanitized
21 && func
22 .params
23 .iter()
24 .all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types) && !is_named_ref_param(p, opaque_types))
25 && is_delegatable_return(&func.return_type)
26}
27
28pub fn can_auto_delegate(method: &MethodDef, opaque_types: &AHashSet<String>) -> bool {
31 if matches!(method.receiver, Some(ReceiverKind::RefMut)) && method.trait_source.is_none() {
33 return false;
34 }
35 !method.sanitized
36 && method
37 .params
38 .iter()
39 .all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types) && !is_named_ref_param(p, opaque_types))
40 && is_delegatable_return(&method.return_type)
41}
42
43fn is_named_ref_param(p: &alef_core::ir::ParamDef, opaque_types: &AHashSet<String>) -> bool {
46 if !p.is_ref {
47 return false;
48 }
49 match &p.ty {
50 TypeRef::Named(name) => !opaque_types.contains(name.as_str()),
51 TypeRef::Vec(inner) => matches!(inner.as_ref(), TypeRef::String | TypeRef::Char),
52 _ => false,
53 }
54}
55
56pub fn is_delegatable_param(ty: &TypeRef, _opaque_types: &AHashSet<String>) -> bool {
58 match ty {
59 TypeRef::Primitive(_)
60 | TypeRef::String
61 | TypeRef::Char
62 | TypeRef::Bytes
63 | TypeRef::Path
64 | TypeRef::Unit
65 | TypeRef::Duration => true,
66 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_param(inner, _opaque_types),
68 TypeRef::Map(k, v) => is_delegatable_param(k, _opaque_types) && is_delegatable_param(v, _opaque_types),
69 TypeRef::Json => false,
70 }
71}
72
73pub fn is_delegatable_return(ty: &TypeRef) -> bool {
75 match ty {
76 TypeRef::Primitive(_)
77 | TypeRef::String
78 | TypeRef::Char
79 | TypeRef::Bytes
80 | TypeRef::Path
81 | TypeRef::Unit
82 | TypeRef::Duration => true,
83 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_return(inner),
85 TypeRef::Map(k, v) => is_delegatable_return(k) && is_delegatable_return(v),
86 TypeRef::Json => false,
87 }
88}
89
90pub fn is_delegatable_type(ty: &TypeRef) -> bool {
94 match ty {
95 TypeRef::Primitive(_)
96 | TypeRef::String
97 | TypeRef::Char
98 | TypeRef::Bytes
99 | TypeRef::Path
100 | TypeRef::Unit
101 | TypeRef::Duration => true,
102 TypeRef::Named(_) => false, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_type(inner),
104 TypeRef::Map(k, v) => is_delegatable_type(k) && is_delegatable_type(v),
105 TypeRef::Json => false,
106 }
107}
108
109pub fn is_opaque_delegatable_type(ty: &TypeRef) -> bool {
112 match ty {
113 TypeRef::Primitive(_)
114 | TypeRef::String
115 | TypeRef::Char
116 | TypeRef::Bytes
117 | TypeRef::Path
118 | TypeRef::Unit
119 | TypeRef::Duration => true,
120 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_opaque_delegatable_type(inner),
122 TypeRef::Map(k, v) => is_opaque_delegatable_type(k) && is_opaque_delegatable_type(v),
123 TypeRef::Json => false,
124 }
125}
126
127pub fn is_simple_type(ty: &TypeRef) -> bool {
129 match ty {
130 TypeRef::Primitive(_)
131 | TypeRef::String
132 | TypeRef::Char
133 | TypeRef::Bytes
134 | TypeRef::Path
135 | TypeRef::Unit
136 | TypeRef::Duration => true,
137 TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_simple_type(inner),
138 TypeRef::Map(k, v) => is_simple_type(k) && is_simple_type(v),
139 TypeRef::Named(_) | TypeRef::Json => false,
140 }
141}
142
143pub fn partition_methods(methods: &[MethodDef]) -> (Vec<&MethodDef>, Vec<&MethodDef>) {
145 let instance: Vec<_> = methods.iter().filter(|m| m.receiver.is_some()).collect();
146 let statics: Vec<_> = methods.iter().filter(|m| m.receiver.is_none()).collect();
147 (instance, statics)
148}
149
150pub fn constructor_parts(fields: &[FieldDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> (String, String, String) {
154 constructor_parts_with_renames_and_cfg_restore(fields, type_mapper, None, &[])
155}
156
157pub fn constructor_parts_with_renames(
161 fields: &[FieldDef],
162 type_mapper: &dyn Fn(&TypeRef) -> String,
163 field_renames: Option<&HashMap<String, String>>,
164) -> (String, String, String) {
165 constructor_parts_with_renames_and_cfg_restore(fields, type_mapper, field_renames, &[])
166}
167
168pub fn constructor_parts_with_renames_and_cfg_restore(
173 fields: &[FieldDef],
174 type_mapper: &dyn Fn(&TypeRef) -> String,
175 field_renames: Option<&HashMap<String, String>>,
176 never_skip_cfg_field_names: &[String],
177) -> (String, String, String) {
178 let mut sorted_fields: Vec<&FieldDef> = fields
185 .iter()
186 .filter(|f| f.cfg.is_none() || never_skip_cfg_field_names.contains(&f.name))
187 .collect();
188 sorted_fields.sort_by_key(|f| (f.optional || f.cfg.is_some()) as u8);
189
190 let params: Vec<String> = sorted_fields
191 .iter()
192 .map(|f| {
193 let is_optional = f.optional || f.cfg.is_some();
194 let ty = if is_optional {
195 match &f.ty {
196 TypeRef::Optional(_) => type_mapper(&f.ty),
197 _ => format!("Option<{}>", type_mapper(&f.ty)),
198 }
199 } else {
200 type_mapper(&f.ty)
201 };
202 format!("{}: {}", f.name, ty)
203 })
204 .collect();
205
206 let defaults: Vec<String> = sorted_fields
207 .iter()
208 .map(|f| {
209 if f.optional || f.cfg.is_some() {
210 format!("{}=None", f.name)
211 } else {
212 f.name.clone()
213 }
214 })
215 .collect();
216
217 let assignments: Vec<String> = fields
222 .iter()
223 .map(|f| {
224 let binding_name = field_renames
225 .and_then(|r| r.get(&f.name))
226 .map_or_else(|| f.name.as_str(), |s| s.as_str());
227 if f.cfg.is_some() && !never_skip_cfg_field_names.contains(&f.name) {
228 return format!("{}: Default::default()", binding_name);
229 }
230 if binding_name != f.name {
231 return format!("{}: {}", binding_name, f.name);
232 }
233 f.name.clone()
234 })
235 .collect();
236
237 let single_line = params.join(", ");
239 let param_list = if single_line.len() > 100 {
240 format!("\n {},\n ", params.join(",\n "))
241 } else {
242 single_line
243 };
244
245 (param_list, defaults.join(", "), assignments.join(", "))
246}
247
248pub fn function_params(params: &[ParamDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
250 let mut seen_optional = false;
253 params
254 .iter()
255 .map(|p| {
256 if p.optional {
257 seen_optional = true;
258 }
259 let ty = if p.optional || seen_optional {
260 format!("Option<{}>", type_mapper(&p.ty))
261 } else {
262 type_mapper(&p.ty)
263 };
264 format!("{}: {}", p.name, ty)
265 })
266 .collect::<Vec<_>>()
267 .join(", ")
268}
269
270pub fn function_sig_defaults(params: &[ParamDef]) -> String {
272 let mut seen_optional = false;
278 params
279 .iter()
280 .map(|p| {
281 if p.optional {
282 seen_optional = true;
283 }
284 if p.optional {
285 format!("{}=None", p.name)
286 } else if seen_optional {
287 let default = match &p.ty {
290 TypeRef::Primitive(PrimitiveType::Bool) => "false",
291 TypeRef::Primitive(_) => "0",
292 _ => "None",
293 };
294 format!("{}={}", p.name, default)
295 } else {
296 p.name.clone()
297 }
298 })
299 .collect::<Vec<_>>()
300 .join(", ")
301}
302
303pub fn format_default_value(default: &DefaultValue) -> String {
306 match default {
307 DefaultValue::BoolLiteral(b) => format!("{}", b),
308 DefaultValue::StringLiteral(s) => format!("\"{}\".to_string()", s.escape_default()),
309 DefaultValue::IntLiteral(i) => format!("{}", i),
310 DefaultValue::FloatLiteral(f) => {
311 let s = format!("{}", f);
312 if s.contains('.') || s.contains('e') || s.contains('E') {
314 s
315 } else {
316 format!("{s}.0")
317 }
318 }
319 DefaultValue::EnumVariant(v) => v.clone(),
320 DefaultValue::Empty => "Default::default()".to_string(),
321 DefaultValue::None => "None".to_string(),
322 }
323}
324
325pub fn config_constructor_parts_with_options(
336 fields: &[FieldDef],
337 type_mapper: &dyn Fn(&TypeRef) -> String,
338 option_duration_on_defaults: bool,
339) -> (String, String, String) {
340 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults, None, &[])
341}
342
343pub fn config_constructor_parts_with_renames(
345 fields: &[FieldDef],
346 type_mapper: &dyn Fn(&TypeRef) -> String,
347 option_duration_on_defaults: bool,
348 field_renames: Option<&HashMap<String, String>>,
349) -> (String, String, String) {
350 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults, field_renames, &[])
351}
352
353pub fn config_constructor_parts_with_renames_and_cfg_restore(
356 fields: &[FieldDef],
357 type_mapper: &dyn Fn(&TypeRef) -> String,
358 option_duration_on_defaults: bool,
359 field_renames: Option<&HashMap<String, String>>,
360 never_skip_cfg_field_names: &[String],
361) -> (String, String, String) {
362 config_constructor_parts_inner(
363 fields,
364 type_mapper,
365 option_duration_on_defaults,
366 field_renames,
367 never_skip_cfg_field_names,
368 )
369}
370
371pub fn config_constructor_parts(
372 fields: &[FieldDef],
373 type_mapper: &dyn Fn(&TypeRef) -> String,
374) -> (String, String, String) {
375 config_constructor_parts_inner(fields, type_mapper, false, None, &[])
376}
377
378fn config_constructor_parts_inner(
379 fields: &[FieldDef],
380 type_mapper: &dyn Fn(&TypeRef) -> String,
381 option_duration_on_defaults: bool,
382 field_renames: Option<&HashMap<String, String>>,
383 never_skip_cfg_field_names: &[String],
384) -> (String, String, String) {
385 let mut sorted_fields: Vec<&FieldDef> = fields
389 .iter()
390 .filter(|f| f.cfg.is_none() || never_skip_cfg_field_names.contains(&f.name))
391 .collect();
392 sorted_fields.sort_by_key(|f| f.optional as u8);
393
394 let params: Vec<String> = sorted_fields
395 .iter()
396 .map(|f| {
397 let ty = type_mapper(&f.ty);
398 if matches!(f.ty, TypeRef::Optional(_)) {
403 format!("{}: {}", f.name, ty)
404 } else {
405 format!("{}: Option<{}>", f.name, ty)
406 }
407 })
408 .collect();
409
410 let defaults = sorted_fields
412 .iter()
413 .map(|f| format!("{}=None", f.name))
414 .collect::<Vec<_>>()
415 .join(", ");
416
417 let assignments: Vec<String> = fields
421 .iter()
422 .map(|f| {
423 let binding_name = field_renames
424 .and_then(|r| r.get(&f.name))
425 .map_or_else(|| f.name.as_str(), |s| s.as_str());
426 if f.cfg.is_some() {
427 if never_skip_cfg_field_names.contains(&f.name) {
428 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
432 return format!("{}: {}", binding_name, f.name);
433 }
434 return format!("{}: {}.unwrap_or_default()", binding_name, f.name);
435 }
436 return format!("{}: Default::default()", binding_name);
437 }
438 if option_duration_on_defaults && matches!(f.ty, TypeRef::Duration) {
441 return format!("{}: {}", binding_name, f.name);
442 }
443 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
444 format!("{}: {}", binding_name, f.name)
446 } else if let Some(ref typed_default) = f.typed_default {
447 match typed_default {
450 DefaultValue::EnumVariant(_) | DefaultValue::Empty => {
451 format!("{}: {}.unwrap_or_default()", binding_name, f.name)
452 }
453 _ => {
454 let default_val = format_default_value(typed_default);
455 match typed_default {
458 DefaultValue::BoolLiteral(_)
459 | DefaultValue::IntLiteral(_)
460 | DefaultValue::FloatLiteral(_) => {
461 format!("{}: {}.unwrap_or({})", binding_name, f.name, default_val)
462 }
463 _ => {
464 format!("{}: {}.unwrap_or_else(|| {})", binding_name, f.name, default_val)
465 }
466 }
467 }
468 }
469 } else {
470 format!("{}: {}.unwrap_or_default()", binding_name, f.name)
473 }
474 })
475 .collect();
476
477 let single_line = params.join(", ");
478 let param_list = if single_line.len() > 100 {
479 format!("\n {},\n ", params.join(",\n "))
480 } else {
481 single_line
482 };
483
484 (param_list, defaults, assignments.join(", "))
485}