use crate::hir::HirExpr;
use crate::rust_gen::context::{CodeGenContext, ToRustExpr};
use anyhow::{bail, Result};
use syn::parse_quote;
pub fn convert_string_method(
method: &str,
args: &[HirExpr],
ctx: &mut CodeGenContext,
) -> Result<Option<syn::Expr>> {
let arg_exprs: Vec<syn::Expr> = args
.iter()
.map(|arg| arg.to_rust_expr(ctx))
.collect::<Result<Vec<_>>>()?;
let result = match method {
"capwords" => convert_capwords(&arg_exprs)?,
_ => bail!("string.{} not implemented yet", method),
};
Ok(Some(result))
}
fn convert_capwords(arg_exprs: &[syn::Expr]) -> Result<syn::Expr> {
if arg_exprs.is_empty() {
bail!("string.capwords() requires at least 1 argument (text)");
}
let text = &arg_exprs[0];
Ok(parse_quote! {
#text.split_whitespace()
.map(|w| {
let mut chars = w.chars();
match chars.next() {
None => String::new(),
Some(first) => {
let mut result = first.to_uppercase().collect::<String>();
result.push_str(&chars.as_str().to_lowercase());
result
}
}
})
.collect::<Vec<_>>()
.join(" ")
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hir::Literal;
#[test]
fn test_convert_string_capwords() {
let mut ctx = CodeGenContext::default();
let args = vec![HirExpr::Literal(Literal::String("hello world".to_string()))];
let result = convert_string_method("capwords", &args, &mut ctx);
assert!(result.is_ok());
let expr = result.unwrap().unwrap();
let code = quote::quote!(#expr).to_string();
assert!(code.contains("split_whitespace"));
assert!(code.contains("to_uppercase"));
}
#[test]
fn test_convert_string_capwords_variable() {
let mut ctx = CodeGenContext::default();
let args = vec![HirExpr::Var("text".to_string())];
let result = convert_string_method("capwords", &args, &mut ctx);
assert!(result.is_ok());
let expr = result.unwrap().unwrap();
let code = quote::quote!(#expr).to_string();
assert!(code.contains("text"));
}
#[test]
fn test_convert_string_capwords_wrong_args() {
let mut ctx = CodeGenContext::default();
let args: Vec<HirExpr> = vec![];
let result = convert_string_method("capwords", &args, &mut ctx);
assert!(result.is_err());
let err = result.err().unwrap();
assert!(err.to_string().contains("requires at least 1 argument"));
}
#[test]
fn test_convert_string_capwords_contains_join() {
let mut ctx = CodeGenContext::default();
let args = vec![HirExpr::Var("s".to_string())];
let result = convert_string_method("capwords", &args, &mut ctx);
assert!(result.is_ok());
let expr = result.unwrap().unwrap();
let code = quote::quote!(#expr).to_string();
assert!(code.contains("join"));
}
#[test]
fn test_convert_string_capwords_contains_collect() {
let mut ctx = CodeGenContext::default();
let args = vec![HirExpr::Var("input".to_string())];
let result = convert_string_method("capwords", &args, &mut ctx);
assert!(result.is_ok());
let expr = result.unwrap().unwrap();
let code = quote::quote!(#expr).to_string();
assert!(code.contains("collect"));
}
#[test]
fn test_convert_capwords_direct_empty() {
let arg_exprs: Vec<syn::Expr> = vec![];
let result = convert_capwords(&arg_exprs);
assert!(result.is_err());
}
#[test]
fn test_convert_capwords_direct_valid() {
let arg_exprs: Vec<syn::Expr> = vec![parse_quote!(my_string)];
let result = convert_capwords(&arg_exprs);
assert!(result.is_ok());
}
#[test]
fn test_convert_string_unknown() {
let mut ctx = CodeGenContext::default();
let args: Vec<HirExpr> = vec![];
let result = convert_string_method("unknown", &args, &mut ctx);
assert!(result.is_err());
let err = result.err().unwrap();
assert!(err.to_string().contains("not implemented"));
}
#[test]
fn test_convert_string_unknown_with_args() {
let mut ctx = CodeGenContext::default();
let args = vec![HirExpr::Var("x".to_string())];
let result = convert_string_method("template", &args, &mut ctx);
assert!(result.is_err());
}
#[test]
fn test_convert_string_digits() {
let mut ctx = CodeGenContext::default();
let args: Vec<HirExpr> = vec![];
let result = convert_string_method("digits", &args, &mut ctx);
assert!(result.is_err());
}
}