use std::{marker, mem};
use o3::base::marker::ThreadBound;
use crate::wire::{self, failure};
type Cause<'d, W> =
<<W as wire::Spec>::TerminalFailure as failure::Class<W, failure::Terminal>>::Error<'d>;
pub struct Error<'d, W: wire::Spec + 'd> {
cause: Cause<'d, W>,
closing: Option<Cause<'d, W>>,
}
impl<'d, W: wire::Spec + 'd> Error<'d, W> {
pub const fn from_cause(cause: Cause<'d, W>) -> Self {
Self {
cause,
closing: None,
}
}
pub const fn causes(&self) -> (&Cause<'d, W>, Option<&Cause<'d, W>>) {
(&self.cause, self.closing.as_ref())
}
pub fn into_parts(self) -> (Cause<'d, W>, Option<Cause<'d, W>>) {
(self.cause, self.closing)
}
}
impl<'d, W> Error<'d, W>
where
W: wire::Spec<TerminalFailure = failure::Never> + 'd,
{
pub fn eliminate<T>(self) -> T {
match self.cause {}
}
}
#[repr(transparent)]
pub struct Drain<'a, 'd, W: wire::Spec + 'd>(
&'a mut Option<Cause<'d, W>>,
marker::PhantomData<ThreadBound>,
);
impl<'d, W: wire::Spec + 'd> Drain<'_, 'd, W> {
pub fn fail(self, cause: Cause<'d, W>) {
*self.0 = Some(cause);
}
}
#[must_use]
pub struct Processed<T> {
output: T,
captured: Captured,
}
impl<T> Processed<T> {
pub fn map<U>(self, map: impl FnOnce(T) -> U) -> Processed<U> {
Processed {
output: map(self.output),
captured: self.captured,
}
}
pub(crate) fn into_parts(self) -> (T, Captured) {
(self.output, self.captured)
}
}
#[must_use]
#[derive(Clone, Copy)]
pub(crate) enum Captured {
None,
Eof,
Terminal,
}
#[must_use]
pub enum Terminal<E> {
Clean,
Failed(E),
}
pub enum Kind {
Clean,
Failed,
}
enum Phase<E> {
Open,
Eof,
ReadClosed,
BothClean,
BothFailed(E),
RecvClean,
RecvFailed(E),
SendClean,
SendFailed(E),
Posted,
ReadyClean,
ReadyFailed(E),
Done,
}
pub struct State<E>(Phase<E>);
#[repr(transparent)]
pub struct Send<'a, E>(&'a mut State<E>, marker::PhantomData<ThreadBound>);
#[repr(transparent)]
pub struct Both<'a, E>(&'a mut State<E>, marker::PhantomData<ThreadBound>);
#[must_use]
pub(crate) enum Admission<T> {
Open(T),
Closed,
}
#[must_use]
pub(crate) enum Wake {
Idle,
Ready,
}
#[must_use]
pub enum Receive<E> {
Idle,
Eof,
Pending,
Ready(Terminal<E>),
}
impl<E> Send<'_, E> {
pub fn output<T>(self, output: T) -> Processed<T> {
Processed {
output,
captured: Captured::None,
}
}
pub fn terminal<T>(self, output: T, end: Terminal<E>) -> Processed<T> {
(self.0).0 = match end {
Terminal::Clean => Phase::SendClean,
Terminal::Failed(cause) => Phase::SendFailed(cause),
};
Processed {
output,
captured: Captured::Terminal,
}
}
}
impl<E> Both<'_, E> {
pub fn output<T>(self, output: T) -> Processed<T> {
Processed {
output,
captured: Captured::None,
}
}
pub fn eof<T>(self, output: T) -> Processed<T> {
(self.0).0 = Phase::Eof;
Processed {
output,
captured: Captured::Eof,
}
}
pub fn terminal<T>(self, output: T, end: Terminal<E>) -> Processed<T> {
(self.0).0 = match end {
Terminal::Clean => Phase::BothClean,
Terminal::Failed(cause) => Phase::BothFailed(cause),
};
Processed {
output,
captured: Captured::Terminal,
}
}
}
impl<'d, W: wire::Spec + 'd> State<Error<'d, W>> {
pub(crate) fn drain(&mut self) -> Option<Drain<'_, 'd, W>> {
match &mut self.0 {
Phase::BothFailed(error) | Phase::SendFailed(error) if error.closing.is_none() => {
Some(Drain(&mut error.closing, marker::PhantomData))
}
_ => None,
}
}
}
impl<E> State<E> {
pub(crate) const fn open() -> Self {
Self(Phase::Open)
}
pub(crate) fn send(&mut self) -> Admission<Send<'_, E>> {
if matches!(&self.0, Phase::Open | Phase::Eof | Phase::ReadClosed) {
Admission::Open(Send(self, marker::PhantomData))
} else {
Admission::Closed
}
}
pub(crate) fn both(&mut self) -> Admission<Both<'_, E>> {
if self.is_open() {
Admission::Open(Both(self, marker::PhantomData))
} else {
Admission::Closed
}
}
fn is_open(&self) -> bool {
matches!(&self.0, Phase::Open)
}
pub(crate) const fn is_read_done(&self) -> bool {
matches!(&self.0, Phase::Eof | Phase::ReadClosed)
}
pub const fn has_eof(&self) -> bool {
matches!(&self.0, Phase::Eof)
}
pub fn finish_recv(&mut self) -> Receive<E> {
match mem::replace(&mut self.0, Phase::Open) {
Phase::Eof => {
self.0 = Phase::ReadClosed;
Receive::Eof
}
phase @ (Phase::ReadClosed | Phase::Done) => {
self.0 = phase;
Receive::Idle
}
Phase::BothClean | Phase::SendClean => {
self.0 = Phase::Posted;
Receive::Ready(Terminal::Clean)
}
Phase::RecvClean | Phase::ReadyClean => {
self.0 = Phase::Done;
Receive::Ready(Terminal::Clean)
}
Phase::RecvFailed(cause) | Phase::ReadyFailed(cause) => {
self.0 = Phase::Done;
Receive::Ready(Terminal::Failed(cause))
}
Phase::BothFailed(cause) | Phase::SendFailed(cause) => {
self.0 = Phase::SendFailed(cause);
Receive::Pending
}
Phase::Posted => {
self.0 = Phase::Posted;
Receive::Pending
}
Phase::Open => Receive::Idle,
}
}
pub(crate) fn finish_send(&mut self) -> Wake {
match mem::replace(&mut self.0, Phase::Open) {
Phase::Open => Wake::Idle,
phase @ (Phase::Eof | Phase::ReadClosed | Phase::Done) => {
self.0 = phase;
Wake::Idle
}
Phase::BothClean | Phase::RecvClean => {
self.0 = Phase::RecvClean;
Wake::Idle
}
Phase::BothFailed(cause) | Phase::RecvFailed(cause) => {
self.0 = Phase::RecvFailed(cause);
Wake::Idle
}
Phase::SendClean | Phase::ReadyClean => {
self.0 = Phase::ReadyClean;
Wake::Ready
}
Phase::SendFailed(cause) | Phase::ReadyFailed(cause) => {
self.0 = Phase::ReadyFailed(cause);
Wake::Ready
}
Phase::Posted => {
self.0 = Phase::Done;
Wake::Ready
}
}
}
pub const fn has_send(&self) -> bool {
matches!(
&self.0,
Phase::BothFailed(_) | Phase::SendFailed(_) | Phase::Posted
)
}
pub const fn is_pending(&self) -> bool {
!matches!(&self.0, Phase::Open | Phase::ReadClosed | Phase::Done)
}
pub const fn is_done(&self) -> bool {
matches!(&self.0, Phase::Done)
}
pub const fn kind(&self) -> Option<Kind> {
match &self.0 {
Phase::Open | Phase::Eof | Phase::ReadClosed | Phase::Done => None,
Phase::BothClean
| Phase::RecvClean
| Phase::SendClean
| Phase::Posted
| Phase::ReadyClean => Some(Kind::Clean),
Phase::BothFailed(_)
| Phase::RecvFailed(_)
| Phase::SendFailed(_)
| Phase::ReadyFailed(_) => Some(Kind::Failed),
}
}
}
const _: () = assert!(mem::size_of::<Wake>() == 1);
const _: () = assert!(mem::size_of::<Processed<()>>() == 1);
const _: () =
assert!(mem::size_of::<Drain<'static, 'static, wire::Identity>>() == mem::size_of::<usize>());
const _: () = assert!(mem::size_of::<Send<'static, ()>>() == mem::size_of::<usize>());
const _: () = assert!(mem::size_of::<Both<'static, ()>>() == mem::size_of::<usize>());
const _: () = assert!(mem::size_of::<Admission<Send<'static, ()>>>() == mem::size_of::<usize>());
const _: () = assert!(mem::size_of::<State<usize>>() <= 2 * mem::size_of::<usize>());