use quick_xml::XmlVersion;
use quick_xml::events::{BytesRef, BytesStart};
pub(super) fn attr(e: &BytesStart, local: &str) -> Option<String> {
e.attributes()
.with_checks(false)
.filter_map(Result::ok)
.find(|a| a.key.local_name().into_inner() == local)
.map(|a| match a.normalized_value(XmlVersion::Implicit1_0) {
Ok(v) => v.into_owned(),
Err(_) => a.value.into_owned(),
})
}
pub(super) fn val(e: &BytesStart) -> Option<String> {
attr(e, "val")
}
pub(super) fn on_off(e: &BytesStart) -> bool {
!matches!(val(e).as_deref(), Some("0" | "false" | "off" | "none"))
}
pub(super) fn int_attr(e: &BytesStart, local: &str) -> Option<i64> {
let v = attr(e, local)?;
v.trim()
.parse::<i64>()
.ok()
.or_else(|| v.trim().parse::<f64>().ok().map(|f| f as i64))
}
pub(super) fn resolve_ref(r: &BytesRef) -> Option<char> {
if let Ok(Some(c)) = r.resolve_char_ref() {
return Some(c);
}
match &**r {
"amp" => Some('&'),
"lt" => Some('<'),
"gt" => Some('>'),
"quot" => Some('"'),
"apos" => Some('\''),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use quick_xml::Reader;
use quick_xml::events::Event;
fn first(xml: &str) -> BytesStart<'_> {
let mut r = Reader::from_str(xml);
loop {
match r.read_event().unwrap() {
Event::Start(e) | Event::Empty(e) => return e,
Event::Eof => panic!("no element"),
_ => {}
}
}
}
#[test]
fn on_off_values() {
assert!(on_off(&first(r#"<w:i/>"#)));
assert!(on_off(&first(r#"<w:i w:val="1"/>"#)));
assert!(!on_off(&first(r#"<w:i w:val="0"/>"#)));
assert!(!on_off(&first(r#"<w:b w:val="false"/>"#)));
}
#[test]
fn attributes_by_local_name() {
let e = first(r#"<w:ind w:firstLine="720.0" w:left="10" r:id="a&b"/>"#);
assert_eq!(int_attr(&e, "firstLine"), Some(720));
assert_eq!(attr(&e, "id").as_deref(), Some("a&b"));
assert_eq!(attr(&e, "missing"), None);
}
}