use crate::lint::rule::Rule;
use crate::markdown::MarkdownParser;
use crate::types::Violation;
use pulldown_cmark::{Event, Tag, TagEnd};
use serde_json::Value;
pub struct MD045;
impl Rule for MD045 {
fn name(&self) -> &str {
"MD045"
}
fn description(&self) -> &str {
"Images should have alternate text (alt text)"
}
fn tags(&self) -> &[&str] {
&["accessibility", "images"]
}
fn check(&self, parser: &MarkdownParser, _config: Option<&Value>) -> Vec<Violation> {
let mut violations = Vec::new();
let mut in_image = false;
let mut image_start_line = 0;
let mut alt_text = String::new();
for (event, range) in parser.parse_with_offsets() {
match event {
Event::Start(Tag::Image { .. }) => {
in_image = true;
image_start_line = parser.offset_to_line(range.start);
alt_text.clear();
}
Event::Text(text) if in_image => {
alt_text.push_str(&text);
}
Event::End(TagEnd::Image) if in_image => {
if alt_text.is_empty() {
violations.push(Violation {
line: image_start_line,
column: Some(1),
rule: self.name().to_string(),
message: "Images should have alternate text (alt text)".to_string(),
fix: None,
});
}
in_image = false;
}
_ => {}
}
}
violations
}
fn fixable(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_image_with_alt() {
let content = "";
let parser = MarkdownParser::new(content);
let rule = MD045;
let violations = rule.check(&parser, None);
assert_eq!(violations.len(), 0);
}
#[test]
fn test_image_without_alt() {
let content = "";
let parser = MarkdownParser::new(content);
let rule = MD045;
let violations = rule.check(&parser, None);
assert_eq!(violations.len(), 1);
}
#[test]
fn test_image_with_whitespace_alt() {
let content = "";
let parser = MarkdownParser::new(content);
let rule = MD045;
let violations = rule.check(&parser, None);
assert_eq!(violations.len(), 0); }
#[test]
fn test_multiple_images() {
let content = " and ";
let parser = MarkdownParser::new(content);
let rule = MD045;
let violations = rule.check(&parser, None);
assert_eq!(violations.len(), 1); }
}