use alloc::boxed::Box;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
pub struct Futurus<T> {
inner: FuturusInner<T>,
}
enum FuturusInner<T> {
Purus(Option<T>),
Effectus(Pin<Box<dyn Future<Output = T> + Send + 'static>>),
}
impl<T> Futurus<T> {
#[inline]
pub fn purus(value: T) -> Self {
Futurus {
inner: FuturusInner::Purus(Some(value)),
}
}
#[inline]
pub fn pure(value: T) -> Self {
Self::purus(value)
}
#[inline]
pub fn new<F>(future: F) -> Self
where
F: Future<Output = T> + Send + 'static,
{
Futurus {
inner: FuturusInner::Effectus(Box::pin(future)),
}
}
#[inline]
pub fn delay<F, Fut>(f: F) -> Self
where
F: FnOnce() -> Fut + Send + 'static,
Fut: Future<Output = T> + Send + 'static,
{
Futurus {
inner: FuturusInner::Effectus(Box::pin(async move { f().await })),
}
}
}
impl<T: Send + 'static> Futurus<T> {
#[inline]
pub fn fmap<B, F>(self, f: F) -> Futurus<B>
where
F: FnOnce(T) -> B + Send + 'static,
B: Send + 'static,
{
match self.inner {
FuturusInner::Purus(Some(value)) => {
Futurus::purus(f(value))
}
FuturusInner::Purus(None) => {
Futurus {
inner: FuturusInner::Effectus(Box::pin(async move {
panic!("Futurus::Purus was already consumed")
})),
}
}
FuturusInner::Effectus(fut) => {
Futurus {
inner: FuturusInner::Effectus(Box::pin(async move { f(fut.await) })),
}
}
}
}
#[inline]
pub fn map<B, F>(self, f: F) -> Futurus<B>
where
F: FnOnce(T) -> B + Send + 'static,
B: Send + 'static,
{
self.fmap(f)
}
#[inline]
pub fn flat_map<B, F>(self, f: F) -> Futurus<B>
where
F: FnOnce(T) -> Futurus<B> + Send + 'static,
B: Send + Unpin + 'static,
{
match self.inner {
FuturusInner::Purus(Some(value)) => {
f(value)
}
FuturusInner::Purus(None) => Futurus {
inner: FuturusInner::Effectus(Box::pin(async move {
panic!("Futurus::Purus was already consumed")
})),
},
FuturusInner::Effectus(fut) => Futurus {
inner: FuturusInner::Effectus(Box::pin(async move { f(fut.await).await })),
},
}
}
#[inline]
pub fn bind<B, F>(self, f: F) -> Futurus<B>
where
F: FnOnce(T) -> Futurus<B> + Send + 'static,
B: Send + Unpin + 'static,
{
self.flat_map(f)
}
#[inline]
pub fn and_then<B, F>(self, f: F) -> Futurus<B>
where
F: FnOnce(T) -> Futurus<B> + Send + 'static,
B: Send + Unpin + 'static,
{
self.flat_map(f)
}
#[inline]
pub fn ap<B, F>(self, ff: Futurus<F>) -> Futurus<B>
where
F: FnOnce(T) -> B + Send + 'static,
B: Send + Unpin + 'static,
{
ff.flat_map(move |f| self.fmap(f))
}
#[inline]
pub fn map2<B, C, F>(self, other: Futurus<B>, f: F) -> Futurus<C>
where
F: FnOnce(T, B) -> C + Send + 'static,
B: Send + 'static,
C: Send + Unpin + 'static,
{
self.flat_map(move |a| other.fmap(move |b| f(a, b)))
}
#[inline]
pub fn then<B>(self, other: Futurus<B>) -> Futurus<B>
where
B: Send + Unpin + 'static,
{
self.flat_map(move |_| other)
}
#[inline]
pub fn skip<B>(self, other: Futurus<B>) -> Futurus<T>
where
B: Send + 'static,
T: Unpin,
{
self.flat_map(move |a| other.fmap(move |_| a))
}
#[inline]
pub fn as_const<B>(self, b: B) -> Futurus<B>
where
B: Send + 'static,
{
self.fmap(move |_| b)
}
#[inline]
pub fn void(self) -> Futurus<()> {
self.fmap(|_| ())
}
}
impl<T: Unpin> Future for Futurus<T> {
type Output = T;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let inner = &mut self.as_mut().get_mut().inner;
match inner {
FuturusInner::Purus(opt) => {
match opt.take() {
Some(value) => Poll::Ready(value),
None => panic!("Futurus::Purus polled after completion"),
}
}
FuturusInner::Effectus(fut) => {
fut.as_mut().poll(cx)
}
}
}
}
impl<T: Send + 'static> Futurus<Futurus<T>> {
#[inline]
pub fn flatten(self) -> Futurus<T>
where
T: Unpin,
{
self.flat_map(|inner| inner)
}
}
impl<T: Default> Default for Futurus<T> {
fn default() -> Self {
Futurus::purus(T::default())
}
}
impl<T: core::fmt::Debug> core::fmt::Debug for Futurus<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match &self.inner {
FuturusInner::Purus(Some(value)) => {
f.debug_tuple("Futurus::Purus").field(value).finish()
}
FuturusInner::Purus(None) => f
.debug_tuple("Futurus::Purus")
.field(&"<consumed>")
.finish(),
FuturusInner::Effectus(_) => f
.debug_tuple("Futurus::Effectus")
.field(&"<future>")
.finish(),
}
}
}
impl<T> From<T> for Futurus<T> {
#[inline]
fn from(value: T) -> Self {
Futurus::purus(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_futurus_purus_creation() {
let fut: Futurus<i32> = Futurus::purus(42);
let _: Futurus<i32> = fut;
}
#[test]
fn test_futurus_fmap_type_check() {
let fut = Futurus::purus(5);
let _mapped: Futurus<i32> = fut.fmap(|x| x * 2);
}
#[test]
fn test_futurus_flat_map_type_check() {
let fut = Futurus::purus(5);
let _chained: Futurus<i32> = fut.flat_map(|x| Futurus::purus(x * 2));
}
#[test]
fn test_futurus_map2_type_check() {
let a = Futurus::purus(5);
let b = Futurus::purus(10);
let _sum: Futurus<i32> = a.map2(b, |x, y| x + y);
}
#[test]
fn test_futurus_debug() {
let fut = Futurus::purus(42);
let debug_str = alloc::format!("{fut:?}");
assert!(debug_str.contains("Purus"));
assert!(debug_str.contains("42"));
}
#[test]
fn test_futurus_from() {
let fut: Futurus<i32> = 42.into();
let _: Futurus<i32> = fut;
}
#[test]
fn test_futurus_default() {
let fut: Futurus<i32> = Futurus::default();
let _: Futurus<i32> = fut;
}
}