use alloc::rc::{Rc, Weak};
use core::cell::RefCell;
use core::fmt::Display;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
#[derive(Debug)]
enum Relay<T> {
Pending,
Polled(Waker),
Computed(T),
Done,
}
#[derive(Debug)]
pub struct Sender<T> {
relay: Weak<RefCell<Relay<T>>>,
}
#[derive(Debug)]
pub struct Receiver<T> {
relay: Rc<RefCell<Relay<T>>>,
}
#[must_use]
pub fn forwarder<T>() -> (Sender<T>, Receiver<T>) {
let relay = Rc::new(RefCell::new(Relay::Pending));
let sender = Sender {
relay: Rc::downgrade(&relay),
};
let receiver = Receiver { relay };
(sender, receiver)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TryReceiveError {
SenderDropped,
NotSent,
AlreadyReceived,
}
impl<T> Sender<T> {
pub fn send(self, value: T) -> Result<(), T> {
let Some(relay) = self.relay.upgrade() else {
return Err(value);
};
let relay = &mut *relay.borrow_mut();
match core::mem::replace(relay, Relay::Computed(value)) {
Relay::Pending => Ok(()),
Relay::Polled(waker) => {
waker.wake();
Ok(())
}
Relay::Computed(_) | Relay::Done => unreachable!(),
}
}
}
impl<T> Receiver<T> {
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
let relay = &mut *self.relay.borrow_mut();
match relay {
Relay::Pending | Relay::Polled(_) => {
if Rc::weak_count(&self.relay) == 0 {
Err(TryReceiveError::SenderDropped)
} else {
Err(TryReceiveError::NotSent)
}
}
Relay::Computed(_) => {
let Relay::Computed(value) = core::mem::replace(relay, Relay::Done) else {
unreachable!()
};
Ok(value)
}
Relay::Done => Err(TryReceiveError::AlreadyReceived),
}
}
}
impl<T> Future for Receiver<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<T> {
let relay = &mut *self.relay.borrow_mut();
match relay {
Relay::Pending | Relay::Polled(_) => {
*relay = Relay::Polled(context.waker().clone());
Poll::Pending
}
Relay::Computed(_) => {
let Relay::Computed(value) = core::mem::replace(relay, Relay::Done) else {
unreachable!()
};
Poll::Ready(value)
}
Relay::Done => panic!("Receiver polled after receiving the value"),
}
}
}
impl Display for TryReceiveError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
TryReceiveError::SenderDropped => "sender already dropped".fmt(f),
TryReceiveError::NotSent => "result not sent yet".fmt(f),
TryReceiveError::AlreadyReceived => "result already received".fmt(f),
}
}
}