use super::{Pass, PassResult};
use crate::ast::{Document, NodeKind};
pub struct CleanupAttrs;
impl Pass for CleanupAttrs {
fn name(&self) -> &'static str {
"cleanupAttrs"
}
fn run(&self, doc: &mut Document) -> PassResult {
let mut changed = false;
let ids = doc.traverse();
for id in ids {
let node = doc.node_mut(id);
if let NodeKind::Element(ref mut elem) = node.kind {
for attr in &mut elem.attributes {
let cleaned = cleanup_whitespace(&attr.value);
if cleaned != attr.value {
attr.value = cleaned;
changed = true;
}
}
}
}
if changed {
PassResult::Changed
} else {
PassResult::Unchanged
}
}
}
fn cleanup_whitespace(s: &str) -> String {
let mut result = String::with_capacity(s.len());
let mut prev_ws = true; for ch in s.chars() {
if ch.is_ascii_whitespace() {
if !prev_ws {
result.push(' ');
prev_ws = true;
}
} else {
result.push(ch);
prev_ws = false;
}
}
if result.ends_with(' ') {
result.pop();
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse;
use crate::serializer::serialize;
#[test]
fn collapses_whitespace_in_attrs() {
let input = "<svg xmlns=\"http://www.w3.org/2000/svg\"><rect class=\" a b \"/></svg>";
let mut doc = parse(input).unwrap();
assert_eq!(CleanupAttrs.run(&mut doc), PassResult::Changed);
let output = serialize(&doc);
assert!(output.contains("class=\"a b\""));
}
#[test]
fn handles_newlines_in_attrs() {
let input = "<svg xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M 0\n0 L\n10 10\"/></svg>";
let mut doc = parse(input).unwrap();
assert_eq!(CleanupAttrs.run(&mut doc), PassResult::Changed);
let output = serialize(&doc);
assert!(output.contains("d=\"M 0 0 L 10 10\""));
}
}