minerva 0.2.0

Causal ordering for distributed systems
extern crate alloc;

use crate::metis::{Anchor, DotFun, DotMap, DotSet, DotStore, Locus, Rhapsody};

use super::page::{BlockDot, Field, Page};

impl Page {
    /// Creates a block at the visual slot after `after` (`None` at the page
    /// start), writing its kind and title fields, and returns its identity:
    /// the order-pair dot. One "create" is three writes across two pairs
    /// (one order weave, two field registers), which is exactly the
    /// cross-pair atomicity seam the coherence scripts pry at.
    pub fn create_block(
        &mut self,
        kind: &'static str,
        title: &'static str,
        after: Option<BlockDot>,
    ) -> BlockDot {
        let anchor = self
            .order
            .state()
            .store()
            .anchor_for_visual_insert(after.map(Into::into));
        // Observe the top sibling's rank so the mint places this block at the
        // chosen visual slot (the editor's weave_at_seam discipline).
        if let Some(top_child) = self.order.state().store().children_of(anchor).next()
            && let Some(locus) = self.order.state().store().locus(top_child)
        {
            self.clock.observe(locus.rank);
        }
        let rank = self.clock.now(0u16);
        let (block, _) = self.order.compose(|assigned| {
            let mut rhapsody = Rhapsody::new();
            let _ = rhapsody.weave(assigned, Locus { anchor, rank });
            (rhapsody, DotSet::new())
        });
        self.set_kind(block, kind);
        self.set_title(block, title);
        block
    }

    /// Writes the block's kind register, superseding the kind dots this
    /// replica has observed (concurrent kind writes therefore surface as
    /// siblings, never resolve).
    pub fn set_kind(&mut self, block: BlockDot, kind: &'static str) {
        let _ = self.fields.compose_super(
            |assigned| {
                DotMap::singleton(
                    block,
                    DotMap::singleton(Field::Kind, DotFun::singleton(assigned, kind)),
                )
            },
            |held| {
                held.get(&block)
                    .and_then(|fields| fields.get(&Field::Kind))
                    .map(DotFun::observed)
                    .unwrap_or_default()
            },
        );
    }

    /// Writes the block's title register, superseding the observed title dots.
    pub fn set_title(&mut self, block: BlockDot, title: &'static str) {
        let _ = self.fields.compose_super(
            |assigned| {
                DotMap::singleton(
                    block,
                    DotMap::singleton(Field::Title, DotFun::singleton(assigned, title)),
                )
            },
            |held| {
                held.get(&block)
                    .and_then(|fields| fields.get(&Field::Title))
                    .map(DotFun::observed)
                    .unwrap_or_default()
            },
        );
    }

    /// Types `ch` into `block`'s paragraph sequence, anchored after `after`
    /// (`None` at the paragraph origin), and returns the char's para-pair
    /// dot. The nested weave mints from the *paragraph pair's* context; the
    /// block dot is only the map key.
    pub fn type_into(
        &mut self,
        block: BlockDot,
        ch: char,
        after: Option<crate::metis::Dot>,
    ) -> crate::metis::Dot {
        let anchor = after.map_or(Anchor::Origin, |dot| Anchor::After(dot.into()));
        let rank = self.clock.now(0u16);
        let (dot, _) = self.para.compose(|assigned| {
            let mut rhapsody = Rhapsody::new();
            let _ = rhapsody.weave(assigned, Locus { anchor, rank });
            (DotMap::singleton(block, rhapsody), DotSet::new())
        });
        let _ = self.chars.insert(dot, ch);
        dot
    }

    /// Writes the cell register at `(block, row)`, superseding the observed
    /// cell dots.
    pub fn set_cell(&mut self, block: BlockDot, row: u32, value: &'static str) {
        let _ = self.table.compose_super(
            |assigned| {
                DotMap::singleton(
                    block,
                    DotMap::singleton(row, DotFun::singleton(assigned, value)),
                )
            },
            |held| {
                held.get(&block)
                    .and_then(|rows| rows.get(&row))
                    .map(DotFun::observed)
                    .unwrap_or_default()
            },
        );
    }

    /// Deletes the block from the *order pair only*: the natural hand-roll,
    /// which leaves every content pair holding the tombstoned block's state
    /// (the no-canonical-drop pain point the kind scripts record).
    pub fn delete_block_order_only(&mut self, block: BlockDot) {
        let mut superseded = DotSet::new();
        let _ = superseded.insert(block);
        let _ = self.order.retract(superseded);
    }

    /// Retracts every observed paragraph char under `block`: the cross-pair
    /// half of an honest delete or kind reassignment, a *separate* delta on a
    /// separate pair (no atomicity with the order or field writes exists,
    /// which is the window the coherence scripts measure).
    pub fn retract_para_content(&mut self, block: BlockDot) {
        let observed = self
            .para
            .state()
            .store()
            .get(&block)
            .map(|rhapsody| {
                let mut set = DotSet::new();
                for dot in rhapsody.dots() {
                    let _ = set.insert(dot);
                }
                set
            })
            .unwrap_or_default();
        let _ = self.para.retract(observed);
    }
}