Skip to main content

sofie_check/
structure.rs

1use std::path::Path;
2
3use sofie_model::ModelError;
4
5use crate::rules::{Finding, Rule};
6use crate::workspace::CrateSource;
7use crate::{CommentDensity, GodType, load_workspace_crates, rank_god_types};
8
9pub const MAX_COMMENT_RATIO: f64 = 0.0286;
10
11/// A tiny crate's few doc lines would otherwise dominate its ratio.
12pub const FREE_COMMENT_LINES: usize = 3;
13
14/// Lower it when the worst type is split; never raise it.
15pub const MAX_GOD_TYPE_SCORE: f64 = 3.29;
16
17pub fn check_structure(root: &Path) -> Result<Vec<Finding>, ModelError> {
18    let crates = load_workspace_crates(root)?;
19    let mut findings: Vec<Finding> = crates
20        .iter()
21        .filter_map(|krate| density_finding(root, krate))
22        .collect();
23    findings.extend(
24        rank_god_types(&crates)
25            .iter()
26            .take_while(|god_type| god_type.score() > MAX_GOD_TYPE_SCORE)
27            .map(|god_type| god_type_finding(root, god_type)),
28    );
29    Ok(findings)
30}
31
32fn density_finding(root: &Path, krate: &CrateSource) -> Option<Finding> {
33    let density = CommentDensity::of_crate(krate);
34    if density.within_limit() {
35        return None;
36    }
37    Some(Finding {
38        rule: Rule::CommentDensity,
39        file: root.join(&krate.name).join("Cargo.toml"),
40        line: 1,
41        message: format!(
42            "`{}` has comments on more than {MAX_COMMENT_RATIO} of its code lines ({density}); \
43             comments that repeat the code go stale",
44            krate.name
45        ),
46        fix: "keep comments that say why; say what through names and smaller functions".to_owned(),
47    })
48}
49
50/// A type is ranked only for an impl found in a file, so `files` is never empty.
51fn god_type_finding(root: &Path, god_type: &GodType) -> Finding {
52    Finding {
53        rule: Rule::GodType,
54        file: root.join(&god_type.files[0]),
55        line: 1,
56        message: format!("a type outgrew the ceiling {MAX_GOD_TYPE_SCORE}: {god_type}"),
57        fix: "split the type by responsibility".to_owned(),
58    }
59}