ferrous-forge 1.9.6

System-wide Rust development standards enforcer
Documentation
//! Project-level formatting operations

use super::types::FormatResult;
use super::utils::ensure_rustfmt_installed;
use crate::{Error, Result};
use std::path::Path;
use tokio::fs;

/// Check formatting for entire project
///
/// # Errors
///
/// Returns [`Error::Process`] if `cargo fmt` fails to execute.
pub async fn check_formatting(project_path: &Path) -> Result<FormatResult> {
    ensure_rustfmt_installed().await?;

    let output = tokio::process::Command::new("cargo")
        .args(&["fmt", "--", "--check"])
        .current_dir(project_path)
        .output()
        .await
        .map_err(|e| Error::process(format!("Failed to run rustfmt: {}", e)))?;

    let formatted = output.status.success();
    let stderr = String::from_utf8_lossy(&output.stderr);

    Ok(FormatResult {
        formatted,
        unformatted_files: if formatted {
            vec![]
        } else {
            vec![format!("Multiple files need formatting (see: {})", stderr)]
        },
        suggestions: vec![],
    })
}

/// Auto-format entire project
///
/// # Errors
///
/// Returns [`Error::Process`] if `cargo fmt` fails to execute.
pub async fn auto_format(project_path: &Path) -> Result<()> {
    ensure_rustfmt_installed().await?;

    let status = tokio::process::Command::new("cargo")
        .args(&["fmt", "--all"])
        .current_dir(project_path)
        .status()
        .await
        .map_err(|e| Error::process(format!("Failed to run cargo fmt: {}", e)))?;

    if !status.success() {
        return Err(Error::process("Formatting failed".to_string()));
    }

    Ok(())
}

/// Get formatting diff for the project
///
/// # Errors
///
/// Returns [`Error::Process`] if `cargo fmt` fails to execute.
pub async fn get_format_diff(project_path: &Path) -> Result<String> {
    ensure_rustfmt_installed().await?;

    let output = tokio::process::Command::new("cargo")
        .args(&["fmt", "--", "--check", "--print-diff"])
        .current_dir(project_path)
        .output()
        .await
        .map_err(|e| Error::process(format!("Failed to get format diff: {}", e)))?;

    Ok(String::from_utf8_lossy(&output.stdout).to_string())
}

/// Apply rustfmt configuration to project
///
/// # Errors
///
/// Returns an error if the configuration file cannot be written to disk.
pub async fn apply_rustfmt_config(project_path: &Path) -> Result<()> {
    let config_content = r#"# Ferrous Forge rustfmt configuration
max_width = 100
tab_spaces = 4
newline_style = "Unix"
use_small_heuristics = "Default"
edition = "2021"
"#;

    let config_path = project_path.join("rustfmt.toml");
    fs::write(&config_path, config_content).await?;

    Ok(())
}