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 {
71 match ty {
72 TypeRef::Primitive(_)
73 | TypeRef::String
74 | TypeRef::Char
75 | TypeRef::Bytes
76 | TypeRef::Path
77 | TypeRef::Unit
78 | TypeRef::Duration
79 | TypeRef::Json => true,
80 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_param(inner, _opaque_types),
82 TypeRef::Map(k, v) => is_delegatable_param(k, _opaque_types) && is_delegatable_param(v, _opaque_types),
83 }
84}
85
86pub fn is_delegatable_return(ty: &TypeRef) -> bool {
91 match ty {
92 TypeRef::Primitive(_)
93 | TypeRef::String
94 | TypeRef::Char
95 | TypeRef::Bytes
96 | TypeRef::Path
97 | TypeRef::Unit
98 | TypeRef::Duration
99 | TypeRef::Json => true,
100 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_return(inner),
102 TypeRef::Map(k, v) => is_delegatable_return(k) && is_delegatable_return(v),
103 }
104}
105
106pub fn is_delegatable_type(ty: &TypeRef) -> bool {
110 match ty {
111 TypeRef::Primitive(_)
112 | TypeRef::String
113 | TypeRef::Char
114 | TypeRef::Bytes
115 | TypeRef::Path
116 | TypeRef::Unit
117 | TypeRef::Duration => true,
118 TypeRef::Named(_) => false, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_type(inner),
120 TypeRef::Map(k, v) => is_delegatable_type(k) && is_delegatable_type(v),
121 TypeRef::Json => false,
122 }
123}
124
125pub fn is_opaque_delegatable_type(ty: &TypeRef) -> bool {
128 match ty {
129 TypeRef::Primitive(_)
130 | TypeRef::String
131 | TypeRef::Char
132 | TypeRef::Bytes
133 | TypeRef::Path
134 | TypeRef::Unit
135 | TypeRef::Duration => true,
136 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_opaque_delegatable_type(inner),
138 TypeRef::Map(k, v) => is_opaque_delegatable_type(k) && is_opaque_delegatable_type(v),
139 TypeRef::Json => false,
140 }
141}
142
143pub fn is_simple_type(ty: &TypeRef) -> bool {
145 match ty {
146 TypeRef::Primitive(_)
147 | TypeRef::String
148 | TypeRef::Char
149 | TypeRef::Bytes
150 | TypeRef::Path
151 | TypeRef::Unit
152 | TypeRef::Duration => true,
153 TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_simple_type(inner),
154 TypeRef::Map(k, v) => is_simple_type(k) && is_simple_type(v),
155 TypeRef::Named(_) | TypeRef::Json => false,
156 }
157}
158
159pub fn partition_methods(methods: &[MethodDef]) -> (Vec<&MethodDef>, Vec<&MethodDef>) {
161 let instance: Vec<_> = methods.iter().filter(|m| m.receiver.is_some()).collect();
162 let statics: Vec<_> = methods.iter().filter(|m| m.receiver.is_none()).collect();
163 (instance, statics)
164}
165
166pub fn constructor_parts(fields: &[FieldDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> (String, String, String) {
170 constructor_parts_with_renames_and_cfg_restore(fields, type_mapper, None, &[])
171}
172
173pub fn constructor_parts_with_renames(
177 fields: &[FieldDef],
178 type_mapper: &dyn Fn(&TypeRef) -> String,
179 field_renames: Option<&HashMap<String, String>>,
180) -> (String, String, String) {
181 constructor_parts_with_renames_and_cfg_restore(fields, type_mapper, field_renames, &[])
182}
183
184pub fn constructor_parts_with_renames_and_cfg_restore(
189 fields: &[FieldDef],
190 type_mapper: &dyn Fn(&TypeRef) -> String,
191 field_renames: Option<&HashMap<String, String>>,
192 never_skip_cfg_field_names: &[String],
193) -> (String, String, String) {
194 let mut sorted_fields: Vec<&FieldDef> = fields
201 .iter()
202 .filter(|f| !f.binding_excluded)
203 .filter(|f| f.cfg.is_none() || never_skip_cfg_field_names.contains(&f.name))
204 .collect();
205 sorted_fields.sort_by_key(|f| (f.optional || f.cfg.is_some()) as u8);
206
207 let params: Vec<String> = sorted_fields
208 .iter()
209 .map(|f| {
210 let is_optional = f.optional || f.cfg.is_some();
211 let ty = if is_optional {
212 match &f.ty {
213 TypeRef::Optional(_) => type_mapper(&f.ty),
214 _ => format!("Option<{}>", type_mapper(&f.ty)),
215 }
216 } else {
217 type_mapper(&f.ty)
218 };
219 format!("{}: {}", f.name, ty)
220 })
221 .collect();
222
223 let defaults: Vec<String> = sorted_fields
224 .iter()
225 .map(|f| {
226 if f.optional || f.cfg.is_some() {
227 format!("{}=None", f.name)
228 } else {
229 f.name.clone()
230 }
231 })
232 .collect();
233
234 let assignments: Vec<String> = fields
239 .iter()
240 .filter(|f| !f.binding_excluded)
241 .map(|f| {
242 let binding_name = field_renames
243 .and_then(|r| r.get(&f.name))
244 .map_or_else(|| f.name.as_str(), |s| s.as_str());
245 if f.cfg.is_some() && !never_skip_cfg_field_names.contains(&f.name) {
246 return format!("{}: Default::default()", binding_name);
247 }
248 if binding_name != f.name {
249 return format!("{}: {}", binding_name, f.name);
250 }
251 f.name.clone()
252 })
253 .collect();
254
255 let single_line = params.join(", ");
257 let param_list = if single_line.len() > 100 {
258 format!("\n {},\n ", params.join(",\n "))
259 } else {
260 single_line
261 };
262
263 (param_list, defaults.join(", "), assignments.join(", "))
264}
265
266pub fn function_params(params: &[ParamDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
268 let mut seen_optional = false;
271 params
272 .iter()
273 .map(|p| {
274 if p.optional {
275 seen_optional = true;
276 }
277 let ty = if p.optional || seen_optional {
278 format!("Option<{}>", type_mapper(&p.ty))
279 } else {
280 type_mapper(&p.ty)
281 };
282 format!("{}: {}", p.name, ty)
283 })
284 .collect::<Vec<_>>()
285 .join(", ")
286}
287
288pub fn function_sig_defaults(params: &[ParamDef]) -> String {
290 let mut seen_optional = false;
296 params
297 .iter()
298 .map(|p| {
299 if p.optional {
300 seen_optional = true;
301 }
302 if p.optional {
303 format!("{}=None", p.name)
304 } else if seen_optional {
305 let default = match &p.ty {
308 TypeRef::Primitive(PrimitiveType::Bool) => "false",
309 TypeRef::Primitive(_) => "0",
310 _ => "None",
311 };
312 format!("{}={}", p.name, default)
313 } else {
314 p.name.clone()
315 }
316 })
317 .collect::<Vec<_>>()
318 .join(", ")
319}
320
321pub fn format_default_value(default: &DefaultValue) -> String {
324 match default {
325 DefaultValue::BoolLiteral(b) => format!("{}", b),
326 DefaultValue::StringLiteral(s) => format!("\"{}\".to_string()", s.escape_default()),
327 DefaultValue::IntLiteral(i) => format!("{}", i),
328 DefaultValue::FloatLiteral(f) => {
329 let s = format!("{}", f);
330 if s.contains('.') || s.contains('e') || s.contains('E') {
332 s
333 } else {
334 format!("{s}.0")
335 }
336 }
337 DefaultValue::EnumVariant(v) => v.clone(),
338 DefaultValue::Empty => "Default::default()".to_string(),
339 DefaultValue::None => "None".to_string(),
340 }
341}
342
343pub fn config_constructor_parts_with_options(
354 fields: &[FieldDef],
355 type_mapper: &dyn Fn(&TypeRef) -> String,
356 option_duration_on_defaults: bool,
357) -> (String, String, String) {
358 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults, None, &[])
359}
360
361pub fn config_constructor_parts_with_renames(
363 fields: &[FieldDef],
364 type_mapper: &dyn Fn(&TypeRef) -> String,
365 option_duration_on_defaults: bool,
366 field_renames: Option<&HashMap<String, String>>,
367) -> (String, String, String) {
368 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults, field_renames, &[])
369}
370
371pub fn config_constructor_parts_with_renames_and_cfg_restore(
374 fields: &[FieldDef],
375 type_mapper: &dyn Fn(&TypeRef) -> String,
376 option_duration_on_defaults: bool,
377 field_renames: Option<&HashMap<String, String>>,
378 never_skip_cfg_field_names: &[String],
379) -> (String, String, String) {
380 config_constructor_parts_inner(
381 fields,
382 type_mapper,
383 option_duration_on_defaults,
384 field_renames,
385 never_skip_cfg_field_names,
386 )
387}
388
389pub fn config_constructor_parts(
390 fields: &[FieldDef],
391 type_mapper: &dyn Fn(&TypeRef) -> String,
392) -> (String, String, String) {
393 config_constructor_parts_inner(fields, type_mapper, false, None, &[])
394}
395
396fn config_constructor_parts_inner(
397 fields: &[FieldDef],
398 type_mapper: &dyn Fn(&TypeRef) -> String,
399 option_duration_on_defaults: bool,
400 field_renames: Option<&HashMap<String, String>>,
401 never_skip_cfg_field_names: &[String],
402) -> (String, String, String) {
403 let mut sorted_fields: Vec<&FieldDef> = fields
407 .iter()
408 .filter(|f| !f.binding_excluded)
409 .filter(|f| f.cfg.is_none() || never_skip_cfg_field_names.contains(&f.name))
410 .collect();
411 sorted_fields.sort_by_key(|f| f.optional as u8);
412
413 let params: Vec<String> = sorted_fields
414 .iter()
415 .map(|f| {
416 let ty = type_mapper(&f.ty);
417 if matches!(f.ty, TypeRef::Optional(_)) {
422 format!("{}: {}", f.name, ty)
423 } else {
424 format!("{}: Option<{}>", f.name, ty)
425 }
426 })
427 .collect();
428
429 let defaults = sorted_fields
431 .iter()
432 .map(|f| format!("{}=None", f.name))
433 .collect::<Vec<_>>()
434 .join(", ");
435
436 let assignments: Vec<String> = fields
440 .iter()
441 .filter(|f| !f.binding_excluded)
442 .map(|f| {
443 let binding_name = field_renames
444 .and_then(|r| r.get(&f.name))
445 .map_or_else(|| f.name.as_str(), |s| s.as_str());
446 if f.cfg.is_some() {
447 if never_skip_cfg_field_names.contains(&f.name) {
448 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
452 return format!("{}: {}", binding_name, f.name);
453 }
454 return format!("{}: {}.unwrap_or_default()", binding_name, f.name);
455 }
456 return format!("{}: Default::default()", binding_name);
457 }
458 if option_duration_on_defaults && matches!(f.ty, TypeRef::Duration) {
461 return format!("{}: {}", binding_name, f.name);
462 }
463 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
464 format!("{}: {}", binding_name, f.name)
466 } else if let Some(ref typed_default) = f.typed_default {
467 match typed_default {
470 DefaultValue::EnumVariant(_) | DefaultValue::Empty => {
471 format!("{}: {}.unwrap_or_default()", binding_name, f.name)
472 }
473 _ => {
474 let default_val = format_default_value(typed_default);
475 match typed_default {
478 DefaultValue::BoolLiteral(_)
479 | DefaultValue::IntLiteral(_)
480 | DefaultValue::FloatLiteral(_) => {
481 format!("{}: {}.unwrap_or({})", binding_name, f.name, default_val)
482 }
483 _ => {
484 format!("{}: {}.unwrap_or_else(|| {})", binding_name, f.name, default_val)
485 }
486 }
487 }
488 }
489 } else {
490 format!("{}: {}.unwrap_or_default()", binding_name, f.name)
493 }
494 })
495 .collect();
496
497 let single_line = params.join(", ");
498 let param_list = if single_line.len() > 100 {
499 format!("\n {},\n ", params.join(",\n "))
500 } else {
501 single_line
502 };
503
504 (param_list, defaults, assignments.join(", "))
505}