use super::Transpiler;
use crate::frontend::ast::{Expr, Param};
use anyhow::Result;
use proc_macro2::TokenStream;
use quote::quote;
impl Transpiler {
pub(crate) fn transpile_lambda_impl(
&self,
params: &[Param],
body: &Expr,
) -> Result<TokenStream> {
let body_tokens = self.transpile_expr(body)?;
if params.is_empty() {
return Ok(quote! { move || #body_tokens });
}
let param_list = self.build_lambda_param_list(params)?;
let closure_str = format!("move |{param_list}| {body_tokens}");
closure_str
.parse()
.map_err(|e| anyhow::anyhow!("Failed to parse closure: {e}"))
}
fn build_lambda_param_list(&self, params: &[Param]) -> Result<String> {
let param_strs: Vec<String> = params
.iter()
.map(|p| {
let name = p.name();
let ty_str = self
.transpile_type(&p.ty)
.map_or_else(|_| "_".to_string(), |t| t.to_string());
if ty_str == "_" {
name
} else {
format!("{name}: {ty_str}")
}
})
.collect();
Ok(param_strs.join(", "))
}
pub(crate) fn transpile_async_lambda_impl(
&self,
params: &[Param],
body: &Expr,
) -> Result<TokenStream> {
let body_tokens = self.transpile_expr(body)?;
if params.is_empty() {
return Ok(quote! { move || async move { #body_tokens } });
}
let param_list = self.build_lambda_param_list(params)?;
let closure_str = format!("move |{param_list}| async move {{ {body_tokens} }}");
closure_str
.parse()
.map_err(|e| anyhow::anyhow!("Failed to parse async closure: {e}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::{BinaryOp, ExprKind, Literal, Pattern, Span, Type, TypeKind};
fn make_transpiler() -> Transpiler {
Transpiler::new()
}
fn make_expr(kind: ExprKind) -> Expr {
Expr {
kind,
span: Span::default(),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
}
}
fn int_expr(n: i64) -> Expr {
make_expr(ExprKind::Literal(Literal::Integer(n, None)))
}
fn ident_expr(name: &str) -> Expr {
make_expr(ExprKind::Identifier(name.to_string()))
}
fn make_param(name: &str) -> Param {
Param {
pattern: Pattern::Identifier(name.to_string()),
ty: Type {
kind: TypeKind::Named("_".to_string()),
span: Span::default(),
},
span: Span::default(),
is_mutable: false,
default_value: None,
}
}
fn make_typed_param(name: &str, type_name: &str) -> Param {
Param {
pattern: Pattern::Identifier(name.to_string()),
ty: Type {
kind: TypeKind::Named(type_name.to_string()),
span: Span::default(),
},
span: Span::default(),
is_mutable: false,
default_value: None,
}
}
#[test]
fn test_lambda_no_params() {
let transpiler = make_transpiler();
let body = int_expr(42);
let result = transpiler.transpile_lambda_impl(&[], &body).unwrap();
let result_str = result.to_string();
assert!(result_str.contains("move"));
assert!(result_str.contains("||"));
assert!(result_str.contains("42"));
}
#[test]
fn test_lambda_single_param_inferred() {
let transpiler = make_transpiler();
let params = vec![make_param("x")];
let body = ident_expr("x");
let result = transpiler.transpile_lambda_impl(¶ms, &body).unwrap();
let result_str = result.to_string();
assert!(result_str.contains("move"));
assert!(result_str.contains("x"));
}
#[test]
fn test_lambda_single_param_typed() {
let transpiler = make_transpiler();
let params = vec![make_typed_param("x", "i32")];
let body = ident_expr("x");
let result = transpiler.transpile_lambda_impl(¶ms, &body).unwrap();
let result_str = result.to_string();
assert!(result_str.contains("move"));
assert!(result_str.contains("i32"));
}
#[test]
fn test_lambda_multiple_params() {
let transpiler = make_transpiler();
let params = vec![make_typed_param("a", "i32"), make_typed_param("b", "i32")];
let body = make_expr(ExprKind::Binary {
left: Box::new(ident_expr("a")),
op: BinaryOp::Add,
right: Box::new(ident_expr("b")),
});
let result = transpiler.transpile_lambda_impl(¶ms, &body).unwrap();
let result_str = result.to_string();
assert!(result_str.contains("move"));
assert!(result_str.contains("a"));
assert!(result_str.contains("b"));
}
#[test]
fn test_lambda_complex_body() {
let transpiler = make_transpiler();
let params = vec![make_param("n")];
let body = make_expr(ExprKind::Binary {
left: Box::new(ident_expr("n")),
op: BinaryOp::Multiply,
right: Box::new(int_expr(2)),
});
let result = transpiler.transpile_lambda_impl(¶ms, &body).unwrap();
let result_str = result.to_string();
assert!(result_str.contains("n"));
assert!(result_str.contains("2"));
}
#[test]
fn test_build_param_list_empty() {
let transpiler = make_transpiler();
let result = transpiler.build_lambda_param_list(&[]).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_build_param_list_single_inferred() {
let transpiler = make_transpiler();
let params = vec![make_param("x")];
let result = transpiler.build_lambda_param_list(¶ms).unwrap();
assert_eq!(result, "x");
}
#[test]
fn test_build_param_list_single_typed() {
let transpiler = make_transpiler();
let params = vec![make_typed_param("x", "i32")];
let result = transpiler.build_lambda_param_list(¶ms).unwrap();
assert!(result.contains("x"));
assert!(result.contains("i32"));
}
#[test]
fn test_build_param_list_multiple() {
let transpiler = make_transpiler();
let params = vec![
make_typed_param("a", "i32"),
make_typed_param("b", "String"),
];
let result = transpiler.build_lambda_param_list(¶ms).unwrap();
assert!(result.contains("a"));
assert!(result.contains("b"));
assert!(result.contains(","));
}
#[test]
fn test_async_lambda_no_params() {
let transpiler = make_transpiler();
let body = int_expr(42);
let result = transpiler.transpile_async_lambda_impl(&[], &body).unwrap();
let result_str = result.to_string();
assert!(result_str.contains("async"));
assert!(result_str.contains("move"));
}
#[test]
fn test_async_lambda_with_params() {
let transpiler = make_transpiler();
let params = vec![make_typed_param("x", "i32")];
let body = ident_expr("x");
let result = transpiler
.transpile_async_lambda_impl(¶ms, &body)
.unwrap();
let result_str = result.to_string();
assert!(result_str.contains("async"));
assert!(result_str.contains("move"));
}
}