1use ahash::AHashSet;
2use alef_core::ir::{DefaultValue, FieldDef, MethodDef, ParamDef, 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))
24 && is_delegatable_return(&func.return_type)
25}
26
27pub fn can_auto_delegate(method: &MethodDef, opaque_types: &AHashSet<String>) -> bool {
29 !method.sanitized
30 && method
31 .params
32 .iter()
33 .all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types))
34 && is_delegatable_return(&method.return_type)
35}
36
37pub fn is_delegatable_param(ty: &TypeRef, _opaque_types: &AHashSet<String>) -> bool {
39 match ty {
40 TypeRef::Primitive(_)
41 | TypeRef::String
42 | TypeRef::Char
43 | TypeRef::Bytes
44 | TypeRef::Path
45 | TypeRef::Unit
46 | TypeRef::Duration => true,
47 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_param(inner, _opaque_types),
49 TypeRef::Map(k, v) => is_delegatable_param(k, _opaque_types) && is_delegatable_param(v, _opaque_types),
50 TypeRef::Json => false,
51 }
52}
53
54pub fn is_delegatable_return(ty: &TypeRef) -> bool {
56 match ty {
57 TypeRef::Primitive(_)
58 | TypeRef::String
59 | TypeRef::Char
60 | TypeRef::Bytes
61 | TypeRef::Path
62 | TypeRef::Unit
63 | TypeRef::Duration => true,
64 TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_return(inner),
66 TypeRef::Map(k, v) => is_delegatable_return(k) && is_delegatable_return(v),
67 TypeRef::Json => false,
68 }
69}
70
71pub fn is_delegatable_type(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(_) => false, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_type(inner),
85 TypeRef::Map(k, v) => is_delegatable_type(k) && is_delegatable_type(v),
86 TypeRef::Json => false,
87 }
88}
89
90pub fn is_opaque_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(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_opaque_delegatable_type(inner),
103 TypeRef::Map(k, v) => is_opaque_delegatable_type(k) && is_opaque_delegatable_type(v),
104 TypeRef::Json => false,
105 }
106}
107
108pub fn is_simple_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::Optional(inner) | TypeRef::Vec(inner) => is_simple_type(inner),
119 TypeRef::Map(k, v) => is_simple_type(k) && is_simple_type(v),
120 TypeRef::Named(_) | TypeRef::Json => false,
121 }
122}
123
124pub fn partition_methods(methods: &[MethodDef]) -> (Vec<&MethodDef>, Vec<&MethodDef>) {
126 let instance: Vec<_> = methods.iter().filter(|m| m.receiver.is_some()).collect();
127 let statics: Vec<_> = methods.iter().filter(|m| m.receiver.is_none()).collect();
128 (instance, statics)
129}
130
131pub fn constructor_parts(fields: &[FieldDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> (String, String, String) {
135 let mut sorted_fields: Vec<&FieldDef> = fields.iter().filter(|f| f.cfg.is_none()).collect();
139 sorted_fields.sort_by_key(|f| f.optional as u8);
140
141 let params: Vec<String> = sorted_fields
142 .iter()
143 .map(|f| {
144 let ty = if f.optional {
145 format!("Option<{}>", type_mapper(&f.ty))
146 } else {
147 type_mapper(&f.ty)
148 };
149 format!("{}: {}", f.name, ty)
150 })
151 .collect();
152
153 let defaults: Vec<String> = sorted_fields
154 .iter()
155 .map(|f| {
156 if f.optional {
157 format!("{}=None", f.name)
158 } else {
159 f.name.clone()
160 }
161 })
162 .collect();
163
164 let assignments: Vec<String> = fields
166 .iter()
167 .filter(|f| f.cfg.is_none())
168 .map(|f| f.name.clone())
169 .collect();
170
171 let single_line = params.join(", ");
173 let param_list = if single_line.len() > 100 {
174 format!("\n {},\n ", params.join(",\n "))
175 } else {
176 single_line
177 };
178
179 (param_list, defaults.join(", "), assignments.join(", "))
180}
181
182pub fn function_params(params: &[ParamDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
184 let mut seen_optional = false;
187 params
188 .iter()
189 .map(|p| {
190 if p.optional {
191 seen_optional = true;
192 }
193 let ty = if p.optional || seen_optional {
194 format!("Option<{}>", type_mapper(&p.ty))
195 } else {
196 type_mapper(&p.ty)
197 };
198 format!("{}: {}", p.name, ty)
199 })
200 .collect::<Vec<_>>()
201 .join(", ")
202}
203
204pub fn function_sig_defaults(params: &[ParamDef]) -> String {
206 let mut seen_optional = false;
209 params
210 .iter()
211 .map(|p| {
212 if p.optional {
213 seen_optional = true;
214 }
215 if p.optional || seen_optional {
216 format!("{}=None", p.name)
217 } else {
218 p.name.clone()
219 }
220 })
221 .collect::<Vec<_>>()
222 .join(", ")
223}
224
225pub fn format_default_value(default: &DefaultValue) -> String {
228 match default {
229 DefaultValue::BoolLiteral(b) => format!("{}", b),
230 DefaultValue::StringLiteral(s) => format!("\"{}\".to_string()", s.escape_default()),
231 DefaultValue::IntLiteral(i) => format!("{}", i),
232 DefaultValue::FloatLiteral(f) => {
233 let s = format!("{}", f);
234 if s.contains('.') || s.contains('e') || s.contains('E') {
236 s
237 } else {
238 format!("{s}.0")
239 }
240 }
241 DefaultValue::EnumVariant(v) => v.clone(),
242 DefaultValue::Empty => "Default::default()".to_string(),
243 DefaultValue::None => "None".to_string(),
244 }
245}
246
247pub fn config_constructor_parts_with_options(
258 fields: &[FieldDef],
259 type_mapper: &dyn Fn(&TypeRef) -> String,
260 option_duration_on_defaults: bool,
261) -> (String, String, String) {
262 config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults)
263}
264
265pub fn config_constructor_parts(
266 fields: &[FieldDef],
267 type_mapper: &dyn Fn(&TypeRef) -> String,
268) -> (String, String, String) {
269 config_constructor_parts_inner(fields, type_mapper, false)
270}
271
272fn config_constructor_parts_inner(
273 fields: &[FieldDef],
274 type_mapper: &dyn Fn(&TypeRef) -> String,
275 option_duration_on_defaults: bool,
276) -> (String, String, String) {
277 let mut sorted_fields: Vec<&FieldDef> = fields.iter().filter(|f| f.cfg.is_none()).collect();
278 sorted_fields.sort_by_key(|f| f.optional as u8);
279
280 let params: Vec<String> = sorted_fields
281 .iter()
282 .map(|f| {
283 let ty = type_mapper(&f.ty);
284 format!("{}: Option<{}>", f.name, ty)
286 })
287 .collect();
288
289 let defaults = sorted_fields
291 .iter()
292 .map(|f| format!("{}=None", f.name))
293 .collect::<Vec<_>>()
294 .join(", ");
295
296 let assignments: Vec<String> = fields
298 .iter()
299 .filter(|f| f.cfg.is_none())
300 .map(|f| {
301 if option_duration_on_defaults && matches!(f.ty, TypeRef::Duration) {
304 return format!("{}: {}", f.name, f.name);
305 }
306 if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
307 format!("{}: {}", f.name, f.name)
309 } else if let Some(ref typed_default) = f.typed_default {
310 match typed_default {
313 DefaultValue::EnumVariant(_) | DefaultValue::Empty => {
314 format!("{}: {}.unwrap_or_default()", f.name, f.name)
315 }
316 _ => {
317 let default_val = format_default_value(typed_default);
318 match typed_default {
321 DefaultValue::BoolLiteral(_)
322 | DefaultValue::IntLiteral(_)
323 | DefaultValue::FloatLiteral(_) => {
324 format!("{}: {}.unwrap_or({})", f.name, f.name, default_val)
325 }
326 _ => {
327 format!("{}: {}.unwrap_or_else(|| {})", f.name, f.name, default_val)
328 }
329 }
330 }
331 }
332 } else {
333 format!("{}: {}.unwrap_or_default()", f.name, f.name)
336 }
337 })
338 .collect();
339
340 let single_line = params.join(", ");
341 let param_list = if single_line.len() > 100 {
342 format!("\n {},\n ", params.join(",\n "))
343 } else {
344 single_line
345 };
346
347 (param_list, defaults, assignments.join(", "))
348}