rust_diff_analyzer/output/
comment.rs1use crate::{
5 config::Config,
6 types::{AnalysisResult, SemanticUnitKind},
7};
8
9const COMMENT_MARKER: &str = "<!-- rust-diff-analyzer-comment -->";
10
11pub fn format_comment(result: &AnalysisResult, config: &Config) -> String {
37 let summary = &result.summary;
38 let status = if summary.exceeds_limit { "❌" } else { "✅" };
39
40 let mut output = String::new();
41
42 output.push_str(COMMENT_MARKER);
43 output.push('\n');
44 output.push_str("## Rust Diff Analysis\n\n");
45
46 output.push_str("| Metric | Production | Test |\n");
47 output.push_str("|--------|------------|------|\n");
48 output.push_str(&format!("| Functions | {} | - |\n", summary.prod_functions));
49 output.push_str(&format!(
50 "| Structs/Enums | {} | - |\n",
51 summary.prod_structs
52 ));
53 output.push_str(&format!("| Other | {} | - |\n", summary.prod_other));
54 output.push_str(&format!(
55 "| Lines added | {} | {} |\n",
56 summary.prod_lines_added, summary.test_lines_added
57 ));
58 output.push_str(&format!(
59 "| Lines removed | {} | {} |\n",
60 summary.prod_lines_removed, summary.test_lines_removed
61 ));
62 output.push_str(&format!(
63 "| Total units | {} | {} |\n",
64 summary.total_prod_units(),
65 summary.test_units
66 ));
67
68 output.push_str("\n### Score\n\n");
69 output.push_str(&format!(
70 "**{}** / {} {}\n",
71 summary.weighted_score, config.limits.max_weighted_score, status
72 ));
73
74 if config.output.include_details && !result.changes.is_empty() {
75 output.push_str("\n<details>\n");
76 output.push_str(&format!(
77 "<summary>Changed units ({})</summary>\n\n",
78 result.changes.len()
79 ));
80
81 let prod_changes: Vec<_> = result.production_changes().collect();
82 let test_changes: Vec<_> = result.test_changes().collect();
83
84 if !prod_changes.is_empty() {
85 output.push_str(&format!("#### Production ({})\n\n", prod_changes.len()));
86 for change in prod_changes {
87 let kind = match change.unit.kind {
88 SemanticUnitKind::Function => "function",
89 SemanticUnitKind::Struct => "struct",
90 SemanticUnitKind::Enum => "enum",
91 SemanticUnitKind::Trait => "trait",
92 SemanticUnitKind::Impl => "impl",
93 _ => "other",
94 };
95 output.push_str(&format!(
96 "- `{}` → `{}` ({})\n",
97 change.file_path.display(),
98 change.unit.name,
99 kind
100 ));
101 }
102 output.push('\n');
103 }
104
105 if !test_changes.is_empty() {
106 output.push_str(&format!("#### Test ({})\n\n", test_changes.len()));
107 for change in test_changes {
108 output.push_str(&format!(
109 "- `{}` → `{}`\n",
110 change.file_path.display(),
111 change.unit.name
112 ));
113 }
114 output.push('\n');
115 }
116
117 output.push_str("</details>\n");
118 }
119
120 output.push_str("\n---\n");
121 output.push_str(
122 "<sub>[Rust Diff Analyzer](https://github.com/RAprogramm/rust-prod-diff-checker)</sub>\n",
123 );
124
125 output
126}
127
128pub fn get_comment_marker() -> &'static str {
143 COMMENT_MARKER
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::types::Summary;
150
151 #[test]
152 fn test_format_comment() {
153 let result = AnalysisResult::new(vec![], Summary::default());
154 let config = Config::default();
155 let output = format_comment(&result, &config);
156
157 assert!(output.contains(COMMENT_MARKER));
158 assert!(output.contains("Rust Diff Analysis"));
159 assert!(output.contains("Production"));
160 assert!(output.contains("Test"));
161 }
162
163 #[test]
164 fn test_format_comment_with_exceeded_limit() {
165 let summary = Summary {
166 exceeds_limit: true,
167 ..Default::default()
168 };
169 let result = AnalysisResult::new(vec![], summary);
170 let config = Config::default();
171 let output = format_comment(&result, &config);
172
173 assert!(output.contains("❌"));
174 }
175
176 #[test]
177 fn test_get_comment_marker() {
178 let marker = get_comment_marker();
179 assert!(marker.contains("rust-diff-analyzer"));
180 }
181}