Skip to main content

futures_enum/
lib.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3/*!
4<!-- Note: Document from sync-markdown-to-rustdoc:start through sync-markdown-to-rustdoc:end
5     is synchronized from README.md. Any changes to that range are not preserved. -->
6<!-- tidy:sync-markdown-to-rustdoc:start -->
7
8\#\[derive(Future, Stream, Sink, AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead)\] for enums.
9
10## Usage
11
12Add this to your `Cargo.toml`:
13
14```toml
15[dependencies]
16futures-enum = "0.1.16"
17futures = "0.3"
18```
19
20## Examples
21
22```
23use std::future::Future;
24
25use futures_enum::{Future, Stream, Sink, AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead};
26
27#[derive(Future, Stream, Sink, AsyncRead, AsyncWrite, AsyncSeek, AsyncBufRead)]
28enum Either<A, B> {
29    A(A),
30    B(B),
31}
32
33fn foo(x: i32) -> impl Future<Output = i32> {
34    if x < 0 {
35        Either::A(async { 1 })
36    } else {
37        Either::B(async move { x })
38    }
39}
40```
41
42futures-enum works well even if the dependency contains only sub-crates such
43as `futures-core`, `futures-io`, `futures-sink`, etc.
44
45See [auto_enums] crate for how to automate patterns like this.
46
47## Supported traits
48
49- [`Future`](https://doc.rust-lang.org/std/future/trait.Future.html) - [example](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/future.rs) | [generated code](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/future.expanded.rs)
50- [`Stream`](https://docs.rs/futures/latest/futures/stream/trait.Stream.html) - [example](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/stream.rs) | [generated code](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/stream.expanded.rs)
51- [`Sink`](https://docs.rs/futures/latest/futures/sink/trait.Sink.html) - [example](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/sink.rs) | [generated code](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/sink.expanded.rs)
52- [`AsyncRead`](https://docs.rs/futures/latest/futures/io/trait.AsyncRead.html) - [example](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/async_read.rs) | [generated code](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/async_read.expanded.rs)
53- [`AsyncWrite`](https://docs.rs/futures/latest/futures/io/trait.AsyncWrite.html) - [example](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/async_write.rs) | [generated code](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/async_write.expanded.rs)
54- [`AsyncSeek`](https://docs.rs/futures/latest/futures/io/trait.AsyncSeek.html) - [example](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/async_seek.rs) | [generated code](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/async_seek.expanded.rs)
55- [`AsyncBufRead`](https://docs.rs/futures/latest/futures/io/trait.AsyncBufRead.html) - [example](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/async_buf_read.rs) | [generated code](https://github.com/taiki-e/futures-enum/blob/HEAD/tests/expand/async_buf_read.expanded.rs)
56
57## Related Projects
58
59- [auto_enums]: A library for to allow multiple return types by automatically generated enum.
60- [derive_utils]: A procedural macro helper for easily writing [derives macros][proc-macro-derive] for enums.
61- [io-enum]: \#\[derive(Read, Write, Seek, BufRead)\] for enums.
62- [iter-enum]: \#\[derive(Iterator, DoubleEndedIterator, ExactSizeIterator, Extend)\] for enums.
63
64[auto_enums]: https://github.com/taiki-e/auto_enums
65[derive_utils]: https://github.com/taiki-e/derive_utils
66[io-enum]: https://github.com/taiki-e/io-enum
67[iter-enum]: https://github.com/taiki-e/iter-enum
68[proc-macro-derive]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros
69
70<!-- tidy:sync-markdown-to-rustdoc:end -->
71*/
72
73#![doc(test(
74    no_crate_inject,
75    attr(allow(
76        dead_code,
77        unused_variables,
78        clippy::undocumented_unsafe_blocks,
79        clippy::unused_trait_names,
80    ))
81))]
82#![forbid(unsafe_code)]
83
84use derive_utils::{derive_trait, quick_derive};
85use proc_macro::TokenStream;
86use quote::{format_ident, quote};
87use syn::{Ident, parse_macro_input, parse_quote};
88
89fn default_crate_name() -> (Ident, Option<String>) {
90    (format_ident!("futures"), None)
91}
92
93#[cfg(feature = "renamed")]
94fn crate_name(crate_names: &[&str]) -> (Ident, Option<String>) {
95    use find_crate::Manifest;
96
97    let Some(manifest) = Manifest::new().ok() else { return default_crate_name() };
98
99    manifest
100        .find2(|name, version| {
101            if name == "futures" {
102                version == "*" || version == "0.3" || version.starts_with("0.3.")
103            } else {
104                crate_names.contains(&name)
105            }
106        })
107        .map_or_else(default_crate_name, |package| {
108            if package.is_original() {
109                (format_ident!("{}", package.name), None)
110            } else {
111                (format_ident!("{}", &package.name), Some(package.original_name().to_owned()))
112            }
113        })
114}
115
116#[cfg(not(feature = "renamed"))]
117fn crate_name(_: &[&str]) -> (Ident, Option<String>) {
118    default_crate_name()
119}
120
121#[proc_macro_derive(Future)]
122pub fn derive_future(input: TokenStream) -> TokenStream {
123    quick_derive! {
124        input,
125        ::core::future::Future,
126        trait Future {
127            type Output;
128            #[inline]
129            fn poll(
130                self: ::core::pin::Pin<&mut Self>,
131                cx: &mut ::core::task::Context<'_>,
132            ) -> ::core::task::Poll<Self::Output>;
133        }
134    }
135}
136
137#[proc_macro_derive(Stream)]
138pub fn derive_stream(input: TokenStream) -> TokenStream {
139    let (crate_, _) = crate_name(&["futures", "futures-util", "futures-core"]);
140
141    derive_trait(
142        &parse_macro_input!(input),
143        parse_quote!(::#crate_::stream::Stream),
144        None,
145        parse_quote! {
146            trait Stream {
147                type Item;
148                #[inline]
149                fn poll_next(
150                    self: ::core::pin::Pin<&mut Self>,
151                    cx: &mut ::core::task::Context<'_>,
152                ) -> ::core::task::Poll<::core::option::Option<Self::Item>>;
153                #[inline]
154                fn size_hint(&self) -> (usize, ::core::option::Option<usize>);
155            }
156        },
157    )
158    .into()
159}
160
161#[proc_macro_derive(Sink)]
162pub fn derive_sink(input: TokenStream) -> TokenStream {
163    let (crate_, original) = crate_name(&["futures", "futures-sink"]);
164    let path = if original.as_deref() == Some("futures-sink") {
165        quote!(::#crate_)
166    } else {
167        quote!(::#crate_::sink)
168    };
169
170    derive_trait(&parse_macro_input!(input), parse_quote!(#path::Sink), None, parse_quote! {
171        trait Sink<__Item> {
172            type Error;
173            #[inline]
174            fn poll_ready(
175                self: ::core::pin::Pin<&mut Self>,
176                cx: &mut ::core::task::Context<'_>,
177            ) -> ::core::task::Poll<::core::result::Result<(), Self::Error>>;
178            #[inline]
179            fn start_send(
180                self: ::core::pin::Pin<&mut Self>,
181                item: __Item,
182            ) -> ::core::result::Result<(), Self::Error>;
183            #[inline]
184            fn poll_flush(
185                self: ::core::pin::Pin<&mut Self>,
186                cx: &mut ::core::task::Context<'_>,
187            ) -> ::core::task::Poll<::core::result::Result<(), Self::Error>>;
188            #[inline]
189            fn poll_close(
190                self: ::core::pin::Pin<&mut Self>,
191                cx: &mut ::core::task::Context<'_>,
192            ) -> ::core::task::Poll<::core::result::Result<(), Self::Error>>;
193        }
194    })
195    .into()
196}
197
198#[proc_macro_derive(AsyncRead)]
199pub fn derive_async_read(input: TokenStream) -> TokenStream {
200    let (crate_, original) = crate_name(&["futures", "futures-io"]);
201
202    let path = if original.as_deref() == Some("futures-io") {
203        quote!(::#crate_)
204    } else {
205        quote!(::#crate_::io)
206    };
207
208    derive_trait(&parse_macro_input!(input), parse_quote!(#path::AsyncRead), None, parse_quote! {
209        trait AsyncRead {
210            #[inline]
211            fn poll_read(
212                self: ::core::pin::Pin<&mut Self>,
213                cx: &mut ::core::task::Context<'_>,
214                buf: &mut [u8],
215            ) -> ::core::task::Poll<::std::io::Result<usize>>;
216            #[inline]
217            fn poll_read_vectored(
218                self: ::core::pin::Pin<&mut Self>,
219                cx: &mut ::core::task::Context<'_>,
220                bufs: &mut [::std::io::IoSliceMut<'_>],
221            ) -> ::core::task::Poll<::std::io::Result<usize>>;
222        }
223    })
224    .into()
225}
226
227#[proc_macro_derive(AsyncWrite)]
228pub fn derive_async_write(input: TokenStream) -> TokenStream {
229    let (crate_, original) = crate_name(&["futures", "futures-io"]);
230
231    let path = if original.as_deref() == Some("futures-io") {
232        quote!(::#crate_)
233    } else {
234        quote!(::#crate_::io)
235    };
236
237    derive_trait(&parse_macro_input!(input), parse_quote!(#path::AsyncWrite), None, parse_quote! {
238        trait AsyncWrite {
239            #[inline]
240            fn poll_write(
241                self: ::core::pin::Pin<&mut Self>,
242                cx: &mut ::core::task::Context<'_>,
243                buf: &[u8],
244            ) -> ::core::task::Poll<::std::io::Result<usize>>;
245            #[inline]
246            fn poll_write_vectored(
247                self: ::core::pin::Pin<&mut Self>,
248                cx: &mut ::core::task::Context<'_>,
249                bufs: &[::std::io::IoSlice<'_>],
250            ) -> ::core::task::Poll<::std::io::Result<usize>>;
251            #[inline]
252            fn poll_flush(
253                self: ::core::pin::Pin<&mut Self>,
254                cx: &mut ::core::task::Context<'_>,
255            ) -> ::core::task::Poll<::std::io::Result<()>>;
256            #[inline]
257            fn poll_close(
258                self: ::core::pin::Pin<&mut Self>,
259                cx: &mut ::core::task::Context<'_>,
260            ) -> ::core::task::Poll<::std::io::Result<()>>;
261        }
262    })
263    .into()
264}
265
266#[proc_macro_derive(AsyncSeek)]
267pub fn derive_async_seek(input: TokenStream) -> TokenStream {
268    let (crate_, original) = crate_name(&["futures", "futures-io"]);
269
270    let path = if original.as_deref() == Some("futures-io") {
271        quote!(::#crate_)
272    } else {
273        quote!(::#crate_::io)
274    };
275
276    derive_trait(&parse_macro_input!(input), parse_quote!(#path::AsyncSeek), None, parse_quote! {
277        trait AsyncSeek {
278            #[inline]
279            fn poll_seek(
280                self: ::core::pin::Pin<&mut Self>,
281                cx: &mut ::core::task::Context<'_>,
282                pos: ::std::io::SeekFrom,
283            ) -> ::core::task::Poll<::std::io::Result<u64>>;
284        }
285    })
286    .into()
287}
288
289#[proc_macro_derive(AsyncBufRead)]
290pub fn derive_async_buf_read(input: TokenStream) -> TokenStream {
291    let (crate_, original) = crate_name(&["futures", "futures-io"]);
292
293    let path = if original.as_deref() == Some("futures-io") {
294        quote!(::#crate_)
295    } else {
296        quote!(::#crate_::io)
297    };
298
299    derive_trait(
300        &parse_macro_input!(input),
301        parse_quote!(#path::AsyncBufRead),
302        None,
303        parse_quote! {
304            trait AsyncBufRead {
305                #[inline]
306                fn poll_fill_buf<'__a>(
307                    self: ::core::pin::Pin<&'__a mut Self>,
308                    cx: &mut ::core::task::Context<'_>,
309                ) -> ::core::task::Poll<::std::io::Result<&'__a [u8]>>;
310                #[inline]
311                fn consume(self: ::core::pin::Pin<&mut Self>, amt: usize);
312            }
313        },
314    )
315    .into()
316}