natural-xml-diff 0.2.0

Natural diffing between XML documents
Documentation
use ahash::HashMap;
use xot::{Error, Node, Xot};

use crate::vtree::{Signatures, Status, Vtree};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct EqualPair(pub(crate) usize, pub(crate) usize);

// tree signature id to indices in vtree
type TreeSignatureIdToIndices = HashMap<u32, Vec<usize>>;

/// A comparison of two documents.
pub(crate) struct Comparison {
    pub(crate) doc_a: Node,
    pub(crate) doc_b: Node,

    pub(crate) vtree_a: Vtree,
    pub(crate) vtree_b: Vtree,

    pub(crate) vtree_b_index: TreeSignatureIdToIndices,
}

impl Comparison {
    /// Construct a comparison between two XML documents from parsed documents.
    pub(crate) fn new(xot: &Xot, doc_a: Node, doc_b: Node) -> Self {
        let mut node_signatures = Signatures::new();
        let mut tree_signatures = Signatures::new();
        let vtree_a = Vtree::new(xot, doc_a, &mut node_signatures, &mut tree_signatures);
        let vtree_b = Vtree::new(xot, doc_b, &mut node_signatures, &mut tree_signatures);
        let mut vtree_b_index: TreeSignatureIdToIndices = HashMap::default();
        for (index, vnode) in vtree_b.nodes.iter().enumerate() {
            vtree_b_index
                .entry(vnode.tree_signature_id)
                .or_default()
                .push(index)
        }
        Self {
            doc_a,
            doc_b,
            vtree_a,
            vtree_b,
            vtree_b_index,
        }
    }

    /// Construct a comparison between two XML documents from XML strings.
    pub(crate) fn from_xml(xot: &mut Xot, xml_a: &str, xml_b: &str) -> Result<Self, Error> {
        let doc_a = xot.parse(xml_a)?;
        let doc_b = xot.parse(xml_b)?;
        Ok(Self::new(xot, doc_a, doc_b))
    }

    pub(crate) fn update_status(&mut self, pairs: &[EqualPair], f: impl Fn(u32) -> Status) {
        for pair in pairs {
            let EqualPair(id_a, id_b) = *pair;
            let vnode_a = &mut self.vtree_a.nodes[id_a];
            vnode_a.status = f(id_b as u32);
            let vnode_b = &mut self.vtree_b.nodes[id_b];
            vnode_b.status = f(id_a as u32);
        }
    }

    #[cfg(test)]
    pub(crate) fn vtrees(&self) -> (&Vtree, &Vtree) {
        (&self.vtree_a, &self.vtree_b)
    }

    pub(crate) fn get_node_a(&self, id: usize) -> Node {
        self.vtree_a.nodes[id].node
    }

    pub(crate) fn get_node_b(&self, id: usize) -> Node {
        self.vtree_b.nodes[id].node
    }
}