use dope_core::driver::{self, route::Identity};
use dope_net::{
Transport,
link::{
egress::reserve::demand,
pool::{self, change, recv::input},
slot::types,
},
wire,
};
use crate::client::{
app, attempt,
close::Directive,
conn::{self, State, claim, closing},
};
pub(in crate::client) struct OwnedDrain<'d, ID: Identity> {
owner: attempt::Id<'d, ID>,
drain: Drain<'d, ID>,
}
#[must_use = "a committed close decision must remain inside its pool claim"]
pub(in crate::client) struct Commit<'d, ID: Identity, C> {
pub(super) app: Application<'d, ID, C>,
pub(super) rule: Directive,
}
#[must_use = "a retired drain must transfer its exact connection and decision"]
pub(in crate::client) struct Retired<'d, ID: Identity, C> {
app: Application<'d, ID, C>,
rule: Directive,
}
impl<'d, ID: Identity, C> Retired<'d, ID, C> {
pub(in crate::client) fn resolve<'w, A, V>(
self,
app: &mut A,
driver: &mut driver::Context<'_, 'd>,
output: impl FnOnce(Directive) -> V,
) -> V
where
'd: 'w,
A: app::Lifecycle<'w, 'd, ID, Conn = C>,
{
let reason = self.rule.reason();
let value = output(self.rule);
match self.app {
Application::Detached => {}
Application::Drained {
proof,
mut conn,
transmit,
} => {
app.drained(
conn::Retired::new(proof, reason, transmit),
&mut conn,
driver,
);
}
}
value
}
}
#[must_use = "application retirement state must reach the connection"]
pub(in crate::client) enum Application<'d, ID: Identity, C> {
Detached,
Drained {
proof: app::Quiesced<'d, ID>,
conn: C,
transmit: Option<conn::Transmit>,
},
}
pub(in crate::client) trait BindOwner<'d, ID: Identity> {
type Bound;
fn bind(self, owner: attempt::Id<'d, ID>) -> Self::Bound;
}
impl<'d, ID: Identity> OwnedDrain<'d, ID> {
pub(super) const fn new(owner: attempt::Id<'d, ID>, drain: Drain<'d, ID>) -> Self {
Self { owner, drain }
}
pub(in crate::client) fn run<R: BindOwner<'d, ID>>(
self,
retire: impl FnOnce(Drain<'d, ID>) -> R,
) -> R::Bound {
retire(self.drain).bind(self.owner)
}
}
#[must_use = "claim restoration must preserve its owner"]
pub(super) enum Restore {
Current(Resume),
Stale,
}
pub(super) enum Resume {
Restored,
Pending,
}
pub(in crate::client) enum Recovery<'d, ID: Identity> {
Restored,
Rescheduled,
Obsolete(Obsolete<'d, ID>),
}
#[must_use = "obsolete drain evidence must reach owner reconciliation"]
pub(in crate::client) struct Obsolete<'d, ID: Identity>(Drain<'d, ID>);
impl<ID: Identity> Obsolete<'_, ID> {
pub(in crate::client) fn resolve(self) {
let Self(Drain { conn: _ }) = self;
}
}
pub(in crate::client) enum Access {
Current(claim::Transport),
Stale,
}
#[must_use = "drain readiness owns one finalized connection generation"]
pub(in crate::client) struct Drain<'d, ID: Identity> {
conn: conn::Id<'d, ID>,
}
impl<'d, ID: Identity> Drain<'d, ID> {
pub(in crate::client) const fn connection(&self) -> conn::Id<'d, ID> {
self.conn
}
pub(super) const fn new(conn: conn::Id<'d, ID>, _: closing::Claim) -> Self {
Self { conn }
}
pub(in crate::client) fn release<'w, T, W, C, O, M, B, const IOV: usize, D>(
self,
pool: &mut pool::Connections<'_, 'w, 'd, ID, T, W, State<'d, C, O>, M, B, IOV, D>,
) -> Recovery<'d, ID>
where
'd: 'w,
T: Transport,
W: wire::Spec,
M: input::Mode<W>,
D: demand::Mode,
{
let Some(slot) = pool.get_mut(self.key()) else {
return Recovery::Obsolete(Obsolete(self));
};
match slot.state.closing.restore() {
Restore::Current(Resume::Restored) => Recovery::Restored,
Restore::Current(Resume::Pending) => {
slot.io().wake_target().wake();
Recovery::Rescheduled
}
Restore::Stale => Recovery::Obsolete(Obsolete(self)),
}
}
pub(in crate::client) const fn key(&self) -> pool::Key<'d, ID> {
self.conn.key()
}
pub(in crate::client) fn obsolete(self) -> Obsolete<'d, ID> {
Obsolete(self)
}
pub(in crate::client) fn authorize<'w, C, O, W>(
&self,
slot: &types::Connection<'w, 'd, ID, W, State<'d, C, O>>,
) -> Access
where
'd: 'w,
W: wire::Spec,
{
if self.key() != slot.key() {
return Access::Stale;
}
match slot.state.closing.authorize() {
Some(io) => Access::Current(io),
None => Access::Stale,
}
}
}
impl<'d, ID: Identity, C> change::Finalize<Commit<'d, ID, C>> for Drain<'d, ID> {
type Output = Retired<'d, ID, C>;
fn finalize(self, proof: Commit<'d, ID, C>) -> Self::Output {
let Self { conn: _ } = self;
let Commit { app, rule } = proof;
Retired { app, rule }
}
}