Skip to main content

sofie_check/
density.rs

1use std::fmt;
2use std::ops::Add;
3
4use crate::lines::LineKind;
5use crate::workspace::{CrateSource, SourceFile};
6
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
8pub struct CommentDensity {
9    pub code_lines: usize,
10    pub comment_lines: usize,
11}
12
13impl CommentDensity {
14    #[must_use]
15    pub fn of_file(file: &SourceFile) -> Self {
16        let count = |wanted: LineKind| file.lines.iter().filter(|kind| **kind == wanted).count();
17        Self {
18            code_lines: count(LineKind::Code),
19            comment_lines: count(LineKind::Comment),
20        }
21    }
22
23    #[must_use]
24    pub fn of_crate(crate_source: &CrateSource) -> Self {
25        crate_source
26            .files
27            .iter()
28            .map(Self::of_file)
29            .fold(Self::default(), Add::add)
30    }
31
32    #[must_use]
33    pub fn within_limit(self) -> bool {
34        self.comment_lines <= crate::structure::FREE_COMMENT_LINES
35            || self.ratio() <= crate::structure::MAX_COMMENT_RATIO
36    }
37
38    #[must_use]
39    pub fn ratio(self) -> f64 {
40        if self.code_lines == 0 {
41            return 0.0;
42        }
43        crate::count_as_f64(self.comment_lines) / crate::count_as_f64(self.code_lines)
44    }
45}
46
47impl Add for CommentDensity {
48    type Output = Self;
49
50    fn add(self, other: Self) -> Self {
51        Self {
52            code_lines: self.code_lines + other.code_lines,
53            comment_lines: self.comment_lines + other.comment_lines,
54        }
55    }
56}
57
58impl fmt::Display for CommentDensity {
59    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(
61            formatter,
62            "ratio {:.3} ({} comment / {} code lines)",
63            self.ratio(),
64            self.comment_lines,
65            self.code_lines
66        )
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use std::path::PathBuf;
73
74    use super::CommentDensity;
75    use crate::workspace::SourceFile;
76
77    fn density(text: &str) -> CommentDensity {
78        CommentDensity::of_file(&SourceFile::parse(PathBuf::from("lib.rs"), text).unwrap())
79    }
80
81    #[test]
82    fn counts_code_and_comment_lines() {
83        let density = density("// why\nfn a() {}\n\nfn b() {} // trailing\n");
84        assert_eq!(
85            density,
86            CommentDensity {
87                code_lines: 2,
88                comment_lines: 1
89            }
90        );
91        assert!((density.ratio() - 0.5).abs() < f64::EPSILON);
92    }
93
94    #[test]
95    fn source_without_code_has_zero_ratio() {
96        assert!(density("// only a comment").ratio().abs() < f64::EPSILON);
97    }
98}