Skip to main content

rust_diff_analyzer/output/
formatter.rs

1// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use masterror::AppError;
5
6use super::{comment::format_comment, github::GithubFormatter, json::JsonFormatter};
7use crate::{
8    config::{Config, OutputFormat},
9    types::AnalysisResult,
10};
11
12/// Trait for output formatters
13pub trait Formatter {
14    /// Formats analysis result as string
15    ///
16    /// # Arguments
17    ///
18    /// * `result` - Analysis result to format
19    /// * `config` - Configuration
20    ///
21    /// # Returns
22    ///
23    /// Formatted string or error
24    ///
25    /// # Errors
26    ///
27    /// Returns error if formatting fails
28    fn format(&self, result: &AnalysisResult, config: &Config) -> Result<String, AppError>;
29}
30
31/// Formats analysis result using configured format
32///
33/// # Arguments
34///
35/// * `result` - Analysis result to format
36/// * `config` - Configuration
37///
38/// # Returns
39///
40/// Formatted string or error
41///
42/// # Errors
43///
44/// Returns error if formatting fails
45///
46/// # Examples
47///
48/// ```
49/// use rust_diff_analyzer::{
50///     config::Config,
51///     output::format_output,
52///     types::{AnalysisResult, AnalysisScope, Summary},
53/// };
54///
55/// let result = AnalysisResult::new(vec![], Summary::default(), AnalysisScope::new());
56/// let config = Config::default();
57/// let output = format_output(&result, &config).unwrap();
58/// ```
59pub fn format_output(result: &AnalysisResult, config: &Config) -> Result<String, AppError> {
60    match config.output.format {
61        OutputFormat::Github => GithubFormatter.format(result, config),
62        OutputFormat::Json => JsonFormatter.format(result, config),
63        OutputFormat::Human => format_human(result, config),
64        OutputFormat::Comment => Ok(format_comment(result, config)),
65    }
66}
67
68fn format_human(result: &AnalysisResult, _config: &Config) -> Result<String, AppError> {
69    use std::fmt::Write;
70
71    let summary = &result.summary;
72    let mut output = String::new();
73
74    output.push_str("=== Rust Diff Analysis ===\n\n");
75
76    output.push_str("Production:\n");
77    let _ = writeln!(output, "  Functions: {}", summary.prod_functions);
78    let _ = writeln!(output, "  Structs: {}", summary.prod_structs);
79    let _ = writeln!(output, "  Other: {}", summary.prod_other);
80    let _ = writeln!(
81        output,
82        "  Lines: +{} -{}",
83        summary.prod_lines_added, summary.prod_lines_removed
84    );
85
86    output.push_str("\nTest:\n");
87    let _ = writeln!(output, "  Units: {}", summary.test_units);
88    let _ = writeln!(
89        output,
90        "  Lines: +{} -{}",
91        summary.test_lines_added, summary.test_lines_removed
92    );
93
94    let _ = writeln!(output, "\nWeighted score: {}", summary.weighted_score);
95
96    if summary.exceeds_limit {
97        output.push_str("\nLIMIT EXCEEDED\n");
98    }
99
100    if !result.changes.is_empty() {
101        output.push_str("\nChanges:\n");
102        for change in &result.changes {
103            let _ = writeln!(
104                output,
105                "  - {} ({}) in {} [+{} -{}]",
106                change.unit.name,
107                change.unit.kind.as_str(),
108                change.file_path.display(),
109                change.lines_added,
110                change.lines_removed
111            );
112        }
113    }
114
115    Ok(output)
116}