use std::{cell::Cell, mem::MaybeUninit};
use dope::{core::driver::route::KeyTag, manifold::client::conn};
use o3::depot::{self, index::Initialized};
use crate::port::{
Disposition,
pool::{access, slot, slot::Phase},
replies,
};
#[derive(Clone, Copy)]
enum Lifecycle {
Vacant,
Handshaking,
Ready {
access: access::State,
receiving: bool,
},
Closing {
failure: replies::Failure,
disposition: Disposition,
},
}
pub(super) struct State<'d, const ID: u8> {
bound: Cell<MaybeUninit<slot::Bound<'d, ID>>>,
phase: Cell<Lifecycle>,
}
impl<'d, const ID: u8> State<'d, ID> {
pub(super) fn new() -> Self {
Self {
bound: Cell::new(MaybeUninit::uninit()),
phase: Cell::new(Lifecycle::Vacant),
}
}
pub(super) fn get(&self) -> Phase<'d, ID> {
let phase = self.phase.get();
if matches!(phase, Lifecycle::Vacant) {
return Phase::Vacant;
}
let bound = unsafe { self.bound.get().assume_init() };
match phase {
Lifecycle::Vacant => Phase::Vacant,
Lifecycle::Handshaking => Phase::Handshaking(bound),
Lifecycle::Ready { access, .. } => Phase::Ready { bound, access },
Lifecycle::Closing {
failure,
disposition: fate,
} => Phase::Closing {
bound,
failure,
disposition: fate,
},
}
}
pub(super) fn set(&self, phase: Phase<'d, ID>) {
let (bound, phase) = match phase {
Phase::Vacant => {
self.phase.set(Lifecycle::Vacant);
return;
}
Phase::Handshaking(bound) => (bound, Lifecycle::Handshaking),
Phase::Ready { bound, access } => (
bound,
Lifecycle::Ready {
access,
receiving: self.is_receiving(),
},
),
Phase::Closing {
bound,
failure,
disposition: fate,
} => (
bound,
Lifecycle::Closing {
failure,
disposition: fate,
},
),
};
self.bound.set(MaybeUninit::new(bound));
self.phase.set(phase);
}
pub(super) fn is_receiving(&self) -> bool {
matches!(
self.phase.get(),
Lifecycle::Ready {
receiving: true,
..
}
)
}
pub(super) fn set_receiving(&self, token: conn::Id<'d, KeyTag<ID>>, receiving: bool) -> bool {
let Lifecycle::Ready {
access,
receiving: current,
} = self.phase.get()
else {
return false;
};
if current == receiving {
return false;
}
let bound = unsafe { self.bound.get().assume_init() };
if bound.token != token {
return false;
}
self.phase.set(Lifecycle::Ready { access, receiving });
true
}
pub(super) fn listed(
proof: depot::raw::Proof<&slot::Slot<'d, ID>, Initialized<slot::Bound<'d, ID>>>,
) -> slot::Bound<'d, ID> {
unsafe { proof.into_inner().phase.bound.get().assume_init() }
}
}