diff options
author | Jeff Vander Stoep <jeffv@google.com> | 2021-02-18 12:05:23 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-02-18 12:05:23 +0000 |
commit | fde7dd2afdb998cac79e73c4bf429e31b47f306c (patch) | |
tree | 52b990e50d273f5e4e561e5a98403aaa8bd14688 | |
parent | e140a6a6538d47f87b3b3538c56f4ca6dba2536b (diff) | |
parent | 20101450eb364c3bd72837b62537ce06b7e67c87 (diff) | |
download | async-stream-impl-fde7dd2afdb998cac79e73c4bf429e31b47f306c.tar.gz |
Import async-stream-impl v0.3.0 am: 4d90486f87 am: c3955df4a0 am: 20101450eb
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/async-stream-impl/+/1594011
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I3a86e0d5874fd042c6f48732eb8b219b1bd476dc
-rw-r--r-- | Android.bp | 19 | ||||
-rw-r--r-- | Cargo.toml | 43 | ||||
-rw-r--r-- | Cargo.toml.orig | 24 | ||||
-rw-r--r-- | LICENSE | 51 | ||||
-rw-r--r-- | METADATA | 19 | ||||
-rw-r--r-- | MODULE_LICENSE_MIT | 0 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | TEST_MAPPING | 17 | ||||
-rw-r--r-- | patches/lib.diff | 9 | ||||
-rw-r--r-- | src/lib.rs | 271 |
10 files changed, 454 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..82b7860 --- /dev/null +++ b/Android.bp @@ -0,0 +1,19 @@ +// This file is generated by cargo2android.py --run --dependencies --device. + +rust_proc_macro { + name: "libasync_stream_impl", + crate_name: "async_stream_impl", + srcs: ["src/lib.rs"], + edition: "2018", + rustlibs: [ + "libproc_macro2", + "libquote", + "libsyn", + ], +} + +// dependent_library ["feature_list"] +// proc-macro2-1.0.24 "default,proc-macro" +// quote-1.0.8 "default,proc-macro" +// syn-1.0.60 "clone-impls,default,derive,extra-traits,full,parsing,printing,proc-macro,quote,visit-mut" +// unicode-xid-0.2.1 "default" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..df59eae --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,43 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "async-stream-impl" +version = "0.3.0" +authors = ["Carl Lerche <me@carllerche.com>"] +description = "proc macros for async-stream crate" +homepage = "https://github.com/tokio-rs/async-stream" +documentation = "https://docs.rs/async-stream-impl/0.3.0/async-stream-impl" +license = "MIT" +repository = "https://github.com/tokio-rs/async-stream" + +[lib] +proc-macro = true +[dependencies.proc-macro2] +version = "1" + +[dependencies.quote] +version = "1" + +[dependencies.syn] +version = "1" +features = ["extra-traits", "full", "visit-mut"] +[dev-dependencies.futures-core] +version = "0.3" + +[dev-dependencies.futures-util] +version = "0.3" + +[dev-dependencies.tokio] +version = "0.2" +features = ["full"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..dd6d9d9 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,24 @@ +[package] +name = "async-stream-impl" +version = "0.3.0" +edition = "2018" +license = "MIT" +authors = ["Carl Lerche <me@carllerche.com>"] +description = "proc macros for async-stream crate" +documentation = "https://docs.rs/async-stream-impl/0.3.0/async-stream-impl" +homepage = "https://github.com/tokio-rs/async-stream" +repository = "https://github.com/tokio-rs/async-stream" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +syn = { version = "1", features = ["extra-traits", "full", "visit-mut"]} +quote = "1" + +[dev-dependencies] +# async-stream = { version = "0.3.0", path = "../async-stream" } +futures-core = "0.3" +futures-util = "0.3" +tokio = { version = "0.2", features = ["full"] } @@ -0,0 +1,51 @@ +Copyright (c) 2019 Carl Lerche + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +Copyright (c) 2018 David Tolnay + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..8e2e26d --- /dev/null +++ b/METADATA @@ -0,0 +1,19 @@ +name: "async-stream-impl" +description: "proc macros for async-stream crate" +third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/async-stream-impl" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/async-stream-impl/async-stream-impl-0.3.0.crate" + } + version: "0.3.0" + license_type: NOTICE + last_upgrade_date { + year: 2021 + month: 2 + day: 8 + } +} diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_MIT @@ -0,0 +1 @@ +include platform/prebuilts/rust:/OWNERS diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 0000000..8fccfe0 --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,17 @@ +// Generated by update_crate_tests.py for tests that depend on this crate. +{ + "presubmit": [ + { + "name": "tokio-test_device_test_tests_block_on" + }, + { + "name": "tokio-test_device_test_tests_io" + }, + { + "name": "tokio-test_device_test_src_lib" + }, + { + "name": "tokio-test_device_test_tests_macros" + } + ] +} diff --git a/patches/lib.diff b/patches/lib.diff new file mode 100644 index 0000000..9f60ea6 --- /dev/null +++ b/patches/lib.diff @@ -0,0 +1,9 @@ +diff --git a/src/lib.rs b/src/lib.rs +index 3d4d185..9081ecf 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -1,3 +1,4 @@ ++extern crate proc_macro; + use proc_macro::TokenStream; + use proc_macro2::{Delimiter, Group, TokenStream as TokenStream2, TokenTree}; + use quote::quote; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9081ecf --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,271 @@ +extern crate proc_macro; +use proc_macro::TokenStream; +use proc_macro2::{Delimiter, Group, TokenStream as TokenStream2, TokenTree}; +use quote::quote; +use syn::visit_mut::VisitMut; + +struct Scrub { + is_xforming: bool, + is_try: bool, + unit: Box<syn::Expr>, + num_yield: u32, +} + +fn parse_input(input: TokenStream) -> syn::Result<Vec<syn::Stmt>> { + let input = replace_for_await(input.into()); + // syn does not provide a way to parse `Vec<Stmt>` directly from `TokenStream`, + // so wrap input in a brace and then parse it as a block. + let input = TokenStream2::from(TokenTree::Group(Group::new(Delimiter::Brace, input))); + let syn::Block { stmts, .. } = syn::parse2(input)?; + + Ok(stmts) +} + +impl VisitMut for Scrub { + fn visit_expr_mut(&mut self, i: &mut syn::Expr) { + if !self.is_xforming { + syn::visit_mut::visit_expr_mut(self, i); + return; + } + + match i { + syn::Expr::Yield(yield_expr) => { + self.num_yield += 1; + + let value_expr = if let Some(ref e) = yield_expr.expr { + e + } else { + &self.unit + }; + + // let ident = &self.yielder; + + *i = if self.is_try { + syn::parse_quote! { __yield_tx.send(Ok(#value_expr)).await } + } else { + syn::parse_quote! { __yield_tx.send(#value_expr).await } + }; + } + syn::Expr::Try(try_expr) => { + syn::visit_mut::visit_expr_try_mut(self, try_expr); + // let ident = &self.yielder; + let e = &try_expr.expr; + + *i = syn::parse_quote! { + match #e { + Ok(v) => v, + Err(e) => { + __yield_tx.send(Err(e.into())).await; + return; + } + } + }; + } + syn::Expr::Closure(_) | syn::Expr::Async(_) => { + let prev = self.is_xforming; + self.is_xforming = false; + syn::visit_mut::visit_expr_mut(self, i); + self.is_xforming = prev; + } + syn::Expr::ForLoop(expr) => { + syn::visit_mut::visit_expr_for_loop_mut(self, expr); + // TODO: Should we allow other attributes? + if expr.attrs.len() != 1 || !expr.attrs[0].path.is_ident("await") { + return; + } + let syn::ExprForLoop { + attrs, + label, + pat, + expr, + body, + .. + } = expr; + + let attr = attrs.pop().unwrap(); + if let Err(e) = syn::parse2::<syn::parse::Nothing>(attr.tokens) { + *i = syn::parse2(e.to_compile_error()).unwrap(); + return; + } + + *i = syn::parse_quote! {{ + let mut __pinned = #expr; + let mut __pinned = unsafe { + ::core::pin::Pin::new_unchecked(&mut __pinned) + }; + #label + loop { + let #pat = match ::async_stream::reexport::next(&mut __pinned).await { + ::core::option::Option::Some(e) => e, + ::core::option::Option::None => break, + }; + #body + } + }} + } + _ => syn::visit_mut::visit_expr_mut(self, i), + } + } + + fn visit_item_mut(&mut self, i: &mut syn::Item) { + let prev = self.is_xforming; + self.is_xforming = false; + syn::visit_mut::visit_item_mut(self, i); + self.is_xforming = prev; + } +} + +/// Asynchronous stream +/// +/// See [crate](index.html) documentation for more details. +/// +/// # Examples +/// +/// ```rust +/// use async_stream::stream; +/// +/// use futures_util::pin_mut; +/// use futures_util::stream::StreamExt; +/// +/// #[tokio::main] +/// async fn main() { +/// let s = stream! { +/// for i in 0..3 { +/// yield i; +/// } +/// }; +/// +/// pin_mut!(s); // needed for iteration +/// +/// while let Some(value) = s.next().await { +/// println!("got {}", value); +/// } +/// } +/// ``` +#[proc_macro] +pub fn stream(input: TokenStream) -> TokenStream { + let mut stmts = match parse_input(input) { + Ok(x) => x, + Err(e) => return e.to_compile_error().into(), + }; + + let mut scrub = Scrub { + is_xforming: true, + is_try: false, + unit: syn::parse_quote!(()), + num_yield: 0, + }; + + for mut stmt in &mut stmts[..] { + scrub.visit_stmt_mut(&mut stmt); + } + + let dummy_yield = if scrub.num_yield == 0 { + Some(quote!(if false { + __yield_tx.send(()).await; + })) + } else { + None + }; + + quote!({ + let (mut __yield_tx, __yield_rx) = ::async_stream::yielder::pair(); + ::async_stream::AsyncStream::new(__yield_rx, async move { + #dummy_yield + #(#stmts)* + }) + }) + .into() +} + +/// Asynchronous fallible stream +/// +/// See [crate](index.html) documentation for more details. +/// +/// # Examples +/// +/// ```rust +/// use tokio::net::{TcpListener, TcpStream}; +/// +/// use async_stream::try_stream; +/// use futures_core::stream::Stream; +/// +/// use std::io; +/// use std::net::SocketAddr; +/// +/// fn bind_and_accept(addr: SocketAddr) +/// -> impl Stream<Item = io::Result<TcpStream>> +/// { +/// try_stream! { +/// let mut listener = TcpListener::bind(addr).await?; +/// +/// loop { +/// let (stream, addr) = listener.accept().await?; +/// println!("received on {:?}", addr); +/// yield stream; +/// } +/// } +/// } +/// ``` +#[proc_macro] +pub fn try_stream(input: TokenStream) -> TokenStream { + let mut stmts = match parse_input(input) { + Ok(x) => x, + Err(e) => return e.to_compile_error().into(), + }; + + let mut scrub = Scrub { + is_xforming: true, + is_try: true, + unit: syn::parse_quote!(()), + num_yield: 0, + }; + + for mut stmt in &mut stmts[..] { + scrub.visit_stmt_mut(&mut stmt); + } + + let dummy_yield = if scrub.num_yield == 0 { + Some(quote!(if false { + __yield_tx.send(()).await; + })) + } else { + None + }; + + quote!({ + let (mut __yield_tx, __yield_rx) = ::async_stream::yielder::pair(); + ::async_stream::AsyncStream::new(__yield_rx, async move { + #dummy_yield + #(#stmts)* + }) + }) + .into() +} + +fn replace_for_await(input: TokenStream2) -> TokenStream2 { + let mut input = input.into_iter().peekable(); + let mut tokens = Vec::new(); + + while let Some(token) = input.next() { + match token { + TokenTree::Ident(ident) => { + match input.peek() { + Some(TokenTree::Ident(next)) if ident == "for" && next == "await" => { + tokens.extend(quote!(#[#next])); + let _ = input.next(); + } + _ => {} + } + tokens.push(ident.into()); + } + TokenTree::Group(group) => { + let stream = replace_for_await(group.stream()); + tokens.push(Group::new(group.delimiter(), stream).into()); + } + _ => tokens.push(token), + } + } + + tokens.into_iter().collect() +} |