haematite-cli 0.21.8

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
//! Partial totals for one jointly settled range of a three-version review.

use std::num::NonZeroUsize;

use haematite::db::{ObservedChange, ReadOnlyDatabase};
use serde::Serialize;

use super::super::{Captured, Catalogue, Failure, ForkProvenance};
use super::{compare, movement, values};
use crate::serve::protocol::ReviewPage;

#[derive(Serialize)]
pub(in crate::serve) struct Partial {
    changes: u64,
    conflicts: u64,
    /// The merged endpoint set needs merge preparation and its resolution policy.
    dangling: Option<()>,
}

pub(in crate::serve) struct Page {
    pub(in crate::serve) selection: Captured,
    pub(in crate::serve) from: ForkProvenance,
    pub(in crate::serve) parent: Captured,
    pub(in crate::serve) partial: Partial,
    pub(in crate::serve) base_moved: u64,
    pub(in crate::serve) next_after: Option<String>,
}

pub(in crate::serve) fn page(
    reader: &ReadOnlyDatabase,
    catalogue: &Catalogue,
    request: &ReviewPage,
    limit: NonZeroUsize,
) -> Result<Page, Failure> {
    let result = compare::page(reader, catalogue, request, limit)?;
    let base_moved = movement(&result.from, &result.parent)?;
    let partial = count(&result.source, &result.target)?;
    Ok(Page {
        selection: result.selection,
        from: result.from,
        parent: result.parent,
        partial,
        base_moved,
        next_after: result.next_after,
    })
}

fn count(source: &[ObservedChange], target: &[ObservedChange]) -> Result<Partial, Failure> {
    let mut result = Partial {
        changes: 0,
        conflicts: 0,
        dangling: None,
    };
    for change in source {
        let (ancestor, here) = compare::sides(change);
        if ancestor == here || !values::is_owner(change.key())? {
            continue;
        }
        result.changes = increment(result.changes)?;
        let there = compare::target_value(change, target)?;
        if there != ancestor && there != here {
            result.conflicts = increment(result.conflicts)?;
        }
    }
    Ok(result)
}

fn increment(value: u64) -> Result<u64, Failure> {
    value
        .checked_add(1)
        .ok_or_else(|| Failure::new("count_overflow", "review total exceeds u64"))
}