scribe_analysis/language_support/
documentation_analysis.rs1use serde::{Deserialize, Serialize};
7use scribe_core::Result;
8use super::ast_language::AstLanguage;
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.iter()
68 .filter(|line| self.is_documentation_line(line))
69 .count()
70 }
71
72 fn is_documentation_line(&self, line: &str) -> bool {
74 let trimmed = line.trim();
75
76 match self.language {
77 AstLanguage::Python => {
78 trimmed.starts_with('#') ||
79 trimmed.starts_with("\"\"\"") ||
80 trimmed.starts_with("'''")
81 }
82 AstLanguage::JavaScript | AstLanguage::TypeScript => {
83 trimmed.starts_with("//") ||
84 trimmed.starts_with("/*") ||
85 trimmed.starts_with("*") ||
86 trimmed.starts_with("/**")
87 }
88 AstLanguage::Rust => {
89 trimmed.starts_with("//") ||
90 trimmed.starts_with("///") ||
91 trimmed.starts_with("//!")
92 }
93 AstLanguage::Go => {
94 trimmed.starts_with("//")
95 }
96 _ => {
97 trimmed.starts_with("//") ||
99 trimmed.starts_with("#") ||
100 trimmed.starts_with("/*") ||
101 trimmed.starts_with("*")
102 }
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_python_documentation_analysis() {
113 let analyzer = DocumentationAnalyzer::new(AstLanguage::Python).unwrap();
114 let python_code = r#"
115# This is a comment
116def hello():
117 """This is a docstring."""
118 print("Hello")
119
120def world():
121 # Another comment
122 print("World")
123"#;
124
125 let coverage = analyzer.analyze_coverage(python_code).unwrap();
126 assert!(coverage.documentation_lines > 0);
127 assert!(coverage.coverage_percentage > 0.0);
128 }
129}