async_ops/
ops.rs

1// Copyright 2021 Sanjin Sehic
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use alloc::boxed::Box;
16
17use core::future::Future;
18use core::pin::Pin;
19use core::ptr;
20use core::task::{Context, Poll};
21
22use futures::future::{BoxFuture, LocalBoxFuture};
23use futures::ready;
24use futures_util::future::{join, Join};
25use paste::paste;
26use pin_project_lite::pin_project;
27
28use crate::Async;
29
30/// Trait for types that can be used with the [`Binary::op_assign`] to assign
31/// the result of the [`Binary`] operation to the left-hand operand.
32///
33/// See [`Async::assignable`](crate::Async::assignable).
34pub trait Assignable<T> {
35  /// Wrap the given value with `Self`.
36  fn from(value: T) -> Self;
37}
38
39impl<'a, T, Fut: Future<Output = T> + Send + 'a> Assignable<Fut> for BoxFuture<'a, T> {
40  /// Wrap the given [`Future`] with [`BoxFuture`].
41  fn from(future: Fut) -> Self {
42    Box::pin(future)
43  }
44}
45
46impl<'a, T, Fut: Future<Output = T> + 'a> Assignable<Fut> for LocalBoxFuture<'a, T> {
47  /// Wrap the given [`Future`] with [`LocalBoxFuture`].
48  fn from(future: Fut) -> Self {
49    Box::pin(future)
50  }
51}
52
53/// Trait that represents an unary operation on the operand of type `Operand`
54/// that returns the result of type `Output`.
55///
56/// See [`Async::unary_op`](crate::Async::unary_op).
57pub trait Unary<Operand> {
58  /// The resulting type after applying the unary operation.
59  type Output;
60
61  /// Do the unary operation on the given operand.
62  fn op(operand: Operand) -> Self::Output;
63}
64
65/// Trait that represents a binary operation on the left-hand operand of type
66/// `Lhs` and the right-hand operand of type `Rhs` that returns the result of
67/// type `Output`.
68///
69/// See [`Async::op`](crate::Async::op) and
70/// [`Async::op_assign`](crate::Async::op_assign).
71pub trait Binary<Lhs, Rhs> {
72  /// The resulting type after applying the binary operation.
73  type Output;
74
75  /// Do the binary operation on given operands.
76  fn op(lhs: Lhs, rhs: Rhs) -> Self::Output;
77
78  /// Do the binary operation on given operands and assign the result to the
79  /// `lhs` operand.
80  fn op_assign(lhs: &mut Lhs, rhs: Rhs)
81  where
82    Lhs: Assignable<Self::Output>,
83  {
84    unsafe {
85      let this = ptr::read(lhs);
86      ptr::write(lhs, Lhs::from(Self::op(this, rhs)))
87    }
88  }
89}
90
91macro_rules! from_std_unary_ops {
92  ($($Op:ident),*) => {$(
93    paste! {
94      #[doc = concat!(
95        "Returns a [`Future`] that will resolve the given `Future` ",
96        "and [`", stringify!([<$Op:lower>]), "`]",
97        "(core::ops::", stringify!($Op), "::", stringify!([<$Op:lower>]), ") ",
98        "its result.\n\n# Example\n\n",
99        "```rust\n",
100        "use futures_executor::block_on;\n",
101        "use async_ops::", stringify!([<$Op:lower>]), ";\n\n",
102        "let a = async { 42 };\n\n",
103        "let result = async {\n",
104        "  ", stringify!([<$Op:lower>]),"(a).await\n",
105        "};\n\n",
106        "assert_eq!(std::ops::", stringify!($Op), "::",
107        stringify!([<$Op:lower>]), "(42), block_on(result));")]
108      pub fn [<$Op:lower>]<Operand: core::ops::$Op>(
109        operand: impl Future<Output = Operand>,
110      ) -> impl Future<Output = Operand::Output> {
111        $Op::op(operand)
112      }
113
114      pin_project! {
115        #[doc = concat!(
116          "A [`Future`] that will resolve a `Future` and ",
117          "[`", stringify!([<$Op:lower>]), "`]",
118          "(core::ops::", stringify!($Op), "::", stringify!([<$Op:lower>]),
119          ") its result.")]
120        #[must_use = "futures do nothing unless you `.await` or poll them"]
121        pub struct [<Async $Op>]<Operand: Future> {
122          #[pin]
123          operand: Operand
124        }
125      }
126
127      impl<Operand: Future> Future for [<Async $Op>]<Operand>
128      where
129        Operand::Output: core::ops::$Op,
130      {
131        type Output = <Operand::Output as core::ops::$Op>::Output;
132
133        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
134          let operand = ready!(self.project().operand.poll(cx));
135          Poll::Ready(core::ops::$Op::[<$Op:lower>](operand))
136        }
137      }
138
139      #[doc = concat!(
140        "A [`Unary`] operation that will concurrently resolve a [`Future`] ",
141        "and [`", stringify!([<$Op:lower>]), "`]",
142        "(core::ops::", stringify!($Op), "::", stringify!([<$Op:lower>]), ") ",
143        "its result.\n\n# Example\n\n",
144        "```rust\n",
145        "use futures_executor::block_on;\n",
146        "use async_ops::", stringify!($Op), ";\n\n",
147        "let a = async { 42 };\n\n",
148        "let result = async {\n",
149        "  async_ops::on(a).unary_op(", stringify!($Op),").await\n",
150        "};\n\n",
151        "assert_eq!(std::ops::", stringify!($Op), "::",
152        stringify!([<$Op:lower>]), "(42), block_on(result));")]
153      pub struct $Op;
154
155      impl<Operand: Future> Unary<Operand> for $Op
156      where
157        Operand::Output: core::ops::$Op,
158      {
159        type Output = [<Async $Op>]<Operand>;
160
161        fn op(operand: Operand) -> Self::Output {
162          [<Async $Op>] { operand }
163        }
164      }
165
166      impl<Operand: Future> core::ops::$Op for Async<Operand>
167      where
168        Operand::Output: core::ops::$Op,
169      {
170        type Output = Async<[<Async $Op>]<Operand>>;
171
172        fn [<$Op:lower>](self) -> Self::Output {
173          crate::on($Op::op(self.future))
174        }
175      }
176    }
177  )*};
178}
179
180macro_rules! from_std_binary_ops {
181  ($($Op:ident),*) => {$(
182    paste! {
183      #[doc = concat!(
184        "Returns a [`Future`] that will concurrently resolve given `Futures` ",
185        "and [`", stringify!([<$Op:lower>]), "`]",
186        "(core::ops::", stringify!($Op), "::", stringify!([<$Op:lower>]), ") ",
187        "their results.\n\n# Example\n\n",
188        "```rust\n",
189        "use futures_executor::block_on;\n",
190        "use async_ops::", stringify!([<$Op:lower>]), ";\n\n",
191        "let a = async { 42 };\n",
192        "let b = async { 2 };\n\n",
193        "let result = async {\n",
194        "  ", stringify!([<$Op:lower>]),"(a, b).await\n",
195        "};\n\n",
196        "assert_eq!(std::ops::", stringify!($Op), "::",
197        stringify!([<$Op:lower>]), "(42, 2), block_on(result));")]
198      pub fn [<$Op:lower>]<Lhs: core::ops::$Op<Rhs>, Rhs>(
199        lhs: impl Future<Output = Lhs>,
200        rhs: impl Future<Output = Rhs>,
201      ) -> impl Future<Output = Lhs::Output> {
202        $Op::op(lhs, rhs)
203      }
204
205      pin_project! {
206        #[doc = concat!(
207          "A [`Future`] that will concurrently resolve two `Futures` and ",
208          "[`", stringify!([<$Op:lower>]), "`]",
209          "(core::ops::", stringify!($Op), "::", stringify!([<$Op:lower>]),
210          ") their results.")]
211        #[must_use = "futures do nothing unless you `.await` or poll them"]
212        pub struct [<Async $Op>]<Lhs: Future, Rhs: Future> {
213          #[pin]
214          future: Join<Lhs, Rhs>
215        }
216      }
217
218      impl<Lhs: Future, Rhs: Future> Future for [<Async $Op>]<Lhs, Rhs>
219      where
220        Lhs::Output: core::ops::$Op<Rhs::Output>,
221      {
222        type Output = <Lhs::Output as core::ops::$Op<Rhs::Output>>::Output;
223
224        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
225          let (lhs, rhs) = ready!(self.project().future.poll(cx));
226          Poll::Ready(core::ops::$Op::[<$Op:lower>](lhs, rhs))
227        }
228      }
229
230      #[doc = concat!(
231        "A [`Binary`] operation that will concurrently resolve two ",
232        "[`Futures`](Future) and [`", stringify!([<$Op:lower>]), "`]",
233        "(core::ops::", stringify!($Op), "::", stringify!([<$Op:lower>]), ") ",
234        "their results.\n\n# Example\n\n",
235        "```rust\n",
236        "use futures_executor::block_on;\n",
237        "use async_ops::", stringify!($Op), ";\n\n",
238        "let a = async { 42 };\n",
239        "let b = async { 2 };\n\n",
240        "let result = async {\n",
241        "  async_ops::on(a).op(", stringify!($Op),", b).await\n",
242        "};\n\n",
243        "assert_eq!(std::ops::", stringify!($Op), "::",
244        stringify!([<$Op:lower>]), "(42, 2), block_on(result));")]
245      pub struct $Op;
246
247      impl<Lhs: Future, Rhs: Future> Binary<Lhs, Rhs> for $Op
248      where
249        Lhs::Output: core::ops::$Op<Rhs::Output>,
250      {
251        type Output = [<Async $Op>]<Lhs, Rhs>;
252
253        fn op(lhs: Lhs, rhs: Rhs) -> Self::Output {
254          [<Async $Op>] {
255            future: join(lhs, rhs),
256          }
257        }
258      }
259
260      impl<Lhs: Future, Rhs: Future> core::ops::$Op<Rhs> for Async<Lhs>
261      where
262        Lhs::Output: core::ops::$Op<Rhs::Output>,
263      {
264        type Output = Async<[<Async $Op>]<Lhs, Rhs>>;
265
266        fn [<$Op:lower>](self, rhs: Rhs) -> Self::Output {
267          crate::on($Op::op(self.future, rhs))
268        }
269      }
270
271      impl<Lhs, Rhs> core::ops::[<$Op Assign>]<Rhs> for Async<Lhs>
272      where
273        Lhs: Assignable<[<Async $Op>]<Lhs, Rhs>> + Future,
274        Rhs: Future,
275        <Lhs as Future>::Output: core::ops::$Op<Rhs::Output>,
276      {
277        fn [<$Op:lower _assign>](&mut self, rhs: Rhs) {
278          $Op::op_assign(&mut self.future, rhs);
279        }
280      }
281    }
282  )*};
283}
284
285from_std_unary_ops!(Neg, Not);
286from_std_binary_ops!(Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub);
287
288#[cfg(test)]
289mod tests {
290  use super::*;
291
292  use core::future::ready;
293
294  use futures_executor::block_on;
295
296  struct ReturnRhs;
297
298  impl<Lhs, Rhs> Binary<Lhs, Rhs> for ReturnRhs {
299    type Output = Rhs;
300
301    fn op(_: Lhs, rhs: Rhs) -> Self::Output {
302      rhs
303    }
304  }
305
306  #[test]
307  fn box_future_op_assign() {
308    let mut future: BoxFuture<usize> = Assignable::from(ready(2));
309
310    ReturnRhs::op_assign(&mut future, ready(42));
311
312    assert_eq!(42, block_on(future));
313  }
314
315  #[test]
316  fn local_box_future_op_assign() {
317    let mut future: LocalBoxFuture<usize> = Assignable::from(ready(2));
318
319    ReturnRhs::op_assign(&mut future, ready(42));
320
321    assert_eq!(42, block_on(future));
322  }
323}