use crate::core::ReplaceInfo;
use rustpython_ast as ast;
use std::borrow::Cow;
use std::collections::HashMap;
pub struct OptimizedParameterMapper<'a> {
param_cache: &'a mut HashMap<String, String>,
}
impl<'a> OptimizedParameterMapper<'a> {
pub fn new(param_cache: &'a mut HashMap<String, String>) -> Self {
Self { param_cache }
}
pub fn map_call_arguments(
&mut self,
args: &[ast::Expr],
keywords: &[ast::Keyword],
replace_info: &ReplaceInfo,
func: &ast::Expr,
expr_to_string: impl Fn(&ast::Expr) -> String,
) -> HashMap<String, String> {
let mut arg_map = HashMap::new();
self.map_positional_args(&mut arg_map, args, replace_info, func, &expr_to_string);
self.map_keyword_args(&mut arg_map, keywords, &expr_to_string);
arg_map
}
fn map_positional_args(
&mut self,
arg_map: &mut HashMap<String, String>,
args: &[ast::Expr],
replace_info: &ReplaceInfo,
func: &ast::Expr,
expr_to_string: &impl Fn(&ast::Expr) -> String,
) {
let is_method_call = matches!(func, ast::Expr::Attribute(_));
if is_method_call {
self.map_method_call_args(arg_map, args, replace_info, func, expr_to_string);
} else {
self.map_function_call_args(arg_map, args, replace_info, expr_to_string);
}
}
fn map_method_call_args(
&mut self,
arg_map: &mut HashMap<String, String>,
args: &[ast::Expr],
replace_info: &ReplaceInfo,
func: &ast::Expr,
expr_to_string: &impl Fn(&ast::Expr) -> String,
) {
if let ast::Expr::Attribute(attr_expr) = func {
let self_str = expr_to_string(&attr_expr.value);
let self_key = self.get_cached_string("self");
arg_map.insert(self_key, self_str);
let method_params: Vec<_> = replace_info
.parameters
.iter()
.filter(|p| p.name != "self")
.collect();
for (i, arg) in args.iter().enumerate() {
if let Some(param) = method_params.get(i) {
let arg_str = expr_to_string(arg);
let param_key = self.get_cached_string(¶m.name);
arg_map.insert(param_key, arg_str);
}
}
}
}
fn map_function_call_args(
&mut self,
arg_map: &mut HashMap<String, String>,
args: &[ast::Expr],
replace_info: &ReplaceInfo,
expr_to_string: &impl Fn(&ast::Expr) -> String,
) {
for (i, arg) in args.iter().enumerate() {
if let Some(param) = replace_info.parameters.get(i) {
let arg_str = expr_to_string(arg);
let param_key = self.get_cached_string(¶m.name);
arg_map.insert(param_key, arg_str);
}
}
}
fn map_keyword_args(
&mut self,
arg_map: &mut HashMap<String, String>,
keywords: &[ast::Keyword],
expr_to_string: &impl Fn(&ast::Expr) -> String,
) {
for keyword in keywords {
if let Some(arg_name) = &keyword.arg {
let arg_str = expr_to_string(&keyword.value);
let arg_key = self.get_cached_string(arg_name);
arg_map.insert(arg_key, arg_str);
}
}
}
fn get_cached_string(&mut self, s: &str) -> String {
if let Some(cached) = self.param_cache.get(s) {
cached.clone()
} else {
let owned = s.to_string();
self.param_cache.insert(owned.clone(), owned.clone());
owned
}
}
}
pub fn optimize_parameter_substitution<'a>(
template: &'a str,
arg_map: &HashMap<String, String>,
) -> Cow<'a, str> {
let needs_substitution = arg_map
.keys()
.any(|param_name| template.contains(&format!("{{{}}}", param_name)));
if !needs_substitution {
return Cow::Borrowed(template);
}
let mut result = template.to_string();
for (param_name, arg_value) in arg_map {
let placeholder = format!("{{{}}}", param_name);
result = result.replace(&placeholder, arg_value);
}
Cow::Owned(result)
}
pub fn extract_source_module(old_name: &str) -> Option<&str> {
old_name
.find('.')
.map(|first_dot_pos| &old_name[..first_dot_pos])
}
pub fn strip_module_prefix<'a>(
replacement: &'a str,
source_module: Option<&str>,
current_module: &str,
) -> Cow<'a, str> {
match source_module {
Some(src_mod) if src_mod == current_module => {
let prefix = format!("{}.", src_mod);
if replacement.contains(&prefix) {
Cow::Owned(replacement.replace(&prefix, ""))
} else {
Cow::Borrowed(replacement)
}
}
_ => Cow::Borrowed(replacement),
}
}
pub struct OptimizedReplacementBuilder<'a> {
template: &'a str,
substitutions: HashMap<&'a str, String>,
}
impl<'a> OptimizedReplacementBuilder<'a> {
pub fn new(template: &'a str) -> Self {
Self {
template,
substitutions: HashMap::new(),
}
}
pub fn add_substitution(&mut self, key: &'a str, value: String) {
self.substitutions.insert(key, value);
}
pub fn build(self) -> String {
let mut result = self.template.to_string();
for (key, value) in self.substitutions {
let placeholder = format!("{{{}}}", key);
result = result.replace(&placeholder, &value);
}
result
}
pub fn build_cow(self) -> Cow<'a, str> {
if self.substitutions.is_empty() {
return Cow::Borrowed(self.template);
}
let has_placeholders = self
.substitutions
.keys()
.any(|key| self.template.contains(&format!("{{{}}}", key)));
if !has_placeholders {
return Cow::Borrowed(self.template);
}
Cow::Owned(self.build())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::ParameterInfo;
#[test]
fn test_optimized_parameter_substitution() {
let mut arg_map = HashMap::new();
arg_map.insert("name".to_string(), "John".to_string());
arg_map.insert("age".to_string(), "30".to_string());
let result1 = optimize_parameter_substitution("Hello {name}, age {age}", &arg_map);
assert!(matches!(result1, Cow::Owned(_)));
assert_eq!(result1, "Hello John, age 30");
let result2 = optimize_parameter_substitution("Hello World", &arg_map);
assert!(matches!(result2, Cow::Borrowed(_)));
assert_eq!(result2, "Hello World");
}
#[test]
fn test_extract_source_module() {
assert_eq!(extract_source_module("module.Class.method"), Some("module"));
assert_eq!(extract_source_module("simple_func"), None);
assert_eq!(extract_source_module("a.b.c.d"), Some("a"));
}
#[test]
fn test_strip_module_prefix() {
let result1 = strip_module_prefix("module.function()", Some("module"), "module");
assert_eq!(result1, "function()");
let result2 =
strip_module_prefix("other_module.function()", Some("module"), "current_module");
assert_eq!(result2, "other_module.function()");
}
#[test]
fn test_optimized_replacement_builder() {
let mut builder = OptimizedReplacementBuilder::new("Hello {name}, age {age}");
builder.add_substitution("name", "John".to_string());
builder.add_substitution("age", "30".to_string());
let result = builder.build_cow();
assert_eq!(result, "Hello John, age 30");
}
#[test]
fn test_replacement_builder_no_placeholders() {
let builder = OptimizedReplacementBuilder::new("Hello World");
let result = builder.build_cow();
assert!(matches!(result, Cow::Borrowed(_)));
assert_eq!(result, "Hello World");
}
}