use super::Transpiler;
use anyhow::Result;
use proc_macro2::TokenStream;
impl Transpiler {
pub(super) fn fill_default_args(
&self,
func_name: &str,
provided_args: Vec<TokenStream>,
) -> Result<Vec<TokenStream>> {
let sig = match self.function_signatures.get(func_name) {
Some(sig) => sig,
None => return Ok(provided_args),
};
let expected_count = sig.param_types.len();
let provided_count = provided_args.len();
if provided_count >= expected_count {
return Ok(provided_args);
}
let defaults = match &sig.default_values {
Some(dv) => dv,
None => return Ok(provided_args),
};
let mut result = provided_args;
for i in provided_count..expected_count {
if let Some(Some(default_expr)) = defaults.get(i) {
let default_tokens = self.transpile_expr(default_expr)?;
let param_type = sig.param_types.get(i).map(|s| s.as_str()).unwrap_or("");
if param_type == "String" || param_type == "Unknown" || param_type == "Any" {
if matches!(
&default_expr.kind,
crate::frontend::ast::ExprKind::Literal(
crate::frontend::ast::Literal::String(_)
)
) {
result.push(quote::quote! { #default_tokens.to_string() });
continue;
}
}
result.push(default_tokens);
} else {
break; }
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::transpiler::FunctionSignature;
use crate::frontend::ast::{Expr, ExprKind, Literal, Span};
use quote::quote;
fn make_transpiler() -> Transpiler {
Transpiler::new()
}
fn string_expr(s: &str) -> Expr {
Expr {
kind: ExprKind::Literal(Literal::String(s.to_string())),
span: Span::default(),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
contracts: Vec::new(),
}
}
fn int_expr(n: i64) -> Expr {
Expr {
kind: ExprKind::Literal(Literal::Integer(n, None)),
span: Span::default(),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
contracts: Vec::new(),
}
}
#[test]
fn test_fill_default_args_no_signature() {
let transpiler = make_transpiler();
let args = vec![quote! { "Smith".to_string() }];
let result = transpiler
.fill_default_args("unknown_fn", args.clone())
.unwrap();
assert_eq!(result.len(), 1, "No signature should return args as-is");
}
#[test]
fn test_fill_default_args_all_provided() {
let mut transpiler = make_transpiler();
transpiler.function_signatures.insert(
"greet".to_string(),
FunctionSignature {
name: "greet".to_string(),
param_types: vec!["String".to_string(), "String".to_string()],
default_values: Some(vec![None, Some(Box::new(string_expr("Mr.")))]),
},
);
let args = vec![quote! { name }, quote! { title }];
let result = transpiler.fill_default_args("greet", args).unwrap();
assert_eq!(result.len(), 2, "All args provided should not add defaults");
}
#[test]
fn test_fill_default_args_missing_one_with_default() {
let mut transpiler = make_transpiler();
transpiler.function_signatures.insert(
"greet".to_string(),
FunctionSignature {
name: "greet".to_string(),
param_types: vec!["String".to_string(), "String".to_string()],
default_values: Some(vec![None, Some(Box::new(string_expr("Mr.")))]),
},
);
let args = vec![quote! { "Smith".to_string() }];
let result = transpiler.fill_default_args("greet", args).unwrap();
assert_eq!(result.len(), 2, "Should fill in default for missing arg");
let second_arg = result[1].to_string();
assert!(
second_arg.contains("Mr."),
"Default value should contain 'Mr.', got: {second_arg}"
);
}
#[test]
fn test_fill_default_args_integer_default() {
let mut transpiler = make_transpiler();
transpiler.function_signatures.insert(
"repeat".to_string(),
FunctionSignature {
name: "repeat".to_string(),
param_types: vec!["String".to_string(), "i32".to_string()],
default_values: Some(vec![None, Some(Box::new(int_expr(1)))]),
},
);
let args = vec![quote! { "hello".to_string() }];
let result = transpiler.fill_default_args("repeat", args).unwrap();
assert_eq!(result.len(), 2);
let second_arg = result[1].to_string();
assert!(
second_arg.contains('1'),
"Default int should be 1, got: {second_arg}"
);
}
#[test]
fn test_fill_default_args_no_defaults_stored() {
let mut transpiler = make_transpiler();
transpiler.function_signatures.insert(
"add".to_string(),
FunctionSignature {
name: "add".to_string(),
param_types: vec!["i32".to_string(), "i32".to_string()],
default_values: None,
},
);
let args = vec![quote! { 1 }];
let result = transpiler.fill_default_args("add", args).unwrap();
assert_eq!(result.len(), 1, "No defaults should not add anything");
}
}