xot 0.31.2

Full-featured XML tree library for Rust
Documentation
use crate::xotdata::{Node, Xot};

pub(crate) fn remove_insignificant_whitespace(xot: &mut Xot, node: Node) {
    let mut to_remove = Vec::new();
    for descendant in xot.descendants(node) {
        if is_insignificant_whitespace(xot, descendant) {
            to_remove.push(descendant);
        }
    }
    for node in to_remove {
        xot.remove(node).unwrap();
    }
}

fn is_whitespace(text: &str) -> bool {
    text.chars().all(|c| c.is_whitespace())
}

fn is_significant_text_node(xot: &Xot, node: Node) -> bool {
    if let Some(text) = xot.text_str(node) {
        !is_whitespace(text)
    } else {
        false
    }
}

fn in_preserve_space(xot: &Xot, node: Node) -> bool {
    let space = xot.xml_space_name();
    for ancestor in xot.ancestors(node) {
        let attributes = xot.attributes(ancestor);
        if let Some(value) = attributes.get(space) {
            return value == "preserve";
        }
    }
    false
}

fn is_insignificant_whitespace(xot: &Xot, node: Node) -> bool {
    if let Some(text) = xot.text_str(node) {
        if in_preserve_space(xot, node) {
            return false;
        }
        if !is_whitespace(text) {
            return false;
        }
        let previous_sibling = xot.previous_sibling(node);
        if let Some(previous_sibling) = previous_sibling {
            if xot
                .preceding_siblings(previous_sibling)
                .any(|n| is_significant_text_node(xot, n))
            {
                return false;
            }
        }
        let next_sibling = xot.next_sibling(node);
        if let Some(next_sibling) = next_sibling {
            if xot
                .following_siblings(next_sibling)
                .any(|n| is_significant_text_node(xot, n))
            {
                return false;
            }
        }
        true
    } else {
        false
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_unpretty() {
        let mut xot = Xot::new();
        let root = xot
            .parse("<doc>  <p>hello <i>world</i>  </p>  </doc>")
            .unwrap();
        remove_insignificant_whitespace(&mut xot, root);
        assert_eq!(
            xot.to_string(root).unwrap(),
            "<doc><p>hello <i>world</i>  </p></doc>"
        );
    }

    #[test]
    fn test_unpretty_xml_space_preserve() {
        let mut xot = Xot::new();
        let root = xot.parse(r#"<doc xml:space="preserve">   </doc>"#).unwrap();
        remove_insignificant_whitespace(&mut xot, root);
        assert_eq!(
            xot.to_string(root).unwrap(),
            r#"<doc xml:space="preserve">   </doc>"#
        );
    }

    #[test]
    fn test_unpretty_xml_space_preserve_nested() {
        let mut xot = Xot::new();
        let root = xot
            .parse(r#"<doc xml:space="preserve"><p>   </p></doc>"#)
            .unwrap();
        remove_insignificant_whitespace(&mut xot, root);
        assert_eq!(
            xot.to_string(root).unwrap(),
            r#"<doc xml:space="preserve"><p>   </p></doc>"#
        );
    }

    #[test]
    fn test_unpretty_xml_space_preserve_reset() {
        let mut xot = Xot::new();
        let root = xot
            .parse(r#"<doc xml:space="preserve">  <p xml:space="default">   </p></doc>"#)
            .unwrap();
        remove_insignificant_whitespace(&mut xot, root);
        assert_eq!(
            xot.to_string(root).unwrap(),
            r#"<doc xml:space="preserve">  <p xml:space="default"/></doc>"#
        );
    }
}