cranpose-app-shell 0.9.21

App lifecycle, frame cadence and input dispatch for Cranpose
Documentation
use cranpose_core::{MemoryApplier, NodeId};
use cranpose_ui::{SemanticsNode, SemanticsTree};

pub(crate) fn assert_matches_rebuild(
    applier: &mut MemoryApplier,
    root: NodeId,
    kept: Option<&SemanticsTree>,
) {
    let rebuilt = match cranpose_ui::build_semantics_tree_from_applier(applier, root) {
        Ok(rebuilt) => rebuilt,
        Err(err) => {
            log::debug!("could not rebuild the semantics tree to check an update: {err}");
            return;
        }
    };
    if kept == rebuilt.as_ref() || format!("{kept:?}") == format!("{rebuilt:?}") {
        return;
    }
    let difference = match (kept, rebuilt.as_ref()) {
        (Some(kept), Some(rebuilt)) => first_difference(kept.root(), rebuilt.root())
            .unwrap_or_else(|| "the top modal".to_owned()),
        _ => format!(
            "the root: kept {}, rebuilt {}",
            kept.is_some(),
            rebuilt.is_some()
        ),
    };
    panic!("an updated semantics tree differs from a rebuilt one at {difference}");
}

fn first_difference(kept: &SemanticsNode, rebuilt: &SemanticsNode) -> Option<String> {
    let node_only = |node: &SemanticsNode| SemanticsNode {
        children: Vec::new(),
        ..node.clone()
    };
    let (kept_node, rebuilt_node) = (node_only(kept), node_only(rebuilt));
    if kept_node != rebuilt_node && format!("{kept_node:?}") != format!("{rebuilt_node:?}") {
        return Some(format!(
            "node #{}: kept {kept_node:?}, rebuilt {rebuilt_node:?}",
            kept.node_id
        ));
    }
    let ids = |node: &SemanticsNode| {
        node.children
            .iter()
            .map(|child| child.node_id)
            .collect::<Vec<_>>()
    };
    if ids(kept) != ids(rebuilt) {
        return Some(format!(
            "the children of #{}: kept {:?}, rebuilt {:?}",
            kept.node_id,
            ids(kept),
            ids(rebuilt)
        ));
    }
    kept.children
        .iter()
        .zip(&rebuilt.children)
        .find_map(|(kept, rebuilt)| first_difference(kept, rebuilt))
}

#[cfg(test)]
#[path = "tests/semantics_check_tests.rs"]
mod tests;