minerva 0.2.0

Causal ordering for distributed systems
extern crate alloc;

use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;

use crate::metis::{
    Anchor, Cut, Dot, DotSet, DotStore, Dotted, Locus, Retired, Rhapsody, Stability,
};

use super::Replica;

impl Replica {
    /// Inserts `ch` anchored after `anchor` (`None` at the origin).
    pub fn insert(&mut self, ch: char, anchor: Option<Dot>) -> Dot {
        let rank = self.clock.now(0u16);
        let anchor = anchor.map_or(Anchor::Origin, |dot| Anchor::After(dot.into()));
        let (dot, _) = self.text.compose(|assigned| {
            let mut rhapsody = Rhapsody::new();
            let _ = rhapsody.weave(assigned, Locus { anchor, rank });
            (rhapsody, DotSet::new())
        });
        let _ = self.chars.insert(dot, ch);
        dot
    }

    /// Inserts `ch` at the visual slot after `after` (`None` at document start).
    pub fn insert_visual(&mut self, ch: char, after: Option<Dot>) -> Dot {
        let anchor = self
            .text
            .state()
            .store()
            .anchor_for_visual_insert(after.map(Into::into));
        self.weave_at_seam(ch, anchor)
    }

    /// Inserts `ch` at the visual slot before `before` (`None` at document end).
    pub fn insert_before_visual(&mut self, ch: char, before: Option<Dot>) -> Dot {
        let order = self.order();
        let after = before.map_or_else(
            || order.last().copied(),
            |target| {
                order
                    .iter()
                    .position(|&d| d == target)
                    .and_then(|idx| idx.checked_sub(1).map(|prev| order[prev]))
            },
        );
        let anchor = self
            .text
            .state()
            .store()
            .anchor_for_visual_insert(after.map(Into::into));
        self.weave_at_seam(ch, anchor)
    }

    fn weave_at_seam(&mut self, ch: char, anchor: Anchor) -> Dot {
        if let Some(top_child) = self.text.state().store().children_of(anchor).next()
            && let Some(locus) = self.text.state().store().locus(top_child)
        {
            self.clock.observe(locus.rank);
        }
        let rank = self.clock.now(0u16);
        let dot = self.text.state().next_dot(self.station);
        let _ = self.text.compose(|assigned| {
            let mut rhapsody = Rhapsody::new();
            let _ = rhapsody.weave(assigned, Locus { anchor, rank });
            (rhapsody, DotSet::new())
        });
        let _ = self.chars.insert(dot, ch);
        dot
    }

    /// Deletes the element at `dot` through an observed remove.
    pub fn delete(&mut self, dot: Dot) {
        let mut superseded = DotSet::new();
        let _ = superseded.insert(dot);
        let _ = self.text.retract(superseded);
    }

    /// The document as a string in visible Rhapsody order.
    #[must_use]
    pub fn text(&self) -> String {
        self.text
            .state()
            .store()
            .order()
            .into_iter()
            .filter_map(|dot| self.chars.get(&dot).copied())
            .collect()
    }

    /// The visible document order.
    #[must_use]
    pub fn order(&self) -> Vec<Dot> {
        self.text.state().store().order()
    }

    /// Everything this text layer has ever seen.
    #[must_use]
    pub fn text_context(&self) -> &DotSet {
        self.text.state().context()
    }

    /// The targeted text delta owed to `peer`, if `peer` is on the roster.
    #[must_use]
    pub fn text_owed(&self, peer: u32) -> Option<Dotted<Rhapsody>> {
        self.purview.owed(self.text.state(), peer)
    }

    /// The text delta owed to an explicit peer context.
    #[must_use]
    pub fn text_owed_to(&self, peer_context: &DotSet) -> Dotted<Rhapsody> {
        self.text.owed_to(peer_context)
    }

    /// The text delta owed to a peer stating its context and its woven
    /// witness (the S191 witnessed read, the flow the field consumer runs).
    #[must_use]
    pub fn text_owed_witnessed(
        &self,
        peer_context: &DotSet,
        peer_woven: &DotSet,
    ) -> Dotted<Rhapsody> {
        self.text.owed_to_witnessed(peer_context, peer_woven)
    }

    /// This replica's woven set, the recording-possession witness a peer's
    /// owed read trims the skeleton against.
    #[must_use]
    pub fn text_woven(&self) -> &DotSet {
        self.text.state().store().woven()
    }

    /// The whole-store text delta floored at a witnessed peer cut.
    #[must_use]
    pub fn text_owed_since(&self, peer: &Cut) -> Dotted<Rhapsody> {
        self.text.state().delta_for_since(peer)
    }

    /// Absorbs a text delta and learns the sender's payloads for its store dots.
    pub fn absorb_text(
        &mut self,
        from: u32,
        delta: &Dotted<Rhapsody>,
        chars: &BTreeMap<Dot, char>,
    ) {
        let received = self.text.absorb(from, delta);
        for dot in delta.store().dots() {
            if let Some(&ch) = chars.get(&dot) {
                let _ = self.chars.insert(dot, ch);
            }
        }
        let _ = self.purview.note(&received);
    }

    /// The live element count.
    #[must_use]
    pub fn visible_len(&self) -> usize {
        self.text.state().store().visible_len()
    }

    /// The grow-only skeleton size.
    #[must_use]
    pub fn skeleton_len(&self) -> usize {
        self.text.state().store().skeleton_len()
    }

    /// Condenses the text skeleton against an honest retirement claim.
    pub fn condense(&mut self, retired: &Retired) -> usize {
        self.text.condense(retired)
    }

    /// Reports this replica's delivered floor into a stability tracker.
    pub fn report_into(&self, stability: &mut Stability) {
        let floor = self.text.state().context().floor();
        let _ = stability.report(self.station, &floor);
    }
}