Skip to main content

ferrous_forge/formatting/
project_ops.rs

1//! Project-level formatting operations
2
3use super::types::FormatResult;
4use super::utils::ensure_rustfmt_installed;
5use crate::{Error, Result};
6use std::path::Path;
7use std::process::Command;
8use tokio::fs;
9
10/// Check formatting for entire project
11///
12/// # Errors
13///
14/// Returns [`Error::Process`] if `cargo fmt` fails to execute.
15pub async fn check_formatting(project_path: &Path) -> Result<FormatResult> {
16    ensure_rustfmt_installed().await?;
17
18    let output = Command::new("cargo")
19        .args(&["fmt", "--", "--check"])
20        .current_dir(project_path)
21        .output()
22        .map_err(|e| Error::process(format!("Failed to run rustfmt: {}", e)))?;
23
24    let formatted = output.status.success();
25    let stderr = String::from_utf8_lossy(&output.stderr);
26
27    Ok(FormatResult {
28        formatted,
29        unformatted_files: if formatted {
30            vec![]
31        } else {
32            vec![format!("Multiple files need formatting (see: {})", stderr)]
33        },
34        suggestions: vec![],
35    })
36}
37
38/// Auto-format entire project
39///
40/// # Errors
41///
42/// Returns [`Error::Process`] if `cargo fmt` fails to execute.
43pub async fn auto_format(project_path: &Path) -> Result<()> {
44    ensure_rustfmt_installed().await?;
45
46    let status = Command::new("cargo")
47        .args(&["fmt", "--all"])
48        .current_dir(project_path)
49        .status()
50        .map_err(|e| Error::process(format!("Failed to run cargo fmt: {}", e)))?;
51
52    if !status.success() {
53        return Err(Error::process("Formatting failed".to_string()));
54    }
55
56    Ok(())
57}
58
59/// Get formatting diff for the project
60///
61/// # Errors
62///
63/// Returns [`Error::Process`] if `cargo fmt` fails to execute.
64pub async fn get_format_diff(project_path: &Path) -> Result<String> {
65    ensure_rustfmt_installed().await?;
66
67    let output = Command::new("cargo")
68        .args(&["fmt", "--", "--check", "--print-diff"])
69        .current_dir(project_path)
70        .output()
71        .map_err(|e| Error::process(format!("Failed to get format diff: {}", e)))?;
72
73    Ok(String::from_utf8_lossy(&output.stdout).to_string())
74}
75
76/// Apply rustfmt configuration to project
77///
78/// # Errors
79///
80/// Returns an error if the configuration file cannot be written to disk.
81pub async fn apply_rustfmt_config(project_path: &Path) -> Result<()> {
82    let config_content = r#"# Ferrous Forge rustfmt configuration
83max_width = 100
84tab_spaces = 4
85newline_style = "Unix"
86use_small_heuristics = "Default"
87edition = "2021"
88"#;
89
90    let config_path = project_path.join("rustfmt.toml");
91    fs::write(&config_path, config_content).await?;
92
93    Ok(())
94}