1use async_trait::async_trait;
2use kowalski_core::error::KowalskiError;
3use kowalski_core::tools::{Tool, ToolInput, ToolOutput, ToolParameter};
4use serde_json::json;
5use std::collections::HashMap;
6
7pub struct JavaAnalysisTool;
9
10impl Default for JavaAnalysisTool {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl JavaAnalysisTool {
17 pub fn new() -> Self {
18 Self
19 }
20
21 fn analyze_java(&self, code: &str) -> Result<serde_json::Value, KowalskiError> {
22 let mut analysis = HashMap::new();
23
24 let lines = code.lines().count();
26 let characters = code.chars().count();
27 let words = code.split_whitespace().count();
28
29 let classes = code.matches("class ").count();
31 let methods = code.matches("public ").count()
32 + code.matches("private ").count()
33 + code.matches("protected ").count();
34 let imports = code.matches("import ").count();
35 let comments = code.matches("//").count() + code.matches("/*").count();
36
37 let complexity = self.calculate_complexity(code);
39
40 analysis.insert(
41 "metrics".to_string(),
42 json!({
43 "lines": lines,
44 "characters": characters,
45 "words": words,
46 "classes": classes,
47 "methods": methods,
48 "imports": imports,
49 "comments": comments,
50 "complexity": complexity
51 }),
52 );
53
54 let suggestions = self.generate_suggestions(code);
56 analysis.insert("suggestions".to_string(), json!(suggestions));
57
58 let syntax_errors = self.check_syntax(code);
60 analysis.insert("syntax_errors".to_string(), json!(syntax_errors));
61
62 Ok(json!(analysis))
63 }
64
65 fn calculate_complexity(&self, code: &str) -> serde_json::Value {
66 let mut complexity = HashMap::new();
67
68 let if_statements = code.matches("if ").count();
70 let for_loops = code.matches("for ").count();
71 let while_loops = code.matches("while ").count();
72 let switch_statements = code.matches("switch ").count();
73
74 let cyclomatic_complexity = 1 + if_statements + for_loops + while_loops + switch_statements;
75
76 complexity.insert(
77 "cyclomatic_complexity".to_string(),
78 json!(cyclomatic_complexity),
79 );
80 complexity.insert("if_statements".to_string(), json!(if_statements));
81 complexity.insert("for_loops".to_string(), json!(for_loops));
82 complexity.insert("while_loops".to_string(), json!(while_loops));
83 complexity.insert("switch_statements".to_string(), json!(switch_statements));
84
85 let level = if cyclomatic_complexity <= 5 {
87 "Low"
88 } else if cyclomatic_complexity <= 10 {
89 "Medium"
90 } else {
91 "High"
92 };
93 complexity.insert("level".to_string(), json!(level));
94
95 json!(complexity)
96 }
97
98 fn generate_suggestions(&self, code: &str) -> Vec<String> {
99 let mut suggestions = Vec::new();
100
101 if code.matches("System.out.println").count() > 0 {
103 suggestions.push(
104 "Consider using a proper logging framework instead of System.out.println"
105 .to_string(),
106 );
107 }
108
109 if code.matches("public static void main").count() > 0 {
110 suggestions.push("Main method found - ensure proper exception handling".to_string());
111 }
112
113 if code.matches("new ArrayList()").count() > 0 {
114 suggestions.push(
115 "Consider specifying initial capacity for ArrayList for better performance"
116 .to_string(),
117 );
118 }
119
120 if code.matches("catch (Exception e)").count() > 0 {
121 suggestions.push(
122 "Consider catching specific exceptions instead of generic Exception".to_string(),
123 );
124 }
125
126 suggestions
127 }
128
129 fn check_syntax(&self, code: &str) -> Vec<String> {
130 let mut errors = Vec::new();
131
132 let open_braces = code.matches('{').count();
134 let close_braces = code.matches('}').count();
135 if open_braces != close_braces {
136 errors.push("Mismatched braces detected".to_string());
137 }
138
139 let open_parens = code.matches('(').count();
140 let close_parens = code.matches(')').count();
141 if open_parens != close_parens {
142 errors.push("Mismatched parentheses detected".to_string());
143 }
144
145 let lines = code.lines().collect::<Vec<_>>();
147 for (i, line) in lines.iter().enumerate() {
148 let trimmed = line.trim();
149 if !trimmed.is_empty()
150 && !trimmed.ends_with(';')
151 && !trimmed.ends_with('{')
152 && !trimmed.ends_with('}')
153 && !trimmed.starts_with("//")
154 && !trimmed.starts_with("/*")
155 && !trimmed.starts_with("import ")
156 && !trimmed.starts_with("package ")
157 && !trimmed.starts_with("public ")
158 && !trimmed.starts_with("private ")
159 && !trimmed.starts_with("protected ")
160 && !trimmed.starts_with("class ")
161 && !trimmed.starts_with("interface ")
162 && !trimmed.starts_with("enum ")
163 {
164 errors.push(format!("Line {}: Possible missing semicolon", i + 1));
165 }
166 }
167
168 errors
169 }
170}
171
172#[async_trait]
173impl Tool for JavaAnalysisTool {
174 async fn execute(&mut self, input: ToolInput) -> Result<ToolOutput, KowalskiError> {
175 match input.task_type.as_str() {
176 "analyze_java" => {
177 let result = self.analyze_java(&input.content)?;
178 Ok(ToolOutput::new(
179 result,
180 Some(json!({
181 "tool": "java_analysis",
182 "language": "java"
183 })),
184 ))
185 }
186 _ => Err(KowalskiError::ToolExecution(format!(
187 "Unsupported task type: {}",
188 input.task_type
189 ))),
190 }
191 }
192
193 fn name(&self) -> &str {
194 "java_analysis"
195 }
196
197 fn description(&self) -> &str {
198 "A tool for analyzing Java code, providing metrics, complexity analysis, and code quality suggestions"
199 }
200
201 fn parameters(&self) -> Vec<ToolParameter> {
202 vec![ToolParameter {
203 name: "content".to_string(),
204 description: "Java code to analyze".to_string(),
205 required: true,
206 default_value: None,
207 parameter_type: kowalski_core::tools::ParameterType::String,
208 }]
209 }
210}
211
212pub struct PythonAnalysisTool;
214
215impl Default for PythonAnalysisTool {
216 fn default() -> Self {
217 Self::new()
218 }
219}
220
221impl PythonAnalysisTool {
222 pub fn new() -> Self {
223 Self
224 }
225
226 fn analyze_python(&self, code: &str) -> Result<serde_json::Value, KowalskiError> {
227 let mut analysis = HashMap::new();
228
229 let lines = code.lines().count();
231 let characters = code.chars().count();
232 let words = code.split_whitespace().count();
233
234 let functions = code.matches("def ").count();
236 let classes = code.matches("class ").count();
237 let imports = code.matches("import ").count() + code.matches("from ").count();
238 let comments = code.matches("#").count();
239
240 let complexity = self.calculate_complexity(code);
242
243 analysis.insert(
244 "metrics".to_string(),
245 json!({
246 "lines": lines,
247 "characters": characters,
248 "words": words,
249 "functions": functions,
250 "classes": classes,
251 "imports": imports,
252 "comments": comments,
253 "complexity": complexity
254 }),
255 );
256
257 let suggestions = self.generate_suggestions(code);
259 analysis.insert("suggestions".to_string(), json!(suggestions));
260
261 let pep8_issues = self.check_pep8(code);
263 analysis.insert("pep8_issues".to_string(), json!(pep8_issues));
264
265 Ok(json!(analysis))
266 }
267
268 fn calculate_complexity(&self, code: &str) -> serde_json::Value {
269 let mut complexity = HashMap::new();
270
271 let if_statements = code.matches("if ").count();
273 let for_loops = code.matches("for ").count();
274 let while_loops = code.matches("while ").count();
275 let try_blocks = code.matches("try:").count();
276
277 let cyclomatic_complexity = 1 + if_statements + for_loops + while_loops + try_blocks;
278
279 complexity.insert(
280 "cyclomatic_complexity".to_string(),
281 json!(cyclomatic_complexity),
282 );
283 complexity.insert("if_statements".to_string(), json!(if_statements));
284 complexity.insert("for_loops".to_string(), json!(for_loops));
285 complexity.insert("while_loops".to_string(), json!(while_loops));
286 complexity.insert("try_blocks".to_string(), json!(try_blocks));
287
288 let level = if cyclomatic_complexity <= 5 {
290 "Low"
291 } else if cyclomatic_complexity <= 10 {
292 "Medium"
293 } else {
294 "High"
295 };
296 complexity.insert("level".to_string(), json!(level));
297
298 json!(complexity)
299 }
300
301 fn generate_suggestions(&self, code: &str) -> Vec<String> {
302 let mut suggestions = Vec::new();
303
304 if code.matches("print(").count() > 0 {
306 suggestions.push("Consider using logging instead of print statements".to_string());
307 }
308
309 if code.matches("except:").count() > 0 {
310 suggestions.push("Avoid bare except clauses - specify exception types".to_string());
311 }
312
313 if code.matches("import *").count() > 0 {
314 suggestions.push("Avoid wildcard imports - import specific modules".to_string());
315 }
316
317 if code.matches("global ").count() > 0 {
318 suggestions.push(
319 "Consider avoiding global variables - use function parameters or class attributes"
320 .to_string(),
321 );
322 }
323
324 suggestions
325 }
326
327 fn check_pep8(&self, code: &str) -> Vec<String> {
328 let mut issues = Vec::new();
329
330 for (i, line) in code.lines().enumerate() {
332 if line.len() > 79 {
333 issues.push(format!(
334 "Line {}: Line too long ({} characters)",
335 i + 1,
336 line.len()
337 ));
338 }
339 }
340
341 for (i, line) in code.lines().enumerate() {
343 if !line.is_empty() && !line.starts_with('#') {
344 let indent = line.chars().take_while(|&c| c == ' ').count();
345 if indent % 4 != 0 {
346 issues.push(format!(
347 "Line {}: Indentation should be multiple of 4 spaces",
348 i + 1
349 ));
350 }
351 }
352 }
353
354 for (i, line) in code.lines().enumerate() {
356 if line.ends_with(' ') {
357 issues.push(format!("Line {}: Trailing whitespace", i + 1));
358 }
359 }
360
361 issues
362 }
363}
364
365#[async_trait]
366impl Tool for PythonAnalysisTool {
367 async fn execute(&mut self, input: ToolInput) -> Result<ToolOutput, KowalskiError> {
368 match input.task_type.as_str() {
369 "analyze_python" => {
370 let result = self.analyze_python(&input.content)?;
371 Ok(ToolOutput::new(
372 result,
373 Some(json!({
374 "tool": "python_analysis",
375 "language": "python"
376 })),
377 ))
378 }
379 _ => Err(KowalskiError::ToolExecution(format!(
380 "Unsupported task type: {}",
381 input.task_type
382 ))),
383 }
384 }
385
386 fn name(&self) -> &str {
387 "python_analysis"
388 }
389
390 fn description(&self) -> &str {
391 "A tool for analyzing Python code, providing metrics, complexity analysis, PEP 8 compliance, and code quality suggestions"
392 }
393
394 fn parameters(&self) -> Vec<ToolParameter> {
395 vec![ToolParameter {
396 name: "content".to_string(),
397 description: "Python code to analyze".to_string(),
398 required: true,
399 default_value: None,
400 parameter_type: kowalski_core::tools::ParameterType::String,
401 }]
402 }
403}
404
405pub struct RustAnalysisTool;
407
408impl Default for RustAnalysisTool {
409 fn default() -> Self {
410 Self::new()
411 }
412}
413
414impl RustAnalysisTool {
415 pub fn new() -> Self {
416 Self
417 }
418
419 fn analyze_rust(&self, code: &str) -> Result<serde_json::Value, KowalskiError> {
420 let mut analysis = HashMap::new();
421
422 let lines = code.lines().count();
424 let characters = code.chars().count();
425 let words = code.split_whitespace().count();
426
427 let functions = code.matches("fn ").count();
429 let structs = code.matches("struct ").count();
430 let enums = code.matches("enum ").count();
431 let traits = code.matches("trait ").count();
432 let modules = code.matches("mod ").count();
433 let comments = code.matches("//").count() + code.matches("/*").count();
434
435 let complexity = self.calculate_complexity(code);
437
438 analysis.insert(
439 "metrics".to_string(),
440 json!({
441 "lines": lines,
442 "characters": characters,
443 "words": words,
444 "functions": functions,
445 "structs": structs,
446 "enums": enums,
447 "traits": traits,
448 "modules": modules,
449 "comments": comments,
450 "complexity": complexity
451 }),
452 );
453
454 let suggestions = self.generate_suggestions(code);
456 analysis.insert("suggestions".to_string(), json!(suggestions));
457
458 let rust_issues = self.check_rust_specific(code);
460 analysis.insert("rust_issues".to_string(), json!(rust_issues));
461
462 Ok(json!(analysis))
463 }
464
465 fn calculate_complexity(&self, code: &str) -> serde_json::Value {
466 let mut complexity = HashMap::new();
467
468 let if_statements = code.matches("if ").count();
470 let for_loops = code.matches("for ").count();
471 let while_loops = code.matches("while ").count();
472 let match_statements = code.matches("match ").count();
473 let let_statements = code.matches("let ").count();
474
475 let cyclomatic_complexity = 1 + if_statements + for_loops + while_loops + match_statements;
476
477 complexity.insert(
478 "cyclomatic_complexity".to_string(),
479 json!(cyclomatic_complexity),
480 );
481 complexity.insert("if_statements".to_string(), json!(if_statements));
482 complexity.insert("for_loops".to_string(), json!(for_loops));
483 complexity.insert("while_loops".to_string(), json!(while_loops));
484 complexity.insert("match_statements".to_string(), json!(match_statements));
485 complexity.insert("let_statements".to_string(), json!(let_statements));
486
487 let level = if cyclomatic_complexity <= 5 {
489 "Low"
490 } else if cyclomatic_complexity <= 10 {
491 "Medium"
492 } else {
493 "High"
494 };
495 complexity.insert("level".to_string(), json!(level));
496
497 json!(complexity)
498 }
499
500 fn generate_suggestions(&self, code: &str) -> Vec<String> {
501 let mut suggestions = Vec::new();
502
503 if code.matches("println!").count() > 0 {
505 suggestions
506 .push("Consider using a proper logging framework instead of println!".to_string());
507 }
508
509 if code.matches("unwrap()").count() > 0 {
510 suggestions
511 .push("Consider using proper error handling instead of unwrap()".to_string());
512 }
513
514 if code.matches("clone()").count() > 0 {
515 suggestions.push("Consider if clone() is necessary - Rust's ownership system might provide alternatives".to_string());
516 }
517
518 if code.matches("unsafe ").count() > 0 {
519 suggestions.push(
520 "Unsafe code detected - ensure it's properly documented and necessary".to_string(),
521 );
522 }
523
524 suggestions
525 }
526
527 fn check_rust_specific(&self, code: &str) -> Vec<String> {
528 let mut issues = Vec::new();
529
530 let lines = code.lines().collect::<Vec<_>>();
532 for (i, line) in lines.iter().enumerate() {
533 let trimmed = line.trim();
534 if !trimmed.is_empty()
535 && !trimmed.ends_with(';')
536 && !trimmed.ends_with('{')
537 && !trimmed.ends_with('}')
538 && !trimmed.starts_with("//")
539 && !trimmed.starts_with("/*")
540 && !trimmed.starts_with("use ")
541 && !trimmed.starts_with("mod ")
542 && !trimmed.starts_with("pub ")
543 && !trimmed.starts_with("fn ")
544 && !trimmed.starts_with("struct ")
545 && !trimmed.starts_with("enum ")
546 && !trimmed.starts_with("trait ")
547 && !trimmed.starts_with("impl ")
548 && !trimmed.starts_with("let ")
549 && !trimmed.starts_with("if ")
550 && !trimmed.starts_with("for ")
551 && !trimmed.starts_with("while ")
552 && !trimmed.starts_with("match ")
553 && !trimmed.starts_with("return ")
554 {
555 issues.push(format!("Line {}: Possible missing semicolon", i + 1));
556 }
557 }
558
559 if code.matches("unwrap()").count() > 0 {
561 issues.push("Found unwrap() calls - consider using proper error handling".to_string());
562 }
563
564 if code.matches("unsafe ").count() > 0 {
566 issues.push("Unsafe code detected - review for necessity and safety".to_string());
567 }
568
569 issues
570 }
571}
572
573#[async_trait]
574impl Tool for RustAnalysisTool {
575 async fn execute(&mut self, input: ToolInput) -> Result<ToolOutput, KowalskiError> {
576 match input.task_type.as_str() {
577 "analyze_rust" => {
578 let result = self.analyze_rust(&input.content)?;
579 Ok(ToolOutput::new(
580 result,
581 Some(json!({
582 "tool": "rust_analysis",
583 "language": "rust"
584 })),
585 ))
586 }
587 _ => Err(KowalskiError::ToolExecution(format!(
588 "Unsupported task type: {}",
589 input.task_type
590 ))),
591 }
592 }
593
594 fn name(&self) -> &str {
595 "rust_analysis"
596 }
597
598 fn description(&self) -> &str {
599 "A tool for analyzing Rust code, providing metrics, complexity analysis, and Rust-specific code quality suggestions"
600 }
601
602 fn parameters(&self) -> Vec<ToolParameter> {
603 vec![ToolParameter {
604 name: "content".to_string(),
605 description: "Rust code to analyze".to_string(),
606 required: true,
607 default_value: None,
608 parameter_type: kowalski_core::tools::ParameterType::String,
609 }]
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[tokio::test]
618 async fn test_java_analysis_tool() {
619 let tool = JavaAnalysisTool::new();
620 assert_eq!(tool.name(), "java_analysis");
621 }
622
623 #[tokio::test]
624 async fn test_python_analysis_tool() {
625 let tool = PythonAnalysisTool::new();
626 assert_eq!(tool.name(), "python_analysis");
627 }
628
629 #[tokio::test]
630 async fn test_rust_analysis_tool() {
631 let tool = RustAnalysisTool::new();
632 assert_eq!(tool.name(), "rust_analysis");
633 }
634}