mdlint/lint/rules/
md042.rs1use crate::lint::rule::Rule;
2use crate::markdown::MarkdownParser;
3use crate::types::Violation;
4use pulldown_cmark::{Event, Tag};
5use serde_json::Value;
6
7pub struct MD042;
8
9impl Rule for MD042 {
10 fn name(&self) -> &'static str {
11 "MD042"
12 }
13
14 fn description(&self) -> &'static str {
15 "No empty links"
16 }
17
18 fn tags(&self) -> &[&str] {
19 &["links"]
20 }
21
22 fn check(&self, parser: &MarkdownParser, _config: Option<&Value>) -> Vec<Violation> {
23 let mut violations = Vec::new();
24
25 for (event, range) in parser.parse_with_offsets() {
26 if let Event::Start(Tag::Link { dest_url, .. }) = event {
27 let url_str = dest_url.to_string();
29 if url_str.is_empty() || url_str == "#" {
30 let line = parser.offset_to_line(range.start);
31 violations.push(Violation {
32 line,
33 column: Some(1),
34 rule: self.name().to_owned(),
35 message: "No empty links".to_owned(),
36 fix: None,
37 });
38 }
39 }
40 }
41
42 violations
43 }
44
45 fn fixable(&self) -> bool {
46 false
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn test_link_with_text() {
56 let content = "[Link text](https://example.com)";
57 let parser = MarkdownParser::new(content);
58 let rule = MD042;
59 let violations = rule.check(&parser, None);
60
61 assert_eq!(violations.len(), 0);
62 }
63
64 #[test]
65 fn test_empty_destination() {
66 let content = "[Link text]()";
67 let parser = MarkdownParser::new(content);
68 let rule = MD042;
69 let violations = rule.check(&parser, None);
70
71 assert_eq!(violations.len(), 1);
72 }
73
74 #[test]
75 fn test_empty_text_non_empty_url() {
76 let content = "[](https://example.com)";
77 let parser = MarkdownParser::new(content);
78 let rule = MD042;
79 let violations = rule.check(&parser, None);
80
81 assert_eq!(violations.len(), 0);
83 }
84
85 #[test]
86 fn test_empty_fragment() {
87 let content = "[Link](#)";
88 let parser = MarkdownParser::new(content);
89 let rule = MD042;
90 let violations = rule.check(&parser, None);
91
92 assert_eq!(violations.len(), 1);
94 }
95
96 #[test]
97 fn test_multiple_links() {
98 let content = "[Good](url1) and [Bad]() and [Also good](url3)";
99 let parser = MarkdownParser::new(content);
100 let rule = MD042;
101 let violations = rule.check(&parser, None);
102
103 assert_eq!(violations.len(), 1); }
105}