use crate::{
protocol::runtime::waiters,
reply::{self, server},
};
use dope::core::driver::route;
use dope::manifold::client::attempt;
use dope::net::link::pool::change::opening;
use std::cell;
pub(super) enum Failure<'d, const ID: u8> {
Driver(crate::Error),
Open(reply::Open<'d, ID>),
Server(server::Diagnostic<'d>),
}
pub(crate) struct Signals<'d, const ID: u8> {
pub(super) cause: cell::Cell<Option<Failure<'d, ID>>>,
pub(crate) egress: waiters::Egress<'d>,
}
impl<'d, const ID: u8> Signals<'d, ID> {
pub(crate) fn is_failed(&self) -> bool {
unsafe { &*self.cause.as_ptr() }.is_some()
}
pub(crate) fn failure(&self) -> Option<reply::Error<'d, ID>> {
match unsafe { &*self.cause.as_ptr() } {
Some(Failure::Driver(error)) => Some(reply::Error::Driver(*error)),
Some(Failure::Open(cause)) => Some(reply::Error::Open(*cause)),
Some(Failure::Server(error)) => Some(match error.try_clone() {
Ok(error) => reply::Error::Server(error),
Err(error) => reply::Error::Driver(crate::Error::ResponseRetain(error)),
}),
None => None,
}
}
pub(crate) fn clear_open(&self) {
let clear = matches!(unsafe { &*self.cause.as_ptr() }, Some(Failure::Open(_)));
if clear {
std::mem::forget(self.cause.replace(None));
}
}
pub(crate) fn record_fatal(&self, error: crate::Error) {
self.record_cause(Failure::Driver(error));
}
pub(in crate::protocol) fn record_reply(&self, error: server::Diagnostic<'d>) {
self.record_cause(Failure::Server(error));
}
fn record_cause(&self, failure: Failure<'d, ID>) {
let replace = matches!(
unsafe { &*self.cause.as_ptr() },
None | Some(Failure::Open(_))
);
if replace {
std::mem::forget(self.cause.replace(Some(failure)));
}
}
pub(in crate::protocol) fn exhaust(
&self,
key: attempt::Id<'d, route::KeyTag<ID>>,
cause: opening::Exhaustion,
) {
self.record_cause(Failure::Open(reply::Open::Exhaustion { key, cause }));
}
pub(in crate::protocol) fn failed(
&self,
key: attempt::Id<'d, route::KeyTag<ID>>,
cause: attempt::ConnectFault,
) {
self.record_cause(Failure::Open(reply::Open::Transport { key, cause }));
}
}