yash_env/system/
future.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2025 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Future-related utilities
18
19use std::fmt::Debug;
20use std::pin::Pin;
21use std::task::{Context, Poll};
22
23/// Future that either returns a precomputed value or delegates to a
24/// heap-allocated, type-erased future.
25///
26/// `FlexFuture` works as a selective wrapper for [`std::future::Ready`],
27/// [`std::future::Pending`], and a generic future in a [`Box`]. When a function
28/// needs to return a future, it can use `FlexFuture` to possibly avoid heap
29/// allocation if the future is already known to be ready.
30///
31/// This type does not have a lifetime parameter, so the contained future must
32/// have a `'static` lifetime. This is because `FlexFuture` is also used in
33/// [`SharedSystem`](super::SharedSystem), which performs dynamic lifetime
34/// checking to access its internal state guarded by a `RefCell`. Instead of
35/// borrowing the system, the future must share ownership of the system to keep
36/// it alive until the future is resolved.
37pub enum FlexFuture<T> {
38    /// Future that is already ready with a value
39    Ready(std::future::Ready<T>),
40    /// Future that is pending and will never resolve
41    Pending(std::future::Pending<T>),
42    /// Heap-allocated, type-erased future
43    Generic(Pin<Box<dyn Future<Output = T>>>),
44}
45
46impl<T: Debug> Debug for FlexFuture<T> {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        match self {
49            FlexFuture::Ready(ready) => ready.fmt(f),
50            FlexFuture::Pending(pending) => pending.fmt(f),
51            FlexFuture::Generic(_) => f.debug_tuple("Generic").finish_non_exhaustive(),
52        }
53    }
54}
55
56impl<T> From<T> for FlexFuture<T> {
57    fn from(value: T) -> Self {
58        FlexFuture::Ready(std::future::ready(value))
59    }
60}
61
62impl<T> From<std::future::Ready<T>> for FlexFuture<T> {
63    fn from(ready: std::future::Ready<T>) -> Self {
64        FlexFuture::Ready(ready)
65    }
66}
67
68impl<T> From<std::future::Pending<T>> for FlexFuture<T> {
69    fn from(pending: std::future::Pending<T>) -> Self {
70        FlexFuture::Pending(pending)
71    }
72}
73
74impl<T> From<Pin<Box<dyn Future<Output = T>>>> for FlexFuture<T> {
75    fn from(future: Pin<Box<dyn Future<Output = T>>>) -> Self {
76        FlexFuture::Generic(future)
77    }
78}
79
80impl<T> From<Box<dyn Future<Output = T>>> for FlexFuture<T> {
81    fn from(future: Box<dyn Future<Output = T>>) -> Self {
82        FlexFuture::Generic(Box::into_pin(future))
83    }
84}
85
86impl<T> FlexFuture<T> {
87    /// Creates a new `FlexFuture` from any future.
88    ///
89    /// This function allocates memory for the future. If the future is already
90    /// allocated on the heap, use [`FlexFuture::from`] instead.
91    pub fn boxed<F>(f: F) -> Self
92    where
93        F: Future<Output = T> + 'static,
94    {
95        FlexFuture::Generic(Box::pin(f))
96    }
97
98    /// Converts this `FlexFuture` into a `Pin<Box<dyn Future<Output = T>>`.
99    pub fn into_boxed(self) -> Pin<Box<dyn Future<Output = T>>>
100    where
101        T: 'static,
102    {
103        match self {
104            FlexFuture::Ready(ready) => Box::pin(ready),
105            FlexFuture::Pending(pending) => Box::pin(pending),
106            FlexFuture::Generic(generic) => generic,
107        }
108    }
109}
110
111impl<T: 'static> From<FlexFuture<T>> for Pin<Box<dyn Future<Output = T>>> {
112    fn from(future: FlexFuture<T>) -> Self {
113        future.into_boxed()
114    }
115}
116
117impl<T> Future for FlexFuture<T> {
118    type Output = T;
119
120    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
121        match self.get_mut() {
122            FlexFuture::Ready(ready) => Pin::new(ready).poll(cx),
123            FlexFuture::Pending(pending) => Pin::new(pending).poll(cx),
124            FlexFuture::Generic(generic) => generic.as_mut().poll(cx),
125        }
126    }
127}