scribe_analysis/language_support/
documentation_analysis.rs1use super::ast_language::AstLanguage;
7use scribe_core::Result;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct DocumentationCoverage {
13 pub total_functions: usize,
15 pub documented_functions: usize,
17 pub total_classes: usize,
19 pub documented_classes: usize,
21 pub coverage_percentage: f64,
23 pub documentation_lines: usize,
25 pub total_lines: usize,
27}
28
29#[derive(Debug)]
31pub struct DocumentationAnalyzer {
32 language: AstLanguage,
33}
34
35impl DocumentationAnalyzer {
36 pub fn new(language: AstLanguage) -> Result<Self> {
38 Ok(Self { language })
39 }
40
41 pub fn analyze_coverage(&self, content: &str) -> Result<DocumentationCoverage> {
43 let lines: Vec<&str> = content.lines().collect();
45 let total_lines = lines.len();
46 let documentation_lines = self.count_documentation_lines(&lines);
47
48 let coverage_percentage = if total_lines > 0 {
49 (documentation_lines as f64 / total_lines as f64) * 100.0
50 } else {
51 0.0
52 };
53
54 Ok(DocumentationCoverage {
55 total_functions: 0, documented_functions: 0, total_classes: 0, documented_classes: 0, coverage_percentage,
60 documentation_lines,
61 total_lines,
62 })
63 }
64
65 fn count_documentation_lines(&self, lines: &[&str]) -> usize {
67 lines
68 .iter()
69 .filter(|line| self.is_documentation_line(line))
70 .count()
71 }
72
73 fn is_documentation_line(&self, line: &str) -> bool {
75 let trimmed = line.trim();
76
77 match self.language {
78 AstLanguage::Python => {
79 trimmed.starts_with('#')
80 || trimmed.starts_with("\"\"\"")
81 || trimmed.starts_with("'''")
82 }
83 AstLanguage::JavaScript | AstLanguage::TypeScript => {
84 trimmed.starts_with("//")
85 || trimmed.starts_with("/*")
86 || trimmed.starts_with("*")
87 || trimmed.starts_with("/**")
88 }
89 AstLanguage::Rust => {
90 trimmed.starts_with("//")
91 || trimmed.starts_with("///")
92 || trimmed.starts_with("//!")
93 }
94 AstLanguage::Go => trimmed.starts_with("//"),
95 _ => {
96 trimmed.starts_with("//")
98 || trimmed.starts_with("#")
99 || trimmed.starts_with("/*")
100 || trimmed.starts_with("*")
101 }
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn test_python_documentation_analysis() {
112 let analyzer = DocumentationAnalyzer::new(AstLanguage::Python).unwrap();
113 let python_code = r#"
114# This is a comment
115def hello():
116 """This is a docstring."""
117 print("Hello")
118
119def world():
120 # Another comment
121 print("World")
122"#;
123
124 let coverage = analyzer.analyze_coverage(python_code).unwrap();
125 assert!(coverage.documentation_lines > 0);
126 assert!(coverage.coverage_percentage > 0.0);
127 }
128}