use super::collections::*;
use super::depyler_value::*;
use super::enums::*;
use super::misc::*;
use super::numeric::*;
use super::options_results::*;
use super::ownership::*;
use super::strings::*;
use super::truthiness::*;
pub(in crate::rust_gen) fn apply_text_level_fixes(mut formatted_code: String) -> String {
formatted_code = formatted_code
.lines()
.filter(|line| {
let trimmed = line.trim();
trimmed != "if TYPE_CHECKING {}" && trimmed != "if TYPE_CHECKING { }"
})
.collect::<Vec<_>>()
.join("\n");
if !formatted_code.ends_with('\n') {
formatted_code.push('\n');
}
while formatted_code.contains(".__name__") {
formatted_code = formatted_code.replace(".__name__", "");
}
formatted_code = formatted_code.replace("Sequence<i32>", "&[i32]");
formatted_code = formatted_code.replace("Sequence<i64>", "&[i64]");
formatted_code = formatted_code.replace("Sequence<f64>", "&[f64]");
formatted_code = formatted_code.replace("Sequence<String>", "&[String]");
formatted_code = formatted_code.replace("Sequence<bool>", "&[bool]");
formatted_code = formatted_code.replace("Sequence<u8>", "&[u8]");
formatted_code = fix_enum_path_separator(&formatted_code);
formatted_code = fix_python_truthiness(&formatted_code);
formatted_code = formatted_code.replace(
"std::io::Cursor::new()",
"std::io::Cursor::new(Vec::<u8>::new())",
);
formatted_code = formatted_code.replace(
".getvalue()",
".get_ref().iter().map(|&b| b as char).collect::<String>()",
);
formatted_code = formatted_code.replace(
"(TypeError::new(",
"(std::io::Error::new(std::io::ErrorKind::InvalidInput, ",
);
formatted_code = fix_docstring_in_main(&formatted_code);
formatted_code = formatted_code.replace("operator.mul", "|a, b| a * b");
formatted_code = formatted_code.replace("operator.add", "|a, b| a + b");
formatted_code = formatted_code.replace("operator.sub", "|a, b| a - b");
formatted_code = fix_depyler_value_casts(&formatted_code);
formatted_code = fix_struct_field_literal_assignment(&formatted_code);
formatted_code = fix_with_block_scope(&formatted_code);
formatted_code = fix_stub_method_ownership(&formatted_code);
formatted_code = fix_nested_hashmap_to_depyler_value(&formatted_code);
formatted_code = formatted_code.replace("pub type JsonValue = UnionType;", "pub type JsonValue = DepylerValue;");
formatted_code = formatted_code.replace("= UnionType;", "= DepylerValue;");
formatted_code = formatted_code.replace(": UnionType", ": DepylerValue");
formatted_code = formatted_code.replace("<UnionType>", "<DepylerValue>");
formatted_code = formatted_code.replace("(UnionType)", "(DepylerValue)");
formatted_code = formatted_code.replace("== vec![]", "== Vec::<DepylerValue>::new()");
formatted_code = formatted_code.replace("!= vec![]", "!= Vec::<DepylerValue>::new()");
formatted_code = formatted_code.replace(
"== {\n let mut map = std::collections::HashMap::new();\n map\n }",
"== std::collections::HashMap::<String, DepylerValue>::new()"
);
if formatted_code.contains(".write_all(") && !formatted_code.contains("use std::io::Write") {
formatted_code = format!("use std::io::Write;\n{}", formatted_code);
}
if formatted_code.contains("HashMap")
&& !formatted_code.contains("use std::collections::HashMap")
{
formatted_code = format!("use std::collections::HashMap;\n{}", formatted_code);
}
formatted_code = formatted_code.replace(".py_sub(", " - (");
formatted_code = formatted_code.replace(".py_div(", ".join(");
formatted_code = formatted_code.replace(
", TypeError::new(",
", std::io::Error::new(std::io::ErrorKind::InvalidInput, ",
);
formatted_code = formatted_code.replace(
" TypeError::new(",
" std::io::Error::new(std::io::ErrorKind::InvalidInput, ",
);
if formatted_code.contains("r#type(") {
formatted_code = formatted_code.replace("r#type(", "py_type_name(&");
let helper = "fn py_type_name<T: ?Sized>(_: &T) -> &'static str { \
std::any::type_name::<T>() }\n";
formatted_code = format!("{}{}", helper, formatted_code);
}
formatted_code = fix_borrow_into_iter_chain(&formatted_code);
formatted_code = fix_enum_dot_to_path_separator(&formatted_code);
formatted_code = fix_stub_arities(&formatted_code);
formatted_code = formatted_code.replace("pathlib::PurePosixPath(", "std::path::Path::new(");
formatted_code = formatted_code.replace("pathlib::Path(", "std::path::Path::new(");
formatted_code = formatted_code.replace(".days()", ".day()");
formatted_code =
formatted_code.replace("as Box<dyn DynDigest>", "as Box<dyn std::hash::Hasher>");
if formatted_code.contains("hex::encode(") && !formatted_code.contains("fn hex_encode") {
formatted_code = formatted_code.replace("hex::encode(", "hex_encode(");
let helper = "fn hex_encode(bytes: impl AsRef<[u8]>) -> String { \
bytes.as_ref().iter().map(|b| format!(\"{:02x}\", b)).collect() }\n";
formatted_code = format!("{}{}", helper, formatted_code);
}
formatted_code = fix_generator_yield_scope(&formatted_code);
formatted_code = fix_bufreader_deserialize(&formatted_code);
formatted_code = fix_power_sqrt_types(&formatted_code);
formatted_code = fix_datetime_subtraction(&formatted_code);
formatted_code = fix_hasher_digest_methods(&formatted_code);
formatted_code = fix_hashmap_empty_value_type(&formatted_code);
formatted_code = fix_path_or_string_union_coercion(&formatted_code);
formatted_code = fix_function_stub_as_type(&formatted_code);
formatted_code = fix_heterogeneous_dict_inserts(&formatted_code);
formatted_code = fix_lazylock_static_as_type(&formatted_code);
formatted_code = fix_broken_lazylock_initializers(&formatted_code);
formatted_code = fix_literal_clone_pattern(&formatted_code);
formatted_code = formatted_code.replace("frozenset<", "HashSet<");
formatted_code = fix_negation_on_non_bool(&formatted_code);
formatted_code = fix_field_access_truthiness(&formatted_code);
formatted_code = fix_hashmap_contains(&formatted_code);
formatted_code = fix_depyler_value_inserts_generalized(&formatted_code);
formatted_code = fix_enum_display(&formatted_code);
formatted_code = fix_orphaned_lazylock_bodies(&formatted_code);
formatted_code = fix_depyler_value_str_match_arm(&formatted_code);
formatted_code = fix_inline_block_expression_parens(&formatted_code);
formatted_code = fix_orphaned_semicolon_paren(&formatted_code);
formatted_code = fix_sorted_vec_reference(&formatted_code);
formatted_code = fix_vec_contains_deref(&formatted_code);
formatted_code = fix_vec_get_membership(&formatted_code);
formatted_code = fix_float_int_comparison(&formatted_code);
formatted_code = fix_enum_new_constructor(&formatted_code);
formatted_code = fix_enum_new_call_args(&formatted_code);
formatted_code = fix_is_none_on_non_option(&formatted_code);
formatted_code = fix_vec_char_join(&formatted_code);
formatted_code = fix_borrowed_alias_in_new_calls(&formatted_code);
formatted_code = fix_deref_string_comparison(&formatted_code);
formatted_code = fix_depyler_value_vec_join(&formatted_code);
formatted_code = fix_not_string_truthiness(&formatted_code);
formatted_code = fix_raw_identifier_booleans(&formatted_code);
formatted_code = fix_deref_unwrap_result(&formatted_code);
formatted_code = fix_str_params_in_new_calls(&formatted_code);
formatted_code = fix_string_to_depyler_value_insert(&formatted_code);
formatted_code = fix_string_array_contains(&formatted_code);
formatted_code = fix_depyler_value_str_clone(&formatted_code);
formatted_code = fix_ref_option_in_new(&formatted_code);
formatted_code = fix_deref_ref_option_unwrap(&formatted_code);
formatted_code = fix_depyler_value_from_enum(&formatted_code);
formatted_code = fix_cse_py_mul_type_annotation(&formatted_code);
formatted_code = fix_add_enum_from_impls(&formatted_code);
formatted_code = fix_validate_not_none_args(&formatted_code);
formatted_code = fix_hashmap_key_type_mismatch(&formatted_code);
formatted_code = fix_tuple_to_vec_when_len_called(&formatted_code);
formatted_code = fix_cse_int_float_comparison(&formatted_code);
formatted_code = fix_vec_to_string_debug(&formatted_code);
formatted_code = fix_depyler_value_hashmap_keys(&formatted_code);
formatted_code = fix_mixed_numeric_min_max(&formatted_code);
formatted_code = fix_bitwise_and_truthiness(&formatted_code);
formatted_code = fix_spurious_i64_conversion(&formatted_code);
formatted_code = fix_result_double_wrap(&formatted_code);
formatted_code = fix_trailing_comma_in_arith_parens(&formatted_code);
formatted_code = fix_immutable_ref_to_mut(&formatted_code);
formatted_code = fix_regex_match_string_arg(&formatted_code);
formatted_code = fix_format_expect(&formatted_code);
formatted_code = fix_deref_expect_on_primitive(&formatted_code);
formatted_code = fix_spurious_to_string_in_numeric_call(&formatted_code);
formatted_code = fix_str_param_return_as_string(&formatted_code);
formatted_code = fix_as_bool_on_bool(&formatted_code);
formatted_code = fix_range_type_annotation(&formatted_code);
formatted_code = fix_hashmap_keys_iter_clone(&formatted_code);
formatted_code = fix_from_utf8_lossy_string_arg(&formatted_code);
formatted_code = fix_closure_to_dyn_fn_ref(&formatted_code);
formatted_code = fix_ref_arg_to_fn_string_param(&formatted_code);
formatted_code = fix_double_expect_on_option_ref(&formatted_code);
formatted_code = fix_usize_to_string_in_constructor(&formatted_code);
formatted_code = fix_pyrange_iteration(&formatted_code);
formatted_code = fix_unclosed_vec_macro(&formatted_code);
formatted_code = fix_missing_inherited_fields(&formatted_code);
formatted_code = fix_remove_async_for_standalone(&formatted_code);
formatted_code = fix_ref_string_to_owned_in_call(&formatted_code);
formatted_code = fix_missing_mut_for_method_calls(&formatted_code);
formatted_code = fix_collect_type_annotation_mismatch(&formatted_code);
formatted_code = fix_negative_literal_type_annotation(&formatted_code);
formatted_code = fix_empty_vec_in_assert(&formatted_code);
formatted_code = fix_depyler_value_to_typed_assignment(&formatted_code);
formatted_code = fix_format_debug_in_int_vec(&formatted_code);
formatted_code = fix_ambiguous_into_on_chain(&formatted_code);
formatted_code = fix_hashmap_contains_to_contains_key(&formatted_code);
formatted_code = fix_ambiguous_into_type_annotation(&formatted_code);
formatted_code = fix_floor_div_type_annotation(&formatted_code);
formatted_code = fix_negate_result_fn_call(&formatted_code);
formatted_code = fix_return_depyler_value_param(&formatted_code);
formatted_code = fix_str_clone_to_string(&formatted_code);
formatted_code = fix_option_as_cast(&formatted_code);
formatted_code = fix_unwrap_or_depyler_value(&formatted_code);
formatted_code = fix_i32_as_i64_cast(&formatted_code);
formatted_code = fix_nested_vec_type_in_assert(&formatted_code);
formatted_code = fix_dict_get_return(&formatted_code);
formatted_code = fix_string_as_ref_ambiguity(&formatted_code);
formatted_code = fix_option_dequeue_unwrap(&formatted_code);
formatted_code = fix_option_hashmap_contains_key(&formatted_code);
formatted_code = fix_char_as_str(&formatted_code);
formatted_code = fix_option_push_after_is_some(&formatted_code);
formatted_code = fix_depyler_value_str_literal(&formatted_code);
formatted_code = fix_vec_arg_type_in_assert(&formatted_code);
formatted_code = fix_format_vec_display(&formatted_code);
formatted_code = fix_iter_on_impl_iterator(&formatted_code);
formatted_code = fix_deref_on_unwrap_or(&formatted_code);
formatted_code = fix_pyindex_depyler_value_wrapper(&formatted_code);
formatted_code = fix_option_field_assignment(&formatted_code);
formatted_code = fix_option_to_string_in_is_some_guard(&formatted_code);
formatted_code = fix_return_option_param_in_is_some(&formatted_code);
formatted_code = fix_let_discard_ok_return(&formatted_code);
formatted_code = fix_void_fn_with_return_value(&formatted_code);
formatted_code = fix_unit_vec_to_tuple_type(&formatted_code);
formatted_code = fix_bare_return_in_result_fn(&formatted_code);
formatted_code = fix_let_unit_type_annotation(&formatted_code);
formatted_code = fix_let_type_from_fn_return(&formatted_code);
formatted_code = fix_assert_vec_type_from_fn_return(&formatted_code);
formatted_code = fix_vec_depyler_value_param_from_callsite(&formatted_code);
formatted_code
}
fn fix_stub_arities(code: &str) -> String {
let stub_names = find_stub_names(code);
if stub_names.is_empty() {
return code.to_string();
}
let mut result = code.to_string();
let mut macros_to_prepend: Vec<String> = Vec::new();
let mut struct_stubs_to_prepend: Vec<String> = Vec::new();
for name in &stub_names {
let type_path = format!("{}::", name);
if result.contains(&type_path) {
if let Some(cleaned) = remove_stub_function(&result, name) {
result = cleaned;
let (struct_def, ctor_macro) = generate_stub_struct(name, &result);
struct_stubs_to_prepend.push(struct_def);
if let Some(mac) = ctor_macro {
macros_to_prepend.push(mac);
let new_call = format!("{}::new(", name);
let macro_call = format!("{}_new!(", name);
result = result.replace(&new_call, ¯o_call);
}
}
continue;
}
if let Some((cleaned, macro_def)) = replace_stub_with_macro(&result, name) {
result = cleaned;
macros_to_prepend.push(macro_def);
result = rewrite_call_sites(&result, name);
}
}
if !macros_to_prepend.is_empty() || !struct_stubs_to_prepend.is_empty() {
let mut prefix_parts = macros_to_prepend;
prefix_parts.extend(struct_stubs_to_prepend);
let prefix = prefix_parts.join("\n");
result = format!("{}\n{}", prefix, result);
}
result
}
fn find_stub_names(code: &str) -> Vec<String> {
let mut stub_names: Vec<String> = Vec::new();
let lines: Vec<&str> = code.lines().collect();
for (i, line) in lines.iter().enumerate() {
if !line.contains("DEPYLER-0615") {
continue;
}
for next_line in lines.iter().take(lines.len().min(i + 5)).skip(i + 1) {
if let Some(fname) = extract_fn_name(next_line) {
stub_names.push(fname);
break;
}
}
}
stub_names
}
fn extract_fn_name(line: &str) -> Option<String> {
let fname = line
.trim()
.strip_prefix("pub fn ")?
.split('(')
.next()?
.split('<')
.next()?
.trim();
if fname.is_empty() {
return None;
}
Some(fname.to_string())
}
fn replace_stub_with_macro(code: &str, name: &str) -> Option<(String, String)> {
let fn_dv = format!(
"pub fn {}(_args: impl std::any::Any) -> DepylerValue {{\n DepylerValue::default()\n}}",
name
);
let fn_unit = format!(
"pub fn {}(_args: impl std::any::Any) -> () {{\n}}",
name
);
let is_unit = code.contains(&fn_unit);
let is_dv = code.contains(&fn_dv);
if !is_unit && !is_dv {
return None;
}
let mut result = if is_dv {
code.replace(&fn_dv, "")
} else {
code.replace(&fn_unit, "")
};
result = result.replace("/// DEPYLER-0615: Generated to allow standalone compilation", "");
result = result.replace("#[allow(dead_code, unused_variables)]", "");
let macro_def = if is_unit {
format!("macro_rules! {} {{ ($($args:expr),* $(,)?) => {{ () }}; }}", name)
} else {
format!(
"macro_rules! {} {{ ($($args:expr),* $(,)?) => {{ DepylerValue::default() }}; }}",
name
)
};
Some((result, macro_def))
}
fn rewrite_call_sites(code: &str, name: &str) -> String {
let macro_def_marker = format!("macro_rules! {}", name);
code.lines()
.map(|line| {
if line.contains(¯o_def_marker) {
return line.to_string();
}
rewrite_line_call_sites(line, name)
})
.collect::<Vec<_>>()
.join("\n")
}
fn rewrite_line_call_sites(line: &str, name: &str) -> String {
let pattern = format!("{}(", name);
let mut result = String::with_capacity(line.len());
let mut remaining = line;
while let Some(pos) = remaining.find(&pattern) {
let is_word_boundary = if pos == 0 {
true
} else {
let prev = remaining.as_bytes()[pos - 1];
!prev.is_ascii_alphanumeric() && prev != b'_'
};
if is_word_boundary {
result.push_str(&remaining[..pos]);
result.push_str(name);
result.push('!');
result.push('(');
remaining = &remaining[pos + pattern.len()..];
} else {
result.push_str(&remaining[..pos + pattern.len()]);
remaining = &remaining[pos + pattern.len()..];
}
}
result.push_str(remaining);
result
}
fn remove_stub_function(code: &str, name: &str) -> Option<String> {
let fn_dv = format!(
"pub fn {}(_args: impl std::any::Any) -> DepylerValue {{\n DepylerValue::default()\n}}",
name
);
let fn_unit = format!(
"pub fn {}(_args: impl std::any::Any) -> () {{\n}}",
name
);
let mut result = if code.contains(&fn_dv) {
code.replace(&fn_dv, "")
} else if code.contains(&fn_unit) {
code.replace(&fn_unit, "")
} else {
return None;
};
result = result.replace("/// DEPYLER-0615: Generated to allow standalone compilation", "");
result = result.replace("#[allow(dead_code, unused_variables)]", "");
Some(result)
}
fn generate_stub_struct(name: &str, code: &str) -> (String, Option<String>) {
let (members, instance_methods) = scan_class_usage(name, code);
let mut fields: Vec<String> = Vec::new();
for method in &instance_methods {
if method == "value" {
continue;
}
let method_call = format!(".{}(", method);
let field_assign = format!(".{} =", method);
let is_method_call = code.contains(&method_call);
let is_field_assign = code.contains(&field_assign);
if is_field_assign || !is_method_call {
fields.push(method.clone());
}
}
let mut parts = Vec::new();
let mut struct_fields = vec!["pub value: DepylerValue".to_string()];
for field in &fields {
struct_fields.push(format!("pub {}: DepylerValue", field));
}
parts.push(format!(
"#[derive(Debug, Clone, PartialEq)] pub struct {} {{ {} }}",
name,
struct_fields.join(", ")
));
let impl_items = build_struct_impl_items(name, code, &members, &instance_methods);
parts.push(format!("impl {} {{ {} }}", name, impl_items.join("\n")));
parts.push(format!(
"impl std::ops::Deref for {} {{ type Target = DepylerValue; fn deref(&self) -> &Self::Target {{ &self.value }} }}",
name
));
let ctor_macro = if members.contains(&"new".to_string()) {
let field_defaults: Vec<String> = struct_fields.iter()
.map(|_| "DepylerValue::default()".to_string())
.collect();
let field_names: Vec<String> = vec!["value".to_string()]
.into_iter()
.chain(fields.iter().cloned())
.collect();
let field_inits: String = field_names.iter()
.zip(field_defaults.iter())
.map(|(n, d)| format!("{}: {}", n, d))
.collect::<Vec<_>>()
.join(", ");
Some(format!(
"macro_rules! {}_new {{ ($($args:expr),* $(,)?) => {{ {} {{ {} }} }}; }}",
name, name, field_inits
))
} else {
None
};
(parts.join("\n"), ctor_macro)
}
fn scan_class_usage(name: &str, code: &str) -> (Vec<String>, Vec<String>) {
let prefix = format!("{}::", name);
let ctor_macro = format!("{}_new!(", name);
let mut members: Vec<String> = Vec::new();
let mut instance_methods: Vec<String> = Vec::new();
let mut instance_vars: Vec<String> = Vec::new();
for line in code.lines() {
scan_static_members(line, &prefix, &mut members, &mut instance_methods);
scan_constructor_vars(line, name, &ctor_macro, &mut instance_vars);
}
scan_instance_var_methods(code, &instance_vars, &mut instance_methods);
(members, instance_methods)
}
fn extract_identifier(text: &str) -> String {
text.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect()
}
fn scan_static_members(
line: &str,
prefix: &str,
members: &mut Vec<String>,
instance_methods: &mut Vec<String>,
) {
let mut search = line;
while let Some(pos) = search.find(prefix) {
let after = &search[pos + prefix.len()..];
let member = extract_identifier(after);
if !member.is_empty() && !members.contains(&member) {
members.push(member.clone());
}
let after_member = &after[member.len()..];
if let Some(rest) = after_member.strip_prefix('.') {
let method = extract_identifier(rest);
if !method.is_empty() && !instance_methods.contains(&method) {
instance_methods.push(method);
}
}
search = &search[pos + prefix.len()..];
}
}
fn scan_constructor_vars(
line: &str,
name: &str,
ctor_macro: &str,
instance_vars: &mut Vec<String>,
) {
let trimmed = line.trim();
let ctor_call = format!("{}::new(", name);
let rest = trimmed
.strip_prefix("let mut ")
.or_else(|| trimmed.strip_prefix("let "));
if let Some(rest) = rest {
if rest.contains(ctor_macro) || rest.contains(&ctor_call) {
let var_name = extract_identifier(rest);
if !var_name.is_empty() && !instance_vars.contains(&var_name) {
instance_vars.push(var_name);
}
}
}
}
fn scan_instance_var_methods(
code: &str,
instance_vars: &[String],
instance_methods: &mut Vec<String>,
) {
for line in code.lines() {
for var in instance_vars {
let dot_prefix = format!("{}.", var);
let mut search = line;
while let Some(pos) = search.find(&dot_prefix) {
let after = &search[pos + dot_prefix.len()..];
let member = extract_identifier(after);
if !member.is_empty() && !instance_methods.contains(&member) {
instance_methods.push(member);
}
search = &search[pos + dot_prefix.len()..];
}
}
}
}
fn build_struct_impl_items(
name: &str,
code: &str,
members: &[String],
instance_methods: &[String],
) -> Vec<String> {
let mut items = Vec::new();
for member in members {
if member == "new" {
continue;
}
if let Some(item) = build_associated_member_item(name, code, member) {
items.push(item);
}
}
for method in instance_methods {
if let Some(item) = build_instance_method_item(code, method) {
items.push(item);
}
}
items
}
fn build_associated_member_item(name: &str, code: &str, member: &str) -> Option<String> {
let call_pattern = format!("{}::{}(", name, member);
let is_method = code.contains(&call_pattern);
if !is_method {
return Some(format!(
"pub const {}: {} = {} {{ value: DepylerValue::None }};",
member, name, name
));
}
let no_arg_pattern = format!("{}::{}()", name, member);
let has_no_args = code.contains(&no_arg_pattern);
if !has_no_args {
return Some(format!(
"pub fn {}(_args: impl std::any::Any) -> DepylerValue {{ DepylerValue::default() }}",
member
));
}
let cast_pattern = format!("{}::{}() as ", name, member);
let is_cast_to_numeric = code.contains(&cast_pattern);
let ret_type = if is_cast_to_numeric { "usize" } else { "DepylerValue" };
let ret_val = if is_cast_to_numeric { "0" } else { "DepylerValue::default()" };
Some(format!("pub fn {}() -> {} {{ {} }}", member, ret_type, ret_val))
}
fn build_instance_method_item(code: &str, method: &str) -> Option<String> {
if method == "value" {
return None;
}
let method_call = format!(".{}(", method);
let is_method_call = code.contains(&method_call);
let field_assign = format!(".{} =", method);
let is_field_assign = code.contains(&field_assign);
if is_field_assign || !is_method_call {
return None;
}
let zero_arg_call = format!(".{}()", method);
let has_zero_args = code.contains(&zero_arg_call);
if has_zero_args {
Some(format!(
"pub fn {}(&self) -> DepylerValue {{ DepylerValue::default() }}",
method
))
} else {
Some(format!(
"pub fn {}(&self, _args: impl std::any::Any) -> DepylerValue {{ DepylerValue::default() }}",
method
))
}
}
fn find_closing_brace(lines: &[&str], start: usize, initial_depth: usize) -> Option<usize> {
let mut depth = initial_depth;
for (j, line) in lines.iter().enumerate().skip(start) {
for ch in line.trim().chars() {
match ch {
'{' => depth += 1,
'}' => depth -= 1,
_ => {}
}
}
if depth == 0 {
return Some(j);
}
}
None
}
fn try_wrap_insert_integer(original_line: &str) -> Option<String> {
let inner = original_line.trim();
if !inner.contains(".insert(") || !inner.ends_with(");") {
return None;
}
let cp = inner.rfind(", ")?;
let val = &inner[cp + 2..inner.len() - 2];
if val.parse::<i64>().is_ok() && !val.contains("DepylerValue") {
Some(original_line.replace(
&format!(", {});", val),
&format!(", DepylerValue::Int({}));", val),
))
} else {
None
}
}
fn process_inner_hashmap_line(original_line: &str, line_idx: usize, close_idx: usize) -> String {
if let Some(wrapped) = try_wrap_insert_integer(original_line) {
return wrapped;
}
let inner = original_line.trim();
if inner == "map" && line_idx + 1 == close_idx {
let indent = original_line.len() - original_line.trim_start().len();
return format!(
"{}DepylerValue::Dict(map.into_iter().map(|(k, v)| (DepylerValue::Str(k), v)).collect())",
" ".repeat(indent)
);
}
original_line.to_string()
}
fn detect_nested_hashmap_insert(lines: &[&str], i: usize) -> Option<usize> {
let trimmed = lines[i].trim();
if !trimmed.contains(".insert(") || !trimmed.ends_with('{') || i + 1 >= lines.len() {
return None;
}
if !lines[i + 1].trim().contains("HashMap::new()") {
return None;
}
find_closing_brace(lines, i + 2, 1)
}
fn fix_nested_hashmap_to_depyler_value(code: &str) -> String {
if !code.contains("HashMap<String, DepylerValue>") {
return code.to_string();
}
let lines: Vec<&str> = code.lines().collect();
let mut result: Vec<String> = Vec::with_capacity(lines.len());
let mut i = 0;
while i < lines.len() {
if let Some(close_idx) = detect_nested_hashmap_insert(&lines, i) {
result.push(lines[i].to_string());
for (j, line) in lines.iter().enumerate().take(close_idx).skip(i + 1) {
result.push(process_inner_hashmap_line(line, j, close_idx));
}
result.push(lines[close_idx].to_string());
i = close_idx + 1;
continue;
}
result.push(lines[i].to_string());
i += 1;
}
result.join("\n")
}
fn extract_receiver(text: &str, dot_pos: usize) -> String {
text[..dot_pos]
.chars()
.rev()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect::<String>()
.chars()
.rev()
.collect()
}
fn extract_method_and_arg(after_dot: &str) -> Option<(&str, String, &str)> {
let paren = after_dot.find('(')?;
let method = &after_dot[..paren];
if method.is_empty() || !method.chars().all(|c| c.is_alphanumeric() || c == '_') {
return None;
}
let args_rest = &after_dot[paren + 1..];
let arg: String = args_rest
.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect();
if arg.is_empty() {
return None;
}
let after_arg = &args_rest[arg.len()..];
Some((method, arg, after_arg))
}
fn apply_clone_to_method_call(line: &str, trimmed: &str) -> String {
let dot_pos = match trimmed.find('.') {
Some(p) => p,
None => return line.to_string(),
};
let receiver = extract_receiver(trimmed, dot_pos);
if receiver.is_empty() {
return line.to_string();
}
let after_dot = &trimmed[dot_pos + 1..];
let (method, arg, after_arg) = match extract_method_and_arg(after_dot) {
Some(parts) => parts,
None => return line.to_string(),
};
if arg == receiver && after_arg.starts_with(')') {
let old = format!("{}.{}({})", receiver, method, arg);
let new = format!("{}.{}({}.clone())", receiver, method, arg);
return line.replace(&old, &new);
}
if after_arg.contains(&format!("{}.{}", arg, method)) {
let old = format!(".{}({})", method, arg);
let new = format!(".{}({}.clone())", method, arg);
return line.replacen(&old, &new, 1);
}
line.to_string()
}
fn fix_stub_method_ownership(code: &str) -> String {
if !code.contains("DEPYLER-0615") {
return code.to_string();
}
let lines: Vec<&str> = code.lines().collect();
let mut result: Vec<String> = Vec::with_capacity(lines.len());
for line in &lines {
let trimmed = line.trim();
if trimmed.starts_with("assert") {
result.push(apply_clone_to_method_call(line, trimmed));
} else {
result.push(line.to_string());
}
}
result.join("\n")
}
fn find_closing_brace_at_indent(
lines: &[&str],
start: usize,
expected_indent: usize,
) -> Option<usize> {
let mut depth: usize = 1;
for (j, line) in lines.iter().enumerate().skip(start) {
for ch in line.trim().chars() {
match ch {
'{' => depth += 1,
'}' => depth -= 1,
_ => {}
}
}
if depth == 0 {
let j_indent = line.len() - line.trim_start().len();
return if j_indent == expected_indent {
Some(j)
} else {
None
};
}
}
None
}
fn extract_let_var_name(let_trimmed: &str) -> String {
let var_rest = let_trimmed
.strip_prefix("let mut ")
.or_else(|| let_trimmed.strip_prefix("let "))
.unwrap_or("");
var_rest
.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect()
}
fn is_var_used_after(lines: &[&str], close: usize, var_name: &str) -> bool {
(close + 1..lines.len()).any(|k| lines[k].trim().contains(var_name))
}
fn try_hoist_with_block(lines: &[&str], i: usize, result: &mut Vec<String>) -> Option<usize> {
let trimmed = lines[i].trim();
if trimmed != "{" || i + 1 >= lines.len() {
return None;
}
let next_trimmed = lines[i + 1].trim();
if !next_trimmed.starts_with("let mut ") && !next_trimmed.starts_with("let ") {
return None;
}
let indent = lines[i].len() - lines[i].trim_start().len();
let close = find_closing_brace_at_indent(lines, i + 2, indent)?;
let var_name = extract_let_var_name(next_trimmed);
if var_name.is_empty() || !is_var_used_after(lines, close, &var_name) {
return None;
}
let let_indent = " ".repeat(indent + 4);
result.push(format!("{}{}", let_indent, next_trimmed));
result.push(lines[i].to_string()); Some(i + 2)
}
fn fix_with_block_scope(code: &str) -> String {
let lines: Vec<&str> = code.lines().collect();
let mut result: Vec<String> = Vec::with_capacity(lines.len());
let mut i = 0;
while i < lines.len() {
if let Some(skip_to) = try_hoist_with_block(&lines, i, &mut result) {
i = skip_to;
continue;
}
result.push(lines[i].to_string());
i += 1;
}
result.join("\n")
}
fn fix_struct_field_literal_assignment(code: &str) -> String {
let mut result = String::with_capacity(code.len());
for line in code.lines() {
if let Some(new_line) = try_wrap_field_literal(line) {
result.push_str(&new_line);
} else {
result.push_str(line);
}
result.push('\n');
}
result
}
fn try_wrap_field_literal(line: &str) -> Option<String> {
let trimmed = line.trim();
let eq_pos = trimmed.find(" = ")?;
let lhs = &trimmed[..eq_pos];
let rhs = trimmed[eq_pos + 3..].trim_end_matches(';').trim();
if !lhs.contains('.') || lhs.contains("::") {
return None;
}
if rhs.parse::<f64>().is_ok() && rhs.contains('.') {
return Some(line.replace(
&format!("= {};", rhs),
&format!("= DepylerValue::Float({});", rhs),
));
}
if rhs.parse::<i64>().is_ok() {
return Some(line.replace(
&format!("= {};", rhs),
&format!("= DepylerValue::Int({});", rhs),
));
}
None
}
fn fix_depyler_value_casts(code: &str) -> String {
let mut result = code.to_string();
for cast_type in &["f64", "i32", "i64", "usize"] {
let cast_suffix = format!(" as {})", cast_type);
let lines: Vec<&str> = result.lines().collect();
let mut new_lines: Vec<String> = Vec::with_capacity(lines.len());
for line in &lines {
if line.contains(&cast_suffix) && line.contains("assert!") {
new_lines.push(line.replace(&cast_suffix, ")"));
} else {
new_lines.push(line.to_string());
}
}
result = new_lines.join("\n");
}
result
}