drft/rules/
redundant_edge.rs1use crate::diagnostic::Diagnostic;
2use crate::rules::{Rule, RuleContext};
3
4pub struct RedundantEdgeRule;
5
6impl Rule for RedundantEdgeRule {
7 fn name(&self) -> &str {
8 "redundant-edge"
9 }
10
11 fn evaluate(&self, ctx: &RuleContext) -> Vec<Diagnostic> {
12 let result = &ctx.graph.transitive_reduction;
13
14 result
15 .redundant_edges
16 .iter()
17 .map(|re| Diagnostic {
18 rule: "redundant-edge".into(),
19 message: "transitively redundant".into(),
20 source: Some(re.source.clone()),
21 target: Some(re.target.clone()),
22 via: Some(re.via.clone()),
23 fix: Some(format!(
24 "{} links directly to {}, but already reaches it via {} \u{2014} remove the direct link",
25 re.source, re.target, re.via
26 )),
27 ..Default::default()
28 })
29 .collect()
30 }
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36 use crate::graph::Graph;
37 use crate::graph::test_helpers::{make_edge, make_enriched, make_node};
38 use crate::rules::RuleContext;
39
40 #[test]
41 fn produces_diagnostics_for_redundant_edges() {
42 let mut graph = Graph::new();
43 graph.add_node(make_node("a.md"));
44 graph.add_node(make_node("b.md"));
45 graph.add_node(make_node("c.md"));
46 graph.add_edge(make_edge("a.md", "b.md"));
47 graph.add_edge(make_edge("b.md", "c.md"));
48 graph.add_edge(make_edge("a.md", "c.md"));
49
50 let enriched = make_enriched(graph);
51 let ctx = RuleContext {
52 graph: &enriched,
53 options: None,
54 };
55 let diagnostics = RedundantEdgeRule.evaluate(&ctx);
56
57 assert_eq!(diagnostics.len(), 1);
58 assert_eq!(diagnostics[0].rule, "redundant-edge");
59 assert_eq!(diagnostics[0].source.as_deref(), Some("a.md"));
60 assert_eq!(diagnostics[0].target.as_deref(), Some("c.md"));
61 assert_eq!(diagnostics[0].via.as_deref(), Some("b.md"));
62 assert_eq!(diagnostics[0].message, "transitively redundant");
63 }
64
65 #[test]
66 fn no_diagnostics_when_no_redundancy() {
67 let mut graph = Graph::new();
68 graph.add_node(make_node("a.md"));
69 graph.add_node(make_node("b.md"));
70 graph.add_node(make_node("c.md"));
71 graph.add_edge(make_edge("a.md", "b.md"));
72 graph.add_edge(make_edge("b.md", "c.md"));
73
74 let enriched = make_enriched(graph);
75 let ctx = RuleContext {
76 graph: &enriched,
77 options: None,
78 };
79 let diagnostics = RedundantEdgeRule.evaluate(&ctx);
80 assert!(diagnostics.is_empty());
81 }
82}