diff --git a/.travis.yml b/.travis.yml index c70fb67..70eea46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,24 @@ language: rust rust: - - stable + - nightly before_script: | - rustup component add rustfmt-preview && - rustup component add clippy-preview + rustup component add clippy-preview; + if ! rustup component add clippy; then + target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/clippy`; + echo "'clippy' is unavailable on the toolchain 'nightly', using the toolchain 'nightly-$target' instead"; + rustup toolchain install nightly-$target; + rustup default nightly-$target; + rustup component add clippy; + fi + rustup component add rustfmt-preview; + if ! rustup component add rustfmt; then + target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`; + echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead"; + rustup toolchain install nightly-$target; + rustup default nightly-$target; + rustup component add rustfmt; + fi script: | cargo fmt -- --check && cargo clippy -- -D clippy && diff --git a/src/join.rs b/src/join.rs index 082eded..3a793d9 100644 --- a/src/join.rs +++ b/src/join.rs @@ -28,20 +28,24 @@ macro_rules! join { $( // Move future into a local so that it is pinned in one place and // is no longer accessible by the end user. - let mut $fut = $crate::maybe_done($fut); + let mut $fut = $crate::MaybeDone::new($fut); )* $crate::utils::poll_fn(move |cx| { + use $crate::utils::future::Future; + use $crate::utils::task::Poll; + use $crate::utils::pin::Pin; + let mut all_done = true; $( - all_done &= $crate::utils::future::Future::poll( - unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }, cx).is_ready(); + let fut = unsafe { Pin::new_unchecked(&mut $fut) }; + all_done &= Future::poll(fut, cx).is_ready(); )* if all_done { - $crate::utils::task::Poll::Ready(($( - unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }.take_output().unwrap(), + Poll::Ready(($( + unsafe { Pin::new_unchecked(&mut $fut) }.take().unwrap(), )*)) } else { - $crate::utils::task::Poll::Pending + Poll::Pending } }).await } diff --git a/src/lib.rs b/src/lib.rs index 6e00b26..3a6a8a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ mod select; mod try_join; mod try_select; -pub use maybe_done::{maybe_done, MaybeDone}; +pub use maybe_done::MaybeDone; /// Helper re-exports for use in macros. pub mod utils { diff --git a/src/maybe_done.rs b/src/maybe_done.rs index 59d934f..a7bc7c9 100644 --- a/src/maybe_done.rs +++ b/src/maybe_done.rs @@ -10,11 +10,6 @@ use core::pin::Pin; use futures_core::ready; use futures_core::task::{Context, Poll}; -/// Create a new instance of `MaybeDone`. -pub fn maybe_done(future: Fut) -> MaybeDone { - MaybeDone::Future(future) -} - /// A future that may have completed. #[derive(Debug)] pub enum MaybeDone { @@ -23,29 +18,19 @@ pub enum MaybeDone { /// The output of the completed future Done(Fut::Output), /// The empty variant after the result of a [`MaybeDone`] has been - /// taken using the [`take_output`](MaybeDone::take_output) method. + /// taken using the [`take`](MaybeDone::take) method. Gone, } impl MaybeDone { - /// Returns an [`Option`] containing a mutable reference to the output of the future. - /// The output of this method will be [`Some`] if and only if the inner - /// future has been completed and [`take_output`](MaybeDone::take_output) - /// has not yet been called. - #[inline] - pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> { - unsafe { - let this = self.get_unchecked_mut(); - match this { - MaybeDone::Done(res) => Some(res), - _ => None, - } - } + /// Create a new instance of `MaybeDone`. + pub fn new(future: Fut) -> MaybeDone { + Self::Future(future) } /// Returns an [`Option`] containing a reference to the output of the future. /// The output of this method will be [`Some`] if and only if the inner - /// future has been completed and [`take_output`](MaybeDone::take_output) + /// future has been completed and [`take`](MaybeDone::take) /// has not yet been called. #[inline] pub fn output(self: Pin<&Self>) -> Option<&Fut::Output> { @@ -56,10 +41,25 @@ impl MaybeDone { } } + /// Returns an [`Option`] containing a mutable reference to the output of the future. + /// The output of this method will be [`Some`] if and only if the inner + /// future has been completed and [`take`](MaybeDone::take) + /// has not yet been called. + #[inline] + pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> { + unsafe { + let this = self.get_unchecked_mut(); + match this { + MaybeDone::Done(res) => Some(res), + _ => None, + } + } + } + /// Attempt to take the output of a `MaybeDone` without driving it /// towards completion. #[inline] - pub fn take_output(self: Pin<&mut Self>) -> Option { + pub fn take(self: Pin<&mut Self>) -> Option { unsafe { let this = self.get_unchecked_mut(); match this { @@ -73,6 +73,8 @@ impl MaybeDone { } } } + + // fn ok(self) -> Option {} } impl Future for MaybeDone { @@ -80,7 +82,7 @@ impl Future for MaybeDone { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let res = unsafe { - match self.as_mut().get_unchecked_mut() { + match Pin::as_mut(&mut self).get_unchecked_mut() { MaybeDone::Future(a) => ready!(Pin::new_unchecked(a).poll(cx)), MaybeDone::Done(_) => return Poll::Ready(()), MaybeDone::Gone => panic!("MaybeDone polled after value taken"), diff --git a/src/select.rs b/src/select.rs index c26df62..4f1ebc3 100644 --- a/src/select.rs +++ b/src/select.rs @@ -35,7 +35,7 @@ macro_rules! select { $( // Move future into a local so that it is pinned in one place and // is no longer accessible by the end user. - let mut $fut = $crate::maybe_done($fut); + let mut $fut = $crate::MaybeDone::new($fut); )* $crate::utils::poll_fn(move |cx| { use $crate::utils::future::Future; @@ -46,7 +46,7 @@ macro_rules! select { let fut = unsafe { Pin::new_unchecked(&mut $fut) }; if Future::poll(fut, cx).is_ready() { let fut = unsafe { Pin::new_unchecked(&mut $fut) }; - let output = fut.take_output().unwrap(); + let output = fut.take().unwrap(); return Poll::Ready(output); } )* diff --git a/src/try_join.rs b/src/try_join.rs index b9b8a7b..4936ad0 100644 --- a/src/try_join.rs +++ b/src/try_join.rs @@ -43,39 +43,49 @@ macro_rules! try_join { ($($fut:ident),* $(,)?) => { { async { + use $crate::utils::future::Future; + use $crate::utils::pin::Pin; + use $crate::utils::poll_fn; + use $crate::utils::result::Result; + use $crate::utils::task::Poll; + $( // Move future into a local so that it is pinned in one place and // is no longer accessible by the end user. - let mut $fut = $crate::maybe_done($fut); + let mut $fut = $crate::MaybeDone::new($fut); )* - let res: $crate::utils::result::Result<_, _> = $crate::utils::poll_fn(move |cx| { + let res: Result<_, _> = poll_fn(move |cx| { let mut all_done = true; $( - if $crate::utils::future::Future::poll( - unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }, cx).is_pending() - { + let fut = unsafe { Pin::new_unchecked(&mut $fut) }; + if Future::poll(fut, cx).is_pending() { all_done = false; - } else if unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }.output_mut().unwrap().is_err() { + } else if unsafe { Pin::new_unchecked(&mut $fut) }.output_mut().unwrap().is_err() { // `.err().unwrap()` rather than `.unwrap_err()` so that we don't introduce // a `T: Debug` bound. - return $crate::utils::task::Poll::Ready( - $crate::utils::result::Result::Err( - unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }.take_output().unwrap().err().unwrap() - ) - ); + return Poll::Ready( + Result::Err(unsafe { Pin::new_unchecked(&mut $fut) } + .take() + .unwrap() + .err() + .unwrap() + )); } )* if all_done { - $crate::utils::task::Poll::Ready( - $crate::utils::result::Result::Ok(($( - // `.ok().unwrap()` rather than `.unwrap()` so that we don't introduce - // an `E: Debug` bound. - unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }.take_output().unwrap().ok().unwrap(), - )*)) - ) + let res = ($( + // `.ok().unwrap()` rather than `.unwrap()` so that we don't introduce + // an `E: Debug` bound. + unsafe { Pin::new_unchecked(&mut $fut) } + .take() + .unwrap() + .ok() + .unwrap(), + )*); + Poll::Ready(Result::Ok(res)) } else { - $crate::utils::task::Poll::Pending + Poll::Pending } }).await; res diff --git a/src/try_select.rs b/src/try_select.rs index f453425..5aeeac2 100644 --- a/src/try_select.rs +++ b/src/try_select.rs @@ -6,7 +6,7 @@ /// /// `try_select!` is similar to [`select!`], but keeps going if a future /// resolved to an error until all futures have been resolved. In which case -/// the last error found will be returned. +/// the error of the last item in the list will be returned. /// /// This macro is only usable inside of async functions, closures, and blocks. /// @@ -19,7 +19,7 @@ /// use futures::future; /// use std::io::{Error, ErrorKind}; /// -/// let a = future::pending::>(); +/// let a = future::pending::>(); /// let b = future::ready(Err(Error::from(ErrorKind::Other))); /// let c = future::ready(Ok(1u8)); /// @@ -33,16 +33,19 @@ macro_rules! try_select { ($($fut:ident),+ $(,)?) => { { async { + use $crate::utils::future::Future; + use $crate::utils::pin::Pin; + use $crate::utils::poll_fn; + use $crate::utils::result::Result; + use $crate::utils::task::Poll; + $( // Move future into a local so that it is pinned in one place and // is no longer accessible by the end user. - let mut $fut = $crate::maybe_done($fut); + let mut $fut = $crate::MaybeDone::new($fut); )* - $crate::utils::poll_fn(move |cx| { - use $crate::utils::future::Future; - use $crate::utils::task::Poll; - use $crate::utils::pin::Pin; + let res: Result<_, _> = poll_fn(move |cx| { let mut all_done = true; $( @@ -51,8 +54,8 @@ macro_rules! try_select { let fut = Pin::new(&$fut); if fut.output().unwrap().is_ok() { let fut = unsafe { Pin::new_unchecked(&mut $fut) }; - let output = fut.take_output().unwrap(); - return Poll::Ready(output); + let res = fut.take().unwrap(); + return Poll::Ready(res); } else { all_done = false; } @@ -62,20 +65,20 @@ macro_rules! try_select { )* if all_done { - // We need to iterate over all items to not get an - // "unreachable code" warning. + // We need to iterate over all items to get the last error. let mut err = None; $( if err.is_none() { let fut = unsafe { Pin::new_unchecked(&mut $fut) }; - err = Some(fut.take_output().unwrap()); + err = Some(fut.take().unwrap()); } )* return Poll::Ready(err.unwrap()); } else { Poll::Pending } - }).await + }).await; + res } } } }