use ahash::AHashSet;
use alef_core::ir::{DefaultValue, FieldDef, MethodDef, ParamDef, TypeRef};
pub fn is_promoted_optional(params: &[ParamDef], idx: usize) -> bool {
if params[idx].optional {
return false; }
params[..idx].iter().any(|p| p.optional)
}
pub fn can_auto_delegate_function(func: &alef_core::ir::FunctionDef, opaque_types: &AHashSet<String>) -> bool {
!func.sanitized
&& func
.params
.iter()
.all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types))
&& is_delegatable_return(&func.return_type)
}
pub fn can_auto_delegate(method: &MethodDef, opaque_types: &AHashSet<String>) -> bool {
!method.sanitized
&& method
.params
.iter()
.all(|p| !p.sanitized && is_delegatable_param(&p.ty, opaque_types))
&& is_delegatable_return(&method.return_type)
}
pub fn is_delegatable_param(ty: &TypeRef, _opaque_types: &AHashSet<String>) -> bool {
match ty {
TypeRef::Primitive(_)
| TypeRef::String
| TypeRef::Char
| TypeRef::Bytes
| TypeRef::Path
| TypeRef::Unit
| TypeRef::Duration => true,
TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_param(inner, _opaque_types),
TypeRef::Map(k, v) => is_delegatable_param(k, _opaque_types) && is_delegatable_param(v, _opaque_types),
TypeRef::Json => false,
}
}
pub fn is_delegatable_return(ty: &TypeRef) -> bool {
match ty {
TypeRef::Primitive(_)
| TypeRef::String
| TypeRef::Char
| TypeRef::Bytes
| TypeRef::Path
| TypeRef::Unit
| TypeRef::Duration => true,
TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_return(inner),
TypeRef::Map(k, v) => is_delegatable_return(k) && is_delegatable_return(v),
TypeRef::Json => false,
}
}
pub fn is_delegatable_type(ty: &TypeRef) -> bool {
match ty {
TypeRef::Primitive(_)
| TypeRef::String
| TypeRef::Char
| TypeRef::Bytes
| TypeRef::Path
| TypeRef::Unit
| TypeRef::Duration => true,
TypeRef::Named(_) => false, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_delegatable_type(inner),
TypeRef::Map(k, v) => is_delegatable_type(k) && is_delegatable_type(v),
TypeRef::Json => false,
}
}
pub fn is_opaque_delegatable_type(ty: &TypeRef) -> bool {
match ty {
TypeRef::Primitive(_)
| TypeRef::String
| TypeRef::Char
| TypeRef::Bytes
| TypeRef::Path
| TypeRef::Unit
| TypeRef::Duration => true,
TypeRef::Named(_) => true, TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_opaque_delegatable_type(inner),
TypeRef::Map(k, v) => is_opaque_delegatable_type(k) && is_opaque_delegatable_type(v),
TypeRef::Json => false,
}
}
pub fn is_simple_type(ty: &TypeRef) -> bool {
match ty {
TypeRef::Primitive(_)
| TypeRef::String
| TypeRef::Char
| TypeRef::Bytes
| TypeRef::Path
| TypeRef::Unit
| TypeRef::Duration => true,
TypeRef::Optional(inner) | TypeRef::Vec(inner) => is_simple_type(inner),
TypeRef::Map(k, v) => is_simple_type(k) && is_simple_type(v),
TypeRef::Named(_) | TypeRef::Json => false,
}
}
pub fn partition_methods(methods: &[MethodDef]) -> (Vec<&MethodDef>, Vec<&MethodDef>) {
let instance: Vec<_> = methods.iter().filter(|m| m.receiver.is_some()).collect();
let statics: Vec<_> = methods.iter().filter(|m| m.receiver.is_none()).collect();
(instance, statics)
}
pub fn constructor_parts(fields: &[FieldDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> (String, String, String) {
let mut sorted_fields: Vec<&FieldDef> = fields.iter().filter(|f| f.cfg.is_none()).collect();
sorted_fields.sort_by_key(|f| f.optional as u8);
let params: Vec<String> = sorted_fields
.iter()
.map(|f| {
let ty = if f.optional {
format!("Option<{}>", type_mapper(&f.ty))
} else {
type_mapper(&f.ty)
};
format!("{}: {}", f.name, ty)
})
.collect();
let defaults: Vec<String> = sorted_fields
.iter()
.map(|f| {
if f.optional {
format!("{}=None", f.name)
} else {
f.name.clone()
}
})
.collect();
let assignments: Vec<String> = fields
.iter()
.filter(|f| f.cfg.is_none())
.map(|f| f.name.clone())
.collect();
let single_line = params.join(", ");
let param_list = if single_line.len() > 100 {
format!("\n {},\n ", params.join(",\n "))
} else {
single_line
};
(param_list, defaults.join(", "), assignments.join(", "))
}
pub fn function_params(params: &[ParamDef], type_mapper: &dyn Fn(&TypeRef) -> String) -> String {
let mut seen_optional = false;
params
.iter()
.map(|p| {
if p.optional {
seen_optional = true;
}
let ty = if p.optional || seen_optional {
format!("Option<{}>", type_mapper(&p.ty))
} else {
type_mapper(&p.ty)
};
format!("{}: {}", p.name, ty)
})
.collect::<Vec<_>>()
.join(", ")
}
pub fn function_sig_defaults(params: &[ParamDef]) -> String {
let mut seen_optional = false;
params
.iter()
.map(|p| {
if p.optional {
seen_optional = true;
}
if p.optional || seen_optional {
format!("{}=None", p.name)
} else {
p.name.clone()
}
})
.collect::<Vec<_>>()
.join(", ")
}
pub fn format_default_value(default: &DefaultValue) -> String {
match default {
DefaultValue::BoolLiteral(b) => format!("{}", b),
DefaultValue::StringLiteral(s) => format!("\"{}\".to_string()", s.escape_default()),
DefaultValue::IntLiteral(i) => format!("{}", i),
DefaultValue::FloatLiteral(f) => {
let s = format!("{}", f);
if s.contains('.') || s.contains('e') || s.contains('E') {
s
} else {
format!("{s}.0")
}
}
DefaultValue::EnumVariant(v) => v.clone(),
DefaultValue::Empty => "Default::default()".to_string(),
DefaultValue::None => "None".to_string(),
}
}
pub fn config_constructor_parts_with_options(
fields: &[FieldDef],
type_mapper: &dyn Fn(&TypeRef) -> String,
option_duration_on_defaults: bool,
) -> (String, String, String) {
config_constructor_parts_inner(fields, type_mapper, option_duration_on_defaults)
}
pub fn config_constructor_parts(
fields: &[FieldDef],
type_mapper: &dyn Fn(&TypeRef) -> String,
) -> (String, String, String) {
config_constructor_parts_inner(fields, type_mapper, false)
}
fn config_constructor_parts_inner(
fields: &[FieldDef],
type_mapper: &dyn Fn(&TypeRef) -> String,
option_duration_on_defaults: bool,
) -> (String, String, String) {
let mut sorted_fields: Vec<&FieldDef> = fields.iter().filter(|f| f.cfg.is_none()).collect();
sorted_fields.sort_by_key(|f| f.optional as u8);
let params: Vec<String> = sorted_fields
.iter()
.map(|f| {
let ty = type_mapper(&f.ty);
format!("{}: Option<{}>", f.name, ty)
})
.collect();
let defaults = sorted_fields
.iter()
.map(|f| format!("{}=None", f.name))
.collect::<Vec<_>>()
.join(", ");
let assignments: Vec<String> = fields
.iter()
.filter(|f| f.cfg.is_none())
.map(|f| {
if option_duration_on_defaults && matches!(f.ty, TypeRef::Duration) {
return format!("{}: {}", f.name, f.name);
}
if f.optional || matches!(&f.ty, TypeRef::Optional(_)) {
format!("{}: {}", f.name, f.name)
} else if let Some(ref typed_default) = f.typed_default {
match typed_default {
DefaultValue::EnumVariant(_) | DefaultValue::Empty => {
format!("{}: {}.unwrap_or_default()", f.name, f.name)
}
_ => {
let default_val = format_default_value(typed_default);
match typed_default {
DefaultValue::BoolLiteral(_)
| DefaultValue::IntLiteral(_)
| DefaultValue::FloatLiteral(_) => {
format!("{}: {}.unwrap_or({})", f.name, f.name, default_val)
}
_ => {
format!("{}: {}.unwrap_or_else(|| {})", f.name, f.name, default_val)
}
}
}
}
} else {
format!("{}: {}.unwrap_or_default()", f.name, f.name)
}
})
.collect();
let single_line = params.join(", ");
let param_list = if single_line.len() > 100 {
format!("\n {},\n ", params.join(",\n "))
} else {
single_line
};
(param_list, defaults, assignments.join(", "))
}