exocortex-cluster 0.1.0

Exocortex cluster coherence: Chubby-style leases with epoch fencing, HMAC-signed invalidation envelopes, and the SSE change feed.
// crates/exocortex-cluster/src/sse.rs
//! Conversion between storage `Invalidation` events and the
//! `exocortex.sse.v1` protobuf shape carried inside envelopes.

use exocortex_storage::Invalidation as StorageInv;
use exocortex_wire::sse::v1 as pb;

fn hex(id: &[u8; 16]) -> Vec<u8> {
    const DIGITS: &[u8; 16] = b"0123456789abcdef";
    let mut out = Vec::with_capacity(32);
    for b in id {
        out.push(DIGITS[(b >> 4) as usize]);
        out.push(DIGITS[(b & 0xF) as usize]);
    }
    out
}

/// Storage invalidation -> protobuf (ยง2.6.3 sse.proto).
pub fn invalidation_to_pb(inv: &StorageInv) -> pb::Invalidation {
    match inv {
        StorageInv::MemoryUpserted { id, lsn } => pb::Invalidation {
            kind: Some(pb::invalidation::Kind::MemoryUpserted(pb::MemoryUpserted {
                id: hex(&id.0),
                snapshot_json: vec![],
            })),
            backend_lsn: *lsn,
        },
        StorageInv::MemoryDeleted { id, lsn } => pb::Invalidation {
            kind: Some(pb::invalidation::Kind::MemoryDeleted(pb::MemoryDeleted {
                id: hex(&id.0),
            })),
            backend_lsn: *lsn,
        },
        StorageInv::RelationshipUpserted {
            id,
            from,
            to,
            kind,
            lsn,
        } => pb::Invalidation {
            kind: Some(pb::invalidation::Kind::RelationshipUpserted(
                pb::RelationshipUpserted {
                    id: hex(&id.0),
                    from: hex(&from.0),
                    to: hex(&to.0),
                    kind: kind.0,
                },
            )),
            backend_lsn: *lsn,
        },
        StorageInv::RelationshipDeleted { id, lsn } => pb::Invalidation {
            kind: Some(pb::invalidation::Kind::RelationshipDeleted(
                pb::RelationshipDeleted { id: hex(&id.0) },
            )),
            backend_lsn: *lsn,
        },
    }
}