#![no_std]
use core::marker::PhantomData;
use core::mem;
use core::ops::Deref;
use core::ops::DerefMut;
use core::pin::Pin;
use core::ptr;
use core::task::Context;
use core::task::Poll;
use core::task::RawWaker;
use core::task::RawWakerVTable;
use core::task::Waker;
#[derive(Default)]
enum Channel<Request, Response> {
Tx(Request),
Rx(Response),
#[default]
None,
}
impl<Request, Response> Channel<Request, Response> {
fn tx(request: Request) -> Self {
Self::Tx(request)
}
fn rx(response: Response) -> Self {
Self::Rx(response)
}
fn take(&mut self) -> Self {
mem::take(self)
}
}
struct SansFuture<Request, Response> {
request: Option<Request>,
_response: PhantomData<Response>,
_not_send_sync: PhantomData<*const ()>,
}
impl<Request: Unpin, Response: Unpin> Future for SansFuture<Request, Response> {
type Output = Result<Response, Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let waker = cx.waker();
if !ptr::eq(waker.vtable(), WAKER_VTABLE) {
return Poll::Ready(Err(Error::Runtime));
}
let ch = unsafe { &mut *(waker.data() as *mut Channel<Request, Response>) };
if let Some(request) = self.request.take() {
*ch = Channel::tx(request);
Poll::Pending
} else {
match ch.take() {
Channel::Rx(response) => Poll::Ready(Ok(response)),
Channel::Tx(_) => Poll::Pending,
Channel::None => Poll::Pending,
}
}
}
}
#[derive(Debug)]
pub struct Sans<Request, Response> {
_request: PhantomData<Request>,
_response: PhantomData<Response>,
_not_send_sync: PhantomData<*const ()>,
}
impl<Request: Unpin, Response: Unpin> Sans<Request, Response> {
pub fn handle(
&self,
request: Request,
) -> impl Future<Output = Result<Response, Error>> + use<Request, Response> {
SansFuture {
request: Some(request),
_response: PhantomData,
_not_send_sync: PhantomData,
}
}
}
#[derive(Debug)]
pub struct Io<Request, Response> {
_request: PhantomData<Request>,
_response: PhantomData<Response>,
_not_send_sync: PhantomData<*const ()>,
}
#[derive(Debug)]
pub struct IoHandle<Request, Response, Task> {
_request: PhantomData<Request>,
_response: PhantomData<Response>,
_not_send_sync: PhantomData<*const ()>,
task: Pin<Task>,
}
impl<Request, Response> Io<Request, Response> {
#[allow(clippy::type_complexity)]
pub fn start<Task>(
&self,
task: Pin<Task>,
) -> Result<Option<(IoHandle<Request, Response, Task>, Request)>, Error>
where
Task: DerefMut,
<Task as Deref>::Target: Future<Output = ()>,
{
let mut handler = IoHandle {
_request: PhantomData,
_response: PhantomData,
_not_send_sync: PhantomData,
task,
};
let request = handler.run_async(Channel::<Request, Response>::None);
request.map(|request| request.map(|request| (handler, request)))
}
#[allow(clippy::type_complexity)]
pub fn handle<Task>(
&self,
mut handler: IoHandle<Request, Response, Task>,
response: Response,
) -> Result<Option<(IoHandle<Request, Response, Task>, Request)>, Error>
where
Task: DerefMut,
<Task as Deref>::Target: Future<Output = ()>,
{
let request = handler.run_async(Channel::rx(response));
request.map(|request| request.map(|request| (handler, request)))
}
}
impl<Request, Response, Task> IoHandle<Request, Response, Task>
where
Task: DerefMut,
<Task as Deref>::Target: Future<Output = ()>,
{
fn run_async(&mut self, mut ch: Channel<Request, Response>) -> Result<Option<Request>, Error> {
let waker = unsafe { Waker::new(&mut ch as *mut _ as *mut (), WAKER_VTABLE) };
let mut cx = Context::from_waker(&waker);
match self.task.as_mut().poll(&mut cx) {
Poll::Ready(_) => Ok(None),
Poll::Pending => {
if let Channel::Tx(request) = ch {
Ok(Some(request))
} else {
Err(Error::Inconsistency)
}
}
}
}
}
#[derive(Debug)]
pub enum Error {
Inconsistency,
Runtime,
}
pub fn new<Request, Response>() -> (Sans<Request, Response>, Io<Request, Response>) {
(
Sans {
_request: PhantomData,
_response: PhantomData,
_not_send_sync: PhantomData,
},
Io {
_request: PhantomData,
_response: PhantomData,
_not_send_sync: PhantomData,
},
)
}
const WAKER_VTABLE: &RawWakerVTable = &RawWakerVTable::new(
|data| RawWaker::new(data, WAKER_VTABLE),
|_| {},
|_| {},
|_| {},
);
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
struct ReadMe;