#![allow(clippy::missing_errors_doc)]
#![allow(clippy::wildcard_imports)]
#![allow(clippy::collapsible_else_if)]
#![allow(clippy::doc_markdown)]
use super::*;
use crate::frontend::ast::{Expr, Param, PipelineStage};
use anyhow::Result;
use proc_macro2::TokenStream;
impl Transpiler {
pub(crate) fn infer_return_type_from_params(
&self,
body: &Expr,
params: &[Param],
) -> Result<Option<proc_macro2::TokenStream>> {
super::return_type_helpers::infer_return_type_from_params(body, params, |ty| {
self.transpile_type(ty)
})
}
pub(crate) fn infer_param_type(
&self,
param: &Param,
body: &Expr,
func_name: &str,
) -> TokenStream {
self.infer_param_type_impl(param, body, func_name)
}
pub(crate) fn is_nested_array_param(&self, param_name: &str, expr: &Expr) -> bool {
self.is_nested_array_param_impl(param_name, expr)
}
pub(crate) fn generate_param_tokens(
&self,
params: &[Param],
body: &Expr,
func_name: &str,
) -> Result<Vec<TokenStream>> {
self.generate_param_tokens_impl(params, body, func_name)
}
pub(crate) fn generate_return_type_tokens(
&self,
name: &str,
return_type: Option<&Type>,
body: &Expr,
params: &[Param],
) -> Result<TokenStream> {
self.generate_return_type_tokens_impl(name, return_type, body, params)
}
pub(crate) fn references_globals(&self, expr: &Expr) -> bool {
self.references_globals_impl(expr)
}
pub(crate) fn generate_body_tokens(&self, body: &Expr, is_async: bool) -> Result<TokenStream> {
self.generate_body_tokens_impl(body, is_async)
}
pub(crate) fn generate_type_param_tokens(
&self,
type_params: &[String],
) -> Result<Vec<TokenStream>> {
self.generate_type_param_tokens_impl(type_params)
}
pub(crate) fn generate_function_signature(
&self,
is_pub: bool,
is_async: bool,
fn_name: &proc_macro2::Ident,
type_param_tokens: &[TokenStream],
param_tokens: &[TokenStream],
return_type_tokens: &TokenStream,
body_tokens: &TokenStream,
attributes: &[crate::frontend::ast::Attribute],
) -> Result<TokenStream> {
self.generate_function_signature_impl(
is_pub,
is_async,
fn_name,
type_param_tokens,
param_tokens,
return_type_tokens,
body_tokens,
attributes,
)
}
pub(crate) fn compute_final_return_type(
&self,
fn_name: &proc_macro2::Ident,
return_type_tokens: &TokenStream,
) -> TokenStream {
self.compute_final_return_type_impl(fn_name, return_type_tokens)
}
pub(crate) fn generate_visibility_token(&self, is_pub: bool) -> TokenStream {
self.generate_visibility_token_impl(is_pub)
}
pub(crate) fn process_attributes(
&self,
attributes: &[crate::frontend::ast::Attribute],
) -> (Vec<TokenStream>, TokenStream) {
self.process_attributes_impl(attributes)
}
pub(crate) fn generate_function_declaration(
&self,
is_async: bool,
type_param_tokens: &[TokenStream],
regular_attrs: &[TokenStream],
visibility: &TokenStream,
modifiers_tokens: &TokenStream,
fn_name: &proc_macro2::Ident,
param_tokens: &[TokenStream],
final_return_type: &TokenStream,
body_tokens: &TokenStream,
) -> Result<TokenStream> {
self.generate_function_declaration_impl(
is_async,
type_param_tokens,
regular_attrs,
visibility,
modifiers_tokens,
fn_name,
param_tokens,
final_return_type,
body_tokens,
)
}
pub(crate) fn transpile_match_with_string_arms(
&self,
expr: &Expr,
arms: &[crate::frontend::ast::MatchArm],
) -> Result<TokenStream> {
self.transpile_match_with_string_arms_impl(expr, arms)
}
pub(crate) fn generate_body_tokens_with_string_conversion(
&self,
body: &Expr,
is_async: bool,
) -> Result<TokenStream> {
self.generate_body_tokens_with_string_conversion_impl(body, is_async)
}
pub(crate) fn generate_param_tokens_with_lifetime(
&self,
params: &[Param],
body: &Expr,
func_name: &str,
) -> Result<Vec<TokenStream>> {
self.generate_param_tokens_with_lifetime_impl(params, body, func_name)
}
pub(crate) fn transpile_type_with_lifetime(&self, ty: &Type) -> Result<TokenStream> {
self.transpile_type_with_lifetime_impl(ty)
}
pub(crate) fn generate_return_type_tokens_with_lifetime(
&self,
name: &str,
return_type: Option<&Type>,
body: &Expr,
) -> Result<TokenStream> {
self.generate_return_type_tokens_with_lifetime_impl(name, return_type, body)
}
pub fn transpile_function(
&self,
name: &str,
type_params: &[String],
params: &[Param],
body: &Expr,
is_async: bool,
return_type: Option<&Type>,
is_pub: bool,
attributes: &[crate::frontend::ast::Attribute],
) -> Result<TokenStream> {
self.transpile_function_impl(
name,
type_params,
params,
body,
is_async,
return_type,
is_pub,
attributes,
)
}
pub fn transpile_lambda(&self, params: &[Param], body: &Expr) -> Result<TokenStream> {
self.transpile_lambda_impl(params, body)
}
pub fn transpile_call(&self, func: &Expr, args: &[Expr]) -> Result<TokenStream> {
self.transpile_call_impl(func, args)
}
pub fn transpile_method_call(
&self,
object: &Expr,
method: &str,
args: &[Expr],
) -> Result<TokenStream> {
self.transpile_method_call_impl(object, method, args)
}
pub(crate) fn try_transpile_dataframe_builder_inline(
&self,
expr: &Expr,
) -> Result<Option<TokenStream>> {
self.try_transpile_dataframe_builder_inline_impl(expr)
}
pub fn transpile_block(&self, exprs: &[Expr]) -> Result<TokenStream> {
self.transpile_block_impl(exprs)
}
pub fn transpile_pipeline(&self, expr: &Expr, stages: &[PipelineStage]) -> Result<TokenStream> {
self.transpile_pipeline_impl(expr, stages)
}
pub(crate) fn try_transpile_dataframe_function(
&self,
base_name: &str,
args: &[Expr],
) -> Result<Option<TokenStream>> {
self.try_transpile_dataframe_function_impl(base_name, args)
}
}
#[cfg(test)]
mod extreme_tdd_tests {
use super::*;
use crate::frontend::ast::{ExprKind, Literal, Span, Type, TypeKind};
fn make_type(kind: TypeKind) -> Type {
Type {
kind,
span: Span::new(0, 0),
}
}
fn make_expr(kind: ExprKind) -> Expr {
Expr {
kind,
span: Span::new(0, 0),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
}
}
fn make_param(name: &str, ty: Type) -> crate::frontend::ast::Param {
crate::frontend::ast::Param {
pattern: crate::frontend::ast::Pattern::Identifier(name.to_string()),
ty,
span: Span::new(0, 0),
is_mutable: false,
default_value: None,
}
}
#[test]
fn test_transpile_function_simple() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let result = t
.transpile_function(
"answer",
&[],
&[],
&body,
false,
Some(&make_type(TypeKind::Named("i32".to_string()))),
true,
&[],
)
.unwrap();
let s = result.to_string();
assert!(s.contains("fn answer"));
assert!(s.contains("i32"));
assert!(s.contains("42"));
}
#[test]
fn test_transpile_function_with_params() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Identifier("x".to_string()));
let params = vec![make_param(
"x",
make_type(TypeKind::Named("i32".to_string())),
)];
let result = t
.transpile_function(
"identity",
&[],
¶ms,
&body,
false,
Some(&make_type(TypeKind::Named("i32".to_string()))),
false,
&[],
)
.unwrap();
let s = result.to_string();
assert!(s.contains("fn identity"));
assert!(s.contains("x"));
}
#[test]
fn test_transpile_function_async() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::Integer(1, None)));
let result = t
.transpile_function("async_func", &[], &[], &body, true, None, true, &[])
.unwrap();
let s = result.to_string();
assert!(s.contains("async"));
assert!(s.contains("async_func"));
}
#[test]
fn test_transpile_function_with_type_params() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Identifier("value".to_string()));
let params = vec![make_param(
"value",
make_type(TypeKind::Named("T".to_string())),
)];
let result = t
.transpile_function(
"generic",
&["T".to_string()],
¶ms,
&body,
false,
Some(&make_type(TypeKind::Named("T".to_string()))),
true,
&[],
)
.unwrap();
let s = result.to_string();
assert!(s.contains("generic"));
assert!(s.contains("<"));
assert!(s.contains("T"));
}
#[test]
fn test_transpile_lambda_no_params() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let result = t.transpile_lambda(&[], &body).unwrap();
let s = result.to_string();
assert!(s.contains("||"));
assert!(s.contains("42"));
}
#[test]
fn test_transpile_lambda_with_params() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Identifier("x".to_string()));
let params = vec![make_param(
"x",
make_type(TypeKind::Named("i32".to_string())),
)];
let result = t.transpile_lambda(¶ms, &body).unwrap();
let s = result.to_string();
assert!(s.contains("|"));
assert!(s.contains("x"));
}
#[test]
fn test_transpile_call_simple() {
let t = Transpiler::new();
let func = make_expr(ExprKind::Identifier("foo".to_string()));
let result = t.transpile_call(&func, &[]).unwrap();
let s = result.to_string();
assert!(s.contains("foo"));
}
#[test]
fn test_transpile_call_with_args() {
let t = Transpiler::new();
let func = make_expr(ExprKind::Identifier("add".to_string()));
let args = vec![
make_expr(ExprKind::Literal(Literal::Integer(1, None))),
make_expr(ExprKind::Literal(Literal::Integer(2, None))),
];
let result = t.transpile_call(&func, &args).unwrap();
let s = result.to_string();
assert!(s.contains("add"));
assert!(s.contains("1"));
assert!(s.contains("2"));
}
#[test]
fn test_transpile_method_call_simple() {
let t = Transpiler::new();
let obj = make_expr(ExprKind::Identifier("vec".to_string()));
let result = t.transpile_method_call(&obj, "len", &[]).unwrap();
let s = result.to_string();
assert!(s.contains("len"));
}
#[test]
fn test_transpile_method_call_with_args() {
let t = Transpiler::new();
let obj = make_expr(ExprKind::Identifier("vec".to_string()));
let args = vec![make_expr(ExprKind::Literal(Literal::Integer(42, None)))];
let result = t.transpile_method_call(&obj, "push", &args).unwrap();
let s = result.to_string();
assert!(s.contains("push"));
assert!(s.contains("42"));
}
#[test]
fn test_transpile_block_empty() {
let t = Transpiler::new();
let result = t.transpile_block(&[]).unwrap();
assert!(!result.is_empty());
}
#[test]
fn test_transpile_block_single() {
let t = Transpiler::new();
let exprs = vec![make_expr(ExprKind::Literal(Literal::Integer(42, None)))];
let result = t.transpile_block(&exprs).unwrap();
let s = result.to_string();
assert!(s.contains("42"));
}
#[test]
fn test_transpile_block_multiple() {
let t = Transpiler::new();
let exprs = vec![
make_expr(ExprKind::Literal(Literal::Integer(1, None))),
make_expr(ExprKind::Literal(Literal::Integer(2, None))),
make_expr(ExprKind::Literal(Literal::Integer(3, None))),
];
let result = t.transpile_block(&exprs).unwrap();
let s = result.to_string();
assert!(s.contains("1"));
assert!(s.contains("2"));
assert!(s.contains("3"));
}
#[test]
fn test_transpile_pipeline_single_stage() {
let t = Transpiler::new();
let expr = make_expr(ExprKind::List(vec![
make_expr(ExprKind::Literal(Literal::Integer(1, None))),
make_expr(ExprKind::Literal(Literal::Integer(2, None))),
]));
let method_call = make_expr(ExprKind::MethodCall {
receiver: Box::new(make_expr(ExprKind::Identifier("_".to_string()))),
method: "len".to_string(),
args: vec![],
});
let stages = vec![crate::frontend::ast::PipelineStage {
op: Box::new(method_call),
span: Span::new(0, 0),
}];
let result = t.transpile_pipeline(&expr, &stages).unwrap();
let s = result.to_string();
assert!(s.contains("len"));
}
#[test]
fn test_generate_visibility_token_public() {
let t = Transpiler::new();
let result = t.generate_visibility_token(true);
assert!(result.to_string().contains("pub"));
}
#[test]
fn test_generate_visibility_token_private() {
let t = Transpiler::new();
let result = t.generate_visibility_token(false);
assert!(result.is_empty());
}
#[test]
fn test_generate_type_param_tokens_simple() {
let t = Transpiler::new();
let result = t.generate_type_param_tokens(&["T".to_string()]).unwrap();
assert_eq!(result.len(), 1);
assert!(result[0].to_string().contains("T"));
}
#[test]
fn test_generate_type_param_tokens_multiple() {
let t = Transpiler::new();
let result = t
.generate_type_param_tokens(&["T".to_string(), "U".to_string()])
.unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_generate_type_param_tokens_with_bound() {
let t = Transpiler::new();
let result = t
.generate_type_param_tokens(&["T: Clone".to_string()])
.unwrap();
assert_eq!(result.len(), 1);
let s = result[0].to_string();
assert!(s.contains("T"));
assert!(s.contains("Clone"));
}
#[test]
fn test_references_globals_false() {
let t = Transpiler::new();
let expr = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
assert!(!t.references_globals(&expr));
}
#[test]
fn test_infer_param_type_basic() {
let t = Transpiler::new();
let param = make_param("x", make_type(TypeKind::Named("_".to_string())));
let body = make_expr(ExprKind::Identifier("x".to_string()));
let result = t.infer_param_type(¶m, &body, "test");
assert!(!result.is_empty());
}
#[test]
fn test_is_nested_array_param_false() {
let t = Transpiler::new();
let expr = make_expr(ExprKind::Identifier("x".to_string()));
assert!(!t.is_nested_array_param("arr", &expr));
}
#[test]
fn test_generate_body_tokens_basic() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let result = t.generate_body_tokens(&body, false).unwrap();
assert!(result.to_string().contains("42"));
}
#[test]
fn test_generate_body_tokens_async() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let result = t.generate_body_tokens(&body, true).unwrap();
assert!(result.to_string().contains("42"));
}
#[test]
fn test_generate_param_tokens_empty() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let result = t.generate_param_tokens(&[], &body, "test").unwrap();
assert!(result.is_empty());
}
#[test]
fn test_generate_param_tokens_single() {
let t = Transpiler::new();
let params = vec![make_param(
"x",
make_type(TypeKind::Named("i32".to_string())),
)];
let body = make_expr(ExprKind::Identifier("x".to_string()));
let result = t.generate_param_tokens(¶ms, &body, "test").unwrap();
assert_eq!(result.len(), 1);
}
#[test]
fn test_generate_return_type_tokens() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let ret_type = make_type(TypeKind::Named("i32".to_string()));
let result = t
.generate_return_type_tokens("test", Some(&ret_type), &body, &[])
.unwrap();
let s = result.to_string();
assert!(s.contains("i32"));
}
#[test]
fn test_compute_final_return_type() {
let t = Transpiler::new();
let fn_name = quote::format_ident!("my_func");
let return_type_tokens = quote::quote! { -> i32 };
let result = t.compute_final_return_type(&fn_name, &return_type_tokens);
assert!(result.to_string().contains("i32"));
}
#[test]
fn test_process_attributes_empty() {
let t = Transpiler::new();
let (regular, modifiers) = t.process_attributes(&[]);
assert!(regular.is_empty());
assert!(modifiers.is_empty());
}
#[test]
fn test_infer_return_type_from_params() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let result = t.infer_return_type_from_params(&body, &[]).unwrap();
assert!(result.is_none() || result.is_some());
}
#[test]
fn test_transpile_type_with_lifetime() {
let t = Transpiler::new();
let ty = make_type(TypeKind::Reference {
is_mut: false,
lifetime: None,
inner: Box::new(make_type(TypeKind::Named("str".to_string()))),
});
let result = t.transpile_type_with_lifetime(&ty).unwrap();
let s = result.to_string();
assert!(s.contains("str"));
}
#[test]
fn test_generate_param_tokens_with_lifetime() {
let t = Transpiler::new();
let params = vec![make_param(
"s",
make_type(TypeKind::Reference {
is_mut: false,
lifetime: None,
inner: Box::new(make_type(TypeKind::Named("str".to_string()))),
}),
)];
let body = make_expr(ExprKind::Identifier("s".to_string()));
let result = t
.generate_param_tokens_with_lifetime(¶ms, &body, "test")
.unwrap();
assert_eq!(result.len(), 1);
}
#[test]
fn test_generate_return_type_tokens_with_lifetime() {
let t = Transpiler::new();
let ret_type = make_type(TypeKind::Reference {
is_mut: false,
lifetime: None,
inner: Box::new(make_type(TypeKind::Named("str".to_string()))),
});
let body = make_expr(ExprKind::Identifier("s".to_string()));
let result = t
.generate_return_type_tokens_with_lifetime("test", Some(&ret_type), &body)
.unwrap();
let s = result.to_string();
assert!(s.contains("str"));
}
#[test]
fn test_generate_body_tokens_with_string_conversion() {
let t = Transpiler::new();
let body = make_expr(ExprKind::Literal(Literal::String("hello".to_string())));
let result = t
.generate_body_tokens_with_string_conversion(&body, false)
.unwrap();
let s = result.to_string();
assert!(s.contains("hello"));
}
#[test]
fn test_try_transpile_dataframe_builder_inline_none() {
let t = Transpiler::new();
let expr = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let result = t.try_transpile_dataframe_builder_inline(&expr).unwrap();
assert!(result.is_none());
}
#[test]
fn test_try_transpile_dataframe_function_col() {
let t = Transpiler::new();
let args = vec![make_expr(ExprKind::Literal(Literal::String(
"name".to_string(),
)))];
let result = t.try_transpile_dataframe_function("col", &args).unwrap();
if let Some(tokens) = result {
let s = tokens.to_string();
assert!(s.contains("col"));
}
}
#[test]
fn test_try_transpile_dataframe_function_unknown() {
let t = Transpiler::new();
let result = t
.try_transpile_dataframe_function("unknown_func", &[])
.unwrap();
assert!(result.is_none());
}
}