use rustpython_parser::ast::Expr;
pub fn infer_params(args: &rustpython_parser::ast::Arguments) -> Vec<(String, String)> {
args.args
.iter()
.map(|a| {
let ty = if a.def.annotation.is_none() {
infer_type_from_name(a.def.arg.as_str())
} else {
infer_type_from_annotation(a.def.annotation.as_deref())
};
(a.def.arg.to_string(), ty)
})
.collect()
}
pub fn infer_type_from_annotation(annotation: Option<&Expr>) -> String {
match annotation {
Some(Expr::Name(n)) if n.id.as_str() == "int" => "usize".to_string(),
Some(Expr::Name(n)) if n.id.as_str() == "float" => "f64".to_string(),
Some(Expr::Attribute(attr)) => {
if let Expr::Name(base) = attr.value.as_ref() {
if base.id.as_str() == "np" || base.id.as_str() == "numpy" {
return "numpy::PyReadonlyArray1<f64>".to_string();
}
if base.id.as_str() == "torch" && attr.attr.as_str() == "Tensor" {
return "Vec<f64>".to_string();
}
}
"Vec<f64>".to_string()
}
_ => "Vec<f64>".to_string(),
}
}
fn infer_type_from_name(name: &str) -> String {
match name {
"text" | "string" | "s" | "line" => "String".to_string(),
"merges" => "Vec<i64>".to_string(),
"window" | "k" | "n" | "m" | "length" | "size" | "count" | "steps" => "usize".to_string(),
_ => "Vec<f64>".to_string(),
}
}
pub fn infer_assign_type(value: &Expr) -> &'static str {
match value {
Expr::Constant(c) => match &c.value {
rustpython_parser::ast::Constant::Float(_) => ": f64",
rustpython_parser::ast::Constant::Int(_) => ": i64",
_ => "",
},
_ => "",
}
}
pub fn render_len_checks(params: &[(String, String)]) -> Option<String> {
let vec_params: Vec<&String> = params
.iter()
.filter(|(_, ty)| ty.contains("Vec<") || ty.contains("[f64]"))
.map(|(n, _)| n)
.collect();
if vec_params.len() < 2 {
return None;
}
let first = vec_params[0];
let mut checks = String::new();
for other in vec_params.iter().skip(1) {
checks.push_str(&format!(
" if {first}.len() != {other}.len() {{\n return Err(pyo3::exceptions::PyValueError::new_err(\"length mismatch\"));\n }}\n",
first = first,
other = other
));
}
Some(checks)
}