pub fn format_rust_code(code: String) -> String {
let code = apply_string_replacements(code);
match run_rustfmt(&code) {
Ok(formatted) => formatted,
Err(_) => code, }
}
fn run_rustfmt(code: &str) -> Result<String, std::io::Error> {
use std::io::Write;
use std::process::{Command, Stdio};
let mut child = Command::new("rustfmt")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
{
if let Some(mut stdin) = child.stdin.take() {
stdin.write_all(code.as_bytes())?;
}
}
let output = child.wait_with_output()?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
Err(std::io::Error::other("rustfmt failed"))
}
}
fn apply_string_replacements(code: String) -> String {
code.replace(" ; ", ";\n ")
.replace(" { ", " {\n ")
.replace(" } ", "\n}\n")
.replace("} ;", "};")
.replace(
"use std :: collections :: HashMap ;",
"use std::collections::HashMap;",
)
.replace(" . ", ".")
.replace(" (", "(")
.replace(" )", ")")
.replace(".len ()", ".len()")
.replace(".push (", ".push(")
.replace(".insert (", ".insert(")
.replace(".get (", ".get(")
.replace(".contains_key (", ".contains_key(")
.replace(".to_string ()", ".to_string()")
.replace(" ::", "::")
.replace(":: ", "::")
.replace("# [", "#[")
.replace(" : ", ": ")
.replace(" , ", ", ")
.replace("=(", " = (")
.replace("= (", " = (")
.replace(" =", " =") .replace(" =", " =") .replace("Vec < ", "Vec<")
.replace(" < ", "<")
.replace(" > ", ">")
.replace("> ", ">")
.replace("< ", "<")
.replace(" >", ">") .replace(" -> ", "->") .replace("-> ", "->") .replace(" ->", "->") .replace("->", " -> ") .replace("& self", "&self")
.replace("& mut", "&mut")
.replace(" !", "!")
.replace("value<", "value < ")
.replace("<self", "< self")
.replace(" .. ", "..")
.replace(" ..", "..")
.replace(".. ", "..")
.replace("in(", "in (")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_semicolon_spacing() {
let input = "fn main() { let x = 5 ; }".to_string();
let result = format_rust_code(input);
assert!(result.contains("let x = 5;") || result.contains(";\n"));
}
#[test]
fn test_format_method_call_spacing() {
let input = "fn main() { let _x = vec . len ( ) ; }".to_string();
let result = format_rust_code(input);
assert!(result.contains("vec.len()") || result.contains("len()"));
}
#[test]
fn test_format_generic_spacing() {
let input = "fn main() { let _v: Vec < i32 > = Vec::new(); }".to_string();
let result = format_rust_code(input);
assert!(result.contains("Vec<i32>") || result.contains("Vec <"));
}
#[test]
fn test_format_return_type_spacing() {
let input = "fn foo()->i32{42}".to_string();
let result = format_rust_code(input);
assert!(result.contains(" -> ") || result.contains("->"));
}
}