#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(future_incompatible, missing_docs)]
#![doc(
html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
use core::task::{Context, Poll};
use event_listener::{EventListener, Listener};
#[doc(hidden)]
pub use pin_project_lite::pin_project;
#[doc(no_inline)]
pub use event_listener;
#[macro_export]
macro_rules! easy_wrapper {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident
$(<
$( $lifetime:lifetime $(: $lifetime_bound:lifetime)? ),* $(,)?
$( $generics:ident
$(: $generics_bound:path)?
$(: ?$generics_unsized_bound:path)?
$(: $generics_lifetime_bound:lifetime)?
$(= $generics_default:ty)?
),* $(,)?
>)?
($inner:ty => $output:ty)
$(where
$( $where_clause_ty:ty
$(: $where_clause_bound:path)?
$(: ?$where_clause_unsized_bound:path)?
$(: $where_clause_lifetime_bound:lifetime)?
),* $(,)?
)?
;
$(#[$wait_meta:meta])*
$wait_vis: vis wait();
) => {
$crate::pin_project! {
$(#[$meta])*
$vis struct $name $(<
$( $lifetime $(: $lifetime_bound)? ),*
$( $generics
$(: $generics_bound)?
$(: ?$generics_unsized_bound)?
$(: $generics_lifetime_bound)?
$(= $generics_default)?
),*
>)? $(
where
$( $where_clause_ty
$(: $where_clause_bound)?
$(: ?$where_clause_unsized_bound)?
$(: $where_clause_lifetime_bound)?
),*
)? {
#[pin]
_inner: $crate::FutureWrapper<$inner>
}
}
impl $(<
$( $lifetime $(: $lifetime_bound)? ,)*
$( $generics
$(: $generics_bound)?
$(: ?$generics_unsized_bound)?
$(: $generics_lifetime_bound)?
$(= $generics_default)?
),*
>)? $name $(<
$( $lifetime ,)*
$( $generics ),*
>)? $(
where
$( $where_clause_ty
$(: $where_clause_bound)?
$(: ?$where_clause_unsized_bound)?
$(: $where_clause_lifetime_bound)?
),*
)? {
#[inline]
fn _new(inner: $inner) -> Self {
Self {
_inner: $crate::FutureWrapper::new(inner)
}
}
$(#[$wait_meta])*
#[inline]
$wait_vis fn wait(self) -> $output {
use $crate::EventListenerFuture;
self._inner.into_inner().wait()
}
pub(crate) fn poll_with_strategy<'__strategy, __S: $crate::Strategy<'__strategy>>(
self: ::core::pin::Pin<&mut Self>,
strategy: &mut __S,
context: &mut __S::Context,
) -> ::core::task::Poll<$output> {
self.project()._inner.get_pin_mut().poll_with_strategy(strategy, context)
}
}
impl $(<
$( $lifetime $(: $lifetime_bound)? ,)*
$( $generics
$(: $generics_bound)?
$(: ?$generics_unsized_bound)?
$(: $generics_lifetime_bound)?
$(= $generics_default)?
),*
>)? ::core::future::Future for $name $(
<
$( $lifetime ,)*
$( $generics ),*
>
)? $(
where
$( $where_clause_ty
$(: $where_clause_bound)?
$(: ?$where_clause_unsized_bound)?
$(: $where_clause_lifetime_bound)?
),*
)? {
type Output = $output;
#[inline]
fn poll(
self: ::core::pin::Pin<&mut Self>,
context: &mut ::core::task::Context<'_>
) -> ::core::task::Poll<Self::Output> {
self.project()._inner.poll(context)
}
}
};
}
pub trait EventListenerFuture {
type Output;
fn poll_with_strategy<'a, S: Strategy<'a>>(
self: Pin<&mut Self>,
strategy: &mut S,
context: &mut S::Context,
) -> Poll<Self::Output>;
#[cfg(all(feature = "std", not(target_family = "wasm")))]
#[cfg_attr(docsrs, doc(all(feature = "std", not(target_family = "wasm"))))]
fn wait(mut self) -> Self::Output
where
Self: Sized,
{
let mut this = unsafe { Pin::new_unchecked(&mut self) };
loop {
if let Poll::Ready(res) = this
.as_mut()
.poll_with_strategy(&mut Blocking::default(), &mut ())
{
return res;
}
}
}
}
pin_project_lite::pin_project! {
#[derive(Debug, Clone)]
pub struct FutureWrapper<F: ?Sized> {
#[pin]
inner: F,
}
}
impl<F: EventListenerFuture> FutureWrapper<F> {
#[inline]
pub fn new(inner: F) -> Self {
Self { inner }
}
#[inline]
pub fn into_inner(self) -> F {
self.inner
}
}
impl<F: ?Sized> FutureWrapper<F> {
#[inline]
pub fn get_ref(&self) -> &F {
&self.inner
}
#[inline]
pub fn get_mut(&mut self) -> &mut F {
&mut self.inner
}
#[inline]
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut F> {
self.project().inner
}
#[inline]
pub fn get_pin_ref(self: Pin<&Self>) -> Pin<&F> {
self.project_ref().inner
}
}
impl<F: EventListenerFuture> From<F> for FutureWrapper<F> {
#[inline]
fn from(inner: F) -> Self {
Self { inner }
}
}
impl<F: EventListenerFuture + ?Sized> Future for FutureWrapper<F> {
type Output = F::Output;
#[inline]
fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
self.project()
.inner
.poll_with_strategy(&mut NonBlocking::default(), context)
}
}
pub trait Strategy<'a> {
type Context: ?Sized;
type Future: Future + 'a;
fn poll<T, L: Listener<T> + Unpin>(
&mut self,
event_listener: &mut Option<L>,
context: &mut Self::Context,
) -> Poll<T>;
fn wait(&mut self, evl: EventListener) -> Self::Future;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct NonBlocking<'a> {
_marker: PhantomData<&'a mut &'a ()>,
}
impl<'a> Strategy<'_> for NonBlocking<'a> {
type Context = Context<'a>;
type Future = EventListener;
#[inline]
fn wait(&mut self, evl: EventListener) -> Self::Future {
evl
}
#[inline]
fn poll<T, L: Listener<T> + Unpin>(
&mut self,
event_listener: &mut Option<L>,
context: &mut Self::Context,
) -> Poll<T> {
let poll = Pin::new(
event_listener
.as_mut()
.expect("`event_listener` should never be `None`"),
)
.poll(context);
if poll.is_ready() {
*event_listener = None;
}
poll
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg(all(feature = "std", not(target_family = "wasm")))]
pub struct Blocking {
_private: (),
}
#[cfg(all(feature = "std", not(target_family = "wasm")))]
impl Strategy<'_> for Blocking {
type Context = ();
type Future = Ready;
#[inline]
fn wait(&mut self, evl: EventListener) -> Self::Future {
evl.wait();
Ready { _private: () }
}
#[inline]
fn poll<T, L: Listener<T> + Unpin>(
&mut self,
event_listener: &mut Option<L>,
_context: &mut Self::Context,
) -> Poll<T> {
let result = event_listener
.take()
.expect("`event_listener` should never be `None`")
.wait();
Poll::Ready(result)
}
}
#[cfg(feature = "std")]
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct Ready {
_private: (),
}
#[cfg(feature = "std")]
impl Future for Ready {
type Output = ();
#[inline]
fn poll(self: Pin<&mut Self>, _context: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(())
}
}
#[test]
fn send_and_sync() {
fn assert_send_and_sync<T: Send + Sync>() {}
#[cfg(all(feature = "std", not(target_family = "wasm")))]
{
assert_send_and_sync::<Blocking>();
assert_send_and_sync::<Ready>();
}
assert_send_and_sync::<NonBlocking<'static>>();
assert_send_and_sync::<FutureWrapper<()>>();
}