use crate::control::cap::mint::ControlOp;
use crate::control::types::{
AtMostOnceCommit, Generation, IncreasingGen, Lane, NoCrossLaneAliasing, One,
};
use core::marker::PhantomData;
pub(crate) trait Tap {
fn emit(&mut self, op: ControlOp);
}
pub(crate) struct NoopTap;
impl Tap for NoopTap {
fn emit(&mut self, _op: ControlOp) {}
}
pub(crate) struct Txn<Inv, GenOrd, Shot> {
_p: PhantomData<(Inv, GenOrd, Shot)>,
}
impl<Inv, GenOrd, Shot> Txn<Inv, GenOrd, Shot> {
pub(crate) unsafe fn new(_lane: Lane, _generation: Generation) -> Self {
Self { _p: PhantomData }
}
}
impl<Inv: AtMostOnceCommit + NoCrossLaneAliasing, S> Txn<Inv, IncreasingGen, S> {
pub(crate) fn begin(self, tap: &mut impl Tap) -> InBegin<Inv, S> {
tap.emit(ControlOp::TopologyBegin);
InBegin { _p: PhantomData }
}
}
pub(crate) struct InBegin<Inv, Shot> {
_p: PhantomData<(Inv, Shot)>,
}
impl<Inv, S> InBegin<Inv, S> {
pub(crate) fn ack(self, tap: &mut impl Tap) -> InAcked<Inv, S> {
tap.emit(ControlOp::TopologyAck);
InAcked { _p: PhantomData }
}
}
pub(crate) struct InAcked<Inv, Shot> {
_p: PhantomData<(Inv, Shot)>,
}
impl<Inv: AtMostOnceCommit> InAcked<Inv, One> {
pub(crate) fn commit(self, tap: &mut impl Tap) -> Closed<Inv> {
tap.emit(ControlOp::TopologyCommit);
Closed { _p: PhantomData }
}
}
pub(crate) struct Closed<Inv> {
_p: PhantomData<Inv>,
}
#[cfg(test)]
mod tests {
use super::*;
struct TestInv;
impl NoCrossLaneAliasing for TestInv {}
impl AtMostOnceCommit for TestInv {}
struct RecordingTap {
ops: [Option<ControlOp>; 3],
len: usize,
}
impl RecordingTap {
fn new() -> Self {
Self {
ops: [None, None, None],
len: 0,
}
}
fn as_slice(&self) -> &[Option<ControlOp>] {
&self.ops[..self.len]
}
}
impl Tap for RecordingTap {
fn emit(&mut self, op: ControlOp) {
self.ops[self.len] = Some(op);
self.len += 1;
}
}
#[test]
fn test_txn_happy_path() {
let mut tap = RecordingTap::new();
let txn: Txn<TestInv, IncreasingGen, crate::control::types::One> =
unsafe { Txn::new(Lane::new(42), Generation::new(10)) };
let in_begin = txn.begin(&mut tap);
let in_acked = in_begin.ack(&mut tap);
in_acked.commit(&mut tap);
assert_eq!(
tap.as_slice(),
&[
Some(ControlOp::TopologyBegin),
Some(ControlOp::TopologyAck),
Some(ControlOp::TopologyCommit),
]
);
}
}