liminal-protocol 0.2.1

Shared participant-lifecycle protocol types for liminal
Documentation
use crate::{
    lifecycle::{
        OrderClaims, OrderHigh, OrderLedger, RetainedCausalRecord, SequenceClaims, SequenceLedger,
    },
    wire::TransactionOrder,
};

pub(super) fn enrollment_sequence(current: SequenceLedger, high: u64) -> Option<SequenceLedger> {
    let claims = current.claims();
    SequenceLedger::try_new(
        high,
        SequenceClaims::new(
            claims.live_members().checked_add(1)?,
            claims.binding_terminals().checked_add(1)?,
            claims.markers(),
            claims.recovery(),
        ),
    )
    .ok()
}

pub(super) fn detached_attach_sequence(
    current: SequenceLedger,
    high: u64,
) -> Option<SequenceLedger> {
    let claims = current.claims();
    SequenceLedger::try_new(
        high,
        SequenceClaims::new(
            claims.live_members(),
            claims.binding_terminals().checked_add(1)?,
            claims.markers(),
            claims.recovery(),
        ),
    )
    .ok()
}

pub(super) fn superseding_attach_sequence(
    current: SequenceLedger,
    rows: &[RetainedCausalRecord],
) -> Option<SequenceLedger> {
    let high = rows.last()?.delivery_seq;
    let first = rows.first()?.delivery_seq;
    (first == current.high_watermark().checked_add(1)? && high == first.checked_add(1)?)
        .then(|| SequenceLedger::try_new(high, current.claims()).ok())
        .flatten()
}

pub(super) fn detach_sequence(current: SequenceLedger, high: u64) -> Option<SequenceLedger> {
    let claims = current.claims();
    SequenceLedger::try_new(
        high,
        SequenceClaims::new(
            claims.live_members(),
            claims.binding_terminals().checked_sub(1)?,
            claims.markers(),
            claims.recovery(),
        ),
    )
    .ok()
}

pub(super) fn enrollment_order(
    current: OrderLedger,
    major: TransactionOrder,
) -> Option<OrderLedger> {
    let claims = current.claims();
    next_order(
        current,
        major,
        OrderClaims::new(
            claims.active_binding_terminals().checked_add(1)?,
            claims.membership_exits().checked_add(1)?,
            claims.recovery_operation(),
            claims.recovery_replacement_terminal(),
        )
        .ok()?,
    )
}

pub(super) fn detached_attach_order(
    current: OrderLedger,
    major: TransactionOrder,
) -> Option<OrderLedger> {
    let claims = current.claims();
    next_order(
        current,
        major,
        OrderClaims::new(
            claims.active_binding_terminals().checked_add(1)?,
            claims.membership_exits(),
            claims.recovery_operation(),
            claims.recovery_replacement_terminal(),
        )
        .ok()?,
    )
}

pub(super) fn superseding_attach_order(
    current: OrderLedger,
    major: TransactionOrder,
) -> Option<OrderLedger> {
    next_order(current, major, current.claims())
}

pub(super) fn detach_order(current: OrderLedger, major: TransactionOrder) -> Option<OrderLedger> {
    let claims = current.claims();
    next_order(
        current,
        major,
        OrderClaims::new(
            claims.active_binding_terminals().checked_sub(1)?,
            claims.membership_exits(),
            claims.recovery_operation(),
            claims.recovery_replacement_terminal(),
        )
        .ok()?,
    )
}

fn next_order(
    current: OrderLedger,
    major: TransactionOrder,
    claims: OrderClaims,
) -> Option<OrderLedger> {
    let expected = match current.high() {
        OrderHigh::Empty => 0,
        OrderHigh::Allocated(high) => high.checked_add(1)?,
    };
    (major == expected)
        .then(|| OrderLedger::try_new(OrderHigh::Allocated(major), claims).ok())
        .flatten()
}