mdlint/lint/rules/
md045.rs1use crate::lint::rule::Rule;
2use crate::markdown::MarkdownParser;
3use crate::types::Violation;
4use pulldown_cmark::{Event, Tag, TagEnd};
5use serde_json::Value;
6
7pub struct MD045;
8
9impl Rule for MD045 {
10 fn name(&self) -> &str {
11 "MD045"
12 }
13
14 fn description(&self) -> &str {
15 "Images should have alternate text (alt text)"
16 }
17
18 fn tags(&self) -> &[&str] {
19 &["accessibility", "images"]
20 }
21
22 fn check(&self, parser: &MarkdownParser, _config: Option<&Value>) -> Vec<Violation> {
23 let mut violations = Vec::new();
24 let mut in_image = false;
25 let mut image_start_line = 0;
26 let mut alt_text = String::new();
27
28 for (event, range) in parser.parse_with_offsets() {
29 match event {
30 Event::Start(Tag::Image { .. }) => {
31 in_image = true;
32 image_start_line = parser.offset_to_line(range.start);
33 alt_text.clear();
34 }
35 Event::Text(text) if in_image => {
36 alt_text.push_str(&text);
37 }
38 Event::End(TagEnd::Image) if in_image => {
39 if alt_text.is_empty() {
41 violations.push(Violation {
42 line: image_start_line,
43 column: Some(1),
44 rule: self.name().to_string(),
45 message: "Images should have alternate text (alt text)".to_string(),
46 fix: None,
47 });
48 }
49 in_image = false;
50 }
51 _ => {}
52 }
53 }
54
55 violations
56 }
57
58 fn fixable(&self) -> bool {
59 false
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn test_image_with_alt() {
69 let content = "";
70 let parser = MarkdownParser::new(content);
71 let rule = MD045;
72 let violations = rule.check(&parser, None);
73
74 assert_eq!(violations.len(), 0);
75 }
76
77 #[test]
78 fn test_image_without_alt() {
79 let content = "";
80 let parser = MarkdownParser::new(content);
81 let rule = MD045;
82 let violations = rule.check(&parser, None);
83
84 assert_eq!(violations.len(), 1);
85 }
86
87 #[test]
88 fn test_image_with_whitespace_alt() {
89 let content = "";
90 let parser = MarkdownParser::new(content);
91 let rule = MD045;
92 let violations = rule.check(&parser, None);
93
94 assert_eq!(violations.len(), 0); }
96
97 #[test]
98 fn test_multiple_images() {
99 let content = " and ";
100 let parser = MarkdownParser::new(content);
101 let rule = MD045;
102 let violations = rule.check(&parser, None);
103
104 assert_eq!(violations.len(), 1); }
106}