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}