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 | 933a786e24ae9749ce6d73dd8c882ce420a6c8db (patch) | |
tree | 3bc261bbf5db0e4e8e5c3e3f55140fd6a182a560 | |
parent | a0b20b1c027183c3ccefef42435b43ba2b62b15e (diff) | |
parent | 1c2147001356d4be84e73b929489036c89882290 (diff) | |
download | async-stream-933a786e24ae9749ce6d73dd8c882ce420a6c8db.tar.gz |
Import async-stream v0.3.0 am: 647493e10d am: d822959c82 am: 1c21470013
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/async-stream/+/1592880
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: Ibd2de8d95e52d8f876e7c60f012ac14886aaaef1
-rw-r--r-- | .cargo_vcs_info.json | 5 | ||||
-rw-r--r-- | Android.bp | 21 | ||||
-rw-r--r-- | Cargo.lock | 552 | ||||
-rw-r--r-- | Cargo.toml | 40 | ||||
-rw-r--r-- | Cargo.toml.orig | 30 | ||||
-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-- | README.md | 173 | ||||
-rw-r--r-- | README.tpl | 13 | ||||
-rw-r--r-- | examples/tcp_accept.rs | 21 | ||||
-rw-r--r-- | src/async_stream.rs | 69 | ||||
-rw-r--r-- | src/lib.rs | 177 | ||||
-rw-r--r-- | src/next.rs | 32 | ||||
-rw-r--r-- | src/yielder.rs | 87 | ||||
-rw-r--r-- | tests/for_await.rs | 23 | ||||
-rw-r--r-- | tests/stream.rs | 165 | ||||
-rw-r--r-- | tests/try_stream.rs | 80 | ||||
-rw-r--r-- | tests/ui/yield_in_async.rs | 11 | ||||
-rw-r--r-- | tests/ui/yield_in_async.stderr | 76 | ||||
-rw-r--r-- | tests/ui/yield_in_closure.rs | 11 | ||||
-rw-r--r-- | tests/ui/yield_in_closure.stderr | 18 | ||||
-rw-r--r-- | tests/ui/yield_in_nested_fn.rs | 9 | ||||
-rw-r--r-- | tests/ui/yield_in_nested_fn.stderr | 16 |
25 files changed, 1700 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..29bac5d --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "d346ad2d77b11855969b82c9d16bd78ec84e4863" + } +} diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..09ca3df --- /dev/null +++ b/Android.bp @@ -0,0 +1,21 @@ +// This file is generated by cargo2android.py --run --dependencies --device. + +rust_library { + name: "libasync_stream", + host_supported: true, + crate_name: "async_stream", + srcs: ["src/lib.rs"], + edition: "2018", + rustlibs: [ + "libfutures_core", + ], + proc_macros: ["libasync_stream_impl"], +} + +// dependent_library ["feature_list"] +// async-stream-impl-0.3.0 +// futures-core-0.3.12 "alloc,default,std" +// 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.lock b/Cargo.lock new file mode 100644 index 0000000..d924233 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,552 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" + +[[package]] +name = "async-stream" +version = "0.3.0" +dependencies = [ + "async-stream-impl", + "futures-core", + "futures-util", + "tokio", + "tokio-test", + "trybuild", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "futures-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hermit-abi" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" +dependencies = [ + "log", + "mio", + "miow 0.3.5", + "winapi 0.3.9", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "net2" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" + +[[package]] +name = "pin-project" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-hack" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" + +[[package]] +name = "proc-macro-nested" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" + +[[package]] +name = "proc-macro2" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + +[[package]] +name = "syn" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "tokio" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-test" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0049c119b6d505c4447f5c64873636c7af6c75ab0d45fd9f618d82acb8016d" +dependencies = [ + "bytes", + "futures-core", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "trybuild" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a4d94e6adf00b96b1ab94fcfcd8c3cf916733b39adf90c8f72693629887b9b8" +dependencies = [ + "glob", + "lazy_static", + "serde", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..39573bd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,40 @@ +# 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" +version = "0.3.0" +authors = ["Carl Lerche <me@carllerche.com>"] +description = "Asynchronous streams using async & await notation" +homepage = "https://github.com/tokio-rs/async-stream" +documentation = "https://docs.rs/async-stream/0.3.0/async-stream" +readme = "README.md" +license = "MIT" +repository = "https://github.com/tokio-rs/async-stream" +[dependencies.async-stream-impl] +version = "0.3.0" + +[dependencies.futures-core] +version = "0.3" +[dev-dependencies.futures-util] +version = "0.3" + +[dev-dependencies.tokio] +version = "0.2" +features = ["full"] + +[dev-dependencies.tokio-test] +version = "0.2" + +[dev-dependencies.trybuild] +version = "1" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..aca3396 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,30 @@ +[package] +name = "async-stream" +# When releasing to crates.io: +# - Update version number +# - lib.rs: html_root_url. +# - README.md +# - Update CHANGELOG.md +# - Update doc URL. +# - Cargo.toml +# - README.md +# - Create git tag +version = "0.3.0" +edition = "2018" +license = "MIT" +authors = ["Carl Lerche <me@carllerche.com>"] +description = "Asynchronous streams using async & await notation" +documentation = "https://docs.rs/async-stream/0.3.0/async-stream" +homepage = "https://github.com/tokio-rs/async-stream" +repository = "https://github.com/tokio-rs/async-stream" +readme = "README.md" + +[dependencies] +async-stream-impl = { version = "0.3.0", path = "../async-stream-impl" } +futures-core = "0.3" + +[dev-dependencies] +futures-util = "0.3" +tokio = { version = "0.2", features = ["full"] } +tokio-test = "0.2" +trybuild = "1" @@ -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..062a50f --- /dev/null +++ b/METADATA @@ -0,0 +1,19 @@ +name: "async-stream" +description: "Asynchronous streams using async & await notation" +third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/async-stream" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/async-stream/async-stream-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/README.md b/README.md new file mode 100644 index 0000000..dd36923 --- /dev/null +++ b/README.md @@ -0,0 +1,173 @@ +# Asynchronous streams for Rust + +Asynchronous stream of elements. + +Provides two macros, `stream!` and `try_stream!`, allowing the caller to +define asynchronous streams of elements. These are implemented using `async` +& `await` notation. The `stream!` macro works without unstable features. + +The `stream!` macro returns an anonymous type implementing the [`Stream`] +trait. The `Item` associated type is the type of the values yielded from the +stream. The `try_stream!` also returns an anonymous type implementing the +[`Stream`] trait, but the `Item` associated type is `Result<T, Error>`. The +`try_stream!` macro supports using `?` notiation as part of the +implementation. + +## Usage + +A basic stream yielding numbers. Values are yielded using the `yield` +keyword. The stream block must return `()`. + +```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); + } +} +``` + +Streams may be returned by using `impl Stream<Item = T>`: + +```rust +use async_stream::stream; + +use futures_core::stream::Stream; +use futures_util::pin_mut; +use futures_util::stream::StreamExt; + +fn zero_to_three() -> impl Stream<Item = u32> { + stream! { + for i in 0..3 { + yield i; + } + } +} + +#[tokio::main] +async fn main() { + let s = zero_to_three(); + pin_mut!(s); // needed for iteration + + while let Some(value) = s.next().await { + println!("got {}", value); + } +} +``` + +Streams may be implemented in terms of other streams: + +```rust +use async_stream::stream; + +use futures_core::stream::Stream; +use futures_util::pin_mut; +use futures_util::stream::StreamExt; + +fn zero_to_three() -> impl Stream<Item = u32> { + stream! { + for i in 0..3 { + yield i; + } + } +} + +fn double<S: Stream<Item = u32>>(input: S) + -> impl Stream<Item = u32> +{ + stream! { + pin_mut!(input); + while let Some(value) = input.next().await { + yield value * 2; + } + } +} + +#[tokio::main] +async fn main() { + let s = double(zero_to_three()); + pin_mut!(s); // needed for iteration + + while let Some(value) = s.next().await { + println!("got {}", value); + } +} +``` + +Rust try notation (`?`) can be used with the `try_stream!` macro. The `Item` +of the returned stream is `Result` with `Ok` being the value yielded and +`Err` the error type returned by `?`. + +```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)?; + + loop { + let (stream, addr) = listener.accept().await?; + println!("received on {:?}", addr); + yield stream; + } + } +} +``` + +## Implementation + +The `stream!` and `try_stream!` macros are implemented using proc macros. +Given that proc macros in expression position are not supported on stable +rust, a hack similar to the one provided by the [`proc-macro-hack`] crate is +used. The macro searches the syntax tree for instances of `sender.send($expr)` and +transforms them into `sender.send($expr).await`. + +The stream uses a lightweight sender to send values from the stream +implementation to the caller. When entering the stream, an `Option<T>` is +stored on the stack. A pointer to the cell is stored in a thread local and +`poll` is called on the async block. When `poll` returns. +`sender.send(value)` stores the value that cell and yields back to the +caller. + +## Limitations + +`async-stream` suffers from the same limitations as the [`proc-macro-hack`] +crate. Primarily, nesting support must be implemented using a `TT-muncher`. +If large `stream!` blocks are used, the caller will be required to add +`#![recursion_limit = "..."]` to their crate. + +A `stream!` macro may only contain up to 64 macro invocations. + +[`Stream`]: https://docs.rs/futures-core/*/futures_core/stream/trait.Stream.html +[`proc-macro-hack`]: https://github.com/dtolnay/proc-macro-hack/ + +## License + +This project is licensed under the [MIT license](LICENSE). + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in `async-stream` by you, shall be licensed as MIT, without any +additional terms or conditions. diff --git a/README.tpl b/README.tpl new file mode 100644 index 0000000..cf0b729 --- /dev/null +++ b/README.tpl @@ -0,0 +1,13 @@ +# Asynchronous streams for Rust + +{{readme}} + +## License + +This project is licensed under the [MIT license](LICENSE). + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in `async-stream` by you, shall be licensed as MIT, without any +additional terms or conditions. diff --git a/examples/tcp_accept.rs b/examples/tcp_accept.rs new file mode 100644 index 0000000..023e1a1 --- /dev/null +++ b/examples/tcp_accept.rs @@ -0,0 +1,21 @@ +use async_stream::stream; +use futures_util::pin_mut; +use futures_util::stream::StreamExt; +use tokio::net::TcpListener; + +#[tokio::main] +async fn main() { + let mut listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + + let incoming = stream! { + loop { + let (socket, _) = listener.accept().await.unwrap(); + yield socket; + } + }; + pin_mut!(incoming); + + while let Some(v) = incoming.next().await { + println!("handle = {:?}", v); + } +} diff --git a/src/async_stream.rs b/src/async_stream.rs new file mode 100644 index 0000000..30115df --- /dev/null +++ b/src/async_stream.rs @@ -0,0 +1,69 @@ +use crate::yielder::Receiver; + +use futures_core::{FusedStream, Stream}; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +#[doc(hidden)] +#[derive(Debug)] +pub struct AsyncStream<T, U> { + rx: Receiver<T>, + done: bool, + generator: U, +} + +impl<T, U> AsyncStream<T, U> { + #[doc(hidden)] + pub fn new(rx: Receiver<T>, generator: U) -> AsyncStream<T, U> { + AsyncStream { + rx, + done: false, + generator, + } + } +} + +impl<T, U> FusedStream for AsyncStream<T, U> +where + U: Future<Output = ()>, +{ + fn is_terminated(&self) -> bool { + self.done + } +} + +impl<T, U> Stream for AsyncStream<T, U> +where + U: Future<Output = ()>, +{ + type Item = T; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { + unsafe { + let me = Pin::get_unchecked_mut(self); + + if me.done { + return Poll::Ready(None); + } + + let mut dst = None; + let res = { + let _enter = me.rx.enter(&mut dst); + Pin::new_unchecked(&mut me.generator).poll(cx) + }; + + me.done = res.is_ready(); + + if dst.is_some() { + return Poll::Ready(dst.take()); + } + + if me.done { + Poll::Ready(None) + } else { + Poll::Pending + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9906677 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,177 @@ +#![doc(html_root_url = "https://docs.rs/async-stream/0.3.0")] +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + unreachable_pub +)] +#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] + +//! Asynchronous stream of elements. +//! +//! Provides two macros, `stream!` and `try_stream!`, allowing the caller to +//! define asynchronous streams of elements. These are implemented using `async` +//! & `await` notation. The `stream!` macro works without unstable features. +//! +//! The `stream!` macro returns an anonymous type implementing the [`Stream`] +//! trait. The `Item` associated type is the type of the values yielded from the +//! stream. The `try_stream!` also returns an anonymous type implementing the +//! [`Stream`] trait, but the `Item` associated type is `Result<T, Error>`. The +//! `try_stream!` macro supports using `?` notiation as part of the +//! implementation. +//! +//! # Usage +//! +//! A basic stream yielding numbers. Values are yielded using the `yield` +//! keyword. The stream block must return `()`. +//! +//! ```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); +//! } +//! } +//! ``` +//! +//! Streams may be returned by using `impl Stream<Item = T>`: +//! +//! ```rust +//! use async_stream::stream; +//! +//! use futures_core::stream::Stream; +//! use futures_util::pin_mut; +//! use futures_util::stream::StreamExt; +//! +//! fn zero_to_three() -> impl Stream<Item = u32> { +//! stream! { +//! for i in 0..3 { +//! yield i; +//! } +//! } +//! } +//! +//! #[tokio::main] +//! async fn main() { +//! let s = zero_to_three(); +//! pin_mut!(s); // needed for iteration +//! +//! while let Some(value) = s.next().await { +//! println!("got {}", value); +//! } +//! } +//! ``` +//! +//! Streams may be implemented in terms of other streams: +//! +//! ```rust +//! use async_stream::stream; +//! +//! use futures_core::stream::Stream; +//! use futures_util::pin_mut; +//! use futures_util::stream::StreamExt; +//! +//! fn zero_to_three() -> impl Stream<Item = u32> { +//! stream! { +//! for i in 0..3 { +//! yield i; +//! } +//! } +//! } +//! +//! fn double<S: Stream<Item = u32>>(input: S) +//! -> impl Stream<Item = u32> +//! { +//! stream! { +//! pin_mut!(input); +//! while let Some(value) = input.next().await { +//! yield value * 2; +//! } +//! } +//! } +//! +//! #[tokio::main] +//! async fn main() { +//! let s = double(zero_to_three()); +//! pin_mut!(s); // needed for iteration +//! +//! while let Some(value) = s.next().await { +//! println!("got {}", value); +//! } +//! } +//! ``` +//! +//! Rust try notation (`?`) can be used with the `try_stream!` macro. The `Item` +//! of the returned stream is `Result` with `Ok` being the value yielded and +//! `Err` the error type returned by `?`. +//! +//! ```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; +//! } +//! } +//! } +//! ``` +//! +//! # Implementation +//! +//! The `stream!` and `try_stream!` macros are implemented using proc macros. +//! Given that proc macros in expression position are not supported on stable +//! rust, a hack similar to the one provided by the [`proc-macro-hack`] crate is +//! used. The macro searches the syntax tree for instances of `sender.send($expr)` and +//! transforms them into `sender.send($expr).await`. +//! +//! The stream uses a lightweight sender to send values from the stream +//! implementation to the caller. When entering the stream, an `Option<T>` is +//! stored on the stack. A pointer to the cell is stored in a thread local and +//! `poll` is called on the async block. When `poll` returns. +//! `sender.send(value)` stores the value that cell and yields back to the +//! caller. +//! +//! [`Stream`]: https://docs.rs/futures-core/*/futures_core/stream/trait.Stream.html + +mod async_stream; +mod next; +#[doc(hidden)] +pub mod yielder; + +// Used by the macro, but not intended to be accessed publically. +#[doc(hidden)] +pub use crate::async_stream::AsyncStream; + +pub use async_stream_impl::{stream, try_stream}; + +#[doc(hidden)] +pub mod reexport { + #[doc(hidden)] + pub use crate::next::next; +} diff --git a/src/next.rs b/src/next.rs new file mode 100644 index 0000000..7b1e046 --- /dev/null +++ b/src/next.rs @@ -0,0 +1,32 @@ +use futures_core::Stream; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +// This is equivalent to the `futures::StreamExt::next` method. +// But we want to make this crate dependency as small as possible, so we define our `next` function. +#[doc(hidden)] +pub fn next<S>(stream: &mut S) -> impl Future<Output = Option<S::Item>> + '_ +where + S: Stream + Unpin, +{ + Next { stream } +} + +#[derive(Debug)] +struct Next<'a, S> { + stream: &'a mut S, +} + +impl<S> Unpin for Next<'_, S> where S: Unpin {} + +impl<S> Future for Next<'_, S> +where + S: Stream + Unpin, +{ + type Output = Option<S::Item>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + Pin::new(&mut self.stream).poll_next(cx) + } +} diff --git a/src/yielder.rs b/src/yielder.rs new file mode 100644 index 0000000..49c4133 --- /dev/null +++ b/src/yielder.rs @@ -0,0 +1,87 @@ +use std::cell::Cell; +use std::future::Future; +use std::marker::PhantomData; +use std::pin::Pin; +use std::ptr; +use std::task::{Context, Poll}; + +#[derive(Debug)] +pub struct Sender<T> { + _p: PhantomData<T>, +} + +#[derive(Debug)] +pub struct Receiver<T> { + _p: PhantomData<T>, +} + +pub(crate) struct Enter<'a, T> { + _rx: &'a mut Receiver<T>, + prev: *mut (), +} + +pub fn pair<T>() -> (Sender<T>, Receiver<T>) { + let tx = Sender { _p: PhantomData }; + let rx = Receiver { _p: PhantomData }; + (tx, rx) +} + +// Tracks the pointer to `Option<T>`. +// +// TODO: Ensure wakers match? +thread_local!(static STORE: Cell<*mut ()> = Cell::new(ptr::null_mut())); + +// ===== impl Sender ===== + +impl<T: Unpin> Sender<T> { + pub fn send(&mut self, value: T) -> impl Future<Output = ()> { + Send { value: Some(value) } + } +} + +struct Send<T> { + value: Option<T>, +} + +impl<T: Unpin> Future for Send<T> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { + if self.value.is_none() { + return Poll::Ready(()); + } + + STORE.with(|cell| unsafe { + let ptr = cell.get() as *mut Option<T>; + let option_ref = ptr.as_mut().expect("invalid usage"); + + if option_ref.is_none() { + *option_ref = self.value.take(); + } + + Poll::Pending + }) + } +} + +// ===== impl Receiver ===== + +impl<T> Receiver<T> { + pub(crate) fn enter<'a>(&'a mut self, dst: &'a mut Option<T>) -> Enter<'a, T> { + let prev = STORE.with(|cell| { + let prev = cell.get(); + cell.set(dst as *mut _ as *mut ()); + prev + }); + + Enter { _rx: self, prev } + } +} + +// ===== impl Enter ===== + +impl<'a, T> Drop for Enter<'a, T> { + fn drop(&mut self) { + STORE.with(|cell| cell.set(self.prev)); + } +} diff --git a/tests/for_await.rs b/tests/for_await.rs new file mode 100644 index 0000000..590ffbd --- /dev/null +++ b/tests/for_await.rs @@ -0,0 +1,23 @@ +use async_stream::stream; + +use futures_util::stream::StreamExt; + +#[tokio::test] +async fn test() { + let s = stream! { + yield "hello"; + yield "world"; + }; + + let s = stream! { + for await x in s { + yield x.to_owned() + "!"; + } + }; + + let values: Vec<_> = s.collect().await; + + assert_eq!(2, values.len()); + assert_eq!("hello!", values[0]); + assert_eq!("world!", values[1]); +} diff --git a/tests/stream.rs b/tests/stream.rs new file mode 100644 index 0000000..e46fc5a --- /dev/null +++ b/tests/stream.rs @@ -0,0 +1,165 @@ +use async_stream::stream; + +use futures_core::stream::{FusedStream, Stream}; +use futures_util::pin_mut; +use futures_util::stream::StreamExt; +use tokio::sync::mpsc; +use tokio_test::assert_ok; + +#[tokio::test] +async fn noop_stream() { + let s = stream! {}; + pin_mut!(s); + + while let Some(_) = s.next().await { + unreachable!(); + } +} + +#[tokio::test] +async fn empty_stream() { + let mut ran = false; + + { + let r = &mut ran; + let s = stream! { + *r = true; + println!("hello world!"); + }; + pin_mut!(s); + + while let Some(_) = s.next().await { + unreachable!(); + } + } + + assert!(ran); +} + +#[tokio::test] +async fn yield_single_value() { + let s = stream! { + yield "hello"; + }; + + let values: Vec<_> = s.collect().await; + + assert_eq!(1, values.len()); + assert_eq!("hello", values[0]); +} + +#[tokio::test] +async fn fused() { + let s = stream! { + yield "hello"; + }; + pin_mut!(s); + + assert!(!s.is_terminated()); + assert_eq!(s.next().await, Some("hello")); + assert_eq!(s.next().await, None); + + assert!(s.is_terminated()); + // This should return None from now on + assert_eq!(s.next().await, None); +} + +#[tokio::test] +async fn yield_multi_value() { + let s = stream! { + yield "hello"; + yield "world"; + yield "dizzy"; + }; + + let values: Vec<_> = s.collect().await; + + assert_eq!(3, values.len()); + assert_eq!("hello", values[0]); + assert_eq!("world", values[1]); + assert_eq!("dizzy", values[2]); +} + +#[tokio::test] +async fn return_stream() { + fn build_stream() -> impl Stream<Item = u32> { + stream! { + yield 1; + yield 2; + yield 3; + } + } + + let s = build_stream(); + + let values: Vec<_> = s.collect().await; + assert_eq!(3, values.len()); + assert_eq!(1, values[0]); + assert_eq!(2, values[1]); + assert_eq!(3, values[2]); +} + +#[tokio::test] +async fn consume_channel() { + let (mut tx, mut rx) = mpsc::channel(10); + + let s = stream! { + while let Some(v) = rx.recv().await { + yield v; + } + }; + + pin_mut!(s); + + for i in 0..3 { + assert_ok!(tx.send(i).await); + assert_eq!(Some(i), s.next().await); + } + + drop(tx); + assert_eq!(None, s.next().await); +} + +#[tokio::test] +async fn borrow_self() { + struct Data(String); + + impl Data { + fn stream<'a>(&'a self) -> impl Stream<Item = &str> + 'a { + stream! { + yield &self.0[..]; + } + } + } + + let data = Data("hello".to_string()); + let s = data.stream(); + pin_mut!(s); + + assert_eq!(Some("hello"), s.next().await); +} + +#[tokio::test] +async fn stream_in_stream() { + let s = stream! { + let s = stream! { + for i in 0..3 { + yield i; + } + }; + + pin_mut!(s); + while let Some(v) = s.next().await { + yield v; + } + }; + + let values: Vec<_> = s.collect().await; + assert_eq!(3, values.len()); +} + +#[test] +fn test() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/tests/try_stream.rs b/tests/try_stream.rs new file mode 100644 index 0000000..063e37a --- /dev/null +++ b/tests/try_stream.rs @@ -0,0 +1,80 @@ +use async_stream::try_stream; + +use futures_core::stream::Stream; +use futures_util::stream::StreamExt; + +#[tokio::test] +async fn single_err() { + let s = try_stream! { + if true { + Err("hello")?; + } else { + yield "world"; + } + + unreachable!(); + }; + + let values: Vec<_> = s.collect().await; + assert_eq!(1, values.len()); + assert_eq!(Err("hello"), values[0]); +} + +#[tokio::test] +async fn yield_then_err() { + let s = try_stream! { + yield "hello"; + Err("world")?; + unreachable!(); + }; + + let values: Vec<_> = s.collect().await; + assert_eq!(2, values.len()); + assert_eq!(Ok("hello"), values[0]); + assert_eq!(Err("world"), values[1]); +} + +#[tokio::test] +async fn convert_err() { + struct ErrorA(u8); + #[derive(PartialEq, Debug)] + struct ErrorB(u8); + impl From<ErrorA> for ErrorB { + fn from(a: ErrorA) -> ErrorB { + ErrorB(a.0) + } + } + + fn test() -> impl Stream<Item = Result<&'static str, ErrorB>> { + try_stream! { + if true { + Err(ErrorA(1))?; + } else { + Err(ErrorB(2))?; + } + yield "unreachable"; + } + } + + let values: Vec<_> = test().collect().await; + assert_eq!(1, values.len()); + assert_eq!(Err(ErrorB(1)), values[0]); +} + +#[tokio::test] +async fn multi_try() { + fn test() -> impl Stream<Item = Result<i32, String>> { + try_stream! { + let a = Ok::<_, String>(Ok::<_, String>(123))??; + for _ in (1..10) { + yield a; + } + } + } + let values: Vec<_> = test().collect().await; + assert_eq!(9, values.len()); + assert_eq!( + std::iter::repeat(123).take(9).map(Ok).collect::<Vec<_>>(), + values + ); +} diff --git a/tests/ui/yield_in_async.rs b/tests/ui/yield_in_async.rs new file mode 100644 index 0000000..24e7330 --- /dev/null +++ b/tests/ui/yield_in_async.rs @@ -0,0 +1,11 @@ +use async_stream::stream; + +fn main() { + stream! { + let f = async { + yield 123; + }; + + let v = f.await; + }; +} diff --git a/tests/ui/yield_in_async.stderr b/tests/ui/yield_in_async.stderr new file mode 100644 index 0000000..1e9d8ff --- /dev/null +++ b/tests/ui/yield_in_async.stderr @@ -0,0 +1,76 @@ +error[E0658]: yield syntax is experimental + --> $DIR/yield_in_async.rs:6:13 + | +6 | yield 123; + | ^^^^^^^^^ + | + = note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information + +error[E0727]: `async` generators are not yet supported + --> $DIR/yield_in_async.rs:6:13 + | +6 | yield 123; + | ^^^^^^^^^ + +error[E0271]: type mismatch resolving `<[static generator@$DIR/tests/ui/yield_in_async.rs:4:5: 10:7 _] as std::ops::Generator<std::future::ResumeTy>>::Yield == ()` + --> $DIR/yield_in_async.rs:4:5 + | +4 | / stream! { +5 | | let f = async { +6 | | yield 123; +7 | | }; +8 | | +9 | | let v = f.await; +10 | | }; + | |______^ expected `()`, found integer + +error[E0698]: type inside `async` block must be known in this context + --> $DIR/yield_in_async.rs:6:19 + | +6 | yield 123; + | ^^^ cannot infer type for type `{integer}` + | +note: the type is part of the `async` block because of this `yield` + --> $DIR/yield_in_async.rs:6:13 + | +6 | yield 123; + | ^^^^^^^^^ + +error[E0698]: type inside `async` block must be known in this context + --> $DIR/yield_in_async.rs:5:13 + | +5 | let f = async { + | ^ cannot infer type for type `{integer}` + | +note: the type is part of the `async` block because of this `await` + --> $DIR/yield_in_async.rs:9:17 + | +9 | let v = f.await; + | ^^^^^^^ + +error[E0698]: type inside `async` block must be known in this context + --> $DIR/yield_in_async.rs:9:17 + | +9 | let v = f.await; + | ^ cannot infer type for type `{integer}` + | +note: the type is part of the `async` block because of this `await` + --> $DIR/yield_in_async.rs:9:17 + | +9 | let v = f.await; + | ^^^^^^^ + +error[E0698]: type inside `async` block must be known in this context + --> $DIR/yield_in_async.rs:9:17 + | +9 | let v = f.await; + | ^^^^^^^ cannot infer type for type `{integer}` + | +note: the type is part of the `async` block because of this `await` + --> $DIR/yield_in_async.rs:9:17 + | +9 | let v = f.await; + | ^^^^^^^ + +Some errors have detailed explanations: E0271, E0658, E0698, E0727. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/yield_in_closure.rs b/tests/ui/yield_in_closure.rs new file mode 100644 index 0000000..cd6ebd9 --- /dev/null +++ b/tests/ui/yield_in_closure.rs @@ -0,0 +1,11 @@ +use async_stream::stream; + +fn main() { + stream! { + Ok("value") + .and_then(|v| { + yield v; + Ok(()) + }); + }; +} diff --git a/tests/ui/yield_in_closure.stderr b/tests/ui/yield_in_closure.stderr new file mode 100644 index 0000000..77092e0 --- /dev/null +++ b/tests/ui/yield_in_closure.stderr @@ -0,0 +1,18 @@ +error[E0658]: yield syntax is experimental + --> $DIR/yield_in_closure.rs:7:17 + | +7 | yield v; + | ^^^^^^^ + | + = note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information + +error[E0277]: expected a `std::ops::FnOnce<(&str,)>` closure, found `[generator@$DIR/tests/ui/yield_in_closure.rs:4:5: 10:7 _]` + --> $DIR/yield_in_closure.rs:6:14 + | +6 | .and_then(|v| { + | ^^^^^^^^ expected an `FnOnce<(&str,)>` closure, found `[generator@$DIR/tests/ui/yield_in_closure.rs:4:5: 10:7 _]` + | + = help: the trait `std::ops::FnOnce<(&str,)>` is not implemented for `[generator@$DIR/tests/ui/yield_in_closure.rs:4:5: 10:7 _]` + +Some errors have detailed explanations: E0277, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/yield_in_nested_fn.rs b/tests/ui/yield_in_nested_fn.rs new file mode 100644 index 0000000..9ae6cf2 --- /dev/null +++ b/tests/ui/yield_in_nested_fn.rs @@ -0,0 +1,9 @@ +use async_stream::stream; + +fn main() { + stream! { + fn foo() { + yield "hello"; + } + }; +} diff --git a/tests/ui/yield_in_nested_fn.stderr b/tests/ui/yield_in_nested_fn.stderr new file mode 100644 index 0000000..a562555 --- /dev/null +++ b/tests/ui/yield_in_nested_fn.stderr @@ -0,0 +1,16 @@ +error[E0658]: yield syntax is experimental + --> $DIR/yield_in_nested_fn.rs:6:13 + | +6 | yield "hello"; + | ^^^^^^^^^^^^^ + | + = note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information + +error[E0627]: yield expression outside of generator literal + --> $DIR/yield_in_nested_fn.rs:6:13 + | +6 | yield "hello"; + | ^^^^^^^^^^^^^ + +Some errors have detailed explanations: E0627, E0658. +For more information about an error, try `rustc --explain E0627`. |