use html5ever::Attribute;
use crate::html::{Dom, NodeData, NodeId};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImgAttrs {
pub src: String,
pub alt: Option<String>,
}
pub fn read_img_attrs(dom: &Dom, node: NodeId) -> Option<ImgAttrs> {
let NodeData::Element { name, attrs, .. } = &dom.node(node).data else {
return None;
};
if &*name.local != "img" {
return None;
}
let src = find_attr(attrs, "src")
.map(|value| value.to_string())
.filter(|s| !s.is_empty())?;
let alt = find_attr(attrs, "alt").map(|value| value.to_string());
Some(ImgAttrs { src, alt })
}
fn find_attr<'a>(attrs: &'a [Attribute], name: &str) -> Option<&'a str> {
attrs
.iter()
.find(|attr| &*attr.name.local == name)
.map(|attr| attr.value.as_ref())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::html;
fn find(dom: &Dom, id: NodeId, tag: &str) -> Option<NodeId> {
if let NodeData::Element { name, .. } = &dom.node(id).data {
if &*name.local == tag {
return Some(id);
}
}
dom.children(id).find_map(|child| find(dom, child, tag))
}
#[test]
fn reads_all_attributes_when_present() {
let dom =
html::parse(br#"<img src="logo.png" width="120" height="40" alt="Company logo">"#);
let img = find(&dom, dom.document(), "img").expect("img not found");
let attrs = read_img_attrs(&dom, img).expect("expected Some");
assert_eq!(attrs.src, "logo.png");
assert_eq!(attrs.alt.as_deref(), Some("Company logo"));
}
#[test]
fn missing_optional_attributes_are_none() {
let dom = html::parse(br#"<img src="logo.png">"#);
let img = find(&dom, dom.document(), "img").expect("img not found");
let attrs = read_img_attrs(&dom, img).expect("expected Some");
assert_eq!(attrs.alt, None);
}
#[test]
fn empty_alt_is_distinguished_from_missing_alt() {
let dom = html::parse(br#"<img src="deco.png" alt="">"#);
let img = find(&dom, dom.document(), "img").expect("img not found");
let attrs = read_img_attrs(&dom, img).expect("expected Some");
assert_eq!(attrs.alt.as_deref(), Some(""));
}
#[test]
fn missing_src_is_none() {
let dom = html::parse(br#"<img alt="no src">"#);
let img = find(&dom, dom.document(), "img").expect("img not found");
assert_eq!(read_img_attrs(&dom, img), None);
}
#[test]
fn empty_src_is_none() {
let dom = html::parse(br#"<img src="">"#);
let img = find(&dom, dom.document(), "img").expect("img not found");
assert_eq!(read_img_attrs(&dom, img), None);
}
#[test]
fn non_img_element_is_none() {
let dom = html::parse(br#"<div src="not-an-img.png"></div>"#);
let div = find(&dom, dom.document(), "div").expect("div not found");
assert_eq!(read_img_attrs(&dom, div), None);
}
#[test]
fn released_node_is_none() {
let mut dom = html::parse(br#"<div><img src="logo.png"></div>"#);
let img = find(&dom, dom.document(), "img").expect("img not found");
dom.release_subtree(img);
assert_eq!(read_img_attrs(&dom, img), None);
}
}