cranpose-core 0.9.7

Composition runtime and reactive state for Cranpose
Documentation
use super::super::{NodeRecord, NodeSlotUpdate, RootNodeIds, SlotTable, SlotWriteSession};
use crate::{AnchorId, NodeId};

impl SlotTable {
    fn subtree_node_records(&self, group_anchor: AnchorId) -> &[NodeRecord] {
        let Some(group_index) = self.active_group_index(group_anchor) else {
            log::error!(
                "slot table ignored root-node collection for stale group anchor {group_anchor:?}"
            );
            return &[];
        };
        let group = &self.groups[group_index];
        let start = group.node_start as usize;
        let records = start
            .checked_add(group.subtree_node_count as usize)
            .and_then(|end| self.nodes.get(start..end));
        records.unwrap_or_else(|| {
            log::error!(
                "slot table ignored root-node collection past node storage at group index {group_index}"
            );
            &[]
        })
    }

    pub(in crate::slot) fn collect_subtree_root_node_ids(
        &self,
        group_anchor: AnchorId,
    ) -> RootNodeIds {
        let records = self.subtree_node_records(group_anchor);
        let parent = records.first().map(|record| record.parent_id);
        let roots: RootNodeIds = records
            .iter()
            .filter(|record| Some(record.parent_id) == parent)
            .map(|record| record.id)
            .collect();
        #[cfg(any(test, debug_assertions))]
        if crate::slot_validation_diagnostics_enabled() {
            let expected: RootNodeIds = super::super::types::root_node_ids(records).collect();
            assert_eq!(
                roots, expected,
                "the roots of a group subtree must be the records sharing its first record's parent"
            );
        }
        roots
    }

    pub(in crate::slot) fn first_subtree_root_node_id(
        &self,
        group_anchor: AnchorId,
    ) -> Option<NodeId> {
        let first = self
            .subtree_node_records(group_anchor)
            .first()
            .map(|record| record.id);
        #[cfg(any(test, debug_assertions))]
        if crate::slot_validation_diagnostics_enabled() {
            assert_eq!(
                first,
                self.collect_subtree_root_node_ids(group_anchor)
                    .first()
                    .copied(),
                "the first record of a group subtree must be one of its roots"
            );
        }
        first
    }
}

impl SlotWriteSession<'_> {
    pub(crate) fn record_node_with_parent(
        &mut self,
        id: NodeId,
        generation: u32,
        parent_id: Option<NodeId>,
        source: crate::Key,
    ) -> NodeSlotUpdate {
        let source = self.state.mix_branch_fold(source);
        let Some(frame) = self.state.group_stack.last_mut() else {
            log::error!(
                "slot writer record_node_with_parent called with an empty group stack; id={id}"
            );
            return NodeSlotUpdate::Inserted { id, generation };
        };
        let group_anchor = frame.group_anchor;
        let result = self.table.record_node_at_cursor(
            group_anchor,
            frame.node_cursor,
            id,
            parent_id,
            generation,
            source,
        );

        frame.advance_node_cursor();
        result
    }

    fn locate_node_record_by_source(
        &mut self,
        source: crate::Key,
        skip_matches: usize,
    ) -> Option<(usize, usize, NodeId, u32)> {
        let mixed = self.state.mix_branch_fold(source);
        let frame = self.state.group_stack.last()?;
        let (group_anchor, cursor) = (frame.group_anchor, frame.node_cursor);
        let mut from = cursor;
        let mut remaining = skip_matches;
        loop {
            let (found, id, generation) =
                self.table
                    .find_node_record_by_source(group_anchor, from, mixed)?;
            if remaining == 0 {
                return Some((found, cursor, id, generation));
            }
            remaining -= 1;
            from = found + 1;
        }
    }

    pub(crate) fn peek_node_record_by_source(
        &mut self,
        source: crate::Key,
        skip_matches: usize,
    ) -> Option<(NodeId, u32)> {
        self.locate_node_record_by_source(source, skip_matches)
            .map(|(_, _, id, generation)| (id, generation))
    }

    pub(crate) fn adopt_node_record_by_source(
        &mut self,
        source: crate::Key,
        skip_matches: usize,
    ) -> Option<(NodeId, u32)> {
        let (found, cursor, id, generation) =
            self.locate_node_record_by_source(source, skip_matches)?;
        if found > cursor {
            let group_anchor = self.state.group_stack.last()?.group_anchor;
            self.table
                .rotate_node_record_to_cursor(group_anchor, found, cursor);
        }
        Some((id, generation))
    }

    #[cfg(test)]
    pub(crate) fn current_node_record(&mut self) -> Option<(NodeId, u32, crate::Key)> {
        let frame = self.state.group_stack.last()?;
        self.table
            .node_identity_at_cursor(frame.group_anchor, frame.node_cursor)
    }
}