blob: c6de3f62cd5ec3f24b25b7a70446980b65588558 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Provides utilities for working with futures.
use std::pin::Pin;
use futures::{future::FusedFuture, task, Future};
/// Future for the [`FutureExt::replace_value`] method.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct ReplaceValue<Fut: Future<Output = ()>, T> {
future: Fut,
value: Option<T>,
}
/// An extension trait for [`futures::Future`] that provides specialized adapters.
pub trait FutureExt: Future<Output = ()> {
/// Map this future's output to a different type, returning a new future of
/// the resulting type.
///
/// This function is similar to futures::FutureExt::map except:
///
/// - it takes a value instead of a closure
///
/// - it returns a type that can be named
///
/// This function is useful when a mapped future is needed and boxing is not
/// desired.
fn replace_value<T>(self, value: T) -> ReplaceValue<Self, T>
where
Self: Sized,
{
ReplaceValue::new(self, value)
}
}
impl<Fut: Future<Output = ()>, T> ReplaceValue<Fut, T> {
fn new(future: Fut, value: T) -> Self {
Self { future, value: Some(value) }
}
}
impl<T: ?Sized + Future<Output = ()>> FutureExt for T {}
impl<Fut: Future<Output = ()> + Unpin, T: Unpin> Future for ReplaceValue<Fut, T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
let Self { future, value } = self.get_mut();
let () = futures::ready!(Pin::new(future).poll(cx));
task::Poll::Ready(
value.take().expect("ReplaceValue must not be polled after it returned `Poll::Ready`"),
)
}
}
impl<Fut: Future<Output = ()> + Unpin, T: Unpin> FusedFuture for ReplaceValue<Fut, T> {
fn is_terminated(&self) -> bool {
self.value.is_none()
}
}
#[cfg(test)]
mod tests {
use fuchsia_async as fasync;
#[fasync::run_singlethreaded(test)]
async fn replace_value_trivial() {
use super::FutureExt as _;
let value = "hello world";
assert_eq!(futures::future::ready(()).replace_value(value).await, value);
}
#[test]
fn is_terminated() {
use super::FutureExt as _;
use futures::future::{FusedFuture as _, FutureExt as _};
let fut = &mut futures::future::ready(()).replace_value(());
assert!(!fut.is_terminated());
assert_eq!(fut.now_or_never(), Some(()));
assert!(fut.is_terminated());
}
}