Skip to main content

drft/rules/
orphan_node.rs

1use crate::diagnostic::Diagnostic;
2use crate::rules::{Rule, RuleContext};
3
4pub struct OrphanNodeRule;
5
6impl Rule for OrphanNodeRule {
7    fn name(&self) -> &str {
8        "orphan-node"
9    }
10
11    fn evaluate(&self, ctx: &RuleContext) -> Vec<Diagnostic> {
12        let result = &ctx.graph.degree;
13
14        result
15            .nodes
16            .iter()
17            .filter(|nd| nd.in_degree == 0)
18            .map(|nd| Diagnostic {
19                rule: "orphan-node".into(),
20                message: "no inbound links".into(),
21                node: Some(nd.node.clone()),
22                fix: Some(format!(
23                    "{} has no inbound links — either link to it from another file or remove it",
24                    nd.node
25                )),
26                ..Default::default()
27            })
28            .collect()
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35    use crate::analyses::EnrichedGraph;
36    use crate::config::Config;
37    use crate::graph::Graph;
38    use crate::graph::test_helpers::{make_edge, make_node};
39    use crate::rules::RuleContext;
40
41    fn make_enriched(graph: Graph) -> EnrichedGraph {
42        crate::analyses::enrich_graph(graph, std::path::Path::new("."), &Config::defaults(), None)
43    }
44
45    #[test]
46    fn detects_orphan() {
47        let mut graph = Graph::new();
48        graph.add_node(make_node("index.md"));
49        graph.add_node(make_node("orphan.md"));
50        graph.add_edge(make_edge("index.md", "setup.md"));
51
52        let enriched = make_enriched(graph);
53        let ctx = RuleContext {
54            graph: &enriched,
55            options: None,
56        };
57        let diagnostics = OrphanNodeRule.evaluate(&ctx);
58
59        let orphan_nodes: Vec<&str> = diagnostics
60            .iter()
61            .map(|d| d.node.as_deref().unwrap())
62            .collect();
63        assert!(orphan_nodes.contains(&"orphan.md"));
64        assert!(orphan_nodes.contains(&"index.md"));
65    }
66
67    #[test]
68    fn linked_file_is_not_orphan() {
69        let mut graph = Graph::new();
70        graph.add_node(make_node("index.md"));
71        graph.add_node(make_node("setup.md"));
72        graph.add_edge(make_edge("index.md", "setup.md"));
73
74        let enriched = make_enriched(graph);
75        let ctx = RuleContext {
76            graph: &enriched,
77            options: None,
78        };
79        let diagnostics = OrphanNodeRule.evaluate(&ctx);
80
81        let orphan_nodes: Vec<&str> = diagnostics
82            .iter()
83            .map(|d| d.node.as_deref().unwrap())
84            .collect();
85        assert!(!orphan_nodes.contains(&"setup.md"));
86        assert!(orphan_nodes.contains(&"index.md"));
87    }
88}