#![doc(html_logo_url = "https://raw.githubusercontent.com/Restioson/catty/master/catty.svg")]
use spin::Mutex;
use std::fmt::{self, Display, Formatter};
use std::task::{Context, Poll, Waker};
use std::{error::Error, future::Future, mem, pin::Pin, sync::Arc};
struct InnerArc<T>(Arc<Mutex<State<T>>>);
impl<T> InnerArc<T> {
fn replace_state<R>(&self, f: impl FnOnce(State<T>) -> (R, State<T>)) -> R {
let mut state = self.0.lock();
let old_state = mem::take(&mut *state);
let (ret, new_state) = f(old_state);
*state = new_state;
ret
}
}
impl<T> Clone for InnerArc<T> {
fn clone(&self) -> Self {
InnerArc(self.0.clone())
}
}
impl<T> Drop for InnerArc<T> {
fn drop(&mut self) {
self.replace_state(|old| match old {
State::ReceiverWaiting(waker) => (waker.wake(), State::Disconnected),
State::ItemSent(it) => ((), State::ItemSent(it)),
_ => ((), State::Disconnected),
})
}
}
enum State<T> {
ReceiverNotYetPolled,
ReceiverWaiting(Waker),
ItemSent(T),
Disconnected,
}
impl<T> Default for State<T> {
fn default() -> Self {
State::ReceiverNotYetPolled
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
pub struct Disconnected;
impl Display for Disconnected {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("oneshot disconnected")
}
}
impl Error for Disconnected {}
pub struct Sender<T>(InnerArc<T>);
impl<T> Sender<T> {
pub fn send(self, item: T) -> Result<(), T> {
self.0.replace_state(|old| match old {
State::Disconnected => (Err(item), State::Disconnected),
State::ReceiverWaiting(waker) => (Ok(waker.wake()), State::ItemSent(item)),
_ => (Ok(()), State::ItemSent(item)),
})
}
}
pub struct Receiver<T>(InnerArc<T>);
impl<T> Receiver<T> {
pub fn try_recv(&mut self) -> Result<Option<T>, Disconnected> {
self.0.replace_state(|old| match old {
State::ItemSent(item) => (Ok(Some(item)), State::Disconnected),
State::Disconnected => (Err(Disconnected), State::Disconnected),
old => (Ok(None), old),
})
}
}
impl<T> Future for Receiver<T> {
type Output = Result<T, Disconnected>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.0.replace_state(|old| match old {
State::ItemSent(item) => (Poll::Ready(Ok(item)), State::Disconnected),
State::Disconnected => (Poll::Ready(Err(Disconnected)), State::Disconnected),
_ => (Poll::Pending, State::ReceiverWaiting(cx.waker().clone())),
})
}
}
#[inline]
pub fn oneshot<T: Send>() -> (Sender<T>, Receiver<T>) {
let inner_arc = InnerArc(Arc::new(Mutex::new(State::default())));
(Sender(inner_arc.clone()), Receiver(inner_arc))
}