1use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
16pub struct CompileMetrics {
17 total_attempts: u64,
19
20 successes: u64,
22
23 failures: u64,
25
26 error_counts: HashMap<String, u64>,
28}
29
30impl CompileMetrics {
31 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn record_success(&mut self) {
38 self.total_attempts += 1;
39 self.successes += 1;
40 }
41
42 pub fn record_failure(&mut self, error_message: &str) {
46 self.total_attempts += 1;
47 self.failures += 1;
48
49 let error_code = extract_error_code(error_message);
51 *self.error_counts.entry(error_code).or_insert(0) += 1;
52 }
53
54 pub fn total_attempts(&self) -> u64 {
56 self.total_attempts
57 }
58
59 pub fn successes(&self) -> u64 {
61 self.successes
62 }
63
64 pub fn failures(&self) -> u64 {
66 self.failures
67 }
68
69 pub fn success_rate(&self) -> f64 {
71 if self.total_attempts == 0 {
72 0.0
73 } else {
74 self.successes as f64 / self.total_attempts as f64
75 }
76 }
77
78 pub fn meets_target(&self, target: f64) -> bool {
83 self.success_rate() >= target
84 }
85
86 pub fn error_histogram(&self) -> &HashMap<String, u64> {
88 &self.error_counts
89 }
90
91 pub fn reset(&mut self) {
93 self.total_attempts = 0;
94 self.successes = 0;
95 self.failures = 0;
96 self.error_counts.clear();
97 }
98
99 pub fn to_markdown(&self) -> String {
101 let rate_pct = self.success_rate() * 100.0;
102 let target_status = if self.meets_target(0.80) {
103 "✅ PASS"
104 } else {
105 "❌ FAIL"
106 };
107
108 let mut report = format!(
109 "## Compile Success Rate Metrics\n\n\
110 | Metric | Value |\n\
111 |--------|-------|\n\
112 | Total Attempts | {} |\n\
113 | Successes | {} |\n\
114 | Failures | {} |\n\
115 | Success Rate | {:.1}% |\n\
116 | Target (80%) | {} |\n",
117 self.total_attempts, self.successes, self.failures, rate_pct, target_status
118 );
119
120 if !self.error_counts.is_empty() {
121 report.push_str("\n### Error Breakdown\n\n");
122 report.push_str("| Error Code | Count |\n");
123 report.push_str("|------------|-------|\n");
124
125 let mut sorted_errors: Vec<_> = self.error_counts.iter().collect();
126 sorted_errors.sort_by(|a, b| b.1.cmp(a.1));
127
128 for (code, count) in sorted_errors {
129 report.push_str(&format!("| {} | {} |\n", code, count));
130 }
131 }
132
133 report
134 }
135
136 pub fn to_json(&self) -> String {
138 serde_json::to_string_pretty(self).unwrap_or_else(|_| "{}".to_string())
139 }
140}
141
142fn extract_error_code(message: &str) -> String {
149 if let Some(start) = message.find('E') {
151 let rest = &message[start..];
152 if rest.len() >= 5 && rest[1..5].chars().all(|c| c.is_ascii_digit()) {
153 return rest[..5].to_string();
154 }
155 }
156
157 if let Some(bracket_start) = message.find("[E") {
159 let rest = &message[bracket_start + 1..];
160 if let Some(bracket_end) = rest.find(']') {
161 return rest[..bracket_end].to_string();
162 }
163 }
164
165 "UNKNOWN".to_string()
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct TranspilationResult {
171 pub rust_code: String,
173
174 pub compiles: bool,
176
177 pub errors: Vec<String>,
179
180 pub warnings: usize,
182}
183
184impl TranspilationResult {
185 pub fn success(rust_code: String) -> Self {
187 Self {
188 rust_code,
189 compiles: true,
190 errors: Vec::new(),
191 warnings: 0,
192 }
193 }
194
195 pub fn failure(rust_code: String, errors: Vec<String>) -> Self {
197 Self {
198 rust_code,
199 compiles: false,
200 errors,
201 warnings: 0,
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_extract_error_code_standard() {
212 assert_eq!(extract_error_code("E0308: mismatched types"), "E0308");
213 }
214
215 #[test]
216 fn test_extract_error_code_with_brackets() {
217 assert_eq!(extract_error_code("error[E0502]: cannot borrow"), "E0502");
218 }
219
220 #[test]
221 fn test_extract_error_code_unknown() {
222 assert_eq!(extract_error_code("some random error"), "UNKNOWN");
223 }
224
225 #[test]
226 fn test_extract_error_code_partial() {
227 assert_eq!(extract_error_code("E03"), "UNKNOWN");
228 }
229}