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
11pub const FREE_COMMENT_LINES: usize = 3;
13
14pub 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
50fn 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}