Import async-stream 0.3.4 upstream upstream/0.3.4
authorWoohyun Jung <wh0705.jung@samsung.com>
Fri, 10 Mar 2023 07:46:47 +0000 (16:46 +0900)
committerWoohyun Jung <wh0705.jung@samsung.com>
Fri, 10 Mar 2023 07:46:47 +0000 (16:46 +0900)
25 files changed:
.cargo_vcs_info.json [new file with mode: 0644]
CHANGELOG.md [new file with mode: 0644]
Cargo.lock [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
README.tpl [new file with mode: 0644]
examples/tcp_accept.rs [new file with mode: 0644]
src/async_stream.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
src/next.rs [new file with mode: 0644]
src/yielder.rs [new file with mode: 0644]
tests/for_await.rs [new file with mode: 0644]
tests/spans_preserved.rs [new file with mode: 0644]
tests/stream.rs [new file with mode: 0644]
tests/try_stream.rs [new file with mode: 0644]
tests/ui/yield_bad_expr_in_macro.rs [new file with mode: 0644]
tests/ui/yield_bad_expr_in_macro.stderr [new file with mode: 0644]
tests/ui/yield_in_async.rs [new file with mode: 0644]
tests/ui/yield_in_async.stderr [new file with mode: 0644]
tests/ui/yield_in_closure.rs [new file with mode: 0644]
tests/ui/yield_in_closure.stderr [new file with mode: 0644]
tests/ui/yield_in_nested_fn.rs [new file with mode: 0644]
tests/ui/yield_in_nested_fn.stderr [new file with mode: 0644]

diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..1354331
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "bf41b9645fe39b8865da2f25edc286eb42d49ec8"
+  },
+  "path_in_vcs": "async-stream"
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644 (file)
index 0000000..d75deb9
--- /dev/null
@@ -0,0 +1,23 @@
+# 0.3.4
+
+* Improve support for `#[track_caller]` (#72)
+* Reduce unsafe code (#77)
+
+# 0.3.3
+
+* Fix a bug where `yield` and `?` cannot be used on the same line (#66)
+
+# 0.3.2
+
+* Expand `yield` in internal macro calls (#57)
+
+# 0.3.1
+
+* Support reexporting (#46)
+* Allow yielding `!Unpin` values (#50)
+* Implement `Stream::size_hint` method on `AsyncStream` (#40)
+* Documentation improvements
+
+# 0.3.0
+
+* Remove proc-macro-hack (#30)
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644 (file)
index 0000000..963f9da
--- /dev/null
@@ -0,0 +1,544 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "async-stream"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3670df70cbc01729f901f94c887814b3c68db038aad1329a418bae178bc5295c"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.4"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "rustversion",
+ "tokio",
+ "tokio-test",
+ "trybuild",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "basic-toml"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e819b667739967cd44d308b8c7b71305d8bb0729ac44a248aa08f33d01950b4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "futures-core"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-task"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
+
+[[package]]
+name = "futures-util"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
+dependencies = [
+ "futures-core",
+ "futures-macro",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
+
+[[package]]
+name = "libc"
+version = "0.2.139"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "mio"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
+
+[[package]]
+name = "ryu"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "socket2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "tokio"
+version = "1.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
+dependencies = [
+ "autocfg",
+ "bytes",
+ "libc",
+ "memchr",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-test"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53474327ae5e166530d17f2d956afcb4f8a004de581b3cae10f12006bc8163e3"
+dependencies = [
+ "async-stream 0.3.0",
+ "bytes",
+ "futures-core",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "trybuild"
+version = "1.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44da5a6f2164c8e14d3bbc0657d69c5966af9f5f6930d4f600b1f5c4a673413"
+dependencies = [
+ "basic-toml",
+ "glob",
+ "once_cell",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "termcolor",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[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-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",
+]
+
+[[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 = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..86aaf75
--- /dev/null
@@ -0,0 +1,46 @@
+# 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 are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.45"
+name = "async-stream"
+version = "0.3.4"
+authors = ["Carl Lerche <me@carllerche.com>"]
+description = "Asynchronous streams using async & await notation"
+readme = "README.md"
+license = "MIT"
+repository = "https://github.com/tokio-rs/async-stream"
+
+[dependencies.async-stream-impl]
+version = "=0.3.4"
+
+[dependencies.futures-core]
+version = "0.3"
+
+[dependencies.pin-project-lite]
+version = "0.2"
+
+[dev-dependencies.futures-util]
+version = "0.3"
+
+[dev-dependencies.rustversion]
+version = "1"
+
+[dev-dependencies.tokio]
+version = "1"
+features = ["full"]
+
+[dev-dependencies.tokio-test]
+version = "0.4"
+
+[dev-dependencies.trybuild]
+version = "1"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644 (file)
index 0000000..1407a68
--- /dev/null
@@ -0,0 +1,24 @@
+[package]
+name = "async-stream"
+# When releasing to crates.io:
+# - Update CHANGELOG.md
+# - Create git tag
+version = "0.3.4"
+edition = "2018"
+rust-version = "1.45"
+license = "MIT"
+authors = ["Carl Lerche <me@carllerche.com>"]
+description = "Asynchronous streams using async & await notation"
+repository = "https://github.com/tokio-rs/async-stream"
+
+[dependencies]
+async-stream-impl = { version = "=0.3.4", path = "../async-stream-impl" }
+futures-core = "0.3"
+pin-project-lite = "0.2"
+
+[dev-dependencies]
+futures-util = "0.3"
+rustversion = "1"
+tokio = { version = "1", features = ["full"] }
+tokio-test = "0.4"
+trybuild = "1"
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..8cbd7d6
--- /dev/null
+++ b/LICENSE
@@ -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/README.md b/README.md
new file mode 100644 (file)
index 0000000..97f1f8e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,165 @@
+# 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. This crate 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 `?` notation 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 - `async-stream` provides `for await`
+syntax to assist with this:
+
+```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! {
+        for await value in input {
+            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.
+The macro searches the syntax tree for instances of `yield $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
+
+## Supported Rust Versions
+
+`async-stream` is built against the latest stable release. The minimum supported version is 1.45 due to [function-like procedural macros in expression, pattern, and statement positions](https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements).
+
+## 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 (file)
index 0000000..cf0b729
--- /dev/null
@@ -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 (file)
index 0000000..1b69bda
--- /dev/null
@@ -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 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 (file)
index 0000000..ff408ab
--- /dev/null
@@ -0,0 +1,79 @@
+use crate::yielder::Receiver;
+
+use futures_core::{FusedStream, Stream};
+use pin_project_lite::pin_project;
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[derive(Debug)]
+    pub struct AsyncStream<T, U> {
+        rx: Receiver<T>,
+        done: bool,
+        #[pin]
+        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>> {
+        let me = self.project();
+
+        if *me.done {
+            return Poll::Ready(None);
+        }
+
+        let mut dst = None;
+        let res = {
+            let _enter = me.rx.enter(&mut dst);
+            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
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        if self.done {
+            (0, Some(0))
+        } else {
+            (0, None)
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..318e404
--- /dev/null
@@ -0,0 +1,242 @@
+#![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. This crate 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 `?` notation 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 - `async-stream` provides `for await`
+//! syntax to assist with this:
+//!
+//! ```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! {
+//!         for await value in input {
+//!             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.
+//! The macro searches the syntax tree for instances of `yield $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;
+mod yielder;
+
+/// Asynchronous stream
+///
+/// See [crate](index.html) documentation for more details.
+///
+/// # Examples
+///
+/// ```
+/// 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);
+///     }
+/// }
+/// ```
+#[macro_export]
+macro_rules! stream {
+    ($($tt:tt)*) => {
+        $crate::__private::stream_inner!(($crate) $($tt)*)
+    }
+}
+
+/// Asynchronous fallible stream
+///
+/// See [crate](index.html) documentation for more details.
+///
+/// # Examples
+///
+/// ```
+/// 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;
+///         }
+///     }
+/// }
+/// ```
+#[macro_export]
+macro_rules! try_stream {
+    ($($tt:tt)*) => {
+        $crate::__private::try_stream_inner!(($crate) $($tt)*)
+    }
+}
+
+// Not public API.
+#[doc(hidden)]
+pub mod __private {
+    pub use crate::async_stream::AsyncStream;
+    pub use crate::next::next;
+    pub use async_stream_impl::{stream_inner, try_stream_inner};
+    pub mod yielder {
+        pub use crate::yielder::pair;
+    }
+}
diff --git a/src/next.rs b/src/next.rs
new file mode 100644 (file)
index 0000000..7b1e046
--- /dev/null
@@ -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 (file)
index 0000000..597e1c9
--- /dev/null
@@ -0,0 +1,94 @@
+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 (),
+}
+
+// Note: It is considered unsound for anyone other than our macros to call
+// this function. This is a private API intended only for calls from our
+// macros, and users should never call it, but some people tend to
+// misinterpret it as fine to call unless it is marked unsafe.
+#[doc(hidden)]
+pub unsafe 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> 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 for Send<T> {}
+
+impl<T> 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| {
+            let ptr = cell.get() as *mut Option<T>;
+            let option_ref = unsafe { 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 (file)
index 0000000..590ffbd
--- /dev/null
@@ -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/spans_preserved.rs b/tests/spans_preserved.rs
new file mode 100644 (file)
index 0000000..f2663b8
--- /dev/null
@@ -0,0 +1,15 @@
+use async_stream::stream;
+use futures_util::pin_mut;
+use futures_util::stream::StreamExt;
+
+#[tokio::test]
+async fn spans_preserved() {
+    let s = stream! {
+     assert_eq!(line!(), 8);
+    };
+    pin_mut!(s);
+
+    while s.next().await.is_some() {
+        unreachable!();
+    }
+}
diff --git a/tests/stream.rs b/tests/stream.rs
new file mode 100644 (file)
index 0000000..4e26a3d
--- /dev/null
@@ -0,0 +1,237 @@
+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 s.next().await.is_some() {
+        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 s.next().await.is_some() {
+            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 unit_yield_in_select() {
+    use tokio::select;
+
+    async fn do_stuff_async() {}
+
+    let s = stream! {
+        select! {
+            _ = do_stuff_async() => yield,
+            else => yield,
+        }
+    };
+
+    let values: Vec<_> = s.collect().await;
+    assert_eq!(values.len(), 1);
+}
+
+#[tokio::test]
+async fn yield_with_select() {
+    use tokio::select;
+
+    async fn do_stuff_async() {}
+    async fn more_async_work() {}
+
+    let s = stream! {
+        select! {
+            _ = do_stuff_async() => yield "hey",
+            _ = more_async_work() => yield "hey",
+            else => yield "hey",
+        }
+    };
+
+    let values: Vec<_> = s.collect().await;
+    assert_eq!(values, vec!["hey"]);
+}
+
+#[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 (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());
+}
+
+#[tokio::test]
+async fn yield_non_unpin_value() {
+    let s: Vec<_> = stream! {
+        for i in 0..3 {
+            yield async move { i };
+        }
+    }
+    .buffered(1)
+    .collect()
+    .await;
+
+    assert_eq!(s, vec![0, 1, 2]);
+}
+
+#[test]
+fn inner_try_stream() {
+    use async_stream::try_stream;
+    use tokio::select;
+
+    async fn do_stuff_async() {}
+
+    let _ = stream! {
+        select! {
+            _ = do_stuff_async() => {
+                let another_s = try_stream! {
+                    yield;
+                };
+                let _: Result<(), ()> = Box::pin(another_s).next().await.unwrap();
+            },
+            else => {},
+        }
+        yield
+    };
+}
+
+#[rustversion::attr(not(stable), ignore)]
+#[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 (file)
index 0000000..c404e62
--- /dev/null
@@ -0,0 +1,87 @@
+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
+    );
+}
+
+#[allow(unused)]
+fn issue_65() -> impl Stream<Item = Result<u32, ()>> {
+    try_stream! {
+        yield Err(())?;
+    }
+}
diff --git a/tests/ui/yield_bad_expr_in_macro.rs b/tests/ui/yield_bad_expr_in_macro.rs
new file mode 100644 (file)
index 0000000..37fcd34
--- /dev/null
@@ -0,0 +1,11 @@
+use async_stream::stream;
+
+fn main() {
+    async fn work() {}
+
+    stream! {
+        tokio::select! {
+            _ = work() => yield fn f() {},
+        }
+    };
+}
diff --git a/tests/ui/yield_bad_expr_in_macro.stderr b/tests/ui/yield_bad_expr_in_macro.stderr
new file mode 100644 (file)
index 0000000..e70e200
--- /dev/null
@@ -0,0 +1,5 @@
+error: expected expression
+ --> tests/ui/yield_bad_expr_in_macro.rs:8:33
+  |
+8 |             _ = work() => yield fn f() {},
+  |                                 ^^
diff --git a/tests/ui/yield_in_async.rs b/tests/ui/yield_in_async.rs
new file mode 100644 (file)
index 0000000..24e7330
--- /dev/null
@@ -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 (file)
index 0000000..4322758
--- /dev/null
@@ -0,0 +1,19 @@
+error[E0658]: yield syntax is experimental
+ --> tests/ui/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
+ --> tests/ui/yield_in_async.rs:6:13
+  |
+6 |             yield 123;
+  |             ^^^^^^^^^
+
+error[E0308]: mismatched types
+ --> tests/ui/yield_in_async.rs:6:19
+  |
+6 |             yield 123;
+  |                   ^^^ expected `()`, found integer
diff --git a/tests/ui/yield_in_closure.rs b/tests/ui/yield_in_closure.rs
new file mode 100644 (file)
index 0000000..cd6ebd9
--- /dev/null
@@ -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 (file)
index 0000000..9f917f3
--- /dev/null
@@ -0,0 +1,26 @@
+error[E0658]: yield syntax is experimental
+ --> tests/ui/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 `FnOnce<(&str,)>` closure, found `[generator@$DIR/tests/ui/yield_in_closure.rs:6:23: 6:26]`
+ --> tests/ui/yield_in_closure.rs:6:23
+  |
+6 |               .and_then(|v| {
+  |  ______________--------_^
+  | |              |
+  | |              required by a bound introduced by this call
+7 | |                 yield v;
+8 | |                 Ok(())
+9 | |             });
+  | |_____________^ expected an `FnOnce<(&str,)>` closure, found `[generator@$DIR/tests/ui/yield_in_closure.rs:6:23: 6:26]`
+  |
+  = help: the trait `FnOnce<(&str,)>` is not implemented for `[generator@$DIR/tests/ui/yield_in_closure.rs:6:23: 6:26]`
+note: required by a bound in `Result::<T, E>::and_then`
+ --> $RUST/core/src/result.rs
+  |
+  |     pub fn and_then<U, F: FnOnce(T) -> Result<U, E>>(self, op: F) -> Result<U, E> {
+  |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Result::<T, E>::and_then`
diff --git a/tests/ui/yield_in_nested_fn.rs b/tests/ui/yield_in_nested_fn.rs
new file mode 100644 (file)
index 0000000..9ae6cf2
--- /dev/null
@@ -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 (file)
index 0000000..54fdee9
--- /dev/null
@@ -0,0 +1,13 @@
+error[E0658]: yield syntax is experimental
+ --> tests/ui/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
+ --> tests/ui/yield_in_nested_fn.rs:6:13
+  |
+6 |             yield "hello";
+  |             ^^^^^^^^^^^^^