dope-manifold 0.26.0

Owned application and service composition for dope
Documentation
use dope_core::driver::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, Reason},
    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<A, V>(
        self,
        resolve: impl FnOnce(Directive) -> (Reason, V),
        notify: impl FnOnce(conn::Retired<'d, ID, A>, &mut C),
    ) -> V
    where
        A: app::Application<'d, ID, Conn = C>,
    {
        let (reason, value) = resolve(self.rule);
        match self.app {
            Application::Detached => {}
            Application::Drained {
                proof,
                mut conn,
                transmit,
            } => {
                notify(conn::Retired::new(proof, reason, transmit), &mut conn);
            }
        }
        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 an exact frozen decision"]
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: _, rule: _ }) = self;
    }
}

#[must_use = "absent drain evidence must reach owner reconciliation"]
pub(in crate::client) struct Absent<'d, ID: Identity>(Drain<'d, ID>);

impl<ID: Identity> Absent<'_, ID> {
    pub(in crate::client) fn resolve(self) {
        let Self(Drain { conn: _, rule: _ }) = 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>,
    rule: Directive,
}

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 rule(&self) -> Directive {
        self.rule
    }

    pub(super) const fn new(conn: conn::Id<'d, ID>, claim: closing::Claim) -> Self {
        Self {
            conn,
            rule: claim.into_rule(),
        }
    }

    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(self.rule) {
            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 absent(self) -> Absent<'d, ID> {
        Absent(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(self.rule) {
            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: _, rule: _ } = self;
        let Commit { app, rule } = proof;
        Retired { app, rule }
    }
}