1use super::Personality;
4use crate::analyzer::CodeIssue;
5
6pub fn analyze(issues: &[CodeIssue]) -> Personality {
8 let total = issues.len() as f64;
9
10 if total == 0.0 {
11 return Personality {
12 title: "The Perfectionist",
13 emoji: "\u{1f45f}",
14 traits: vec![
15 "No issues detected — suspiciously clean code",
16 "Probably over-engineers everything",
17 "Definitely has a linter on save",
18 "Has never shipped a bug (or a feature on time)",
19 ],
20 advice: vec![
21 "Ship something imperfect once in a while",
22 "Your code is great but your deadlines are crying",
23 "Perfect is the enemy of shipped",
24 ],
25 score: 100.0,
26 };
27 }
28
29 let mut unwrap_count = 0u32;
31 let mut naming_count = 0u32;
32 let mut nesting_count = 0u32;
33 let mut long_fn_count = 0u32;
34 let mut magic_count = 0u32;
35 let mut dup_count = 0u32;
36
37 for issue in issues {
38 let rule = issue.rule_name.to_lowercase();
39 if rule.contains("unwrap") {
40 unwrap_count += 1;
41 } else if rule.contains("name")
42 || rule.contains("single_letter")
43 || rule.contains("meaningless")
44 {
45 naming_count += 1;
46 } else if rule.contains("nest") || rule.contains("complex") {
47 nesting_count += 1;
48 } else if rule.contains("long") || rule.contains("function_length") {
49 long_fn_count += 1;
50 } else if rule.contains("magic") {
51 magic_count += 1;
52 } else if rule.contains("duplicat") {
53 dup_count += 1;
54 }
55 }
56
57 let counts = [
59 (unwrap_count, "unwrap"),
60 (naming_count, "naming"),
61 (nesting_count, "nesting"),
62 (long_fn_count, "long_fn"),
63 (magic_count, "magic"),
64 (dup_count, "dup"),
65 ];
66
67 let dominant = counts
68 .iter()
69 .max_by_key(|(c, _)| *c)
70 .unwrap_or(&(0, "none"));
71
72 match dominant.1 {
73 "unwrap" => panic_personality(unwrap_count, total),
74 "naming" => naming_personality(naming_count, total),
75 "nesting" => nesting_personality(nesting_count, total),
76 "long_fn" => long_fn_personality(long_fn_count, total),
77 "magic" => magic_personality(magic_count, total),
78 "dup" => dup_personality(dup_count, total),
79 _ => balanced_personality(total),
80 }
81}
82
83fn panic_personality(count: u32, _total: f64) -> Personality {
84 Personality {
85 title: "The Optimist",
86 emoji: "\u{1f60f}",
87 traits: vec![
88 "Believes the world is full of happy paths",
89 "unwrap() is your safety blanket",
90 "Error handling is someone else's problem",
91 "Probably says 'it works on my machine' a lot",
92 "Treats panics as 'unexpected features'",
93 ],
94 advice: vec![
95 "Learn Result<T, E> — your future self will thank you",
96 "Every unwrap() is a potential production incident",
97 "Try `.unwrap_or_default()` at minimum",
98 "Use `?` operator to propagate errors gracefully",
99 ],
100 score: (100.0 - count as f64 * 3.0).max(0.0),
101 }
102}
103
104fn naming_personality(count: u32, _total: f64) -> Personality {
105 Personality {
106 title: "The Minimalist",
107 emoji: "\u{270d}\u{fe0f}",
108 traits: vec![
109 "Why use many word when few letter do trick",
110 "Variables named like chess coordinates",
111 "Your code reads like a math textbook",
112 "Comments explain what x, y, z mean",
113 "Considers 'data' a descriptive name",
114 ],
115 advice: vec![
116 "Descriptive names are not a luxury",
117 "Your IDE has autocomplete — use it",
118 "Future you won't remember what `d` meant",
119 "A good variable name eliminates the need for a comment",
120 ],
121 score: (100.0 - count as f64 * 2.0).max(0.0),
122 }
123}
124
125fn nesting_personality(count: u32, _total: f64) -> Personality {
126 Personality {
127 title: "The Architect",
128 emoji: "\u{1f3d7}\u{fe0f}",
129 traits: vec![
130 "Loves building pyramids of doom",
131 "Indentation is a competitive sport",
132 "Each function is a journey through layers",
133 "Probably dreams in nested brackets",
134 "Thinks 'flat is justice' only applies to anime",
135 ],
136 advice: vec![
137 "Extract inner logic into helper functions",
138 "Use early returns to reduce nesting",
139 "Consider the 'guard clause' pattern",
140 "If you need 4+ levels of nesting, the logic needs refactoring",
141 ],
142 score: (100.0 - count as f64 * 4.0).max(0.0),
143 }
144}
145
146fn long_fn_personality(count: u32, _total: f64) -> Personality {
147 Personality {
148 title: "The Storyteller",
149 emoji: "\u{1f4dd}",
150 traits: vec![
151 "Every function tells a complete story",
152 "Believes in 'single responsibility' — for files, not functions",
153 "Your scroll wheel gets a workout",
154 "Probably writes long commit messages too",
155 "Considers 200 lines a 'concise' function",
156 ],
157 advice: vec![
158 "If a function needs a comment to explain its sections, split it",
159 "Aim for functions that fit on one screen",
160 "The Single Responsibility Principle applies to functions too",
161 "Break complex logic into smaller, testable units",
162 ],
163 score: (100.0 - count as f64 * 3.0).max(0.0),
164 }
165}
166
167fn magic_personality(count: u32, _total: f64) -> Personality {
168 Personality {
169 title: "The Sorcerer",
170 emoji: "\u{1f9d9}",
171 traits: vec![
172 "Numbers have meaning — only to you",
173 "42 appears in your code more than in Hitchhiker's Guide",
174 "Constants are for the weak",
175 "Your code has its own secret numerology",
176 "Believes named constants are 'over-engineering'",
177 ],
178 advice: vec![
179 "Extract magic numbers into named constants",
180 "Your future self won't remember what 86400 means",
181 "Use enums or constants for repeated values",
182 "If a number appears twice, it needs a name",
183 ],
184 score: (100.0 - count as f64 * 2.0).max(0.0),
185 }
186}
187
188fn dup_personality(count: u32, _total: f64) -> Personality {
189 Personality {
190 title: "The Copy-Paste Artist",
191 emoji: "\u{1f4cb}",
192 traits: vec![
193 "Ctrl+C, Ctrl+V is your IDE's most used shortcut",
194 "Why abstract when you can duplicate",
195 "Same bug in 5 places = 5x the debugging fun",
196 "DRY stands for 'Don't Repeat... wait, too late'",
197 "Thinks 'reusable code' means copying it again",
198 ],
199 advice: vec![
200 "Extract common code into shared functions",
201 "One bug fix should fix it everywhere",
202 "Consider a utility module for repeated patterns",
203 "If you're copying code, you're copying bugs too",
204 ],
205 score: (100.0 - count as f64 * 3.0).max(0.0),
206 }
207}
208
209fn balanced_personality(total: f64) -> Personality {
210 Personality {
211 title: "The Pragmatist",
212 emoji: "\u{2696}\u{fe0f}",
213 traits: vec![
214 "A balanced mix of code smells",
215 "Not great at anything, not terrible at anything",
216 "The 'average developer' experience",
217 "Your code has character — like a diverse zoo",
218 "Jack of all trades, master of technical debt",
219 ],
220 advice: vec![
221 "Pick one area to improve at a time",
222 "Focus on the highest-severity issues first",
223 "Consistency is better than perfection",
224 "Tackle your highest-count issue category first",
225 ],
226 score: (100.0 - total * 1.5).max(0.0),
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use std::path::PathBuf;
234
235 fn make_issue(rule: &str) -> CodeIssue {
236 CodeIssue {
237 file_path: PathBuf::from("test.rs"),
238 line: 1,
239 column: 0,
240 rule_name: rule.to_string(),
241 message: "test".to_string(),
242 severity: crate::analyzer::Severity::Spicy,
243 }
244 }
245
246 #[test]
247 fn test_empty_issues() {
248 let p = analyze(&[]);
249 assert_eq!(p.title, "The Perfectionist");
250 }
251
252 #[test]
253 fn test_unwrap_dominant() {
254 let issues = vec![
255 make_issue("unwrap_abuse"),
256 make_issue("unwrap_abuse"),
257 make_issue("unwrap_abuse"),
258 ];
259 let p = analyze(&issues);
260 assert_eq!(p.title, "The Optimist");
261 }
262
263 #[test]
264 fn test_naming_dominant() {
265 let issues = vec![
266 make_issue("single_letter_variable"),
267 make_issue("meaningless_name"),
268 ];
269 let p = analyze(&issues);
270 assert_eq!(p.title, "The Minimalist");
271 }
272
273 #[test]
274 fn test_nesting_dominant() {
275 let issues = vec![
276 make_issue("deep_nesting"),
277 make_issue("complex_function"),
278 make_issue("high_complexity"),
279 ];
280 let p = analyze(&issues);
281 assert_eq!(p.title, "The Architect");
282 }
283
284 #[test]
285 fn test_long_fn_dominant() {
286 let issues = vec![make_issue("long_function"), make_issue("function_length")];
287 let p = analyze(&issues);
288 assert_eq!(p.title, "The Storyteller");
289 }
290
291 #[test]
292 fn test_magic_dominant() {
293 let issues = vec![make_issue("magic_number"), make_issue("magic_number")];
294 let p = analyze(&issues);
295 assert_eq!(p.title, "The Sorcerer");
296 }
297
298 #[test]
299 fn test_dup_dominant() {
300 let issues = vec![
301 make_issue("code_duplication"),
302 make_issue("code_duplication"),
303 make_issue("code_duplication"),
304 ];
305 let p = analyze(&issues);
306 assert_eq!(p.title, "The Copy-Paste Artist");
307 }
308
309 #[test]
310 fn test_balanced_mixed() {
311 let issues = vec![
312 make_issue("unwrap_abuse"),
313 make_issue("single_letter_variable"),
314 make_issue("deep_nesting"),
315 ];
316 let p = analyze(&issues);
317 assert!(!p.title.is_empty());
319 assert!(p.score > 0.0);
320 }
321}