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 tokio::fs;
8
9/// Check formatting for entire project
10///
11/// # Errors
12///
13/// Returns [`Error::Process`] if `cargo fmt` fails to execute.
14pub async fn check_formatting(project_path: &Path) -> Result<FormatResult> {
15    ensure_rustfmt_installed().await?;
16
17    let output = tokio::process::Command::new("cargo")
18        .args(&["fmt", "--", "--check"])
19        .current_dir(project_path)
20        .output()
21        .await
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 = tokio::process::Command::new("cargo")
47        .args(&["fmt", "--all"])
48        .current_dir(project_path)
49        .status()
50        .await
51        .map_err(|e| Error::process(format!("Failed to run cargo fmt: {}", e)))?;
52
53    if !status.success() {
54        return Err(Error::process("Formatting failed".to_string()));
55    }
56
57    Ok(())
58}
59
60/// Get formatting diff for the project
61///
62/// # Errors
63///
64/// Returns [`Error::Process`] if `cargo fmt` fails to execute.
65pub async fn get_format_diff(project_path: &Path) -> Result<String> {
66    ensure_rustfmt_installed().await?;
67
68    let output = tokio::process::Command::new("cargo")
69        .args(&["fmt", "--", "--check", "--print-diff"])
70        .current_dir(project_path)
71        .output()
72        .await
73        .map_err(|e| Error::process(format!("Failed to get format diff: {}", e)))?;
74
75    Ok(String::from_utf8_lossy(&output.stdout).to_string())
76}
77
78/// Apply rustfmt configuration to project
79///
80/// # Errors
81///
82/// Returns an error if the configuration file cannot be written to disk.
83pub async fn apply_rustfmt_config(project_path: &Path) -> Result<()> {
84    let config_content = r#"# Ferrous Forge rustfmt configuration
85max_width = 100
86tab_spaces = 4
87newline_style = "Unix"
88use_small_heuristics = "Default"
89edition = "2021"
90"#;
91
92    let config_path = project_path.join("rustfmt.toml");
93    fs::write(&config_path, config_content).await?;
94
95    Ok(())
96}