#![doc = include_utils::include_md!("README.md:description")]
#![doc = include_str!("../examples/context/mod.rs")]
use std::{fmt::Debug, future::Future};
use future::ScopedFutureWithValue;
use imp::FutureLocalKey;
pub mod future;
mod imp;
pub struct FutureOnceCell<T>(imp::FutureLocalKey<T>);
impl<T> FutureOnceCell<T> {
#[must_use]
pub const fn new() -> Self {
Self(imp::FutureLocalKey::new())
}
}
impl<T> Default for FutureOnceCell<T> {
#[must_use]
fn default() -> Self {
Self::new()
}
}
impl<T: Send + 'static> FutureOnceCell<T> {
#[inline]
pub fn with<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
let value = self.0.local_key().borrow();
f(value
.as_ref()
.expect("cannot access a future local value without setting it first"))
}
#[inline]
pub fn get(&'static self) -> T
where
T: Copy,
{
self.0.local_key().borrow().unwrap()
}
#[inline]
pub fn scope<F>(&'static self, value: T, future: F) -> ScopedFutureWithValue<T, F>
where
F: Future,
{
future.with_scope(self, value)
}
}
impl<T: Debug + Send + 'static> Debug for FutureOnceCell<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("FutureOnceCell").field(&self.0).finish()
}
}
impl<T> AsRef<FutureLocalKey<T>> for FutureOnceCell<T> {
fn as_ref(&self) -> &FutureLocalKey<T> {
&self.0
}
}
pub trait FutureLocalStorage: Future + Sized + private::Sealed {
fn with_scope<T, S>(self, scope: &'static S, value: T) -> ScopedFutureWithValue<T, Self>
where
T: Send,
S: AsRef<FutureLocalKey<T>>;
}
mod private {
use std::future::Future;
pub trait Sealed {}
impl<F: Future> Sealed for F {}
}
#[cfg(test)]
mod tests {
use std::cell::{Cell, RefCell};
use pretty_assertions::assert_eq;
use super::*;
use crate::FutureLocalStorage;
#[test]
fn test_once_cell_without_future() {
static LOCK: FutureOnceCell<RefCell<String>> = FutureOnceCell::new();
LOCK.0
.local_key()
.borrow_mut()
.replace(RefCell::new("0".to_owned()));
assert_eq!(LOCK.with(|x| x.borrow().clone()), "0".to_owned());
LOCK.with(|x| x.replace("42".to_owned()));
assert_eq!(LOCK.with(|x| x.borrow().clone()), "42".to_owned());
}
#[tokio::test]
async fn test_future_once_cell_output() {
static VALUE: FutureOnceCell<Cell<u64>> = FutureOnceCell::new();
let (output, ()) = VALUE
.scope(Cell::from(0), async {
VALUE.with(|x| {
let value = x.get();
x.set(value + 1);
});
})
.await;
assert_eq!(output.into_inner(), 1);
}
#[tokio::test]
async fn test_future_once_cell_discard_value() {
static VALUE: FutureOnceCell<Cell<u64>> = FutureOnceCell::new();
let fut_1 = async {
for _ in 0..42 {
VALUE.with(|x| {
let value = x.get();
x.set(value + 1);
});
tokio::task::yield_now().await;
}
VALUE.with(Cell::get)
}
.with_scope(&VALUE, Cell::new(0))
.discard_value();
let fut_2 = async { VALUE.with(Cell::get) }
.with_scope(&VALUE, Cell::new(15))
.discard_value();
assert_eq!(fut_1.await, 42);
assert_eq!(fut_2.await, 15);
assert_eq!(
tokio::spawn(
async { VALUE.with(Cell::get) }
.with_scope(&VALUE, Cell::new(115))
.discard_value()
)
.await
.unwrap(),
115
);
}
}