1use ahash::AHashSet;
2use alef_core::ir::{DefaultValue, FieldDef, MethodDef, ParamDef, PrimitiveType, ReceiverKind, TypeRef};
3
4pub fn is_promoted_optional(params: &[ParamDef], idx: usize) -> bool {
8 if params[idx].optional {
9 return false; }
11 params[..idx].iter().any(|p| p.optional)
13}
14
15pub fn can_auto_delegate_function(func: &alef_core::ir::FunctionDef, opaque_types: &AHashSet<String>) -> bool {
19 !func.sanitized
20 && func
21 .params
22 .iter()
23 .all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types) && !is_named_ref_param(p, opaque_types))
24 && is_delegatable_return(&func.return_type)
25}
26
27pub fn can_auto_delegate(method: &MethodDef, opaque_types: &AHashSet<String>) -> bool {
30 if matches!(method.receiver, Some(ReceiverKind::RefMut)) && method.trait_source.is_none() {
32 return false;
33 }
34 !method.sanitized
35 && method
36 .params
37 .iter()
38 .all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types) && !is_named_ref_param(p, opaque_types))
39 && is_delegatable_return(&method.return_type)
40}
41
42fn is_named_ref_param(p: &alef_core::ir::ParamDef, opaque_types: &AHashSet<String>) -> bool {
45 if !p.is_ref {
46 return false;
47 }
48 match &p.ty {
49 TypeRef::Named(name) => !opaque_types.contains(name.as_str()),
50 TypeRef::Vec(inner) => matches!(inner.as_ref(), TypeRef::String | TypeRef::Char),
51 _ => false,
52 }
53}
54
55pub fn is_delegatable_param(ty: &TypeRef, _opaque_types: &AHashSet<String>) -> bool {
57 match ty {
58 TypeRef::Primitive(_)
59 | TypeRef::String
60 | TypeRef::Char
61 | TypeRef::Bytes
62 | TypeRef::Path
63 | TypeRef::Unit
64 | TypeRef::Duration => true,
65 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_param(inner, _opaque_types),
67 TypeRef::Map(k, v) => is_delegatable_param(k, _opaque_types) && is_delegatable_param(v, _opaque_types),
68 TypeRef::Json => false,
69 }
70}
71
72pub fn is_delegatable_return(ty: &TypeRef) -> bool {
74 match ty {
75 TypeRef::Primitive(_)
76 | TypeRef::String
77 | TypeRef::Char
78 | TypeRef::Bytes
79 | TypeRef::Path
80 | TypeRef::Unit
81 | TypeRef::Duration => true,
82 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_return(inner),
84 TypeRef::Map(k, v) => is_delegatable_return(k) && is_delegatable_return(v),
85 TypeRef::Json => false,
86 }
87}
88
89pub fn is_delegatable_type(ty: &TypeRef) -> bool {
93 match ty {
94 TypeRef::Primitive(_)
95 | TypeRef::String
96 | TypeRef::Char
97 | TypeRef::Bytes
98 | TypeRef::Path
99 | TypeRef::Unit
100 | TypeRef::Duration => true,
101 TypeRef::Named(_) => false, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_type(inner),
103 TypeRef::Map(k, v) => is_delegatable_type(k) && is_delegatable_type(v),
104 TypeRef::Json => false,
105 }
106}
107
108pub fn is_opaque_delegatable_type(ty: &TypeRef) -> bool {
111 match ty {
112 TypeRef::Primitive(_)
113 | TypeRef::String
114 | TypeRef::Char
115 | TypeRef::Bytes
116 | TypeRef::Path
117 | TypeRef::Unit
118 | TypeRef::Duration => true,
119 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_opaque_delegatable_type(inner),
121 TypeRef::Map(k, v) => is_opaque_delegatable_type(k) && is_opaque_delegatable_type(v),
122 TypeRef::Json => false,
123 }
124}
125
126pub fn is_simple_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::Optional(inner) | TypeRef::Vec(inner) => is_simple_type(inner),
137 TypeRef::Map(k, v) => is_simple_type(k) && is_simple_type(v),
138 TypeRef::Named(_) | TypeRef::Json => false,
139 }
140}
141
142pub fn partition_methods(methods: &[MethodDef]) -> (Vec<&MethodDef>, Vec<&MethodDef>) {
144 let instance: Vec<_> = methods.iter().filter(|m| m.receiver.is_some()).collect();
145 let statics: Vec<_> = methods.iter().filter(|m| m.receiver.is_none()).collect();
146 (instance, statics)
147}
148
149pub fn constructor_parts(fields: &[FieldDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> (String, String, String) {
153 let mut sorted_fields: Vec<&FieldDef> = fields.iter().filter(|f| f.cfg.is_none()).collect();
157 sorted_fields.sort_by_key(|f| f.optional as u8);
158
159 let params: Vec<String> = sorted_fields
160 .iter()
161 .map(|f| {
162 let ty = if f.optional {
163 format!("Option<{}>", type_mapper(&f.ty))
164 } else {
165 type_mapper(&f.ty)
166 };
167 format!("{}: {}", f.name, ty)
168 })
169 .collect();
170
171 let defaults: Vec<String> = sorted_fields
172 .iter()
173 .map(|f| {
174 if f.optional {
175 format!("{}=None", f.name)
176 } else {
177 f.name.clone()
178 }
179 })
180 .collect();
181
182 let assignments: Vec<String> = fields
184 .iter()
185 .filter(|f| f.cfg.is_none())
186 .map(|f| f.name.clone())
187 .collect();
188
189 let single_line = params.join(", ");
191 let param_list = if single_line.len() > 100 {
192 format!("\n {},\n ", params.join(",\n "))
193 } else {
194 single_line
195 };
196
197 (param_list, defaults.join(", "), assignments.join(", "))
198}
199
200pub fn function_params(params: &[ParamDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
202 let mut seen_optional = false;
205 params
206 .iter()
207 .map(|p| {
208 if p.optional {
209 seen_optional = true;
210 }
211 let ty = if p.optional || seen_optional {
212 format!("Option<{}>", type_mapper(&p.ty))
213 } else {
214 type_mapper(&p.ty)
215 };
216 format!("{}: {}", p.name, ty)
217 })
218 .collect::<Vec<_>>()
219 .join(", ")
220}
221
222pub fn function_sig_defaults(params: &[ParamDef]) -> String {
224 let mut seen_optional = false;
230 params
231 .iter()
232 .map(|p| {
233 if p.optional {
234 seen_optional = true;
235 }
236 if p.optional {
237 format!("{}=None", p.name)
238 } else if seen_optional {
239 let default = match &p.ty {
242 TypeRef::Primitive(PrimitiveType::Bool) => "false",
243 TypeRef::Primitive(_) => "0",
244 _ => "None",
245 };
246 format!("{}={}", p.name, default)
247 } else {
248 p.name.clone()
249 }
250 })
251 .collect::<Vec<_>>()
252 .join(", ")
253}
254
255pub fn format_default_value(default: &DefaultValue) -> String {
258 match default {
259 DefaultValue::BoolLiteral(b) => format!("{}", b),
260 DefaultValue::StringLiteral(s) => format!("\"{}\".to_string()", s.escape_default()),
261 DefaultValue::IntLiteral(i) => format!("{}", i),
262 DefaultValue::FloatLiteral(f) => {
263 let s = format!("{}", f);
264 if s.contains('.') || s.contains('e') || s.contains('E') {
266 s
267 } else {
268 format!("{s}.0")
269 }
270 }
271 DefaultValue::EnumVariant(v) => v.clone(),
272 DefaultValue::Empty => "Default::default()".to_string(),
273 DefaultValue::None => "None".to_string(),
274 }
275}
276
277pub fn config_constructor_parts_with_options(
288 fields: &[FieldDef],
289 type_mapper: &dyn Fn(&TypeRef) -> String,
290 option_duration_on_defaults: bool,
291) -> (String, String, String) {
292 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults)
293}
294
295pub fn config_constructor_parts(
296 fields: &[FieldDef],
297 type_mapper: &dyn Fn(&TypeRef) -> String,
298) -> (String, String, String) {
299 config_constructor_parts_inner(fields, type_mapper, false)
300}
301
302fn config_constructor_parts_inner(
303 fields: &[FieldDef],
304 type_mapper: &dyn Fn(&TypeRef) -> String,
305 option_duration_on_defaults: bool,
306) -> (String, String, String) {
307 let mut sorted_fields: Vec<&FieldDef> = fields.iter().filter(|f| f.cfg.is_none()).collect();
308 sorted_fields.sort_by_key(|f| f.optional as u8);
309
310 let params: Vec<String> = sorted_fields
311 .iter()
312 .map(|f| {
313 let ty = type_mapper(&f.ty);
314 if matches!(f.ty, TypeRef::Optional(_)) {
319 format!("{}: {}", f.name, ty)
320 } else {
321 format!("{}: Option<{}>", f.name, ty)
322 }
323 })
324 .collect();
325
326 let defaults = sorted_fields
328 .iter()
329 .map(|f| format!("{}=None", f.name))
330 .collect::<Vec<_>>()
331 .join(", ");
332
333 let assignments: Vec<String> = fields
335 .iter()
336 .filter(|f| f.cfg.is_none())
337 .map(|f| {
338 if option_duration_on_defaults && matches!(f.ty, TypeRef::Duration) {
341 return format!("{}: {}", f.name, f.name);
342 }
343 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
344 format!("{}: {}", f.name, f.name)
346 } else if let Some(ref typed_default) = f.typed_default {
347 match typed_default {
350 DefaultValue::EnumVariant(_) | DefaultValue::Empty => {
351 format!("{}: {}.unwrap_or_default()", f.name, f.name)
352 }
353 _ => {
354 let default_val = format_default_value(typed_default);
355 match typed_default {
358 DefaultValue::BoolLiteral(_)
359 | DefaultValue::IntLiteral(_)
360 | DefaultValue::FloatLiteral(_) => {
361 format!("{}: {}.unwrap_or({})", f.name, f.name, default_val)
362 }
363 _ => {
364 format!("{}: {}.unwrap_or_else(|| {})", f.name, f.name, default_val)
365 }
366 }
367 }
368 }
369 } else {
370 format!("{}: {}.unwrap_or_default()", f.name, f.name)
373 }
374 })
375 .collect();
376
377 let single_line = params.join(", ");
378 let param_list = if single_line.len() > 100 {
379 format!("\n {},\n ", params.join(",\n "))
380 } else {
381 single_line
382 };
383
384 (param_list, defaults, assignments.join(", "))
385}