1use ahash::AHashSet;
2use alef_core::ir::{DefaultValue, FieldDef, MethodDef, ParamDef, PrimitiveType, ReceiverKind, TypeRef};
3use std::collections::HashMap;
4
5pub fn binding_fields(fields: &[FieldDef]) -> impl Iterator<Item = &FieldDef> {
11 fields.iter().filter(|field| !field.binding_excluded)
12}
13
14pub fn is_promoted_optional(params: &[ParamDef], idx: usize) -> bool {
18 if params[idx].optional {
19 return false; }
21 params[..idx].iter().any(|p| p.optional)
23}
24
25pub fn can_auto_delegate_function(func: &alef_core::ir::FunctionDef, opaque_types: &AHashSet<String>) -> bool {
29 !func.sanitized
30 && func
31 .params
32 .iter()
33 .all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types) && !is_named_ref_param(p, opaque_types))
34 && is_delegatable_return(&func.return_type)
35}
36
37pub fn can_auto_delegate(method: &MethodDef, opaque_types: &AHashSet<String>) -> bool {
40 if matches!(method.receiver, Some(ReceiverKind::RefMut)) && method.trait_source.is_none() {
42 return false;
43 }
44 !method.sanitized
45 && method
46 .params
47 .iter()
48 .all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types) && !is_named_ref_param(p, opaque_types))
49 && is_delegatable_return(&method.return_type)
50}
51
52fn is_named_ref_param(p: &alef_core::ir::ParamDef, opaque_types: &AHashSet<String>) -> bool {
55 if !p.is_ref {
56 return false;
57 }
58 match &p.ty {
59 TypeRef::Named(name) => !opaque_types.contains(name.as_str()),
60 TypeRef::Vec(inner) => matches!(inner.as_ref(), TypeRef::String | TypeRef::Char),
61 _ => false,
62 }
63}
64
65pub fn is_delegatable_param(ty: &TypeRef, _opaque_types: &AHashSet<String>) -> bool {
67 match ty {
68 TypeRef::Primitive(_)
69 | TypeRef::String
70 | TypeRef::Char
71 | TypeRef::Bytes
72 | TypeRef::Path
73 | TypeRef::Unit
74 | TypeRef::Duration => true,
75 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_param(inner, _opaque_types),
77 TypeRef::Map(k, v) => is_delegatable_param(k, _opaque_types) && is_delegatable_param(v, _opaque_types),
78 TypeRef::Json => false,
79 }
80}
81
82pub fn is_delegatable_return(ty: &TypeRef) -> bool {
84 match ty {
85 TypeRef::Primitive(_)
86 | TypeRef::String
87 | TypeRef::Char
88 | TypeRef::Bytes
89 | TypeRef::Path
90 | TypeRef::Unit
91 | TypeRef::Duration => true,
92 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_return(inner),
94 TypeRef::Map(k, v) => is_delegatable_return(k) && is_delegatable_return(v),
95 TypeRef::Json => false,
96 }
97}
98
99pub fn is_delegatable_type(ty: &TypeRef) -> bool {
103 match ty {
104 TypeRef::Primitive(_)
105 | TypeRef::String
106 | TypeRef::Char
107 | TypeRef::Bytes
108 | TypeRef::Path
109 | TypeRef::Unit
110 | TypeRef::Duration => true,
111 TypeRef::Named(_) => false, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_type(inner),
113 TypeRef::Map(k, v) => is_delegatable_type(k) && is_delegatable_type(v),
114 TypeRef::Json => false,
115 }
116}
117
118pub fn is_opaque_delegatable_type(ty: &TypeRef) -> bool {
121 match ty {
122 TypeRef::Primitive(_)
123 | TypeRef::String
124 | TypeRef::Char
125 | TypeRef::Bytes
126 | TypeRef::Path
127 | TypeRef::Unit
128 | TypeRef::Duration => true,
129 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_opaque_delegatable_type(inner),
131 TypeRef::Map(k, v) => is_opaque_delegatable_type(k) && is_opaque_delegatable_type(v),
132 TypeRef::Json => false,
133 }
134}
135
136pub fn is_simple_type(ty: &TypeRef) -> bool {
138 match ty {
139 TypeRef::Primitive(_)
140 | TypeRef::String
141 | TypeRef::Char
142 | TypeRef::Bytes
143 | TypeRef::Path
144 | TypeRef::Unit
145 | TypeRef::Duration => true,
146 TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_simple_type(inner),
147 TypeRef::Map(k, v) => is_simple_type(k) && is_simple_type(v),
148 TypeRef::Named(_) | TypeRef::Json => false,
149 }
150}
151
152pub fn partition_methods(methods: &[MethodDef]) -> (Vec<&MethodDef>, Vec<&MethodDef>) {
154 let instance: Vec<_> = methods.iter().filter(|m| m.receiver.is_some()).collect();
155 let statics: Vec<_> = methods.iter().filter(|m| m.receiver.is_none()).collect();
156 (instance, statics)
157}
158
159pub fn constructor_parts(fields: &[FieldDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> (String, String, String) {
163 constructor_parts_with_renames_and_cfg_restore(fields, type_mapper, None, &[])
164}
165
166pub fn constructor_parts_with_renames(
170 fields: &[FieldDef],
171 type_mapper: &dyn Fn(&TypeRef) -> String,
172 field_renames: Option<&HashMap<String, String>>,
173) -> (String, String, String) {
174 constructor_parts_with_renames_and_cfg_restore(fields, type_mapper, field_renames, &[])
175}
176
177pub fn constructor_parts_with_renames_and_cfg_restore(
182 fields: &[FieldDef],
183 type_mapper: &dyn Fn(&TypeRef) -> String,
184 field_renames: Option<&HashMap<String, String>>,
185 never_skip_cfg_field_names: &[String],
186) -> (String, String, String) {
187 let mut sorted_fields: Vec<&FieldDef> = fields
194 .iter()
195 .filter(|f| !f.binding_excluded)
196 .filter(|f| f.cfg.is_none() || never_skip_cfg_field_names.contains(&f.name))
197 .collect();
198 sorted_fields.sort_by_key(|f| (f.optional || f.cfg.is_some()) as u8);
199
200 let params: Vec<String> = sorted_fields
201 .iter()
202 .map(|f| {
203 let is_optional = f.optional || f.cfg.is_some();
204 let ty = if is_optional {
205 match &f.ty {
206 TypeRef::Optional(_) => type_mapper(&f.ty),
207 _ => format!("Option<{}>", type_mapper(&f.ty)),
208 }
209 } else {
210 type_mapper(&f.ty)
211 };
212 format!("{}: {}", f.name, ty)
213 })
214 .collect();
215
216 let defaults: Vec<String> = sorted_fields
217 .iter()
218 .map(|f| {
219 if f.optional || f.cfg.is_some() {
220 format!("{}=None", f.name)
221 } else {
222 f.name.clone()
223 }
224 })
225 .collect();
226
227 let assignments: Vec<String> = fields
232 .iter()
233 .filter(|f| !f.binding_excluded)
234 .map(|f| {
235 let binding_name = field_renames
236 .and_then(|r| r.get(&f.name))
237 .map_or_else(|| f.name.as_str(), |s| s.as_str());
238 if f.cfg.is_some() && !never_skip_cfg_field_names.contains(&f.name) {
239 return format!("{}: Default::default()", binding_name);
240 }
241 if binding_name != f.name {
242 return format!("{}: {}", binding_name, f.name);
243 }
244 f.name.clone()
245 })
246 .collect();
247
248 let single_line = params.join(", ");
250 let param_list = if single_line.len() > 100 {
251 format!("\n {},\n ", params.join(",\n "))
252 } else {
253 single_line
254 };
255
256 (param_list, defaults.join(", "), assignments.join(", "))
257}
258
259pub fn function_params(params: &[ParamDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
261 let mut seen_optional = false;
264 params
265 .iter()
266 .map(|p| {
267 if p.optional {
268 seen_optional = true;
269 }
270 let ty = if p.optional || seen_optional {
271 format!("Option<{}>", type_mapper(&p.ty))
272 } else {
273 type_mapper(&p.ty)
274 };
275 format!("{}: {}", p.name, ty)
276 })
277 .collect::<Vec<_>>()
278 .join(", ")
279}
280
281pub fn function_sig_defaults(params: &[ParamDef]) -> String {
283 let mut seen_optional = false;
289 params
290 .iter()
291 .map(|p| {
292 if p.optional {
293 seen_optional = true;
294 }
295 if p.optional {
296 format!("{}=None", p.name)
297 } else if seen_optional {
298 let default = match &p.ty {
301 TypeRef::Primitive(PrimitiveType::Bool) => "false",
302 TypeRef::Primitive(_) => "0",
303 _ => "None",
304 };
305 format!("{}={}", p.name, default)
306 } else {
307 p.name.clone()
308 }
309 })
310 .collect::<Vec<_>>()
311 .join(", ")
312}
313
314pub fn format_default_value(default: &DefaultValue) -> String {
317 match default {
318 DefaultValue::BoolLiteral(b) => format!("{}", b),
319 DefaultValue::StringLiteral(s) => format!("\"{}\".to_string()", s.escape_default()),
320 DefaultValue::IntLiteral(i) => format!("{}", i),
321 DefaultValue::FloatLiteral(f) => {
322 let s = format!("{}", f);
323 if s.contains('.') || s.contains('e') || s.contains('E') {
325 s
326 } else {
327 format!("{s}.0")
328 }
329 }
330 DefaultValue::EnumVariant(v) => v.clone(),
331 DefaultValue::Empty => "Default::default()".to_string(),
332 DefaultValue::None => "None".to_string(),
333 }
334}
335
336pub fn config_constructor_parts_with_options(
347 fields: &[FieldDef],
348 type_mapper: &dyn Fn(&TypeRef) -> String,
349 option_duration_on_defaults: bool,
350) -> (String, String, String) {
351 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults, None, &[])
352}
353
354pub fn config_constructor_parts_with_renames(
356 fields: &[FieldDef],
357 type_mapper: &dyn Fn(&TypeRef) -> String,
358 option_duration_on_defaults: bool,
359 field_renames: Option<&HashMap<String, String>>,
360) -> (String, String, String) {
361 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults, field_renames, &[])
362}
363
364pub fn config_constructor_parts_with_renames_and_cfg_restore(
367 fields: &[FieldDef],
368 type_mapper: &dyn Fn(&TypeRef) -> String,
369 option_duration_on_defaults: bool,
370 field_renames: Option<&HashMap<String, String>>,
371 never_skip_cfg_field_names: &[String],
372) -> (String, String, String) {
373 config_constructor_parts_inner(
374 fields,
375 type_mapper,
376 option_duration_on_defaults,
377 field_renames,
378 never_skip_cfg_field_names,
379 )
380}
381
382pub fn config_constructor_parts(
383 fields: &[FieldDef],
384 type_mapper: &dyn Fn(&TypeRef) -> String,
385) -> (String, String, String) {
386 config_constructor_parts_inner(fields, type_mapper, false, None, &[])
387}
388
389fn config_constructor_parts_inner(
390 fields: &[FieldDef],
391 type_mapper: &dyn Fn(&TypeRef) -> String,
392 option_duration_on_defaults: bool,
393 field_renames: Option<&HashMap<String, String>>,
394 never_skip_cfg_field_names: &[String],
395) -> (String, String, String) {
396 let mut sorted_fields: Vec<&FieldDef> = fields
400 .iter()
401 .filter(|f| !f.binding_excluded)
402 .filter(|f| f.cfg.is_none() || never_skip_cfg_field_names.contains(&f.name))
403 .collect();
404 sorted_fields.sort_by_key(|f| f.optional as u8);
405
406 let params: Vec<String> = sorted_fields
407 .iter()
408 .map(|f| {
409 let ty = type_mapper(&f.ty);
410 if matches!(f.ty, TypeRef::Optional(_)) {
415 format!("{}: {}", f.name, ty)
416 } else {
417 format!("{}: Option<{}>", f.name, ty)
418 }
419 })
420 .collect();
421
422 let defaults = sorted_fields
424 .iter()
425 .map(|f| format!("{}=None", f.name))
426 .collect::<Vec<_>>()
427 .join(", ");
428
429 let assignments: Vec<String> = fields
433 .iter()
434 .filter(|f| !f.binding_excluded)
435 .map(|f| {
436 let binding_name = field_renames
437 .and_then(|r| r.get(&f.name))
438 .map_or_else(|| f.name.as_str(), |s| s.as_str());
439 if f.cfg.is_some() {
440 if never_skip_cfg_field_names.contains(&f.name) {
441 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
445 return format!("{}: {}", binding_name, f.name);
446 }
447 return format!("{}: {}.unwrap_or_default()", binding_name, f.name);
448 }
449 return format!("{}: Default::default()", binding_name);
450 }
451 if option_duration_on_defaults && matches!(f.ty, TypeRef::Duration) {
454 return format!("{}: {}", binding_name, f.name);
455 }
456 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
457 format!("{}: {}", binding_name, f.name)
459 } else if let Some(ref typed_default) = f.typed_default {
460 match typed_default {
463 DefaultValue::EnumVariant(_) | DefaultValue::Empty => {
464 format!("{}: {}.unwrap_or_default()", binding_name, f.name)
465 }
466 _ => {
467 let default_val = format_default_value(typed_default);
468 match typed_default {
471 DefaultValue::BoolLiteral(_)
472 | DefaultValue::IntLiteral(_)
473 | DefaultValue::FloatLiteral(_) => {
474 format!("{}: {}.unwrap_or({})", binding_name, f.name, default_val)
475 }
476 _ => {
477 format!("{}: {}.unwrap_or_else(|| {})", binding_name, f.name, default_val)
478 }
479 }
480 }
481 }
482 } else {
483 format!("{}: {}.unwrap_or_default()", binding_name, f.name)
486 }
487 })
488 .collect();
489
490 let single_line = params.join(", ");
491 let param_list = if single_line.len() > 100 {
492 format!("\n {},\n ", params.join(",\n "))
493 } else {
494 single_line
495 };
496
497 (param_list, defaults, assignments.join(", "))
498}