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(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 let mut sorted_fields: Vec<&FieldDef> = fields.iter().filter(|f| f.cfg.is_none()).collect();
169 sorted_fields.sort_by_key(|f| f.optional as u8);
170
171 let params: Vec<String> = sorted_fields
172 .iter()
173 .map(|f| {
174 let ty = if f.optional {
175 match &f.ty {
176 TypeRef::Optional(_) => type_mapper(&f.ty),
177 _ => format!("Option<{}>", type_mapper(&f.ty)),
178 }
179 } else {
180 type_mapper(&f.ty)
181 };
182 format!("{}: {}", f.name, ty)
183 })
184 .collect();
185
186 let defaults: Vec<String> = sorted_fields
187 .iter()
188 .map(|f| {
189 if f.optional {
190 format!("{}=None", f.name)
191 } else {
192 f.name.clone()
193 }
194 })
195 .collect();
196
197 let assignments: Vec<String> = fields
200 .iter()
201 .filter(|f| f.cfg.is_none())
202 .map(|f| {
203 if let Some(renames) = field_renames {
204 if let Some(renamed) = renames.get(&f.name) {
205 return format!("{}: {}", renamed, f.name);
206 }
207 }
208 f.name.clone()
209 })
210 .collect();
211
212 let single_line = params.join(", ");
214 let param_list = if single_line.len() > 100 {
215 format!("\n {},\n ", params.join(",\n "))
216 } else {
217 single_line
218 };
219
220 (param_list, defaults.join(", "), assignments.join(", "))
221}
222
223pub fn function_params(params: &[ParamDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
225 let mut seen_optional = false;
228 params
229 .iter()
230 .map(|p| {
231 if p.optional {
232 seen_optional = true;
233 }
234 let ty = if p.optional || seen_optional {
235 format!("Option<{}>", type_mapper(&p.ty))
236 } else {
237 type_mapper(&p.ty)
238 };
239 format!("{}: {}", p.name, ty)
240 })
241 .collect::<Vec<_>>()
242 .join(", ")
243}
244
245pub fn function_sig_defaults(params: &[ParamDef]) -> String {
247 let mut seen_optional = false;
253 params
254 .iter()
255 .map(|p| {
256 if p.optional {
257 seen_optional = true;
258 }
259 if p.optional {
260 format!("{}=None", p.name)
261 } else if seen_optional {
262 let default = match &p.ty {
265 TypeRef::Primitive(PrimitiveType::Bool) => "false",
266 TypeRef::Primitive(_) => "0",
267 _ => "None",
268 };
269 format!("{}={}", p.name, default)
270 } else {
271 p.name.clone()
272 }
273 })
274 .collect::<Vec<_>>()
275 .join(", ")
276}
277
278pub fn format_default_value(default: &DefaultValue) -> String {
281 match default {
282 DefaultValue::BoolLiteral(b) => format!("{}", b),
283 DefaultValue::StringLiteral(s) => format!("\"{}\".to_string()", s.escape_default()),
284 DefaultValue::IntLiteral(i) => format!("{}", i),
285 DefaultValue::FloatLiteral(f) => {
286 let s = format!("{}", f);
287 if s.contains('.') || s.contains('e') || s.contains('E') {
289 s
290 } else {
291 format!("{s}.0")
292 }
293 }
294 DefaultValue::EnumVariant(v) => v.clone(),
295 DefaultValue::Empty => "Default::default()".to_string(),
296 DefaultValue::None => "None".to_string(),
297 }
298}
299
300pub fn config_constructor_parts_with_options(
311 fields: &[FieldDef],
312 type_mapper: &dyn Fn(&TypeRef) -> String,
313 option_duration_on_defaults: bool,
314) -> (String, String, String) {
315 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults, None)
316}
317
318pub fn config_constructor_parts_with_renames(
320 fields: &[FieldDef],
321 type_mapper: &dyn Fn(&TypeRef) -> String,
322 option_duration_on_defaults: bool,
323 field_renames: Option<&HashMap<String, String>>,
324) -> (String, String, String) {
325 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults, field_renames)
326}
327
328pub fn config_constructor_parts(
329 fields: &[FieldDef],
330 type_mapper: &dyn Fn(&TypeRef) -> String,
331) -> (String, String, String) {
332 config_constructor_parts_inner(fields, type_mapper, false, None)
333}
334
335fn config_constructor_parts_inner(
336 fields: &[FieldDef],
337 type_mapper: &dyn Fn(&TypeRef) -> String,
338 option_duration_on_defaults: bool,
339 field_renames: Option<&HashMap<String, String>>,
340) -> (String, String, String) {
341 let mut sorted_fields: Vec<&FieldDef> = fields.iter().filter(|f| f.cfg.is_none()).collect();
342 sorted_fields.sort_by_key(|f| f.optional as u8);
343
344 let params: Vec<String> = sorted_fields
345 .iter()
346 .map(|f| {
347 let ty = type_mapper(&f.ty);
348 if matches!(f.ty, TypeRef::Optional(_)) {
353 format!("{}: {}", f.name, ty)
354 } else {
355 format!("{}: Option<{}>", f.name, ty)
356 }
357 })
358 .collect();
359
360 let defaults = sorted_fields
362 .iter()
363 .map(|f| format!("{}=None", f.name))
364 .collect::<Vec<_>>()
365 .join(", ");
366
367 let assignments: Vec<String> = fields
371 .iter()
372 .filter(|f| f.cfg.is_none())
373 .map(|f| {
374 let binding_name = field_renames
375 .and_then(|r| r.get(&f.name))
376 .map_or_else(|| f.name.as_str(), |s| s.as_str());
377 if option_duration_on_defaults && matches!(f.ty, TypeRef::Duration) {
380 return format!("{}: {}", binding_name, f.name);
381 }
382 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
383 format!("{}: {}", binding_name, f.name)
385 } else if let Some(ref typed_default) = f.typed_default {
386 match typed_default {
389 DefaultValue::EnumVariant(_) | DefaultValue::Empty => {
390 format!("{}: {}.unwrap_or_default()", binding_name, f.name)
391 }
392 _ => {
393 let default_val = format_default_value(typed_default);
394 match typed_default {
397 DefaultValue::BoolLiteral(_)
398 | DefaultValue::IntLiteral(_)
399 | DefaultValue::FloatLiteral(_) => {
400 format!("{}: {}.unwrap_or({})", binding_name, f.name, default_val)
401 }
402 _ => {
403 format!("{}: {}.unwrap_or_else(|| {})", binding_name, f.name, default_val)
404 }
405 }
406 }
407 }
408 } else {
409 format!("{}: {}.unwrap_or_default()", binding_name, f.name)
412 }
413 })
414 .collect();
415
416 let single_line = params.join(", ");
417 let param_list = if single_line.len() > 100 {
418 format!("\n {},\n ", params.join(",\n "))
419 } else {
420 single_line
421 };
422
423 (param_list, defaults, assignments.join(", "))
424}