drft/rules/
orphan_node.rs1use 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 && nd.out_degree == 0)
18 .map(|nd| Diagnostic {
19 rule: "orphan-node".into(),
20 message: "no connections".into(),
21 node: Some(nd.node.clone()),
22 fix: Some(format!(
23 "{} has no inbound or outbound 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::graph::Graph;
36 use crate::graph::test_helpers::{make_edge, make_enriched, make_node};
37 use crate::rules::RuleContext;
38
39 #[test]
40 fn detects_isolated_node() {
41 let mut graph = Graph::new();
42 graph.add_node(make_node("index.md"));
43 graph.add_node(make_node("orphan.md"));
44 graph.add_edge(make_edge("index.md", "setup.md"));
45
46 let enriched = make_enriched(graph);
47 let ctx = RuleContext {
48 graph: &enriched,
49 options: None,
50 };
51 let diagnostics = OrphanNodeRule.evaluate(&ctx);
52
53 let orphan_nodes: Vec<&str> = diagnostics
54 .iter()
55 .map(|d| d.node.as_deref().unwrap())
56 .collect();
57 assert!(orphan_nodes.contains(&"orphan.md"));
58 }
59
60 #[test]
61 fn root_node_is_not_orphan() {
62 let mut graph = Graph::new();
63 graph.add_node(make_node("index.md"));
64 graph.add_node(make_node("setup.md"));
65 graph.add_edge(make_edge("index.md", "setup.md"));
66
67 let enriched = make_enriched(graph);
68 let ctx = RuleContext {
69 graph: &enriched,
70 options: None,
71 };
72 let diagnostics = OrphanNodeRule.evaluate(&ctx);
73
74 let orphan_nodes: Vec<&str> = diagnostics
75 .iter()
76 .map(|d| d.node.as_deref().unwrap())
77 .collect();
78 assert!(!orphan_nodes.contains(&"setup.md"));
79 assert!(!orphan_nodes.contains(&"index.md"));
80 }
81}