Merge branch 'master' into add_stdin_lock

poc-serde-support
k-nasa 5 years ago
commit 48b255897e

@ -11,6 +11,8 @@ jobs:
build_and_test: build_and_test:
name: Build and test name: Build and test
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env:
RUSTFLAGS: -Dwarnings
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
@ -29,7 +31,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --all --benches --bins --examples --tests args: --all --bins --examples
- name: check unstable - name: check unstable
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
@ -41,11 +43,13 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all --doc --features unstable args: --all --features unstable
check_fmt_and_docs: check_fmt_and_docs:
name: Checking fmt and docs name: Checking fmt and docs
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
RUSTFLAGS: -Dwarnings
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
@ -77,6 +81,9 @@ jobs:
clippy_check: clippy_check:
name: Clippy check name: Clippy check
runs-on: ubuntu-latest runs-on: ubuntu-latest
# TODO: There is a lot of warnings
# env:
# RUSTFLAGS: -Dwarnings
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- id: component - id: component

@ -1,68 +0,0 @@
language: rust
env:
- RUSTFLAGS="-D warnings"
# Cache the whole `~/.cargo` directory to keep `~/cargo/.crates.toml`.
cache:
directories:
- /home/travis/.cargo
# Don't cache the cargo registry because it's too big.
before_cache:
- rm -rf /home/travis/.cargo/registry
branches:
only:
- master
- staging
- trying
matrix:
fast_finish: true
include:
- rust: nightly
os: linux
- rust: nightly
os: osx
osx_image: xcode9.2
- rust: nightly-x86_64-pc-windows-msvc
os: windows
- name: fmt
rust: nightly
os: linux
before_script: |
if ! rustup component add rustfmt; then
target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`;
echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead";
rustup toolchain install nightly-$target;
rustup default nightly-$target;
rustup component add rustfmt;
fi
script:
- cargo fmt --all -- --check
- name: docs
rust: nightly
os: linux
script:
- cargo doc --features docs
- name: book
rust: nightly
os: linux
before_script:
- test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh
- cargo build # to find 'extern crate async_std' by `mdbook test`
script:
- mdbook build docs
- mdbook test -L ./target/debug/deps docs
script:
- cargo check --all --benches --bins --examples --tests
- cargo check --features unstable --all --benches --bins --examples --tests
- cargo test --all --doc --features unstable

@ -7,6 +7,104 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview
## [Unreleased] ## [Unreleased]
# [0.99.10] - 2019-10-16
This patch stabilizes several core concurrency macros, introduces async versions
of `Path` and `PathBuf`, and adds almost 100 other commits.
## Examples
__Asynchronously read directories from the filesystem__
```rust
use async_std::fs;
use async_std::path::Path;
use async_std::prelude::*;
let path = Path::new("/laputa");
let mut dir = fs::read_dir(&path).await.unwrap();
while let Some(entry) = dir.next().await {
if let Ok(entry) = entry {
println!("{:?}", entry.path());
}
}
```
__Cooperatively reschedule the current task on the executor__
```rust
use async_std::prelude::*;
use async_std::task;
task::spawn(async {
let x = fibonnacci(1000); // Do expensive work
task::yield_now().await; // Allow other tasks to run
x + fibonnacci(100) // Do more work
})
```
__Create an interval stream__
```rust
use async_std::prelude::*;
use async_std::stream;
use std::time::Duration;
let mut interval = stream::interval(Duration::from_secs(4));
while let Some(_) = interval.next().await {
println!("prints every four seconds");
}
```
## Added
- Added `FutureExt` to the `prelude`, allowing us to extend `Future`
- Added `Stream::cmp`
- Added `Stream::ge`
- Added `Stream::last`
- Added `Stream::le`
- Added `Stream::lt`
- Added `Stream::merge` as "unstable", replacing `stream::join!`
- Added `Stream::partial_cmp`
- Added `Stream::take_while`
- Added `Stream::try_fold`
- Added `future::IntoFuture` as "unstable"
- Added `io::BufRead::split`
- Added `io::Write::write_fmt`
- Added `print!`, `println!`, `eprint!`, `eprintln!` macros as "unstable"
- Added `process` as "unstable", re-exporting std types only for now
- Added `std::net` re-exports to the `net` submodule
- Added `std::path::PathBuf` with all associated methods
- Added `std::path::Path` with all associated methods
- Added `stream::ExactSizeStream` as "unstable"
- Added `stream::FusedStream` as "unstable"
- Added `stream::Product`
- Added `stream::Sum`
- Added `stream::from_fn`
- Added `stream::interval` as "unstable"
- Added `stream::repeat_with`
- Added `task::spawn_blocking` as "unstable", replacing `task::blocking`
- Added `task::yield_now`
- Added `write!` and `writeln!` macros as "unstable"
- Stabilized `future::join!` and `future::try_join!`
- Stabilized `future::timeout`
- Stabilized `path`
- Stabilized `task::ready!`
## Changed
- Fixed `BufWriter::into_inner` so it calls `flush` before yielding
- Refactored `io::BufWriter` internals
- Refactored `net::ToSocketAddrs` internals
- Removed Travis CI entirely
- Rewrote the README.md
- Stabilized `io::Cursor`
- Switched bors over to use GitHub actions
- Updated the `io` documentation to match std's `io` docs
- Updated the `task` documentation to match std's `thread` docs
## Removed
- Removed the "unstable" `stream::join!` in favor of `Stream::merge`
- Removed the "unstable" `task::blocking` in favor of `task::spawn_blocking`
# [0.99.9] - 2019-10-08 # [0.99.9] - 2019-10-08
This patch upgrades our `futures-rs` version, allowing us to build on the 1.39 This patch upgrades our `futures-rs` version, allowing us to build on the 1.39
@ -183,7 +281,8 @@ task::blocking(async {
- Initial beta release - Initial beta release
[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.9...HEAD [Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.10...HEAD
[0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10
[0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9 [0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9
[0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8 [0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8
[0.99.7]: https://github.com/async-rs/async-std/compare/v0.99.6...v0.99.7 [0.99.7]: https://github.com/async-rs/async-std/compare/v0.99.6...v0.99.7

@ -1,6 +1,6 @@
[package] [package]
name = "async-std" name = "async-std"
version = "0.99.9" version = "0.99.10"
authors = [ authors = [
"Stjepan Glavina <stjepang@gmail.com>", "Stjepan Glavina <stjepang@gmail.com>",
"Yoshua Wuyts <yoshuawuyts@gmail.com>", "Yoshua Wuyts <yoshuawuyts@gmail.com>",
@ -21,18 +21,18 @@ features = ["docs"]
rustdoc-args = ["--cfg", "feature=\"docs\""] rustdoc-args = ["--cfg", "feature=\"docs\""]
[features] [features]
docs = ["broadcaster"] docs = ["unstable"]
unstable = ["broadcaster"] unstable = ["broadcaster"]
[dependencies] [dependencies]
async-macros = "1.0.0" async-macros = "1.0.0"
async-task = "1.0.0" async-task = "1.0.0"
cfg-if = "0.1.9"
crossbeam-channel = "0.3.9" crossbeam-channel = "0.3.9"
crossbeam-deque = "0.7.1" crossbeam-deque = "0.7.1"
crossbeam-utils = "0.6.6"
futures-core-preview = "=0.3.0-alpha.19" futures-core-preview = "=0.3.0-alpha.19"
futures-io-preview = "=0.3.0-alpha.19" futures-io-preview = "=0.3.0-alpha.19"
futures-timer = "0.4.0" futures-timer = "1.0.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = { version = "0.4.8", features = ["kv_unstable"] } log = { version = "0.4.8", features = ["kv_unstable"] }
memchr = "2.2.1" memchr = "2.2.1"
@ -43,9 +43,11 @@ pin-utils = "0.1.0-alpha.4"
slab = "0.4.2" slab = "0.4.2"
kv-log-macro = "1.0.4" kv-log-macro = "1.0.4"
broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] }
pin-project-lite = "0.1"
[dev-dependencies] [dev-dependencies]
femme = "1.2.0" femme = "1.2.0"
rand = "0.7.2"
# surf = "1.0.2" # surf = "1.0.2"
tempdir = "0.3.7" tempdir = "0.3.7"
futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] }

@ -1,41 +1,78 @@
# Async version of the Rust standard library <h1 align="center">async-std</h1>
<div align="center">
[![Build Status](https://travis-ci.com/async-rs/async-std.svg?branch=master)](https://travis-ci.com/async-rs/async-std) <strong>
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/async-rs/async-std) Async version of the Rust standard library
[![Cargo](https://img.shields.io/crates/v/async-std.svg)](https://crates.io/crates/async-std) </strong>
[![Documentation](https://docs.rs/async-std/badge.svg)](https://docs.rs/async-std) </div>
[![chat](https://img.shields.io/discord/598880689856970762.svg?logo=discord)](https://discord.gg/JvZeVNe)
<br />
This crate provides an async version of [`std`]. It provides all the interfaces you
are used to, but in an async version and ready for Rust's `async`/`await` syntax. <div align="center">
<!-- Crates version -->
<a href="https://crates.io/crates/async-std">
<img src="https://img.shields.io/crates/v/async-std.svg?style=flat-square"
alt="Crates.io version" />
</a>
<!-- Downloads -->
<a href="https://crates.io/crates/async-std">
<img src="https://img.shields.io/crates/d/async-std.svg?style=flat-square"
alt="Download" />
</a>
<!-- docs.rs docs -->
<a href="https://docs.rs/async-std">
<img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
alt="docs.rs docs" />
</a>
<a href="https://discord.gg/JvZeVNe">
<img src="https://img.shields.io/discord/598880689856970762.svg?logo=discord&style=flat-square"
alt="chat" />
</a>
</div>
<div align="center">
<h3>
<a href="https://docs.rs/async-std">
API Docs
</a>
<span> | </span>
<a href="https://book.async.rs">
Book
</a>
<span> | </span>
<a href="https://github.com/async-rs/async-std/releases">
Releases
</a>
<span> | </span>
<a href="https://async.rs/contribute">
Contributing
</a>
</h3>
</div>
<br/>
This crate provides an async version of [`std`]. It provides all the interfaces
you are used to, but in an async version and ready for Rust's `async`/`await`
syntax.
[`std`]: https://doc.rust-lang.org/std/index.html [`std`]: https://doc.rust-lang.org/std/index.html
## Documentation ## Features
`async-std` comes with [extensive API documentation][docs] and a [book][book]. - __Modern:__ Built from the ground up for `std::future` and `async/await` with
blazing fast compilation times.
- __Fast:__ Our robust allocator and threadpool designs provide ultra-high
throughput with predictably low latency.
- __Intuitive:__ Complete parity with the stdlib means you only need to learn
APIs once.
- __Clear:__ [Detailed documentation][docs] and [accessible guides][book] mean
using async Rust was never easier.
[docs]: https://docs.rs/async-std [docs]: https://docs.rs/async-std
[book]: https://book.async.rs [book]: https://book.async.rs
## Quickstart ## Examples
Add the following lines to your `Cargo.toml`:
```toml
[dependencies]
async-std = "0.99"
```
Or use [cargo add][cargo-add] if you have it installed:
```sh
$ cargo add async-std
```
[cargo-add]: https://github.com/killercup/cargo-edit
## Hello world
```rust ```rust
use async_std::task; use async_std::task;
@ -47,96 +84,48 @@ fn main() {
} }
``` ```
## Low-Friction Sockets with Built-In Timeouts More examples, including networking and file access, can be found in our
[`examples`] directory.
```rust
use std::time::Duration;
use async_std::{
prelude::*,
task,
io,
net::TcpStream,
};
async fn get() -> io::Result<Vec<u8>> {
let mut stream = TcpStream::connect("example.com:80").await?;
stream.write_all(b"GET /index.html HTTP/1.0\r\n\r\n").await?;
let mut buf = vec![];
io::timeout(Duration::from_secs(5), async {
stream.read_to_end(&mut buf).await?;
Ok(buf)
}).await
}
fn main() {
task::block_on(async {
let raw_response = get().await.expect("request");
let response = String::from_utf8(raw_response)
.expect("utf8 conversion");
println!("received: {}", response);
});
}
```
## Features
`async-std` is strongly commited to following semver. This means your code won't
break unless _you_ decide to upgrade.
However every now and then we come up with something that we think will work
_great_ for `async-std`, and we want to provide a sneak-peek so you can try it
out. This is what we call _"unstable"_ features. You can try out the unstable
features by enabling the `unstable` feature in your `Cargo.toml` file:
```toml
[dependencies.async-std]
version = "0.99"
features = ["unstable"]
```
Just be careful when using these features, as they may change between
versions.
## Take a look around [`examples`]: https://github.com/async-rs/async-std/tree/master/examples
Clone the repo: ## Philosophy
``` We believe Async Rust should be as easy to pick up as Sync Rust. We also believe
git clone git@github.com:async-rs/async-std.git && cd async-std that the best API is the one you already know. And finally, we believe that
``` providing an asynchronous counterpart to the standard library is the best way
stdlib provides a reliable basis for both performance and productivity.
Generate docs: Async-std is the embodiment of that vision. It combines single-allocation task
creation, with an adaptive lock-free executor, threadpool and network driver to
create a smooth system that processes work at a high pace with low latency,
using Rust's familiar stdlib API.
``` ## Installation
cargo +nightly doc --features docs --open
```
Check out the [examples](examples). To run an example: With [cargo add][cargo-add] installed run:
```sh
$ cargo add async-std
``` ```
cargo +nightly run --example hello-world
```
## Contributing
See [our contribution document][contribution]. We also provide a set of "unstable" features with async-std. See the [features
documentation] on how to enable them.
[contribution]: https://async.rs/contribute [cargo-add]: https://github.com/killercup/cargo-edit
[features documentation]: https://docs.rs/async-std/#features
## License ## License
Licensed under either of <sup>
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) </sup>
at your option.
#### Contribution <br/>
<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
dual licensed as above, without any additional terms or conditions. be dual licensed as above, without any additional terms or conditions.
</sub>

@ -32,5 +32,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::canonicalize(&path).map(Into::into) }).await blocking::spawn(move || std::fs::canonicalize(&path).map(Into::into)).await
} }

@ -41,5 +41,5 @@ use crate::task::blocking;
pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
let from = from.as_ref().to_owned(); let from = from.as_ref().to_owned();
let to = to.as_ref().to_owned(); let to = to.as_ref().to_owned();
blocking::spawn(async move { std::fs::copy(&from, &to) }).await blocking::spawn(move || std::fs::copy(&from, &to)).await
} }

@ -34,5 +34,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::create_dir(path) }).await blocking::spawn(move || std::fs::create_dir(path)).await
} }

@ -29,5 +29,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::create_dir_all(path) }).await blocking::spawn(move || std::fs::create_dir_all(path)).await
} }

@ -1,6 +1,5 @@
use cfg_if::cfg_if; use std::future::Future;
use crate::future::Future;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::blocking;
@ -108,26 +107,17 @@ impl DirBuilder {
} }
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
async move { blocking::spawn(async move { builder.create(path) }).await } async move { blocking::spawn(move || builder.create(path)).await }
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::fs::DirBuilderExt;
use crate::os::unix::fs::DirBuilderExt;
} else if #[cfg(unix)] {
use std::os::unix::fs::DirBuilderExt;
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl DirBuilderExt for DirBuilder {
cfg_if! { fn mode(&mut self, mode: u32) -> &mut Self {
if #[cfg(any(unix, feature = "docs"))] { self.mode = Some(mode);
impl DirBuilderExt for DirBuilder { self
fn mode(&mut self, mode: u32) -> &mut Self {
self.mode = Some(mode);
self
}
} }
} }
} }

@ -2,8 +2,6 @@ use std::ffi::OsString;
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use cfg_if::cfg_if;
use crate::fs::{FileType, Metadata}; use crate::fs::{FileType, Metadata};
use crate::io; use crate::io;
use crate::path::PathBuf; use crate::path::PathBuf;
@ -89,7 +87,7 @@ impl DirEntry {
/// ``` /// ```
pub async fn metadata(&self) -> io::Result<Metadata> { pub async fn metadata(&self) -> io::Result<Metadata> {
let inner = self.0.clone(); let inner = self.0.clone();
blocking::spawn(async move { inner.metadata() }).await blocking::spawn(move || inner.metadata()).await
} }
/// Reads the file type for this entry. /// Reads the file type for this entry.
@ -127,7 +125,7 @@ impl DirEntry {
/// ``` /// ```
pub async fn file_type(&self) -> io::Result<FileType> { pub async fn file_type(&self) -> io::Result<FileType> {
let inner = self.0.clone(); let inner = self.0.clone();
blocking::spawn(async move { inner.file_type() }).await blocking::spawn(move || inner.file_type()).await
} }
/// Returns the bare name of this entry without the leading path. /// Returns the bare name of this entry without the leading path.
@ -160,21 +158,12 @@ impl fmt::Debug for DirEntry {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::fs::DirEntryExt;
use crate::os::unix::fs::DirEntryExt;
} else if #[cfg(unix)] {
use std::os::unix::fs::DirEntryExt;
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl DirEntryExt for DirEntry {
cfg_if! { fn ino(&self) -> u64 {
if #[cfg(any(unix, feature = "docs"))] { self.0.ino()
impl DirEntryExt for DirEntry {
fn ino(&self) -> u64 {
self.0.ino()
}
} }
} }
} }

@ -7,8 +7,6 @@ use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use cfg_if::cfg_if;
use crate::fs::{Metadata, Permissions}; use crate::fs::{Metadata, Permissions};
use crate::future; use crate::future;
use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::io::{self, Read, Seek, SeekFrom, Write};
@ -97,7 +95,7 @@ impl File {
/// ``` /// ```
pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> { pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let file = blocking::spawn(async move { std::fs::File::open(&path) }).await?; let file = blocking::spawn(move || std::fs::File::open(&path)).await?;
Ok(file.into()) Ok(file.into())
} }
@ -132,7 +130,7 @@ impl File {
/// ``` /// ```
pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> { pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let file = blocking::spawn(async move { std::fs::File::create(&path) }).await?; let file = blocking::spawn(move || std::fs::File::create(&path)).await?;
Ok(file.into()) Ok(file.into())
} }
@ -165,7 +163,7 @@ impl File {
}) })
.await?; .await?;
blocking::spawn(async move { state.file.sync_all() }).await blocking::spawn(move || state.file.sync_all()).await
} }
/// Synchronizes OS-internal buffered contents to disk. /// Synchronizes OS-internal buffered contents to disk.
@ -201,7 +199,7 @@ impl File {
}) })
.await?; .await?;
blocking::spawn(async move { state.file.sync_data() }).await blocking::spawn(move || state.file.sync_data()).await
} }
/// Truncates or extends the file. /// Truncates or extends the file.
@ -234,7 +232,7 @@ impl File {
}) })
.await?; .await?;
blocking::spawn(async move { state.file.set_len(size) }).await blocking::spawn(move || state.file.set_len(size)).await
} }
/// Reads the file's metadata. /// Reads the file's metadata.
@ -253,7 +251,7 @@ impl File {
/// ``` /// ```
pub async fn metadata(&self) -> io::Result<Metadata> { pub async fn metadata(&self) -> io::Result<Metadata> {
let file = self.file.clone(); let file = self.file.clone();
blocking::spawn(async move { file.metadata() }).await blocking::spawn(move || file.metadata()).await
} }
/// Changes the permissions on the file. /// Changes the permissions on the file.
@ -282,7 +280,7 @@ impl File {
/// ``` /// ```
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
let file = self.file.clone(); let file = self.file.clone();
blocking::spawn(async move { file.set_permissions(perm) }).await blocking::spawn(move || file.set_permissions(perm)).await
} }
} }
@ -401,67 +399,54 @@ impl From<std::fs::File> for File {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
} else if #[cfg(windows)] {
use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for File {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { self.file.as_raw_fd()
impl AsRawFd for File {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
} }
}
impl FromRawFd for File { impl FromRawFd for File {
unsafe fn from_raw_fd(fd: RawFd) -> File { unsafe fn from_raw_fd(fd: RawFd) -> File {
std::fs::File::from_raw_fd(fd).into() std::fs::File::from_raw_fd(fd).into()
}
} }
}
impl IntoRawFd for File { impl IntoRawFd for File {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
let file = self.file.clone(); let file = self.file.clone();
drop(self); drop(self);
Arc::try_unwrap(file) Arc::try_unwrap(file)
.expect("cannot acquire ownership of the file handle after drop") .expect("cannot acquire ownership of the file handle after drop")
.into_raw_fd() .into_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(windows)))] cfg_windows! {
cfg_if! { use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] {
impl AsRawHandle for File { impl AsRawHandle for File {
fn as_raw_handle(&self) -> RawHandle { fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle() self.file.as_raw_handle()
}
} }
}
impl FromRawHandle for File { impl FromRawHandle for File {
unsafe fn from_raw_handle(handle: RawHandle) -> File { unsafe fn from_raw_handle(handle: RawHandle) -> File {
std::fs::File::from_raw_handle(handle).into() std::fs::File::from_raw_handle(handle).into()
}
} }
}
impl IntoRawHandle for File { impl IntoRawHandle for File {
fn into_raw_handle(self) -> RawHandle { fn into_raw_handle(self) -> RawHandle {
let file = self.file.clone(); let file = self.file.clone();
drop(self); drop(self);
Arc::try_unwrap(file) Arc::try_unwrap(file)
.expect("cannot acquire ownership of the file handle after drop") .expect("cannot acquire ownership of the file handle after drop")
.into_raw_handle() .into_raw_handle()
}
} }
} }
} }
@ -702,7 +687,7 @@ impl LockGuard<State> {
self.register(cx); self.register(cx);
// Start a read operation asynchronously. // Start a read operation asynchronously.
blocking::spawn(async move { blocking::spawn(move || {
// Read some data from the file into the cache. // Read some data from the file into the cache.
let res = { let res = {
let State { file, cache, .. } = &mut *self; let State { file, cache, .. } = &mut *self;
@ -811,7 +796,7 @@ impl LockGuard<State> {
self.register(cx); self.register(cx);
// Start a write operation asynchronously. // Start a write operation asynchronously.
blocking::spawn(async move { blocking::spawn(move || {
match (&*self.file).write_all(&self.cache) { match (&*self.file).write_all(&self.cache) {
Ok(_) => { Ok(_) => {
// Switch to idle mode. // Switch to idle mode.
@ -844,7 +829,7 @@ impl LockGuard<State> {
self.register(cx); self.register(cx);
// Start a flush operation asynchronously. // Start a flush operation asynchronously.
blocking::spawn(async move { blocking::spawn(move || {
match (&*self.file).flush() { match (&*self.file).flush() {
Ok(()) => { Ok(()) => {
// Mark the file as flushed. // Mark the file as flushed.

@ -1,86 +1,84 @@
use cfg_if::cfg_if; cfg_not_docs! {
pub use std::fs::FileType;
}
cfg_docs! {
/// The type of a file or directory.
///
/// A file type is returned by [`Metadata::file_type`].
///
/// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`],
/// [`is_file`], and [`is_symlink`] can return `true`.
///
/// This type is a re-export of [`std::fs::FileType`].
///
/// [`Metadata::file_type`]: struct.Metadata.html#method.file_type
/// [`is_dir`]: #method.is_dir
/// [`is_file`]: #method.is_file
/// [`is_symlink`]: #method.is_symlink
/// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct FileType {
_private: (),
}
cfg_if! { impl FileType {
if #[cfg(feature = "docs")] { /// Returns `true` if this file type represents a regular directory.
/// The type of a file or directory.
/// ///
/// A file type is returned by [`Metadata::file_type`]. /// If this file type represents a symbolic link, this method returns `false`.
/// ///
/// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`], /// # Examples
/// [`is_file`], and [`is_symlink`] can return `true`.
/// ///
/// This type is a re-export of [`std::fs::FileType`]. /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::fs;
/// ///
/// [`Metadata::file_type`]: struct.Metadata.html#method.file_type /// let file_type = fs::metadata(".").await?.file_type();
/// [`is_dir`]: #method.is_dir /// println!("{:?}", file_type.is_dir());
/// [`is_file`]: #method.is_file /// #
/// [`is_symlink`]: #method.is_symlink /// # Ok(()) }) }
/// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html /// ```
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub fn is_dir(&self) -> bool {
pub struct FileType { unimplemented!()
_private: (),
} }
impl FileType { /// Returns `true` if this file type represents a regular file.
/// Returns `true` if this file type represents a regular directory. ///
/// /// If this file type represents a symbolic link, this method returns `false`.
/// If this file type represents a symbolic link, this method returns `false`. ///
/// /// # Examples
/// # Examples ///
/// /// ```no_run
/// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// #
/// # /// use async_std::fs;
/// use async_std::fs; ///
/// /// let file_type = fs::metadata("a.txt").await?.file_type();
/// let file_type = fs::metadata(".").await?.file_type(); /// println!("{:?}", file_type.is_file());
/// println!("{:?}", file_type.is_dir()); /// #
/// # /// # Ok(()) }) }
/// # Ok(()) }) } /// ```
/// ``` pub fn is_file(&self) -> bool {
pub fn is_dir(&self) -> bool { unimplemented!()
unimplemented!() }
}
/// Returns `true` if this file type represents a regular file.
///
/// If this file type represents a symbolic link, this method returns `false`.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::fs;
///
/// let file_type = fs::metadata("a.txt").await?.file_type();
/// println!("{:?}", file_type.is_file());
/// #
/// # Ok(()) }) }
/// ```
pub fn is_file(&self) -> bool {
unimplemented!()
}
/// Returns `true` if this file type represents a symbolic link. /// Returns `true` if this file type represents a symbolic link.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # /// #
/// use async_std::fs; /// use async_std::fs;
/// ///
/// let file_type = fs::metadata("a.txt").await?.file_type(); /// let file_type = fs::metadata("a.txt").await?.file_type();
/// println!("{:?}", file_type.is_symlink()); /// println!("{:?}", file_type.is_symlink());
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn is_symlink(&self) -> bool { pub fn is_symlink(&self) -> bool {
unimplemented!() unimplemented!()
}
} }
} else {
pub use std::fs::FileType;
} }
} }

@ -32,5 +32,5 @@ use crate::task::blocking;
pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
let from = from.as_ref().to_owned(); let from = from.as_ref().to_owned();
let to = to.as_ref().to_owned(); let to = to.as_ref().to_owned();
blocking::spawn(async move { std::fs::hard_link(&from, &to) }).await blocking::spawn(move || std::fs::hard_link(&from, &to)).await
} }

@ -1,5 +1,3 @@
use cfg_if::cfg_if;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::blocking;
@ -36,196 +34,196 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::metadata(path) }).await blocking::spawn(move || std::fs::metadata(path)).await
}
cfg_not_docs! {
pub use std::fs::Metadata;
} }
cfg_if! { cfg_docs! {
if #[cfg(feature = "docs")] { use std::time::SystemTime;
use std::time::SystemTime;
use crate::fs::{FileType, Permissions}; use crate::fs::{FileType, Permissions};
/// Metadata for a file or directory. /// Metadata for a file or directory.
///
/// Metadata is returned by [`metadata`] and [`symlink_metadata`].
///
/// This type is a re-export of [`std::fs::Metadata`].
///
/// [`metadata`]: fn.metadata.html
/// [`symlink_metadata`]: fn.symlink_metadata.html
/// [`is_dir`]: #method.is_dir
/// [`is_file`]: #method.is_file
/// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html
#[derive(Clone, Debug)]
pub struct Metadata {
_private: (),
}
impl Metadata {
/// Returns the file type from this metadata.
/// ///
/// Metadata is returned by [`metadata`] and [`symlink_metadata`]. /// # Examples
/// ///
/// This type is a re-export of [`std::fs::Metadata`]. /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::fs;
/// ///
/// [`metadata`]: fn.metadata.html /// let metadata = fs::metadata("a.txt").await?;
/// [`symlink_metadata`]: fn.symlink_metadata.html /// println!("{:?}", metadata.file_type());
/// [`is_dir`]: #method.is_dir /// #
/// [`is_file`]: #method.is_file /// # Ok(()) }) }
/// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html /// ```
#[derive(Clone, Debug)] pub fn file_type(&self) -> FileType {
pub struct Metadata { unimplemented!()
_private: (),
} }
impl Metadata { /// Returns `true` if this metadata is for a regular directory.
/// Returns the file type from this metadata. ///
/// /// If this metadata is for a symbolic link, this method returns `false`.
/// # Examples ///
/// /// # Examples
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// # /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::fs; /// #
/// /// use async_std::fs;
/// let metadata = fs::metadata("a.txt").await?; ///
/// println!("{:?}", metadata.file_type()); /// let metadata = fs::metadata(".").await?;
/// # /// println!("{:?}", metadata.is_dir());
/// # Ok(()) }) } /// #
/// ``` /// # Ok(()) }) }
pub fn file_type(&self) -> FileType { /// ```
unimplemented!() pub fn is_dir(&self) -> bool {
} unimplemented!()
}
/// Returns `true` if this metadata is for a regular directory.
///
/// If this metadata is for a symbolic link, this method returns `false`.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::fs;
///
/// let metadata = fs::metadata(".").await?;
/// println!("{:?}", metadata.is_dir());
/// #
/// # Ok(()) }) }
/// ```
pub fn is_dir(&self) -> bool {
unimplemented!()
}
/// Returns `true` if this metadata is for a regular file. /// Returns `true` if this metadata is for a regular file.
/// ///
/// If this metadata is for a symbolic link, this method returns `false`. /// If this metadata is for a symbolic link, this method returns `false`.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # /// #
/// use async_std::fs; /// use async_std::fs;
/// ///
/// let metadata = fs::metadata("a.txt").await?; /// let metadata = fs::metadata("a.txt").await?;
/// println!("{:?}", metadata.is_file()); /// println!("{:?}", metadata.is_file());
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn is_file(&self) -> bool { pub fn is_file(&self) -> bool {
unimplemented!() unimplemented!()
} }
/// Returns the file size in bytes. /// Returns the file size in bytes.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # /// #
/// use async_std::fs; /// use async_std::fs;
/// ///
/// let metadata = fs::metadata("a.txt").await?; /// let metadata = fs::metadata("a.txt").await?;
/// println!("{}", metadata.len()); /// println!("{}", metadata.len());
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn len(&self) -> u64 { pub fn len(&self) -> u64 {
unimplemented!() unimplemented!()
} }
/// Returns the permissions from this metadata. /// Returns the permissions from this metadata.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # /// #
/// use async_std::fs; /// use async_std::fs;
/// ///
/// let metadata = fs::metadata("a.txt").await?; /// let metadata = fs::metadata("a.txt").await?;
/// println!("{:?}", metadata.permissions()); /// println!("{:?}", metadata.permissions());
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn permissions(&self) -> Permissions { pub fn permissions(&self) -> Permissions {
unimplemented!() unimplemented!()
} }
/// Returns the last modification time. /// Returns the last modification time.
/// ///
/// # Errors /// # Errors
/// ///
/// This data may not be available on all platforms, in which case an error will be /// This data may not be available on all platforms, in which case an error will be
/// returned. /// returned.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # /// #
/// use async_std::fs; /// use async_std::fs;
/// ///
/// let metadata = fs::metadata("a.txt").await?; /// let metadata = fs::metadata("a.txt").await?;
/// println!("{:?}", metadata.modified()); /// println!("{:?}", metadata.modified());
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn modified(&self) -> io::Result<SystemTime> { pub fn modified(&self) -> io::Result<SystemTime> {
unimplemented!() unimplemented!()
} }
/// Returns the last access time. /// Returns the last access time.
/// ///
/// # Errors /// # Errors
/// ///
/// This data may not be available on all platforms, in which case an error will be /// This data may not be available on all platforms, in which case an error will be
/// returned. /// returned.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # /// #
/// use async_std::fs; /// use async_std::fs;
/// ///
/// let metadata = fs::metadata("a.txt").await?; /// let metadata = fs::metadata("a.txt").await?;
/// println!("{:?}", metadata.accessed()); /// println!("{:?}", metadata.accessed());
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn accessed(&self) -> io::Result<SystemTime> { pub fn accessed(&self) -> io::Result<SystemTime> {
unimplemented!() unimplemented!()
} }
/// Returns the creation time. /// Returns the creation time.
/// ///
/// # Errors /// # Errors
/// ///
/// This data may not be available on all platforms, in which case an error will be /// This data may not be available on all platforms, in which case an error will be
/// returned. /// returned.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # /// #
/// use async_std::fs; /// use async_std::fs;
/// ///
/// let metadata = fs::metadata("a.txt").await?; /// let metadata = fs::metadata("a.txt").await?;
/// println!("{:?}", metadata.created()); /// println!("{:?}", metadata.created());
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn created(&self) -> io::Result<SystemTime> { pub fn created(&self) -> io::Result<SystemTime> {
unimplemented!() unimplemented!()
}
} }
} else {
pub use std::fs::Metadata;
} }
} }

@ -1,7 +1,6 @@
use cfg_if::cfg_if; use std::future::Future;
use crate::fs::File; use crate::fs::File;
use crate::future::Future;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::blocking;
@ -285,7 +284,7 @@ impl OpenOptions {
pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> { pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let options = self.0.clone(); let options = self.0.clone();
async move { blocking::spawn(async move { options.open(path).map(|f| f.into()) }).await } async move { blocking::spawn(move || options.open(path).map(|f| f.into())).await }
} }
} }
@ -295,27 +294,18 @@ impl Default for OpenOptions {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::fs::OpenOptionsExt;
use crate::os::unix::fs::OpenOptionsExt;
} else if #[cfg(unix)] {
use std::os::unix::fs::OpenOptionsExt;
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl OpenOptionsExt for OpenOptions {
cfg_if! { fn mode(&mut self, mode: u32) -> &mut Self {
if #[cfg(any(unix, feature = "docs"))] { self.0.mode(mode);
impl OpenOptionsExt for OpenOptions { self
fn mode(&mut self, mode: u32) -> &mut Self { }
self.0.mode(mode);
self
}
fn custom_flags(&mut self, flags: i32) -> &mut Self { fn custom_flags(&mut self, flags: i32) -> &mut Self {
self.0.custom_flags(flags); self.0.custom_flags(flags);
self self
}
} }
} }
} }

@ -1,58 +1,56 @@
use cfg_if::cfg_if; cfg_not_docs! {
pub use std::fs::Permissions;
}
cfg_docs! {
/// A set of permissions on a file or directory.
///
/// This type is a re-export of [`std::fs::Permissions`].
///
/// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Permissions {
_private: (),
}
cfg_if! { impl Permissions {
if #[cfg(feature = "docs")] { /// Returns the read-only flag.
/// A set of permissions on a file or directory.
/// ///
/// This type is a re-export of [`std::fs::Permissions`]. /// # Examples
/// ///
/// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html /// ```no_run
#[derive(Clone, PartialEq, Eq, Debug)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
pub struct Permissions { /// #
_private: (), /// use async_std::fs;
///
/// let perm = fs::metadata("a.txt").await?.permissions();
/// println!("{:?}", perm.readonly());
/// #
/// # Ok(()) }) }
/// ```
pub fn readonly(&self) -> bool {
unimplemented!()
} }
impl Permissions { /// Configures the read-only flag.
/// Returns the read-only flag. ///
/// /// [`fs::set_permissions`]: fn.set_permissions.html
/// # Examples ///
/// /// # Examples
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// # /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::fs; /// #
/// /// use async_std::fs;
/// let perm = fs::metadata("a.txt").await?.permissions(); ///
/// println!("{:?}", perm.readonly()); /// let mut perm = fs::metadata("a.txt").await?.permissions();
/// # /// perm.set_readonly(true);
/// # Ok(()) }) } /// fs::set_permissions("a.txt", perm).await?;
/// ``` /// #
pub fn readonly(&self) -> bool { /// # Ok(()) }) }
unimplemented!() /// ```
} pub fn set_readonly(&mut self, readonly: bool) {
unimplemented!()
/// Configures the read-only flag.
///
/// [`fs::set_permissions`]: fn.set_permissions.html
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::fs;
///
/// let mut perm = fs::metadata("a.txt").await?.permissions();
/// perm.set_readonly(true);
/// fs::set_permissions("a.txt", perm).await?;
/// #
/// # Ok(()) }) }
/// ```
pub fn set_readonly(&mut self, readonly: bool) {
unimplemented!()
}
} }
} else {
pub use std::fs::Permissions;
} }
} }

@ -36,5 +36,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> { pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::read(path) }).await blocking::spawn(move || std::fs::read(path)).await
} }

@ -45,7 +45,7 @@ use crate::task::{blocking, Context, JoinHandle, Poll};
/// ``` /// ```
pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::read_dir(path) }) blocking::spawn(move || std::fs::read_dir(path))
.await .await
.map(ReadDir::new) .map(ReadDir::new)
} }
@ -91,7 +91,7 @@ impl Stream for ReadDir {
let mut inner = opt.take().unwrap(); let mut inner = opt.take().unwrap();
// Start the operation asynchronously. // Start the operation asynchronously.
self.0 = State::Busy(blocking::spawn(async move { self.0 = State::Busy(blocking::spawn(move || {
let next = inner.next(); let next = inner.next();
(inner, next) (inner, next)
})); }));

@ -28,5 +28,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::read_link(path).map(Into::into) }).await blocking::spawn(move || std::fs::read_link(path).map(Into::into)).await
} }

@ -37,5 +37,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> { pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::read_to_string(path) }).await blocking::spawn(move || std::fs::read_to_string(path)).await
} }

@ -29,5 +29,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::remove_dir(path) }).await blocking::spawn(move || std::fs::remove_dir(path)).await
} }

@ -29,5 +29,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::remove_dir_all(path) }).await blocking::spawn(move || std::fs::remove_dir_all(path)).await
} }

@ -29,5 +29,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::remove_file(path) }).await blocking::spawn(move || std::fs::remove_file(path)).await
} }

@ -34,5 +34,5 @@ use crate::task::blocking;
pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
let from = from.as_ref().to_owned(); let from = from.as_ref().to_owned();
let to = to.as_ref().to_owned(); let to = to.as_ref().to_owned();
blocking::spawn(async move { std::fs::rename(&from, &to) }).await blocking::spawn(move || std::fs::rename(&from, &to)).await
} }

@ -32,5 +32,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> { pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::set_permissions(path, perm) }).await blocking::spawn(move || std::fs::set_permissions(path, perm)).await
} }

@ -34,5 +34,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { std::fs::symlink_metadata(path) }).await blocking::spawn(move || std::fs::symlink_metadata(path)).await
} }

@ -33,5 +33,5 @@ use crate::task::blocking;
pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let contents = contents.as_ref().to_owned(); let contents = contents.as_ref().to_owned();
blocking::spawn(async move { std::fs::write(path, contents) }).await blocking::spawn(move || std::fs::write(path, contents)).await
} }

@ -0,0 +1,139 @@
extension_trait! {
use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll};
#[doc = r#"
A future represents an asynchronous computation.
A future is a value that may not have finished computing yet. This kind of
"asynchronous value" makes it possible for a thread to continue doing useful
work while it waits for the value to become available.
# The `poll` method
The core method of future, `poll`, *attempts* to resolve the future into a
final value. This method does not block if the value is not ready. Instead,
the current task is scheduled to be woken up when it's possible to make
further progress by `poll`ing again. The `context` passed to the `poll`
method can provide a [`Waker`], which is a handle for waking up the current
task.
When using a future, you generally won't call `poll` directly, but instead
`.await` the value.
[`Waker`]: ../task/struct.Waker.html
"#]
pub trait Future {
#[doc = r#"
The type of value produced on completion.
"#]
type Output;
#[doc = r#"
Attempt to resolve the future to a final value, registering
the current task for wakeup if the value is not yet available.
# Return value
This function returns:
- [`Poll::Pending`] if the future is not ready yet
- [`Poll::Ready(val)`] with the result `val` of this future if it
finished successfully.
Once a future has finished, clients should not `poll` it again.
When a future is not ready yet, `poll` returns `Poll::Pending` and
stores a clone of the [`Waker`] copied from the current [`Context`].
This [`Waker`] is then woken once the future can make progress.
For example, a future waiting for a socket to become
readable would call `.clone()` on the [`Waker`] and store it.
When a signal arrives elsewhere indicating that the socket is readable,
[`Waker::wake`] is called and the socket future's task is awoken.
Once a task has been woken up, it should attempt to `poll` the future
again, which may or may not produce a final value.
Note that on multiple calls to `poll`, only the [`Waker`] from the
[`Context`] passed to the most recent call should be scheduled to
receive a wakeup.
# Runtime characteristics
Futures alone are *inert*; they must be *actively* `poll`ed to make
progress, meaning that each time the current task is woken up, it should
actively re-`poll` pending futures that it still has an interest in.
The `poll` function is not called repeatedly in a tight loop -- instead,
it should only be called when the future indicates that it is ready to
make progress (by calling `wake()`). If you're familiar with the
`poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures
typically do *not* suffer the same problems of "all wakeups must poll
all events"; they are more like `epoll(4)`.
An implementation of `poll` should strive to return quickly, and should
not block. Returning quickly prevents unnecessarily clogging up
threads or event loops. If it is known ahead of time that a call to
`poll` may end up taking awhile, the work should be offloaded to a
thread pool (or something similar) to ensure that `poll` can return
quickly.
# Panics
Once a future has completed (returned `Ready` from `poll`), calling its
`poll` method again may panic, block forever, or cause other kinds of
problems; the `Future` trait places no requirements on the effects of
such a call. However, as the `poll` method is not marked `unsafe`,
Rust's usual rules apply: calls must never cause undefined behavior
(memory corruption, incorrect use of `unsafe` functions, or the like),
regardless of the future's state.
[`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending
[`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready
[`Context`]: ../task/struct.Context.html
[`Waker`]: ../task/struct.Waker.html
[`Waker::wake`]: ../task/struct.Waker.html#method.wake
"#]
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
}
pub trait FutureExt: std::future::Future {
}
impl<F: Future + Unpin + ?Sized> Future for Box<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<F: Future + Unpin + ?Sized> Future for &mut F {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<P> Future for Pin<P>
where
P: DerefMut + Unpin,
<P as Deref>::Target: Future,
{
type Output = <<P as Deref>::Target as Future>::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<F: Future> Future for std::panic::AssertUnwindSafe<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
}

@ -0,0 +1,54 @@
use crate::future::Future;
/// Convert a type into a `Future`.
///
/// # Examples
///
/// ```
/// use async_std::future::{Future, IntoFuture};
/// use async_std::io;
/// use async_std::pin::Pin;
///
/// struct Client;
///
/// impl Client {
/// pub async fn send(self) -> io::Result<()> {
/// // Send a request
/// Ok(())
/// }
/// }
///
/// impl IntoFuture for Client {
/// type Output = io::Result<()>;
///
/// type Future = Pin<Box<dyn Future<Output = Self::Output>>>;
///
/// fn into_future(self) -> Self::Future {
/// Box::pin(async {
/// self.send().await
/// })
/// }
/// }
/// ```
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub trait IntoFuture {
/// The type of value produced on completion.
type Output;
/// Which kind of future are we turning this into?
type Future: Future<Output = Self::Output>;
/// Create a future from a value
fn into_future(self) -> Self::Future;
}
impl<T: Future> IntoFuture for T {
type Output = T::Output;
type Future = T;
fn into_future(self) -> Self::Future {
self
}
}

@ -41,29 +41,25 @@
//! | `future::select` | `Result<T, E>` | Return on first value //! | `future::select` | `Result<T, E>` | Return on first value
//! | `future::try_select` | `Result<T, E>` | Return on first `Ok`, reject on last Err //! | `future::try_select` | `Result<T, E>` | Return on first `Ok`, reject on last Err
#[doc(inline)]
pub use std::future::Future;
#[doc(inline)] #[doc(inline)]
pub use async_macros::{join, try_join}; pub use async_macros::{join, try_join};
#[doc(inline)] pub use future::Future;
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub use async_macros::{select, try_select};
use cfg_if::cfg_if;
pub use pending::pending; pub use pending::pending;
pub use poll_fn::poll_fn; pub use poll_fn::poll_fn;
pub use ready::ready; pub use ready::ready;
pub use timeout::{timeout, TimeoutError};
pub(crate) mod future;
mod pending; mod pending;
mod poll_fn; mod poll_fn;
mod ready; mod ready;
mod timeout;
cfg_unstable! {
#[doc(inline)]
pub use async_macros::{select, try_select};
cfg_if! { pub use into_future::IntoFuture;
if #[cfg(any(feature = "unstable", feature = "docs"))] { mod into_future;
mod timeout;
pub use timeout::{timeout, TimeoutError};
}
} }

@ -4,6 +4,7 @@ use std::pin::Pin;
use std::time::Duration; use std::time::Duration;
use futures_timer::Delay; use futures_timer::Delay;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -28,8 +29,6 @@ use crate::task::{Context, Poll};
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub async fn timeout<F, T>(dur: Duration, f: F) -> Result<T, TimeoutError> pub async fn timeout<F, T>(dur: Duration, f: F) -> Result<T, TimeoutError>
where where
F: Future<Output = T>, F: Future<Output = T>,
@ -41,26 +40,24 @@ where
f.await f.await
} }
/// A future that times out after a duration of time. pin_project! {
#[doc(hidden)] /// A future that times out after a duration of time.
#[allow(missing_debug_implementations)] struct TimeoutFuture<F> {
struct TimeoutFuture<F> { #[pin]
future: F, future: F,
delay: Delay, #[pin]
} delay: Delay,
}
impl<F> TimeoutFuture<F> {
pin_utils::unsafe_pinned!(future: F);
pin_utils::unsafe_pinned!(delay: Delay);
} }
impl<F: Future> Future for TimeoutFuture<F> { impl<F: Future> Future for TimeoutFuture<F> {
type Output = Result<F::Output, TimeoutError>; type Output = Result<F::Output, TimeoutError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.as_mut().future().poll(cx) { let this = self.project();
match this.future.poll(cx) {
Poll::Ready(v) => Poll::Ready(Ok(v)), Poll::Ready(v) => Poll::Ready(Ok(v)),
Poll::Pending => match self.delay().poll(cx) { Poll::Pending => match this.delay.poll(cx) {
Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })), Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })),
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
}, },
@ -69,8 +66,6 @@ impl<F: Future> Future for TimeoutFuture<F> {
} }
/// An error returned when a future times out. /// An error returned when a future times out.
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TimeoutError { pub struct TimeoutError {
_private: (), _private: (),

@ -2,50 +2,55 @@ use std::mem;
use std::pin::Pin; use std::pin::Pin;
use std::str; use std::str;
use pin_project_lite::pin_project;
use super::read_until_internal; use super::read_until_internal;
use crate::io::{self, BufRead}; use crate::io::{self, BufRead};
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream of lines in a byte stream. pin_project! {
/// /// A stream of lines in a byte stream.
/// This stream is created by the [`lines`] method on types that implement [`BufRead`]. ///
/// /// This stream is created by the [`lines`] method on types that implement [`BufRead`].
/// This type is an async version of [`std::io::Lines`]. ///
/// /// This type is an async version of [`std::io::Lines`].
/// [`lines`]: trait.BufRead.html#method.lines ///
/// [`BufRead`]: trait.BufRead.html /// [`lines`]: trait.BufRead.html#method.lines
/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html /// [`BufRead`]: trait.BufRead.html
#[derive(Debug)] /// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html
pub struct Lines<R> { #[derive(Debug)]
pub(crate) reader: R, pub struct Lines<R> {
pub(crate) buf: String, #[pin]
pub(crate) bytes: Vec<u8>, pub(crate) reader: R,
pub(crate) read: usize, pub(crate) buf: String,
pub(crate) bytes: Vec<u8>,
pub(crate) read: usize,
}
} }
impl<R: BufRead> Stream for Lines<R> { impl<R: BufRead> Stream for Lines<R> {
type Item = io::Result<String>; type Item = io::Result<String>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let Self { let this = self.project();
reader, let n = futures_core::ready!(read_line_internal(
buf, this.reader,
bytes, cx,
read, this.buf,
} = unsafe { self.get_unchecked_mut() }; this.bytes,
let reader = unsafe { Pin::new_unchecked(reader) }; this.read
let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?; ))?;
if n == 0 && buf.is_empty() { if n == 0 && this.buf.is_empty() {
return Poll::Ready(None); return Poll::Ready(None);
} }
if buf.ends_with('\n') { if this.buf.ends_with('\n') {
buf.pop(); this.buf.pop();
if buf.ends_with('\r') { if this.buf.ends_with('\r') {
buf.pop(); this.buf.pop();
} }
} }
Poll::Ready(Some(Ok(mem::replace(buf, String::new())))) Poll::Ready(Some(Ok(mem::replace(this.buf, String::new()))))
} }
} }

@ -12,19 +12,12 @@ use read_until::ReadUntilFuture;
use std::mem; use std::mem;
use std::pin::Pin; use std::pin::Pin;
use cfg_if::cfg_if;
use crate::io; use crate::io;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use crate::utils::extension_trait;
cfg_if! {
if #[cfg(feature = "docs")] {
use std::ops::{Deref, DerefMut};
}
}
extension_trait! { extension_trait! {
use std::ops::{Deref, DerefMut};
#[doc = r#" #[doc = r#"
Allows reading from a buffered byte stream. Allows reading from a buffered byte stream.
@ -44,7 +37,7 @@ extension_trait! {
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html
[provided methods]: #provided-methods [provided methods]: #provided-methods
"#] "#]
pub trait BufRead [BufReadExt: futures_io::AsyncBufRead] { pub trait BufRead {
#[doc = r#" #[doc = r#"
Returns the contents of the internal buffer, filling it with more data from the Returns the contents of the internal buffer, filling it with more data from the
inner reader if it is empty. inner reader if it is empty.
@ -67,7 +60,9 @@ extension_trait! {
should no longer be returned in calls to `read`. should no longer be returned in calls to `read`.
"#] "#]
fn consume(self: Pin<&mut Self>, amt: usize); fn consume(self: Pin<&mut Self>, amt: usize);
}
pub trait BufReadExt: futures_io::AsyncBufRead {
#[doc = r#" #[doc = r#"
Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. Reads all bytes into `buf` until the delimiter `byte` or EOF is reached.

@ -1,46 +1,51 @@
use std::mem; use std::mem;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use super::read_until_internal; use super::read_until_internal;
use crate::io::{self, BufRead}; use crate::io::{self, BufRead};
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream over the contents of an instance of [`BufRead`] split on a particular byte. pin_project! {
/// /// A stream over the contents of an instance of [`BufRead`] split on a particular byte.
/// This stream is created by the [`split`] method on types that implement [`BufRead`]. ///
/// /// This stream is created by the [`split`] method on types that implement [`BufRead`].
/// This type is an async version of [`std::io::Split`]. ///
/// /// This type is an async version of [`std::io::Split`].
/// [`split`]: trait.BufRead.html#method.lines ///
/// [`BufRead`]: trait.BufRead.html /// [`split`]: trait.BufRead.html#method.lines
/// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html /// [`BufRead`]: trait.BufRead.html
#[derive(Debug)] /// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html
pub struct Split<R> { #[derive(Debug)]
pub(crate) reader: R, pub struct Split<R> {
pub(crate) buf: Vec<u8>, #[pin]
pub(crate) read: usize, pub(crate) reader: R,
pub(crate) delim: u8, pub(crate) buf: Vec<u8>,
pub(crate) read: usize,
pub(crate) delim: u8,
}
} }
impl<R: BufRead> Stream for Split<R> { impl<R: BufRead> Stream for Split<R> {
type Item = io::Result<Vec<u8>>; type Item = io::Result<Vec<u8>>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let Self { let this = self.project();
reader, let n = futures_core::ready!(read_until_internal(
buf, this.reader,
read, cx,
delim, *this.delim,
} = unsafe { self.get_unchecked_mut() }; this.buf,
let reader = unsafe { Pin::new_unchecked(reader) }; this.read
let n = futures_core::ready!(read_until_internal(reader, cx, *delim, buf, read))?; ))?;
if n == 0 && buf.is_empty() { if n == 0 && this.buf.is_empty() {
return Poll::Ready(None); return Poll::Ready(None);
} }
if buf[buf.len() - 1] == *delim { if this.buf[this.buf.len() - 1] == *this.delim {
buf.pop(); this.buf.pop();
} }
Poll::Ready(Some(Ok(mem::replace(buf, vec![])))) Poll::Ready(Some(Ok(mem::replace(this.buf, vec![]))))
} }
} }

@ -2,51 +2,56 @@ use std::io::{IoSliceMut, Read as _};
use std::pin::Pin; use std::pin::Pin;
use std::{cmp, fmt}; use std::{cmp, fmt};
use pin_project_lite::pin_project;
use crate::io::{self, BufRead, Read, Seek, SeekFrom}; use crate::io::{self, BufRead, Read, Seek, SeekFrom};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
const DEFAULT_CAPACITY: usize = 8 * 1024; const DEFAULT_CAPACITY: usize = 8 * 1024;
/// Adds buffering to any reader. pin_project! {
/// /// Adds buffering to any reader.
/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader` ///
/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer /// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader`
/// of the incoming byte stream. /// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer
/// /// of the incoming byte stream.
/// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to ///
/// the same file or network socket. It does not help when reading very large amounts at once, or /// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to
/// reading just one or a few times. It also provides no advantage when reading from a source that /// the same file or network socket. It does not help when reading very large amounts at once, or
/// is already in memory, like a `Vec<u8>`. /// reading just one or a few times. It also provides no advantage when reading from a source that
/// /// is already in memory, like a `Vec<u8>`.
/// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating ///
/// multiple instances of a `BufReader` on the same stream can cause data loss. /// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating
/// /// multiple instances of a `BufReader` on the same stream can cause data loss.
/// This type is an async version of [`std::io::BufReader`]. ///
/// /// This type is an async version of [`std::io::BufReader`].
/// [`Read`]: trait.Read.html ///
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html /// [`Read`]: trait.Read.html
/// /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// # Examples ///
/// /// # Examples
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// # /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::fs::File; /// #
/// use async_std::io::BufReader; /// use async_std::fs::File;
/// use async_std::prelude::*; /// use async_std::io::BufReader;
/// /// use async_std::prelude::*;
/// let mut file = BufReader::new(File::open("a.txt").await?); ///
/// /// let mut file = BufReader::new(File::open("a.txt").await?);
/// let mut line = String::new(); ///
/// file.read_line(&mut line).await?; /// let mut line = String::new();
/// # /// file.read_line(&mut line).await?;
/// # Ok(()) }) } /// #
/// ``` /// # Ok(()) }) }
pub struct BufReader<R> { /// ```
inner: R, pub struct BufReader<R> {
buf: Box<[u8]>, #[pin]
pos: usize, inner: R,
cap: usize, buf: Box<[u8]>,
pos: usize,
cap: usize,
}
} }
impl<R: io::Read> BufReader<R> { impl<R: io::Read> BufReader<R> {
@ -95,10 +100,6 @@ impl<R: io::Read> BufReader<R> {
} }
impl<R> BufReader<R> { impl<R> BufReader<R> {
pin_utils::unsafe_pinned!(inner: R);
pin_utils::unsafe_unpinned!(pos: usize);
pin_utils::unsafe_unpinned!(cap: usize);
/// Gets a reference to the underlying reader. /// Gets a reference to the underlying reader.
/// ///
/// It is inadvisable to directly read from the underlying reader. /// It is inadvisable to directly read from the underlying reader.
@ -141,6 +142,13 @@ impl<R> BufReader<R> {
&mut self.inner &mut self.inner
} }
/// Gets a pinned mutable reference to the underlying reader.
///
/// It is inadvisable to directly read from the underlying reader.
fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> {
self.project().inner
}
/// Returns a reference to the internal buffer. /// Returns a reference to the internal buffer.
/// ///
/// This function will not attempt to fill the buffer if it is empty. /// This function will not attempt to fill the buffer if it is empty.
@ -185,9 +193,10 @@ impl<R> BufReader<R> {
/// Invalidates all data in the internal buffer. /// Invalidates all data in the internal buffer.
#[inline] #[inline]
fn discard_buffer(mut self: Pin<&mut Self>) { fn discard_buffer(self: Pin<&mut Self>) {
*self.as_mut().pos() = 0; let this = self.project();
*self.cap() = 0; *this.pos = 0;
*this.cap = 0;
} }
} }
@ -201,7 +210,7 @@ impl<R: Read> Read for BufReader<R> {
// (larger than our internal buffer), bypass our internal buffer // (larger than our internal buffer), bypass our internal buffer
// entirely. // entirely.
if self.pos == self.cap && buf.len() >= self.buf.len() { if self.pos == self.cap && buf.len() >= self.buf.len() {
let res = futures_core::ready!(self.as_mut().inner().poll_read(cx, buf)); let res = futures_core::ready!(self.as_mut().get_pin_mut().poll_read(cx, buf));
self.discard_buffer(); self.discard_buffer();
return Poll::Ready(res); return Poll::Ready(res);
} }
@ -218,7 +227,8 @@ impl<R: Read> Read for BufReader<R> {
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>(); let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
if self.pos == self.cap && total_len >= self.buf.len() { if self.pos == self.cap && total_len >= self.buf.len() {
let res = futures_core::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); let res =
futures_core::ready!(self.as_mut().get_pin_mut().poll_read_vectored(cx, bufs));
self.discard_buffer(); self.discard_buffer();
return Poll::Ready(res); return Poll::Ready(res);
} }
@ -234,28 +244,23 @@ impl<R: Read> BufRead for BufReader<R> {
self: Pin<&'a mut Self>, self: Pin<&'a mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
) -> Poll<io::Result<&'a [u8]>> { ) -> Poll<io::Result<&'a [u8]>> {
let Self { let mut this = self.project();
inner,
buf,
cap,
pos,
} = unsafe { self.get_unchecked_mut() };
let mut inner = unsafe { Pin::new_unchecked(inner) };
// If we've reached the end of our internal buffer then we need to fetch // If we've reached the end of our internal buffer then we need to fetch
// some more data from the underlying reader. // some more data from the underlying reader.
// Branch using `>=` instead of the more correct `==` // Branch using `>=` instead of the more correct `==`
// to tell the compiler that the pos..cap slice is always valid. // to tell the compiler that the pos..cap slice is always valid.
if *pos >= *cap { if *this.pos >= *this.cap {
debug_assert!(*pos == *cap); debug_assert!(*this.pos == *this.cap);
*cap = futures_core::ready!(inner.as_mut().poll_read(cx, buf))?; *this.cap = futures_core::ready!(this.inner.as_mut().poll_read(cx, this.buf))?;
*pos = 0; *this.pos = 0;
} }
Poll::Ready(Ok(&buf[*pos..*cap])) Poll::Ready(Ok(&this.buf[*this.pos..*this.cap]))
} }
fn consume(mut self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
*self.as_mut().pos() = cmp::min(self.pos + amt, self.cap); let this = self.project();
*this.pos = cmp::min(*this.pos + amt, *this.cap);
} }
} }
@ -305,24 +310,26 @@ impl<R: Seek> Seek for BufReader<R> {
if let Some(offset) = n.checked_sub(remainder) { if let Some(offset) = n.checked_sub(remainder) {
result = futures_core::ready!( result = futures_core::ready!(
self.as_mut() self.as_mut()
.inner() .get_pin_mut()
.poll_seek(cx, SeekFrom::Current(offset)) .poll_seek(cx, SeekFrom::Current(offset))
)?; )?;
} else { } else {
// seek backwards by our remainder, and then by the offset // seek backwards by our remainder, and then by the offset
futures_core::ready!( futures_core::ready!(
self.as_mut() self.as_mut()
.inner() .get_pin_mut()
.poll_seek(cx, SeekFrom::Current(-remainder)) .poll_seek(cx, SeekFrom::Current(-remainder))
)?; )?;
self.as_mut().discard_buffer(); self.as_mut().discard_buffer();
result = futures_core::ready!( result = futures_core::ready!(
self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)) self.as_mut()
.get_pin_mut()
.poll_seek(cx, SeekFrom::Current(n))
)?; )?;
} }
} else { } else {
// Seeking with Start/End doesn't care about our buffer length. // Seeking with Start/End doesn't care about our buffer length.
result = futures_core::ready!(self.as_mut().inner().poll_seek(cx, pos))?; result = futures_core::ready!(self.as_mut().get_pin_mut().poll_seek(cx, pos))?;
} }
self.discard_buffer(); self.discard_buffer();
Poll::Ready(Ok(result)) Poll::Ready(Ok(result))

@ -2,6 +2,7 @@ use std::fmt;
use std::pin::Pin; use std::pin::Pin;
use futures_core::ready; use futures_core::ready;
use pin_project_lite::pin_project;
use crate::io::write::WriteExt; use crate::io::write::WriteExt;
use crate::io::{self, Seek, SeekFrom, Write}; use crate::io::{self, Seek, SeekFrom, Write};
@ -9,88 +10,88 @@ use crate::task::{Context, Poll};
const DEFAULT_CAPACITY: usize = 8 * 1024; const DEFAULT_CAPACITY: usize = 8 * 1024;
/// Wraps a writer and buffers its output. pin_project! {
/// /// Wraps a writer and buffers its output.
/// It can be excessively inefficient to work directly with something that ///
/// implements [`Write`]. For example, every call to /// It can be excessively inefficient to work directly with something that
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A /// implements [`Write`]. For example, every call to
/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying /// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
/// writer in large, infrequent batches. /// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying
/// /// writer in large, infrequent batches.
/// `BufWriter` can improve the speed of programs that make *small* and ///
/// *repeated* write calls to the same file or network socket. It does not /// `BufWriter` can improve the speed of programs that make *small* and
/// help when writing very large amounts at once, or writing just one or a few /// *repeated* write calls to the same file or network socket. It does not
/// times. It also provides no advantage when writing to a destination that is /// help when writing very large amounts at once, or writing just one or a few
/// in memory, like a `Vec<u8>`. /// times. It also provides no advantage when writing to a destination that is
/// /// in memory, like a `Vec<u8>`.
/// When the `BufWriter` is dropped, the contents of its buffer will be written ///
/// out. However, any errors that happen in the process of flushing the buffer /// When the `BufWriter` is dropped, the contents of its buffer will be written
/// when the writer is dropped will be ignored. Code that wishes to handle such /// out. However, any errors that happen in the process of flushing the buffer
/// errors must manually call [`flush`] before the writer is dropped. /// when the writer is dropped will be ignored. Code that wishes to handle such
/// /// errors must manually call [`flush`] before the writer is dropped.
/// This type is an async version of [`std::io::BufReader`]. ///
/// /// This type is an async version of [`std::io::BufReader`].
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html ///
/// /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// # Examples ///
/// /// # Examples
/// Let's write the numbers one through ten to a [`TcpStream`]: ///
/// /// Let's write the numbers one through ten to a [`TcpStream`]:
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// use async_std::net::TcpStream; /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::prelude::*; /// use async_std::net::TcpStream;
/// /// use async_std::prelude::*;
/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?; ///
/// /// let mut stream = TcpStream::connect("127.0.0.1:34254").await?;
/// for i in 0..10 { ///
/// let arr = [i+1]; /// for i in 0..10 {
/// stream.write(&arr).await?; /// let arr = [i+1];
/// } /// stream.write(&arr).await?;
/// # /// }
/// # Ok(()) }) } /// #
/// ``` /// # Ok(()) }) }
/// /// ```
/// Because we're not buffering, we write each one in turn, incurring the ///
/// overhead of a system call per byte written. We can fix this with a /// Because we're not buffering, we write each one in turn, incurring the
/// `BufWriter`: /// overhead of a system call per byte written. We can fix this with a
/// /// `BufWriter`:
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// use async_std::io::BufWriter; /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::net::TcpStream; /// use async_std::io::BufWriter;
/// use async_std::prelude::*; /// use async_std::net::TcpStream;
/// /// use async_std::prelude::*;
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); ///
/// for i in 0..10 { /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?);
/// let arr = [i+1]; /// for i in 0..10 {
/// stream.write(&arr).await?; /// let arr = [i+1];
/// }; /// stream.write(&arr).await?;
/// # /// };
/// # Ok(()) }) } /// #
/// ``` /// # Ok(()) }) }
/// /// ```
/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped ///
/// together by the buffer, and will all be written out in one system call when /// By wrapping the stream with a `BufWriter`, these ten writes are all grouped
/// the `stream` is dropped. /// together by the buffer, and will all be written out in one system call when
/// /// the `stream` is dropped.
/// [`Write`]: trait.Write.html ///
/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write /// [`Write`]: trait.Write.html
/// [`TcpStream`]: ../net/struct.TcpStream.html /// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write
/// [`flush`]: trait.Write.html#tymethod.flush /// [`TcpStream`]: ../net/struct.TcpStream.html
pub struct BufWriter<W> { /// [`flush`]: trait.Write.html#tymethod.flush
inner: W, pub struct BufWriter<W> {
buf: Vec<u8>, #[pin]
written: usize, inner: W,
buf: Vec<u8>,
written: usize,
}
} }
#[derive(Debug)] #[derive(Debug)]
pub struct IntoInnerError<W>(W, std::io::Error); pub struct IntoInnerError<W>(W, std::io::Error);
impl<W: Write> BufWriter<W> { impl<W: Write> BufWriter<W> {
pin_utils::unsafe_pinned!(inner: W);
pin_utils::unsafe_unpinned!(buf: Vec<u8>);
/// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB,
/// but may change in the future. /// but may change in the future.
/// ///
@ -178,6 +179,13 @@ impl<W: Write> BufWriter<W> {
&mut self.inner &mut self.inner
} }
/// Gets a pinned mutable reference to the underlying writer.
///
/// It is inadvisable to directly write to the underlying writer.
fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> {
self.project().inner
}
/// Consumes BufWriter, returning the underlying writer /// Consumes BufWriter, returning the underlying writer
/// ///
/// This method will not write leftover data, it will be lost. /// This method will not write leftover data, it will be lost.
@ -234,16 +242,15 @@ impl<W: Write> BufWriter<W> {
/// ///
/// [`LineWriter`]: struct.LineWriter.html /// [`LineWriter`]: struct.LineWriter.html
fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let Self { let mut this = self.project();
inner, let len = this.buf.len();
buf,
written,
} = unsafe { Pin::get_unchecked_mut(self) };
let mut inner = unsafe { Pin::new_unchecked(inner) };
let len = buf.len();
let mut ret = Ok(()); let mut ret = Ok(());
while *written < len { while *this.written < len {
match inner.as_mut().poll_write(cx, &buf[*written..]) { match this
.inner
.as_mut()
.poll_write(cx, &this.buf[*this.written..])
{
Poll::Ready(Ok(0)) => { Poll::Ready(Ok(0)) => {
ret = Err(io::Error::new( ret = Err(io::Error::new(
io::ErrorKind::WriteZero, io::ErrorKind::WriteZero,
@ -251,7 +258,7 @@ impl<W: Write> BufWriter<W> {
)); ));
break; break;
} }
Poll::Ready(Ok(n)) => *written += n, Poll::Ready(Ok(n)) => *this.written += n,
Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {} Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {}
Poll::Ready(Err(e)) => { Poll::Ready(Err(e)) => {
ret = Err(e); ret = Err(e);
@ -260,10 +267,10 @@ impl<W: Write> BufWriter<W> {
Poll::Pending => return Poll::Pending, Poll::Pending => return Poll::Pending,
} }
} }
if *written > 0 { if *this.written > 0 {
buf.drain(..*written); this.buf.drain(..*this.written);
} }
*written = 0; *this.written = 0;
Poll::Ready(ret) Poll::Ready(ret)
} }
} }
@ -278,20 +285,20 @@ impl<W: Write> Write for BufWriter<W> {
ready!(self.as_mut().poll_flush_buf(cx))?; ready!(self.as_mut().poll_flush_buf(cx))?;
} }
if buf.len() >= self.buf.capacity() { if buf.len() >= self.buf.capacity() {
self.inner().poll_write(cx, buf) self.get_pin_mut().poll_write(cx, buf)
} else { } else {
Pin::new(&mut *self.buf()).poll_write(cx, buf) Pin::new(&mut *self.project().buf).poll_write(cx, buf)
} }
} }
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
ready!(self.as_mut().poll_flush_buf(cx))?; ready!(self.as_mut().poll_flush_buf(cx))?;
self.inner().poll_flush(cx) self.get_pin_mut().poll_flush(cx)
} }
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
ready!(self.as_mut().poll_flush_buf(cx))?; ready!(self.as_mut().poll_flush_buf(cx))?;
self.inner().poll_close(cx) self.get_pin_mut().poll_close(cx)
} }
} }
@ -314,6 +321,6 @@ impl<W: Write + Seek> Seek for BufWriter<W> {
pos: SeekFrom, pos: SeekFrom,
) -> Poll<io::Result<u64>> { ) -> Poll<io::Result<u64>> {
ready!(self.as_mut().poll_flush_buf(cx))?; ready!(self.as_mut().poll_flush_buf(cx))?;
self.inner().poll_seek(cx, pos) self.get_pin_mut().poll_seek(cx, pos)
} }
} }

@ -1,5 +1,7 @@
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::io::{self, BufRead, BufReader, Read, Write};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -46,47 +48,38 @@ where
R: Read + Unpin + ?Sized, R: Read + Unpin + ?Sized,
W: Write + Unpin + ?Sized, W: Write + Unpin + ?Sized,
{ {
pub struct CopyFuture<'a, R, W: ?Sized> { pin_project! {
reader: R, struct CopyFuture<R, W> {
writer: &'a mut W, #[pin]
amt: u64, reader: R,
} #[pin]
writer: W,
impl<R, W: Unpin + ?Sized> CopyFuture<'_, R, W> { amt: u64,
fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) {
unsafe {
let this = self.get_unchecked_mut();
(
Pin::new_unchecked(&mut this.reader),
Pin::new(&mut *this.writer),
&mut this.amt,
)
}
} }
} }
impl<R, W> Future for CopyFuture<'_, R, W> impl<R, W> Future for CopyFuture<R, W>
where where
R: BufRead, R: BufRead,
W: Write + Unpin + ?Sized, W: Write + Unpin,
{ {
type Output = io::Result<u64>; type Output = io::Result<u64>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let (mut reader, mut writer, amt) = self.project(); let mut this = self.project();
loop { loop {
let buffer = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?; let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?;
if buffer.is_empty() { if buffer.is_empty() {
futures_core::ready!(writer.as_mut().poll_flush(cx))?; futures_core::ready!(this.writer.as_mut().poll_flush(cx))?;
return Poll::Ready(Ok(*amt)); return Poll::Ready(Ok(*this.amt));
} }
let i = futures_core::ready!(writer.as_mut().poll_write(cx, buffer))?; let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?;
if i == 0 { if i == 0 {
return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
} }
*amt += i as u64; *this.amt += i as u64;
reader.as_mut().consume(i); this.reader.as_mut().consume(i);
} }
} }
} }

@ -1,24 +1,273 @@
//! Basic input and output. //! Traits, helpers, and type definitions for core I/O functionality.
//!
//! The `async_std::io` module contains a number of common things you'll need
//! when doing input and output. The most core part of this module is
//! the [`Read`] and [`Write`] traits, which provide the
//! most general interface for reading and writing input and output.
//! //!
//! This module is an async version of [`std::io`]. //! This module is an async version of [`std::io`].
//! //!
//! [`std::io`]: https://doc.rust-lang.org/std/io/index.html //! [`std::io`]: https://doc.rust-lang.org/std/io/index.html
//! //!
//! # Examples //! # Read and Write
//! //!
//! Read a line from the standard input: //! Because they are traits, [`Read`] and [`Write`] are implemented by a number
//! of other types, and you can implement them for your types too. As such,
//! you'll see a few different types of I/O throughout the documentation in
//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec<T>`]s. For
//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on
//! [`File`]s:
//! //!
//! ```no_run //! ```no_run
//! use async_std::prelude::*;
//! use async_std::fs::File;
//!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! #
//! let mut f = File::open("foo.txt").await?;
//! let mut buffer = [0; 10];
//!
//! // read up to 10 bytes
//! let n = f.read(&mut buffer).await?;
//!
//! println!("The bytes: {:?}", &buffer[..n]);
//! #
//! # Ok(()) }) }
//! ```
//!
//! [`Read`] and [`Write`] are so important, implementors of the two traits have a
//! nickname: readers and writers. So you'll sometimes see 'a reader' instead
//! of 'a type that implements the [`Read`] trait'. Much easier!
//!
//! ## Seek and BufRead
//!
//! Beyond that, there are two important traits that are provided: [`Seek`]
//! and [`BufRead`]. Both of these build on top of a reader to control
//! how the reading happens. [`Seek`] lets you control where the next byte is
//! coming from:
//!
//! ```no_run
//! use async_std::io::prelude::*;
//! use async_std::io::SeekFrom;
//! use async_std::fs::File;
//!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! # //! #
//! let mut f = File::open("foo.txt").await?;
//! let mut buffer = [0; 10];
//!
//! // skip to the last 10 bytes of the file
//! f.seek(SeekFrom::End(-10)).await?;
//!
//! // read up to 10 bytes
//! let n = f.read(&mut buffer).await?;
//!
//! println!("The bytes: {:?}", &buffer[..n]);
//! #
//! # Ok(()) }) }
//! ```
//!
//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but
//! to show it off, we'll need to talk about buffers in general. Keep reading!
//!
//! ## BufReader and BufWriter
//!
//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be
//! making near-constant calls to the operating system. To help with this,
//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap
//! readers and writers. The wrapper uses a buffer, reducing the number of
//! calls and providing nicer methods for accessing exactly what you want.
//!
//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra
//! methods to any reader:
//!
//! ```no_run
//! use async_std::io::prelude::*;
//! use async_std::io::BufReader;
//! use async_std::fs::File;
//!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! #
//! let f = File::open("foo.txt").await?;
//! let mut reader = BufReader::new(f);
//! let mut buffer = String::new();
//!
//! // read a line into buffer
//! reader.read_line(&mut buffer).await?;
//!
//! println!("{}", buffer);
//! #
//! # Ok(()) }) }
//! ```
//!
//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call
//! to [`write`][`Write::write`]:
//!
//! ```no_run
//! use async_std::io::prelude::*;
//! use async_std::io::BufWriter;
//! use async_std::fs::File;
//!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! #
//! let f = File::create("foo.txt").await?;
//! {
//! let mut writer = BufWriter::new(f);
//!
//! // write a byte to the buffer
//! writer.write(&[42]).await?;
//!
//! } // the buffer is flushed once writer goes out of scope
//! #
//! # Ok(()) }) }
//! ```
//!
//! ## Standard input and output
//!
//! A very common source of input is standard input:
//!
//! ```no_run
//! use async_std::io; //! use async_std::io;
//! //!
//! let stdin = io::stdin(); //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! let mut line = String::new(); //! #
//! stdin.read_line(&mut line).await?; //! let mut input = String::new();
//!
//! io::stdin().read_line(&mut input).await?;
//!
//! println!("You typed: {}", input.trim());
//! # //! #
//! # Ok(()) }) } //! # Ok(()) }) }
//! ``` //! ```
//!
//! Note that you cannot use the [`?` operator] in functions that do not return
//! a [`Result<T, E>`][`Result`]. Instead, you can call [`.unwrap()`]
//! or `match` on the return value to catch any possible errors:
//!
//! ```no_run
//! use async_std::io;
//!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! #
//! let mut input = String::new();
//!
//! io::stdin().read_line(&mut input).await.unwrap();
//! #
//! # Ok(()) }) }
//! ```
//!
//! And a very common source of output is standard output:
//!
//! ```no_run
//! use async_std::io;
//! use async_std::io::prelude::*;
//!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! #
//! io::stdout().write(&[42]).await?;
//! #
//! # Ok(()) }) }
//! ```
//!
//! Of course, using [`io::stdout`] directly is less common than something like
//! [`println!`].
//!
//! ## Iterator types
//!
//! A large number of the structures provided by `std::io` are for various
//! ways of iterating over I/O. For example, [`Lines`] is used to split over
//! lines:
//!
//! ```no_run
//! use async_std::prelude::*;
//! use async_std::io::BufReader;
//! use async_std::fs::File;
//!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! #
//! let f = File::open("foo.txt").await?;
//! let reader = BufReader::new(f);
//!
//! let mut lines = reader.lines();
//! while let Some(line) = lines.next().await {
//! println!("{}", line?);
//! }
//! #
//! # Ok(()) }) }
//! ```
//!
//! ## Functions
//!
//! There are a number of [functions][functions-list] that offer access to various
//! features. For example, we can use three of these functions to copy everything
//! from standard input to standard output:
//!
//! ```no_run
//! use async_std::io;
//!
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
//! #
//! io::copy(&mut io::stdin(), &mut io::stdout()).await?;
//! #
//! # Ok(()) }) }
//! ```
//!
//! [functions-list]: #functions-1
//!
//! ## io::Result
//!
//! Last, but certainly not least, is [`io::Result`]. This type is used
//! as the return type of many `std::io` functions that can cause an error, and
//! can be returned from your own functions as well. Many of the examples in this
//! module use the [`?` operator]:
//!
//! ```
//! #![allow(dead_code)]
//! use async_std::io;
//!
//! async fn read_input() -> io::Result<()> {
//! let mut input = String::new();
//!
//! io::stdin().read_line(&mut input).await?;
//!
//! println!("You typed: {}", input.trim());
//!
//! Ok(())
//! }
//! ```
//!
//! The return type of `read_input`, [`io::Result<()>`][`io::Result`], is a very
//! common type for functions which don't have a 'real' return value, but do want to
//! return errors if they happen. In this case, the only purpose of this function is
//! to read the line and print it, so we use `()`.
//!
//! ## Platform-specific behavior
//!
//! Many I/O functions throughout the standard library are documented to indicate
//! what various library or syscalls they are delegated to. This is done to help
//! applications both understand what's happening under the hood as well as investigate
//! any possibly unclear semantics. Note, however, that this is informative, not a binding
//! contract. The implementation of many of these functions are subject to change over
//! time and may call fewer or more syscalls/library functions.
//!
//! [`Read`]: trait.Read.html
//! [`Write`]: trait.Write.html
//! [`Seek`]: trait.Seek.html
//! [`BufRead`]: trait.BufRead.html
//! [`File`]: ../fs/struct.File.html
//! [`TcpStream`]: ../net/struct.TcpStream.html
//! [`Vec<T>`]: ../vec/struct.Vec.html
//! [`BufReader`]: struct.BufReader.html
//! [`BufWriter`]: struct.BufWriter.html
//! [`Write::write`]: trait.Write.html#tymethod.write
//! [`io::stdout`]: fn.stdout.html
//! [`println!`]: ../macro.println.html
//! [`Lines`]: struct.Lines.html
//! [`io::Result`]: type.Result.html
//! [`?` operator]: https://doc.rust-lang.org/stable/book/appendix-02-operators.html
//! [`Read::read`]: trait.Read.html#tymethod.read
//! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
//! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap
#[doc(inline)] #[doc(inline)]
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};

@ -1,20 +1,25 @@
use crate::io::IoSliceMut;
use std::fmt; use std::fmt;
use std::pin::Pin; use std::pin::Pin;
use crate::io::{self, BufRead, Read}; use pin_project_lite::pin_project;
use crate::io::{self, BufRead, IoSliceMut, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// Adaptor to chain together two readers. pin_project! {
/// /// Adaptor to chain together two readers.
/// This struct is generally created by calling [`chain`] on a reader. ///
/// Please see the documentation of [`chain`] for more details. /// This struct is generally created by calling [`chain`] on a reader.
/// /// Please see the documentation of [`chain`] for more details.
/// [`chain`]: trait.Read.html#method.chain ///
pub struct Chain<T, U> { /// [`chain`]: trait.Read.html#method.chain
pub(crate) first: T, pub struct Chain<T, U> {
pub(crate) second: U, #[pin]
pub(crate) done_first: bool, pub(crate) first: T,
#[pin]
pub(crate) second: U,
pub(crate) done_first: bool,
}
} }
impl<T, U> Chain<T, U> { impl<T, U> Chain<T, U> {
@ -98,76 +103,64 @@ impl<T: fmt::Debug, U: fmt::Debug> fmt::Debug for Chain<T, U> {
} }
} }
impl<T: Read + Unpin, U: Read + Unpin> Read for Chain<T, U> { impl<T: Read, U: Read> Read for Chain<T, U> {
fn poll_read( fn poll_read(
mut self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
if !self.done_first { let this = self.project();
let rd = Pin::new(&mut self.first); if !*this.done_first {
match futures_core::ready!(this.first.poll_read(cx, buf)) {
match futures_core::ready!(rd.poll_read(cx, buf)) { Ok(0) if !buf.is_empty() => *this.done_first = true,
Ok(0) if !buf.is_empty() => self.done_first = true,
Ok(n) => return Poll::Ready(Ok(n)), Ok(n) => return Poll::Ready(Ok(n)),
Err(err) => return Poll::Ready(Err(err)), Err(err) => return Poll::Ready(Err(err)),
} }
} }
let rd = Pin::new(&mut self.second); this.second.poll_read(cx, buf)
rd.poll_read(cx, buf)
} }
fn poll_read_vectored( fn poll_read_vectored(
mut self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>], bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
if !self.done_first { let this = self.project();
let rd = Pin::new(&mut self.first); if !*this.done_first {
match futures_core::ready!(this.first.poll_read_vectored(cx, bufs)) {
match futures_core::ready!(rd.poll_read_vectored(cx, bufs)) { Ok(0) if !bufs.is_empty() => *this.done_first = true,
Ok(0) if !bufs.is_empty() => self.done_first = true,
Ok(n) => return Poll::Ready(Ok(n)), Ok(n) => return Poll::Ready(Ok(n)),
Err(err) => return Poll::Ready(Err(err)), Err(err) => return Poll::Ready(Err(err)),
} }
} }
let rd = Pin::new(&mut self.second); this.second.poll_read_vectored(cx, bufs)
rd.poll_read_vectored(cx, bufs)
} }
} }
impl<T: BufRead + Unpin, U: BufRead + Unpin> BufRead for Chain<T, U> { impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
let Self { let this = self.project();
first, if !*this.done_first {
second, match futures_core::ready!(this.first.poll_fill_buf(cx)) {
done_first,
} = unsafe { self.get_unchecked_mut() };
if !*done_first {
let first = unsafe { Pin::new_unchecked(first) };
match futures_core::ready!(first.poll_fill_buf(cx)) {
Ok(buf) if buf.is_empty() => { Ok(buf) if buf.is_empty() => {
*done_first = true; *this.done_first = true;
} }
Ok(buf) => return Poll::Ready(Ok(buf)), Ok(buf) => return Poll::Ready(Ok(buf)),
Err(err) => return Poll::Ready(Err(err)), Err(err) => return Poll::Ready(Err(err)),
} }
} }
let second = unsafe { Pin::new_unchecked(second) }; this.second.poll_fill_buf(cx)
second.poll_fill_buf(cx)
} }
fn consume(mut self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
if !self.done_first { let this = self.project();
let rd = Pin::new(&mut self.first); if !*this.done_first {
rd.consume(amt) this.first.consume(amt)
} else { } else {
let rd = Pin::new(&mut self.second); this.second.consume(amt)
rd.consume(amt)
} }
} }
} }

@ -13,23 +13,17 @@ use read_to_end::{read_to_end_internal, ReadToEndFuture};
use read_to_string::ReadToStringFuture; use read_to_string::ReadToStringFuture;
use read_vectored::ReadVectoredFuture; use read_vectored::ReadVectoredFuture;
use cfg_if::cfg_if;
use std::mem; use std::mem;
use crate::io::IoSliceMut; use crate::io::IoSliceMut;
use crate::utils::extension_trait;
cfg_if! { extension_trait! {
if #[cfg(feature = "docs")] { use std::pin::Pin;
use std::pin::Pin; use std::ops::{Deref, DerefMut};
use std::ops::{Deref, DerefMut};
use crate::io; use crate::io;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
}
}
extension_trait! {
#[doc = r#" #[doc = r#"
Allows reading from a byte stream. Allows reading from a byte stream.
@ -50,7 +44,7 @@ extension_trait! {
[`poll_read`]: #tymethod.poll_read [`poll_read`]: #tymethod.poll_read
[`poll_read_vectored`]: #method.poll_read_vectored [`poll_read_vectored`]: #method.poll_read_vectored
"#] "#]
pub trait Read [ReadExt: futures_io::AsyncRead] { pub trait Read {
#[doc = r#" #[doc = r#"
Attempt to read from the `AsyncRead` into `buf`. Attempt to read from the `AsyncRead` into `buf`.
"#] "#]
@ -70,7 +64,9 @@ extension_trait! {
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
unreachable!("this impl only appears in the rendered docs") unreachable!("this impl only appears in the rendered docs")
} }
}
pub trait ReadExt: futures_io::AsyncRead {
#[doc = r#" #[doc = r#"
Reads some bytes from the byte stream. Reads some bytes from the byte stream.
@ -307,34 +303,34 @@ extension_trait! {
#[doc = r#" #[doc = r#"
Creates a "by reference" adaptor for this instance of `Read`. Creates a "by reference" adaptor for this instance of `Read`.
The returned adaptor also implements `Read` and will simply borrow this The returned adaptor also implements `Read` and will simply borrow this
current reader. current reader.
# Examples # Examples
[`File`][file]s implement `Read`: [`File`][file]s implement `Read`:
[file]: ../fs/struct.File.html [file]: ../fs/struct.File.html
```no_run ```no_run
# fn main() -> std::io::Result<()> { async_std::task::block_on(async { # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
use async_std::fs::File; use async_std::fs::File;
let mut f = File::open("foo.txt").await?; let mut f = File::open("foo.txt").await?;
let mut buffer = Vec::new(); let mut buffer = Vec::new();
let mut other_buffer = Vec::new(); let mut other_buffer = Vec::new();
{ {
let reference = f.by_ref(); let reference = f.by_ref();
// read at most 5 bytes // read at most 5 bytes
reference.take(5).read_to_end(&mut buffer).await?; reference.take(5).read_to_end(&mut buffer).await?;
} // drop our &mut reference so we can use f again } // drop our &mut reference so we can use f again
// original file still usable, read the rest // original file still usable, read the rest
f.read_to_end(&mut other_buffer).await?; f.read_to_end(&mut other_buffer).await?;
# #
@ -346,27 +342,27 @@ extension_trait! {
#[doc = r#" #[doc = r#"
Transforms this `Read` instance to a `Stream` over its bytes. Transforms this `Read` instance to a `Stream` over its bytes.
The returned type implements `Stream` where the `Item` is The returned type implements `Stream` where the `Item` is
`Result<u8, io::Error>`. `Result<u8, io::Error>`.
The yielded item is `Ok` if a byte was successfully read and `Err` The yielded item is `Ok` if a byte was successfully read and `Err`
otherwise. EOF is mapped to returning `None` from this iterator. otherwise. EOF is mapped to returning `None` from this iterator.
# Examples # Examples
[`File`][file]s implement `Read`: [`File`][file]s implement `Read`:
[file]: ../fs/struct.File.html [file]: ../fs/struct.File.html
```no_run ```no_run
# fn main() -> std::io::Result<()> { async_std::task::block_on(async { # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
use async_std::fs::File; use async_std::fs::File;
let f = File::open("foo.txt").await?; let f = File::open("foo.txt").await?;
let mut s = f.bytes(); let mut s = f.bytes();
while let Some(byte) = s.next().await { while let Some(byte) = s.next().await {
println!("{}", byte.unwrap()); println!("{}", byte.unwrap());
} }
@ -380,29 +376,29 @@ extension_trait! {
#[doc = r#" #[doc = r#"
Creates an adaptor which will chain this stream with another. Creates an adaptor which will chain this stream with another.
The returned `Read` instance will first read all bytes from this object The returned `Read` instance will first read all bytes from this object
until EOF is encountered. Afterwards the output is equivalent to the until EOF is encountered. Afterwards the output is equivalent to the
output of `next`. output of `next`.
# Examples # Examples
[`File`][file]s implement `Read`: [`File`][file]s implement `Read`:
[file]: ../fs/struct.File.html [file]: ../fs/struct.File.html
```no_run ```no_run
# fn main() -> std::io::Result<()> { async_std::task::block_on(async { # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
use async_std::fs::File; use async_std::fs::File;
let f1 = File::open("foo.txt").await?; let f1 = File::open("foo.txt").await?;
let f2 = File::open("bar.txt").await?; let f2 = File::open("bar.txt").await?;
let mut handle = f1.chain(f2); let mut handle = f1.chain(f2);
let mut buffer = String::new(); let mut buffer = String::new();
// read the value into a String. We could use any Read method here, // read the value into a String. We could use any Read method here,
// this is just one example. // this is just one example.
handle.read_to_string(&mut buffer).await?; handle.read_to_string(&mut buffer).await?;

@ -1,19 +1,24 @@
use std::cmp; use std::cmp;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::io::{self, BufRead, Read}; use crate::io::{self, BufRead, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// Reader adaptor which limits the bytes read from an underlying reader. pin_project! {
/// /// Reader adaptor which limits the bytes read from an underlying reader.
/// This struct is generally created by calling [`take`] on a reader. ///
/// Please see the documentation of [`take`] for more details. /// This struct is generally created by calling [`take`] on a reader.
/// /// Please see the documentation of [`take`] for more details.
/// [`take`]: trait.Read.html#method.take ///
#[derive(Debug)] /// [`take`]: trait.Read.html#method.take
pub struct Take<T> { #[derive(Debug)]
pub(crate) inner: T, pub struct Take<T> {
pub(crate) limit: u64, #[pin]
pub(crate) inner: T,
pub(crate) limit: u64,
}
} }
impl<T> Take<T> { impl<T> Take<T> {
@ -152,15 +157,15 @@ impl<T> Take<T> {
} }
} }
impl<T: Read + Unpin> Read for Take<T> { impl<T: Read> Read for Take<T> {
/// Attempt to read from the `AsyncRead` into `buf`. /// Attempt to read from the `AsyncRead` into `buf`.
fn poll_read( fn poll_read(
mut self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
let Self { inner, limit } = &mut *self; let this = self.project();
take_read_internal(Pin::new(inner), cx, buf, limit) take_read_internal(this.inner, cx, buf, this.limit)
} }
} }
@ -186,31 +191,30 @@ pub fn take_read_internal<R: Read + ?Sized>(
} }
} }
impl<T: BufRead + Unpin> BufRead for Take<T> { impl<T: BufRead> BufRead for Take<T> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
let Self { inner, limit } = unsafe { self.get_unchecked_mut() }; let this = self.project();
let inner = unsafe { Pin::new_unchecked(inner) };
if *limit == 0 { if *this.limit == 0 {
return Poll::Ready(Ok(&[])); return Poll::Ready(Ok(&[]));
} }
match futures_core::ready!(inner.poll_fill_buf(cx)) { match futures_core::ready!(this.inner.poll_fill_buf(cx)) {
Ok(buf) => { Ok(buf) => {
let cap = cmp::min(buf.len() as u64, *limit) as usize; let cap = cmp::min(buf.len() as u64, *this.limit) as usize;
Poll::Ready(Ok(&buf[..cap])) Poll::Ready(Ok(&buf[..cap]))
} }
Err(e) => Poll::Ready(Err(e)), Err(e) => Poll::Ready(Err(e)),
} }
} }
fn consume(mut self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
let this = self.project();
// Don't let callers reset the limit by passing an overlarge value // Don't let callers reset the limit by passing an overlarge value
let amt = cmp::min(amt as u64, self.limit) as usize; let amt = cmp::min(amt as u64, *this.limit) as usize;
self.limit -= amt as u64; *this.limit -= amt as u64;
let rd = Pin::new(&mut self.inner); this.inner.consume(amt);
rd.consume(amt);
} }
} }

@ -1,19 +1,16 @@
use std::pin::Pin; mod seek;
use cfg_if::cfg_if; use seek::SeekFuture;
use crate::future::Future; use crate::io::SeekFrom;
use crate::io::{self, SeekFrom};
use crate::task::{Context, Poll};
use crate::utils::extension_trait;
cfg_if! {
if #[cfg(feature = "docs")] {
use std::ops::{Deref, DerefMut};
}
}
extension_trait! { extension_trait! {
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use crate::io;
use crate::task::{Context, Poll};
#[doc = r#" #[doc = r#"
Allows seeking through a byte stream. Allows seeking through a byte stream.
@ -33,7 +30,7 @@ extension_trait! {
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html
[provided methods]: #provided-methods [provided methods]: #provided-methods
"#] "#]
pub trait Seek [SeekExt: futures_io::AsyncSeek] { pub trait Seek {
#[doc = r#" #[doc = r#"
Attempt to seek to an offset, in bytes, in a stream. Attempt to seek to an offset, in bytes, in a stream.
"#] "#]
@ -42,7 +39,9 @@ extension_trait! {
cx: &mut Context<'_>, cx: &mut Context<'_>,
pos: SeekFrom, pos: SeekFrom,
) -> Poll<io::Result<u64>>; ) -> Poll<io::Result<u64>>;
}
pub trait SeekExt: futures_io::AsyncSeek {
#[doc = r#" #[doc = r#"
Seeks to a new position in a byte stream. Seeks to a new position in a byte stream.
@ -70,7 +69,7 @@ extension_trait! {
fn seek( fn seek(
&mut self, &mut self,
pos: SeekFrom, pos: SeekFrom,
) -> impl Future<Output = io::Result<u64>> [SeekFuture<'_, Self>] ) -> impl Future<Output = io::Result<u64>> + '_ [SeekFuture<'_, Self>]
where where
Self: Unpin, Self: Unpin,
{ {
@ -112,19 +111,3 @@ extension_trait! {
} }
} }
} }
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct SeekFuture<'a, T: Unpin + ?Sized> {
seeker: &'a mut T,
pos: SeekFrom,
}
impl<T: SeekExt + Unpin + ?Sized> Future for SeekFuture<'_, T> {
type Output = io::Result<u64>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let pos = self.pos;
Pin::new(&mut *self.seeker).poll_seek(cx, pos)
}
}

@ -0,0 +1,21 @@
use std::pin::Pin;
use crate::future::Future;
use crate::io::{self, Seek, SeekFrom};
use crate::task::{Context, Poll};
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct SeekFuture<'a, T: Unpin + ?Sized> {
pub(crate) seeker: &'a mut T,
pub(crate) pos: SeekFrom,
}
impl<T: Seek + Unpin + ?Sized> Future for SeekFuture<'_, T> {
type Output = io::Result<u64>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let pos = self.pos;
Pin::new(&mut *self.seeker).poll_seek(cx, pos)
}
}

@ -2,8 +2,6 @@ use lazy_static::lazy_static;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use cfg_if::cfg_if;
use crate::future::Future; use crate::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{blocking, Context, JoinHandle, Poll};
@ -146,7 +144,7 @@ impl Write for Stderr {
inner.buf[..buf.len()].copy_from_slice(buf); inner.buf[..buf.len()].copy_from_slice(buf);
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(async move { *state = State::Busy(blocking::spawn(move || {
let res = std::io::Write::write(&mut inner.stderr, &inner.buf); let res = std::io::Write::write(&mut inner.stderr, &inner.buf);
inner.last_op = Some(Operation::Write(res)); inner.last_op = Some(Operation::Write(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -174,7 +172,7 @@ impl Write for Stderr {
let mut inner = opt.take().unwrap(); let mut inner = opt.take().unwrap();
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(async move { *state = State::Busy(blocking::spawn(move || {
let res = std::io::Write::flush(&mut inner.stderr); let res = std::io::Write::flush(&mut inner.stderr);
inner.last_op = Some(Operation::Flush(res)); inner.last_op = Some(Operation::Flush(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -192,35 +190,22 @@ impl Write for Stderr {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::windows::io::{AsRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, RawFd};
} else if #[cfg(windows)] {
use std::os::windows::io::{AsRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for Stderr {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { std::io::stderr().as_raw_fd()
impl AsRawFd for Stderr {
fn as_raw_fd(&self) -> RawFd {
std::io::stderr().as_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(unix)))] cfg_windows! {
cfg_if! { use crate::os::windows::io::{AsRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] {
impl AsRawHandle for Stderr { impl AsRawHandle for Stderr {
fn as_raw_handle(&self) -> RawHandle { fn as_raw_handle(&self) -> RawHandle {
std::io::stderr().as_raw_handle() std::io::stderr().as_raw_handle()
}
} }
} }
} }

@ -2,8 +2,6 @@ use lazy_static::lazy_static;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use cfg_if::cfg_if;
use crate::future::{self, Future}; use crate::future::{self, Future};
use crate::io::{self, Read}; use crate::io::{self, Read};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{blocking, Context, JoinHandle, Poll};
@ -125,7 +123,7 @@ impl Stdin {
let mut inner = opt.take().unwrap(); let mut inner = opt.take().unwrap();
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(async move { *state = State::Busy(blocking::spawn(move || {
inner.line.clear(); inner.line.clear();
let res = inner.stdin.read_line(&mut inner.line); let res = inner.stdin.read_line(&mut inner.line);
inner.last_op = Some(Operation::ReadLine(res)); inner.last_op = Some(Operation::ReadLine(res));
@ -167,7 +165,7 @@ impl Stdin {
static ref STDIN: std::io::Stdin = std::io::stdin(); static ref STDIN: std::io::Stdin = std::io::stdin();
} }
blocking::spawn(async { StdinLock(STDIN.lock()) }).await blocking::spawn(move || { StdinLock(STDIN.lock()) }).await
} }
} }
@ -207,7 +205,7 @@ impl Read for Stdin {
} }
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(async move { *state = State::Busy(blocking::spawn(move || {
let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf); let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf);
inner.last_op = Some(Operation::Read(res)); inner.last_op = Some(Operation::Read(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -221,35 +219,22 @@ impl Read for Stdin {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::windows::io::{AsRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, RawFd};
} else if #[cfg(windows)] {
use std::os::windows::io::{AsRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for Stdin {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { std::io::stdin().as_raw_fd()
impl AsRawFd for Stdin {
fn as_raw_fd(&self) -> RawFd {
std::io::stdin().as_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(unix)))] cfg_windows! {
cfg_if! { use crate::os::windows::io::{AsRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] {
impl AsRawHandle for Stdin { impl AsRawHandle for Stdin {
fn as_raw_handle(&self) -> RawHandle { fn as_raw_handle(&self) -> RawHandle {
std::io::stdin().as_raw_handle() std::io::stdin().as_raw_handle()
}
} }
} }
} }

@ -2,8 +2,6 @@ use lazy_static::lazy_static;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use cfg_if::cfg_if;
use crate::future::Future; use crate::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{blocking, Context, JoinHandle, Poll};
@ -146,7 +144,7 @@ impl Write for Stdout {
inner.buf[..buf.len()].copy_from_slice(buf); inner.buf[..buf.len()].copy_from_slice(buf);
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(async move { *state = State::Busy(blocking::spawn(move || {
let res = std::io::Write::write(&mut inner.stdout, &inner.buf); let res = std::io::Write::write(&mut inner.stdout, &inner.buf);
inner.last_op = Some(Operation::Write(res)); inner.last_op = Some(Operation::Write(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -174,7 +172,7 @@ impl Write for Stdout {
let mut inner = opt.take().unwrap(); let mut inner = opt.take().unwrap();
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(async move { *state = State::Busy(blocking::spawn(move || {
let res = std::io::Write::flush(&mut inner.stdout); let res = std::io::Write::flush(&mut inner.stdout);
inner.last_op = Some(Operation::Flush(res)); inner.last_op = Some(Operation::Flush(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -192,35 +190,22 @@ impl Write for Stdout {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::windows::io::{AsRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, RawFd};
} else if #[cfg(windows)] {
use std::os::windows::io::{AsRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for Stdout {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { std::io::stdout().as_raw_fd()
impl AsRawFd for Stdout {
fn as_raw_fd(&self) -> RawFd {
std::io::stdout().as_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(unix)))] cfg_windows! {
cfg_if! { use crate::os::windows::io::{AsRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] {
impl AsRawHandle for Stdout { impl AsRawHandle for Stdout {
fn as_raw_handle(&self) -> RawHandle { fn as_raw_handle(&self) -> RawHandle {
std::io::stdout().as_raw_handle() std::io::stdout().as_raw_handle()
}
} }
} }
} }

@ -2,8 +2,8 @@ use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::Duration; use std::time::Duration;
use futures_core::future::TryFuture;
use futures_timer::Delay; use futures_timer::Delay;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::io; use crate::io;
@ -36,45 +36,42 @@ pub async fn timeout<F, T>(dur: Duration, f: F) -> io::Result<T>
where where
F: Future<Output = io::Result<T>>, F: Future<Output = io::Result<T>>,
{ {
let f = TimeoutFuture { Timeout {
timeout: Delay::new(dur), timeout: Delay::new(dur),
future: f, future: f,
}; }
f.await .await
}
// Future returned by the [`io::timeout`](./fn.timeout.html) function.
#[derive(Debug)]
pub struct TimeoutFuture<F, T>
where
F: Future<Output = io::Result<T>>,
{
future: F,
timeout: Delay,
} }
impl<F, T> TimeoutFuture<F, T> pin_project! {
where /// Future returned by the `FutureExt::timeout` method.
F: Future<Output = io::Result<T>>, #[derive(Debug)]
{ pub struct Timeout<F, T>
pin_utils::unsafe_pinned!(future: F); where
pin_utils::unsafe_pinned!(timeout: Delay); F: Future<Output = io::Result<T>>,
{
#[pin]
future: F,
#[pin]
timeout: Delay,
}
} }
impl<F, T> Future for TimeoutFuture<F, T> impl<F, T> Future for Timeout<F, T>
where where
F: Future<Output = io::Result<T>>, F: Future<Output = io::Result<T>>,
{ {
type Output = io::Result<T>; type Output = io::Result<T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.as_mut().future().try_poll(cx) { let this = self.project();
match this.future.poll(cx) {
Poll::Pending => {} Poll::Pending => {}
other => return other, other => return other,
} }
if self.timeout().poll(cx).is_ready() { if this.timeout.poll(cx).is_ready() {
let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out"));
Poll::Ready(err) Poll::Ready(err)
} else { } else {
Poll::Pending Poll::Pending

@ -10,22 +10,14 @@ use write_all::WriteAllFuture;
use write_fmt::WriteFmtFuture; use write_fmt::WriteFmtFuture;
use write_vectored::WriteVectoredFuture; use write_vectored::WriteVectoredFuture;
use cfg_if::cfg_if; use crate::io::{self, IoSlice};
use crate::io::IoSlice; extension_trait! {
use crate::utils::extension_trait; use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::io;
cfg_if! { use crate::task::{Context, Poll};
if #[cfg(feature = "docs")] {
use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll};
}
}
extension_trait! {
#[doc = r#" #[doc = r#"
Allows writing to a byte stream. Allows writing to a byte stream.
@ -49,7 +41,7 @@ extension_trait! {
[`poll_flush`]: #tymethod.poll_flush [`poll_flush`]: #tymethod.poll_flush
[`poll_close`]: #tymethod.poll_close [`poll_close`]: #tymethod.poll_close
"#] "#]
pub trait Write [WriteExt: futures_io::AsyncWrite] { pub trait Write {
#[doc = r#" #[doc = r#"
Attempt to write bytes from `buf` into the object. Attempt to write bytes from `buf` into the object.
"#] "#]
@ -80,7 +72,9 @@ extension_trait! {
Attempt to close the object. Attempt to close the object.
"#] "#]
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>; fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>;
}
pub trait WriteExt: futures_io::AsyncWrite {
#[doc = r#" #[doc = r#"
Writes some bytes into the byte stream. Writes some bytes into the byte stream.

@ -32,7 +32,7 @@ impl<T: Write + Unpin + ?Sized> Future for WriteFmtFuture<'_, T> {
buffer, buffer,
.. ..
} = &mut *self; } = &mut *self;
let mut buffer = buffer.as_mut().unwrap(); let buffer = buffer.as_mut().unwrap();
// Copy the data from the buffer into the writer until it's done. // Copy the data from the buffer into the writer until it's done.
loop { loop {
@ -40,7 +40,7 @@ impl<T: Write + Unpin + ?Sized> Future for WriteFmtFuture<'_, T> {
futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?; futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?;
return Poll::Ready(Ok(())); return Poll::Ready(Ok(()));
} }
let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, &mut buffer))?; let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, buffer))?;
if i == 0 { if i == 0 {
return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
} }

@ -48,7 +48,8 @@
#![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")]
#![recursion_limit = "1024"] #![recursion_limit = "1024"]
use cfg_if::cfg_if; #[macro_use]
mod utils;
pub mod fs; pub mod fs;
pub mod future; pub mod future;
@ -61,26 +62,19 @@ pub mod stream;
pub mod sync; pub mod sync;
pub mod task; pub mod task;
cfg_if! { cfg_unstable! {
if #[cfg(any(feature = "unstable", feature = "docs"))] { pub mod pin;
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub mod process;
pub mod pin;
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub mod process;
mod unit; mod unit;
mod vec; mod vec;
mod result; mod result;
mod option; mod option;
mod string; mod string;
mod collections; mod collections;
}
#[doc(inline)]
pub use std::{write, writeln};
} }
mod macros; mod macros;
pub(crate) mod utils;
#[cfg(any(feature = "unstable", feature = "docs"))]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[doc(inline)]
pub use std::{write, writeln};

@ -43,7 +43,7 @@
/// # /// #
/// # }) /// # })
/// ``` /// ```
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[macro_export] #[macro_export]
macro_rules! print { macro_rules! print {
@ -81,12 +81,15 @@ macro_rules! print {
/// # /// #
/// # }) /// # })
/// ``` /// ```
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[macro_export] #[macro_export]
macro_rules! println { macro_rules! println {
() => ($crate::print!("\n")); () => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))) ($($arg:tt)*) => (async {
$crate::io::_print(format_args!($($arg)*)).await;
$crate::io::_print(format_args!("\n")).await;
})
} }
/// Prints to the standard error. /// Prints to the standard error.
@ -116,7 +119,7 @@ macro_rules! println {
/// # /// #
/// # }) /// # })
/// ``` /// ```
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[macro_export] #[macro_export]
macro_rules! eprint { macro_rules! eprint {
@ -150,7 +153,7 @@ macro_rules! eprint {
/// # /// #
/// # }) /// # })
/// ``` /// ```
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[macro_export] #[macro_export]
macro_rules! eprintln { macro_rules! eprintln {
@ -158,6 +161,7 @@ macro_rules! eprintln {
($($arg:tt)*) => ( ($($arg:tt)*) => (
async { async {
$crate::io::_eprint(format_args!($($arg)*)).await; $crate::io::_eprint(format_args!($($arg)*)).await;
$crate::io::_eprint(format_args!("\n")).await;
} }
); );
} }

@ -3,24 +3,22 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::pin::Pin; use std::pin::Pin;
use cfg_if::cfg_if;
use crate::future::Future; use crate::future::Future;
use crate::io; use crate::io;
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{blocking, Context, JoinHandle, Poll};
cfg_if! { cfg_not_docs! {
if #[cfg(feature = "docs")] { macro_rules! ret {
#[doc(hidden)] (impl Future<Output = $out:ty>, $fut:ty) => ($fut);
pub struct ImplFuture<T>(std::marker::PhantomData<T>); }
}
cfg_docs! {
#[doc(hidden)]
pub struct ImplFuture<T>(std::marker::PhantomData<T>);
macro_rules! ret { macro_rules! ret {
(impl Future<Output = $out:ty>, $fut:ty) => (ImplFuture<$out>); (impl Future<Output = $out:ty>, $fut:ty) => (ImplFuture<$out>);
}
} else {
macro_rules! ret {
(impl Future<Output = $out:ty>, $fut:ty) => ($fut);
}
} }
} }
@ -196,7 +194,7 @@ impl ToSocketAddrs for (&str, u16) {
} }
let host = host.to_string(); let host = host.to_string();
let task = blocking::spawn(async move { let task = blocking::spawn(move || {
std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port))
}); });
ToSocketAddrsFuture::Resolving(task) ToSocketAddrsFuture::Resolving(task)
@ -212,13 +210,12 @@ impl ToSocketAddrs for str {
impl Future<Output = Self::Iter>, impl Future<Output = Self::Iter>,
ToSocketAddrsFuture<Self::Iter> ToSocketAddrsFuture<Self::Iter>
) { ) {
if let Some(addr) = self.parse().ok() { if let Ok(addr) = self.parse() {
return ToSocketAddrsFuture::Ready(Ok(vec![addr].into_iter())); return ToSocketAddrsFuture::Ready(Ok(vec![addr].into_iter()));
} }
let addr = self.to_string(); let addr = self.to_string();
let task = let task = blocking::spawn(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str()));
blocking::spawn(async move { std::net::ToSocketAddrs::to_socket_addrs(addr.as_str()) });
ToSocketAddrsFuture::Resolving(task) ToSocketAddrsFuture::Resolving(task)
} }
} }

@ -1,13 +1,10 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use cfg_if::cfg_if;
use super::TcpStream;
use crate::future::{self, Future}; use crate::future::{self, Future};
use crate::io; use crate::io;
use crate::net::driver::Watcher; use crate::net::driver::Watcher;
use crate::net::ToSocketAddrs; use crate::net::{TcpStream, ToSocketAddrs};
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -213,59 +210,46 @@ impl From<std::net::TcpListener> for TcpListener {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
} else if #[cfg(windows)] {
// use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for TcpListener {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { self.watcher.get_ref().as_raw_fd()
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd()
}
} }
}
impl FromRawFd for TcpListener { impl FromRawFd for TcpListener {
unsafe fn from_raw_fd(fd: RawFd) -> TcpListener { unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
std::net::TcpListener::from_raw_fd(fd).into() std::net::TcpListener::from_raw_fd(fd).into()
}
} }
}
impl IntoRawFd for TcpListener { impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_inner().into_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(windows)))] cfg_windows! {
cfg_if! { // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] { //
// impl AsRawSocket for TcpListener { // impl AsRawSocket for TcpListener {
// fn as_raw_socket(&self) -> RawSocket { // fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket // self.raw_socket
// } // }
// } // }
// //
// impl FromRawSocket for TcpListener { // impl FromRawSocket for TcpListener {
// unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener { // unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener {
// net::TcpListener::from_raw_socket(handle).try_into().unwrap() // net::TcpListener::from_raw_socket(handle).try_into().unwrap()
// } // }
// } // }
// //
// impl IntoRawSocket for TcpListener { // impl IntoRawSocket for TcpListener {
// fn into_raw_socket(self) -> RawSocket { // fn into_raw_socket(self) -> RawSocket {
// self.raw_socket // self.raw_socket
// } // }
// } // }
}
} }

@ -2,8 +2,6 @@ use std::io::{IoSlice, IoSliceMut, Read as _, Write as _};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use cfg_if::cfg_if;
use crate::future; use crate::future;
use crate::io::{self, Read, Write}; use crate::io::{self, Read, Write};
use crate::net::driver::Watcher; use crate::net::driver::Watcher;
@ -76,7 +74,7 @@ impl TcpStream {
let mut last_err = None; let mut last_err = None;
for addr in addrs.to_socket_addrs().await? { for addr in addrs.to_socket_addrs().await? {
let res = blocking::spawn(async move { let res = blocking::spawn(move || {
let std_stream = std::net::TcpStream::connect(addr)?; let std_stream = std::net::TcpStream::connect(addr)?;
let mio_stream = mio::net::TcpStream::from_stream(std_stream)?; let mio_stream = mio::net::TcpStream::from_stream(std_stream)?;
Ok(TcpStream { Ok(TcpStream {
@ -367,59 +365,46 @@ impl From<std::net::TcpStream> for TcpStream {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
} else if #[cfg(windows)] {
// use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for TcpStream {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { self.watcher.get_ref().as_raw_fd()
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd()
}
} }
}
impl FromRawFd for TcpStream { impl FromRawFd for TcpStream {
unsafe fn from_raw_fd(fd: RawFd) -> TcpStream { unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
std::net::TcpStream::from_raw_fd(fd).into() std::net::TcpStream::from_raw_fd(fd).into()
}
} }
}
impl IntoRawFd for TcpStream { impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_inner().into_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(windows)))] cfg_windows! {
cfg_if! { // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] { //
// impl AsRawSocket for TcpStream { // impl AsRawSocket for TcpStream {
// fn as_raw_socket(&self) -> RawSocket { // fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket // self.raw_socket
// } // }
// } // }
// //
// impl FromRawSocket for TcpStream { // impl FromRawSocket for TcpStream {
// unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { // unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream {
// net::TcpStream::from_raw_socket(handle).try_into().unwrap() // net::TcpStream::from_raw_socket(handle).try_into().unwrap()
// } // }
// } // }
// //
// impl IntoRawSocket for TcpListener { // impl IntoRawSocket for TcpListener {
// fn into_raw_socket(self) -> RawSocket { // fn into_raw_socket(self) -> RawSocket {
// self.raw_socket // self.raw_socket
// } // }
// } // }
}
} }

@ -1,7 +1,5 @@
use std::io; use std::io;
use std::net::SocketAddr; use std::net::SocketAddr;
use cfg_if::cfg_if;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use crate::future; use crate::future;
@ -463,61 +461,46 @@ impl From<std::net::UdpSocket> for UdpSocket {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
} else if #[cfg(windows)] {
// use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for UdpSocket {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { self.watcher.get_ref().as_raw_fd()
impl AsRawFd for UdpSocket {
fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd()
}
} }
}
impl FromRawFd for UdpSocket { impl FromRawFd for UdpSocket {
unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket { unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket {
std::net::UdpSocket::from_raw_fd(fd).into() std::net::UdpSocket::from_raw_fd(fd).into()
}
} }
}
impl IntoRawFd for UdpSocket { impl IntoRawFd for UdpSocket {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_inner().into_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(windows)))] cfg_windows! {
cfg_if! { // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] { //
// use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; // impl AsRawSocket for UdpSocket {
// // fn as_raw_socket(&self) -> RawSocket {
// impl AsRawSocket for UdpSocket { // self.raw_socket
// fn as_raw_socket(&self) -> RawSocket { // }
// self.raw_socket // }
// } //
// } // impl FromRawSocket for UdpSocket {
// // unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket {
// impl FromRawSocket for UdpSocket { // net::UdpSocket::from_raw_socket(handle).into()
// unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket { // }
// net::UdpSocket::from_raw_socket(handle).into() // }
// } //
// } // impl IntoRawSocket for UdpSocket {
// // fn into_raw_socket(self) -> RawSocket {
// impl IntoRawSocket for UdpSocket { // self.raw_socket
// fn into_raw_socket(self) -> RawSocket { // }
// self.raw_socket // }
// }
// }
}
} }

@ -1,9 +1,9 @@
//! OS-specific extensions. //! OS-specific extensions.
#[cfg(any(unix, feature = "docs"))] cfg_unix! {
#[cfg_attr(feature = "docs", doc(cfg(unix)))] pub mod unix;
pub mod unix; }
#[cfg(any(windows, feature = "docs"))] cfg_windows! {
#[cfg_attr(feature = "docs", doc(cfg(windows)))] pub mod windows;
pub mod windows; }

@ -1,7 +1,5 @@
//! Unix-specific filesystem extensions. //! Unix-specific filesystem extensions.
use cfg_if::cfg_if;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::blocking;
@ -28,46 +26,46 @@ use crate::task::blocking;
pub async fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { pub async fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
let src = src.as_ref().to_owned(); let src = src.as_ref().to_owned();
let dst = dst.as_ref().to_owned(); let dst = dst.as_ref().to_owned();
blocking::spawn(async move { std::os::unix::fs::symlink(&src, &dst) }).await blocking::spawn(move || std::os::unix::fs::symlink(&src, &dst)).await
} }
cfg_if! { cfg_not_docs! {
if #[cfg(feature = "docs")] { pub use std::os::unix::fs::{DirBuilderExt, DirEntryExt, OpenOptionsExt};
/// Unix-specific extensions to `DirBuilder`. }
pub trait DirBuilderExt {
/// Sets the mode to create new directories with. This option defaults to
/// `0o777`.
fn mode(&mut self, mode: u32) -> &mut Self;
}
/// Unix-specific extension methods for `DirEntry`. cfg_docs! {
pub trait DirEntryExt { /// Unix-specific extensions to `DirBuilder`.
/// Returns the underlying `d_ino` field in the contained `dirent` pub trait DirBuilderExt {
/// structure. /// Sets the mode to create new directories with. This option defaults to
fn ino(&self) -> u64; /// `0o777`.
} fn mode(&mut self, mode: u32) -> &mut Self;
}
/// Unix-specific extension methods for `DirEntry`.
pub trait DirEntryExt {
/// Returns the underlying `d_ino` field in the contained `dirent`
/// structure.
fn ino(&self) -> u64;
}
/// Unix-specific extensions to `OpenOptions`. /// Unix-specific extensions to `OpenOptions`.
pub trait OpenOptionsExt { pub trait OpenOptionsExt {
/// Sets the mode bits that a new file will be created with. /// Sets the mode bits that a new file will be created with.
/// ///
/// If a new file is created as part of a `File::open_opts` call then this /// If a new file is created as part of a `File::open_opts` call then this
/// specified `mode` will be used as the permission bits for the new file. /// specified `mode` will be used as the permission bits for the new file.
/// If no `mode` is set, the default of `0o666` will be used. /// If no `mode` is set, the default of `0o666` will be used.
/// The operating system masks out bits with the systems `umask`, to produce /// The operating system masks out bits with the systems `umask`, to produce
/// the final permissions. /// the final permissions.
fn mode(&mut self, mode: u32) -> &mut Self; fn mode(&mut self, mode: u32) -> &mut Self;
/// Pass custom flags to the `flags` argument of `open`. /// Pass custom flags to the `flags` argument of `open`.
/// ///
/// The bits that define the access mode are masked out with `O_ACCMODE`, to /// The bits that define the access mode are masked out with `O_ACCMODE`, to
/// ensure they do not interfere with the access mode set by Rusts options. /// ensure they do not interfere with the access mode set by Rusts options.
/// ///
/// Custom flags can only set flags, not remove flags set by Rusts options. /// Custom flags can only set flags, not remove flags set by Rusts options.
/// This options overwrites any previously set custom flags. /// This options overwrites any previously set custom flags.
fn custom_flags(&mut self, flags: i32) -> &mut Self; fn custom_flags(&mut self, flags: i32) -> &mut Self;
}
} else {
pub use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt};
} }
} }

@ -1,56 +1,54 @@
//! Unix-specific I/O extensions. //! Unix-specific I/O extensions.
use cfg_if::cfg_if; cfg_not_docs! {
pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
}
cfg_if! { cfg_docs! {
if #[cfg(feature = "docs")] { /// Raw file descriptors.
/// Raw file descriptors. pub type RawFd = std::os::raw::c_int;
pub type RawFd = std::os::raw::c_int;
/// A trait to extract the raw unix file descriptor from an underlying /// A trait to extract the raw unix file descriptor from an underlying
/// object. /// object.
///
/// This is only available on unix platforms and must be imported in order
/// to call the method. Windows platforms have a corresponding `AsRawHandle`
/// and `AsRawSocket` set of traits.
pub trait AsRawFd {
/// Extracts the raw file descriptor.
/// ///
/// This is only available on unix platforms and must be imported in order /// This method does **not** pass ownership of the raw file descriptor
/// to call the method. Windows platforms have a corresponding `AsRawHandle` /// to the caller. The descriptor is only guaranteed to be valid while
/// and `AsRawSocket` set of traits. /// the original object has not yet been destroyed.
pub trait AsRawFd { fn as_raw_fd(&self) -> RawFd;
/// Extracts the raw file descriptor. }
///
/// This method does **not** pass ownership of the raw file descriptor
/// to the caller. The descriptor is only guaranteed to be valid while
/// the original object has not yet been destroyed.
fn as_raw_fd(&self) -> RawFd;
}
/// A trait to express the ability to construct an object from a raw file /// A trait to express the ability to construct an object from a raw file
/// descriptor.
pub trait FromRawFd {
/// Constructs a new instance of `Self` from the given raw file
/// descriptor. /// descriptor.
pub trait FromRawFd { ///
/// Constructs a new instance of `Self` from the given raw file /// This function **consumes ownership** of the specified file
/// descriptor. /// descriptor. The returned object will take responsibility for closing
/// /// it when the object goes out of scope.
/// This function **consumes ownership** of the specified file ///
/// descriptor. The returned object will take responsibility for closing /// This function is also unsafe as the primitives currently returned
/// it when the object goes out of scope. /// have the contract that they are the sole owner of the file
/// /// descriptor they are wrapping. Usage of this function could
/// This function is also unsafe as the primitives currently returned /// accidentally allow violating this contract which can cause memory
/// have the contract that they are the sole owner of the file /// unsafety in code that relies on it being true.
/// descriptor they are wrapping. Usage of this function could unsafe fn from_raw_fd(fd: RawFd) -> Self;
/// accidentally allow violating this contract which can cause memory }
/// unsafety in code that relies on it being true.
unsafe fn from_raw_fd(fd: RawFd) -> Self;
}
/// A trait to express the ability to consume an object and acquire ownership of /// A trait to express the ability to consume an object and acquire ownership of
/// its raw file descriptor. /// its raw file descriptor.
pub trait IntoRawFd { pub trait IntoRawFd {
/// Consumes this object, returning the raw underlying file descriptor. /// Consumes this object, returning the raw underlying file descriptor.
/// ///
/// This function **transfers ownership** of the underlying file descriptor /// This function **transfers ownership** of the underlying file descriptor
/// to the caller. Callers are then the unique owners of the file descriptor /// to the caller. Callers are then the unique owners of the file descriptor
/// and must close the descriptor once it's no longer needed. /// and must close the descriptor once it's no longer needed.
fn into_raw_fd(self) -> RawFd; fn into_raw_fd(self) -> RawFd;
}
} else {
pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
} }
} }

@ -67,7 +67,7 @@ impl UnixDatagram {
/// ``` /// ```
pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> { pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let socket = blocking::spawn(async move { mio_uds::UnixDatagram::bind(path) }).await?; let socket = blocking::spawn(move || mio_uds::UnixDatagram::bind(path)).await?;
Ok(UnixDatagram::new(socket)) Ok(UnixDatagram::new(socket))
} }

@ -68,7 +68,7 @@ impl UnixListener {
/// ``` /// ```
pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> { pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let listener = blocking::spawn(async move { mio_uds::UnixListener::bind(path) }).await?; let listener = blocking::spawn(move || mio_uds::UnixListener::bind(path)).await?;
Ok(UnixListener { Ok(UnixListener {
watcher: Watcher::new(listener), watcher: Watcher::new(listener),

@ -1,7 +1,5 @@
//! Unix-specific networking extensions. //! Unix-specific networking extensions.
use cfg_if::cfg_if;
pub use datagram::UnixDatagram; pub use datagram::UnixDatagram;
pub use listener::{Incoming, UnixListener}; pub use listener::{Incoming, UnixListener};
pub use stream::UnixStream; pub use stream::UnixStream;
@ -10,90 +8,90 @@ mod datagram;
mod listener; mod listener;
mod stream; mod stream;
cfg_if! { cfg_not_docs! {
if #[cfg(feature = "docs")] { pub use std::os::unix::net::SocketAddr;
use std::fmt; }
cfg_docs! {
use std::fmt;
use crate::path::Path;
use crate::path::Path; /// An address associated with a Unix socket.
///
/// # Examples
///
/// ```
/// use async_std::os::unix::net::UnixListener;
///
/// let socket = UnixListener::bind("/tmp/socket").await?;
/// let addr = socket.local_addr()?;
/// ```
#[derive(Clone)]
pub struct SocketAddr {
_private: (),
}
/// An address associated with a Unix socket. impl SocketAddr {
/// Returns `true` if the address is unnamed.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// A named address:
///
/// ```no_run
/// use async_std::os::unix::net::UnixListener; /// use async_std::os::unix::net::UnixListener;
/// ///
/// let socket = UnixListener::bind("/tmp/socket").await?; /// let socket = UnixListener::bind("/tmp/socket").await?;
/// let addr = socket.local_addr()?; /// let addr = socket.local_addr()?;
/// assert_eq!(addr.is_unnamed(), false);
/// ``` /// ```
#[derive(Clone)] ///
pub struct SocketAddr { /// An unnamed address:
_private: (), ///
/// ```no_run
/// use async_std::os::unix::net::UnixDatagram;
///
/// let socket = UnixDatagram::unbound().await?;
/// let addr = socket.local_addr()?;
/// assert_eq!(addr.is_unnamed(), true);
/// ```
pub fn is_unnamed(&self) -> bool {
unreachable!("this impl only appears in the rendered docs")
} }
impl SocketAddr { /// Returns the contents of this address if it is a `pathname` address.
/// Returns `true` if the address is unnamed. ///
/// /// # Examples
/// # Examples ///
/// /// With a pathname:
/// A named address: ///
/// /// ```no_run
/// ```no_run /// use async_std::os::unix::net::UnixListener;
/// use async_std::os::unix::net::UnixListener; /// use async_std::path::Path;
/// ///
/// let socket = UnixListener::bind("/tmp/socket").await?; /// let socket = UnixListener::bind("/tmp/socket").await?;
/// let addr = socket.local_addr()?; /// let addr = socket.local_addr()?;
/// assert_eq!(addr.is_unnamed(), false); /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/socket")));
/// ``` /// ```
/// ///
/// An unnamed address: /// Without a pathname:
/// ///
/// ```no_run /// ```
/// use async_std::os::unix::net::UnixDatagram; /// use async_std::os::unix::net::UnixDatagram;
/// ///
/// let socket = UnixDatagram::unbound().await?; /// let socket = UnixDatagram::unbound()?;
/// let addr = socket.local_addr()?; /// let addr = socket.local_addr()?;
/// assert_eq!(addr.is_unnamed(), true); /// assert_eq!(addr.as_pathname(), None);
/// ``` /// ```
pub fn is_unnamed(&self) -> bool { pub fn as_pathname(&self) -> Option<&Path> {
unreachable!("this impl only appears in the rendered docs") unreachable!("this impl only appears in the rendered docs")
}
/// Returns the contents of this address if it is a `pathname` address.
///
/// # Examples
///
/// With a pathname:
///
/// ```no_run
/// use async_std::os::unix::net::UnixListener;
/// use async_std::path::Path;
///
/// let socket = UnixListener::bind("/tmp/socket").await?;
/// let addr = socket.local_addr()?;
/// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/socket")));
/// ```
///
/// Without a pathname:
///
/// ```
/// use async_std::os::unix::net::UnixDatagram;
///
/// let socket = UnixDatagram::unbound()?;
/// let addr = socket.local_addr()?;
/// assert_eq!(addr.as_pathname(), None);
/// ```
pub fn as_pathname(&self) -> Option<&Path> {
unreachable!("this impl only appears in the rendered docs")
}
} }
}
impl fmt::Debug for SocketAddr { impl fmt::Debug for SocketAddr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
unreachable!("this impl only appears in the rendered docs") unreachable!("this impl only appears in the rendered docs")
}
} }
} else {
pub use std::os::unix::net::SocketAddr;
} }
} }

@ -58,7 +58,7 @@ impl UnixStream {
pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> { pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(async move { blocking::spawn(move || {
let std_stream = std::os::unix::net::UnixStream::connect(path)?; let std_stream = std::os::unix::net::UnixStream::connect(path)?;
let mio_stream = mio_uds::UnixStream::from_stream(std_stream)?; let mio_stream = mio_uds::UnixStream::from_stream(std_stream)?;
Ok(UnixStream { Ok(UnixStream {

@ -1,50 +1,48 @@
//! Windows-specific I/O extensions. //! Windows-specific I/O extensions.
use cfg_if::cfg_if; cfg_not_docs! {
pub use std::os::windows::io::{
AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket,
};
}
cfg_if! { cfg_docs! {
if #[cfg(feature = "docs")] { /// Raw HANDLEs.
/// Raw HANDLEs. pub type RawHandle = *mut std::os::raw::c_void;
pub type RawHandle = *mut std::os::raw::c_void;
/// Raw SOCKETs. /// Raw SOCKETs.
pub type RawSocket = u64; pub type RawSocket = u64;
/// Extracts raw handles. /// Extracts raw handles.
pub trait AsRawHandle { pub trait AsRawHandle {
/// Extracts the raw handle, without taking any ownership. /// Extracts the raw handle, without taking any ownership.
fn as_raw_handle(&self) -> RawHandle; fn as_raw_handle(&self) -> RawHandle;
} }
/// Construct I/O objects from raw handles. /// Construct I/O objects from raw handles.
pub trait FromRawHandle { pub trait FromRawHandle {
/// Constructs a new I/O object from the specified raw handle. /// Constructs a new I/O object from the specified raw handle.
/// ///
/// This function will **consume ownership** of the handle given, /// This function will **consume ownership** of the handle given,
/// passing responsibility for closing the handle to the returned /// passing responsibility for closing the handle to the returned
/// object. /// object.
/// ///
/// This function is also unsafe as the primitives currently returned /// This function is also unsafe as the primitives currently returned
/// have the contract that they are the sole owner of the file /// have the contract that they are the sole owner of the file
/// descriptor they are wrapping. Usage of this function could /// descriptor they are wrapping. Usage of this function could
/// accidentally allow violating this contract which can cause memory /// accidentally allow violating this contract which can cause memory
/// unsafety in code that relies on it being true. /// unsafety in code that relies on it being true.
unsafe fn from_raw_handle(handle: RawHandle) -> Self; unsafe fn from_raw_handle(handle: RawHandle) -> Self;
} }
/// A trait to express the ability to consume an object and acquire ownership of /// A trait to express the ability to consume an object and acquire ownership of
/// its raw `HANDLE`. /// its raw `HANDLE`.
pub trait IntoRawHandle { pub trait IntoRawHandle {
/// Consumes this object, returning the raw underlying handle. /// Consumes this object, returning the raw underlying handle.
/// ///
/// This function **transfers ownership** of the underlying handle to the /// This function **transfers ownership** of the underlying handle to the
/// caller. Callers are then the unique owners of the handle and must close /// caller. Callers are then the unique owners of the handle and must close
/// it once it's no longer needed. /// it once it's no longer needed.
fn into_raw_handle(self) -> RawHandle; fn into_raw_handle(self) -> RawHandle;
}
} else {
pub use std::os::windows::io::{
AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket,
};
} }
} }

@ -799,7 +799,7 @@ impl AsRef<Path> for String {
impl AsRef<Path> for std::path::PathBuf { impl AsRef<Path> for std::path::PathBuf {
fn as_ref(&self) -> &Path { fn as_ref(&self) -> &Path {
Path::new(self.into()) Path::new(self)
} }
} }

@ -5,7 +5,7 @@ use crate::path::Path;
/// This struct is an async version of [`std::path::PathBuf`]. /// This struct is an async version of [`std::path::PathBuf`].
/// ///
/// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Default)]
pub struct PathBuf { pub struct PathBuf {
inner: std::path::PathBuf, inner: std::path::PathBuf,
} }
@ -206,7 +206,7 @@ impl From<std::path::PathBuf> for PathBuf {
impl Into<std::path::PathBuf> for PathBuf { impl Into<std::path::PathBuf> for PathBuf {
fn into(self) -> std::path::PathBuf { fn into(self) -> std::path::PathBuf {
self.inner.into() self.inner
} }
} }

@ -11,8 +11,6 @@
//! use async_std::prelude::*; //! use async_std::prelude::*;
//! ``` //! ```
use cfg_if::cfg_if;
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::future::Future; pub use crate::future::Future;
#[doc(no_inline)] #[doc(no_inline)]
@ -28,6 +26,8 @@ pub use crate::stream::Stream;
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::task_local; pub use crate::task_local;
#[doc(hidden)]
pub use crate::future::future::FutureExt as _;
#[doc(hidden)] #[doc(hidden)]
pub use crate::io::buf_read::BufReadExt as _; pub use crate::io::buf_read::BufReadExt as _;
#[doc(hidden)] #[doc(hidden)]
@ -39,12 +39,9 @@ pub use crate::io::write::WriteExt as _;
#[doc(hidden)] #[doc(hidden)]
pub use crate::stream::stream::StreamExt as _; pub use crate::stream::stream::StreamExt as _;
cfg_if! { cfg_unstable! {
if #[cfg(any(feature = "unstable", feature = "docs"))] { #[doc(no_inline)]
#[doc(no_inline)] pub use crate::stream::DoubleEndedStream;
pub use crate::stream::DoubleEndedStream; #[doc(no_inline)]
pub use crate::stream::ExactSizeStream;
#[doc(no_inline)]
pub use crate::stream::ExactSizeStream;
}
} }

@ -10,8 +10,8 @@ use std::task::{Context, Poll};
/// `Item`s from the back, as well as the front. /// `Item`s from the back, as well as the front.
/// ///
/// [`Stream`]: trait.Stream.html /// [`Stream`]: trait.Stream.html
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub trait DoubleEndedStream: Stream { pub trait DoubleEndedStream: Stream {
/// Removes and returns an element from the end of the stream. /// Removes and returns an element from the end of the stream.
/// ///

@ -59,7 +59,7 @@ pub use crate::stream::Stream;
/// # } /// # }
/// # } /// # }
/// # } /// # }
/// # fn main() { async_std::task::block_on(async { /// # async_std::task::block_on(async {
/// # /// #
/// impl ExactSizeStream for Counter { /// impl ExactSizeStream for Counter {
/// // We can easily calculate the remaining number of iterations. /// // We can easily calculate the remaining number of iterations.
@ -74,10 +74,9 @@ pub use crate::stream::Stream;
/// ///
/// assert_eq!(5, counter.len()); /// assert_eq!(5, counter.len());
/// # }); /// # });
/// # }
/// ``` /// ```
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub trait ExactSizeStream: Stream { pub trait ExactSizeStream: Stream {
/// Returns the exact number of times the stream will iterate. /// Returns the exact number of times the stream will iterate.
/// ///

@ -14,7 +14,7 @@ use crate::stream::IntoStream;
/// ## Examples /// ## Examples
/// ///
/// ``` /// ```
/// # fn main() { async_std::task::block_on(async { /// # async_std::task::block_on(async {
/// # /// #
/// use async_std::prelude::*; /// use async_std::prelude::*;
/// use async_std::stream::{self, Extend}; /// use async_std::stream::{self, Extend};
@ -25,8 +25,9 @@ use crate::stream::IntoStream;
/// ///
/// assert_eq!(v, vec![1, 2, 3, 3, 3]); /// assert_eq!(v, vec![1, 2, 3, 3, 3]);
/// # /// #
/// # }) } /// # })
/// ``` /// ```
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub trait Extend<A> { pub trait Extend<A> {
/// Extends a collection with the contents of a stream. /// Extends a collection with the contents of a stream.

@ -0,0 +1,97 @@
use std::marker::PhantomData;
use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future;
use crate::stream::Stream;
use crate::task::{Context, Poll};
pin_project! {
/// A stream that yields elements by calling a closure.
///
/// This stream is constructed by [`from_fn`] function.
///
/// [`from_fn`]: fn.from_fn.html
#[derive(Debug)]
pub struct FromFn<F, Fut, T> {
f: F,
#[pin]
future: Option<Fut>,
__t: PhantomData<T>,
}
}
/// Creates a new stream where to produce each new element a provided closure is called.
///
/// This allows creating a custom stream with any behaviour without using the more verbose
/// syntax of creating a dedicated type and implementing a `Stream` trait for it.
///
/// # Examples
///
/// ```
/// # async_std::task::block_on(async {
/// #
/// use async_std::prelude::*;
/// use async_std::sync::Mutex;
/// use std::sync::Arc;
/// use async_std::stream;
///
/// let count = Arc::new(Mutex::new(0u8));
/// let s = stream::from_fn(|| {
/// let count = Arc::clone(&count);
///
/// async move {
/// *count.lock().await += 1;
///
/// if *count.lock().await > 3 {
/// None
/// } else {
/// Some(*count.lock().await)
/// }
/// }
/// });
///
/// pin_utils::pin_mut!(s);
/// assert_eq!(s.next().await, Some(1));
/// assert_eq!(s.next().await, Some(2));
/// assert_eq!(s.next().await, Some(3));
/// assert_eq!(s.next().await, None);
/// #
/// # })
/// ```
pub fn from_fn<T, F, Fut>(f: F) -> FromFn<F, Fut, T>
where
F: FnMut() -> Fut,
Fut: Future<Output = Option<T>>,
{
FromFn {
f,
future: None,
__t: PhantomData,
}
}
impl<F, Fut, T> Stream for FromFn<F, Fut, T>
where
F: FnMut() -> Fut,
Fut: Future<Output = Option<T>>,
{
type Item = T;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
loop {
if this.future.is_some() {
let next =
futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx));
this.future.set(None);
return Poll::Ready(next);
} else {
let fut = (this.f)();
this.future.set(Some(fut));
}
}
}
}

@ -106,8 +106,8 @@ use std::pin::Pin;
///``` ///```
/// ///
/// [`IntoStream`]: trait.IntoStream.html /// [`IntoStream`]: trait.IntoStream.html
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub trait FromStream<T> { pub trait FromStream<T> {
/// Creates a value from a stream. /// Creates a value from a stream.
/// ///

@ -14,7 +14,7 @@ use crate::stream::Stream;
/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
/// [`Stream::fuse`]: trait.Stream.html#method.fuse /// [`Stream::fuse`]: trait.Stream.html#method.fuse
/// [`Fuse`]: struct.Fuse.html /// [`Fuse`]: struct.Fuse.html
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub trait FusedStream: Stream {} pub trait FusedStream: Stream {}

@ -0,0 +1,192 @@
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
use futures_core::future::Future;
use futures_core::stream::Stream;
use futures_timer::Delay;
/// Creates a new stream that yields at a set interval.
///
/// The stream first yields after `dur`, and continues to yield every
/// `dur` after that. The stream accounts for time elapsed between calls, and
/// will adjust accordingly to prevent time skews.
///
/// Each interval may be slightly longer than the specified duration, but never
/// less.
///
/// Note that intervals are not intended for high resolution timers, but rather
/// they will likely fire some granularity after the exact instant that they're
/// otherwise indicated to fire at.
///
/// See also: [`task::sleep`].
///
/// [`task::sleep`]: ../task/fn.sleep.html
///
/// # Examples
///
/// Basic example:
///
/// ```no_run
/// use async_std::prelude::*;
/// use async_std::stream;
/// use std::time::Duration;
///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// let mut interval = stream::interval(Duration::from_secs(4));
/// while let Some(_) = interval.next().await {
/// println!("prints every four seconds");
/// }
/// #
/// # Ok(()) }) }
/// ```
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub fn interval(dur: Duration) -> Interval {
Interval {
delay: Delay::new(dur),
interval: dur,
}
}
/// A stream representing notifications at fixed interval
///
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[derive(Debug)]
pub struct Interval {
delay: Delay,
interval: Duration,
}
impl Stream for Interval {
type Item = ();
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if Pin::new(&mut self.delay).poll(cx).is_pending() {
return Poll::Pending;
}
let when = Instant::now();
let next = next_interval(when, Instant::now(), self.interval);
self.delay.reset(next);
Poll::Ready(Some(()))
}
}
/// Converts Duration object to raw nanoseconds if possible
///
/// This is useful to divide intervals.
///
/// While technically for large duration it's impossible to represent any
/// duration as nanoseconds, the largest duration we can represent is about
/// 427_000 years. Large enough for any interval we would use or calculate in
/// tokio.
fn duration_to_nanos(dur: Duration) -> Option<u64> {
dur.as_secs()
.checked_mul(1_000_000_000)
.and_then(|v| v.checked_add(u64::from(dur.subsec_nanos())))
}
fn next_interval(prev: Instant, now: Instant, interval: Duration) -> Instant {
let new = prev + interval;
if new > now {
return new;
}
let spent_ns = duration_to_nanos(now.duration_since(prev)).expect("interval should be expired");
let interval_ns =
duration_to_nanos(interval).expect("interval is less that 427 thousand years");
let mult = spent_ns / interval_ns + 1;
assert!(
mult < (1 << 32),
"can't skip more than 4 billion intervals of {:?} \
(trying to skip {})",
interval,
mult
);
prev + interval * (mult as u32)
}
#[cfg(test)]
mod test {
use super::next_interval;
use std::time::{Duration, Instant};
struct Timeline(Instant);
impl Timeline {
fn new() -> Timeline {
Timeline(Instant::now())
}
fn at(&self, millis: u64) -> Instant {
self.0 + Duration::from_millis(millis)
}
fn at_ns(&self, sec: u64, nanos: u32) -> Instant {
self.0 + Duration::new(sec, nanos)
}
}
fn dur(millis: u64) -> Duration {
Duration::from_millis(millis)
}
// The math around Instant/Duration isn't 100% precise due to rounding
// errors, see #249 for more info
fn almost_eq(a: Instant, b: Instant) -> bool {
if a == b {
true
} else if a > b {
a - b < Duration::from_millis(1)
} else {
b - a < Duration::from_millis(1)
}
}
#[test]
fn norm_next() {
let tm = Timeline::new();
assert!(almost_eq(
next_interval(tm.at(1), tm.at(2), dur(10)),
tm.at(11)
));
assert!(almost_eq(
next_interval(tm.at(7777), tm.at(7788), dur(100)),
tm.at(7877)
));
assert!(almost_eq(
next_interval(tm.at(1), tm.at(1000), dur(2100)),
tm.at(2101)
));
}
#[test]
fn fast_forward() {
let tm = Timeline::new();
assert!(almost_eq(
next_interval(tm.at(1), tm.at(1000), dur(10)),
tm.at(1001)
));
assert!(almost_eq(
next_interval(tm.at(7777), tm.at(8888), dur(100)),
tm.at(8977)
));
assert!(almost_eq(
next_interval(tm.at(1), tm.at(10000), dur(2100)),
tm.at(10501)
));
}
/// TODO: this test actually should be successful, but since we can't
/// multiply Duration on anything larger than u32 easily we decided
/// to allow it to fail for now
#[test]
#[should_panic(expected = "can't skip more than 4 billion intervals")]
fn large_skip() {
let tm = Timeline::new();
assert_eq!(
next_interval(tm.at_ns(0, 1), tm.at_ns(25, 0), Duration::new(0, 2)),
tm.at_ns(25, 1)
);
}
}

@ -13,8 +13,8 @@ use crate::stream::Stream;
/// See also: [`FromStream`]. /// See also: [`FromStream`].
/// ///
/// [`FromStream`]: trait.FromStream.html /// [`FromStream`]: trait.FromStream.html
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub trait IntoStream { pub trait IntoStream {
/// The type of the elements being iterated over. /// The type of the elements being iterated over.
type Item; type Item;

@ -1,4 +1,4 @@
//! Asynchronous iteration. //! Composable asynchronous iteration.
//! //!
//! This module is an async version of [`std::iter`]. //! This module is an async version of [`std::iter`].
//! //!
@ -21,11 +21,11 @@
//! # }) //! # })
//! ``` //! ```
use cfg_if::cfg_if;
pub use empty::{empty, Empty}; pub use empty::{empty, Empty};
pub use from_fn::{from_fn, FromFn};
pub use once::{once, Once}; pub use once::{once, Once};
pub use repeat::{repeat, Repeat}; pub use repeat::{repeat, Repeat};
pub use repeat_with::{repeat_with, RepeatWith};
pub use stream::{ pub use stream::{
Chain, Filter, Fuse, Inspect, Scan, Skip, SkipWhile, StepBy, Stream, Take, TakeWhile, Zip, Chain, Filter, Fuse, Inspect, Scan, Skip, SkipWhile, StepBy, Stream, Take, TakeWhile, Zip,
}; };
@ -33,25 +33,30 @@ pub use stream::{
pub(crate) mod stream; pub(crate) mod stream;
mod empty; mod empty;
mod from_fn;
mod once; mod once;
mod repeat; mod repeat;
mod repeat_with;
cfg_if! { cfg_unstable! {
if #[cfg(any(feature = "unstable", feature = "docs"))] { mod double_ended_stream;
mod double_ended_stream; mod exact_size_stream;
mod exact_size_stream; mod extend;
mod fused_stream; mod from_stream;
mod extend; mod fused_stream;
mod from_stream; mod interval;
mod into_stream; mod into_stream;
mod product;
pub use double_ended_stream::DoubleEndedStream; mod sum;
pub use exact_size_stream::ExactSizeStream;
pub use extend::Extend;
pub use from_stream::FromStream;
pub use into_stream::IntoStream;
pub use fused_stream::FusedStream;
pub use stream::Merge; pub use double_ended_stream::DoubleEndedStream;
} pub use exact_size_stream::ExactSizeStream;
pub use extend::Extend;
pub use from_stream::FromStream;
pub use fused_stream::FusedStream;
pub use interval::{interval, Interval};
pub use into_stream::IntoStream;
pub use product::Product;
pub use stream::Merge;
pub use sum::Sum;
} }

@ -1,5 +1,7 @@
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -24,20 +26,22 @@ pub fn once<T>(t: T) -> Once<T> {
Once { value: Some(t) } Once { value: Some(t) }
} }
/// A stream that yields a single item. pin_project! {
/// /// A stream that yields a single item.
/// This stream is constructed by the [`once`] function. ///
/// /// This stream is constructed by the [`once`] function.
/// [`once`]: fn.once.html ///
#[derive(Debug)] /// [`once`]: fn.once.html
pub struct Once<T> { #[derive(Debug)]
value: Option<T>, pub struct Once<T> {
value: Option<T>,
}
} }
impl<T: Unpin> Stream for Once<T> { impl<T: Unpin> Stream for Once<T> {
type Item = T; type Item = T;
fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> { fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
Poll::Ready(self.value.take()) Poll::Ready(self.project().value.take())
} }
} }

@ -0,0 +1,23 @@
use crate::future::Future;
use crate::stream::Stream;
/// Trait to represent types that can be created by productming up a stream.
///
/// This trait is used to implement the [`product`] method on streams. Types which
/// implement the trait can be generated by the [`product`] method. Like
/// [`FromStream`] this trait should rarely be called directly and instead
/// interacted with through [`Stream::product`].
///
/// [`product`]: trait.Product.html#tymethod.product
/// [`FromStream`]: trait.FromStream.html
/// [`Stream::product`]: trait.Stream.html#method.product
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub trait Product<A = Self>: Sized {
/// Method which takes a stream and generates `Self` from the elements by
/// multiplying the items.
fn product<S, F>(stream: S) -> F
where
S: Stream<Item = A>,
F: Future<Output = Self>;
}

@ -0,0 +1,100 @@
use std::marker::PhantomData;
use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future;
use crate::stream::Stream;
use crate::task::{Context, Poll};
pin_project! {
/// A stream that repeats elements of type `T` endlessly by applying a provided closure.
///
/// This stream is constructed by the [`repeat_with`] function.
///
/// [`repeat_with`]: fn.repeat_with.html
#[derive(Debug)]
pub struct RepeatWith<F, Fut, A> {
f: F,
#[pin]
future: Option<Fut>,
__a: PhantomData<A>,
}
}
/// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # async_std::task::block_on(async {
/// #
/// use async_std::prelude::*;
/// use async_std::stream;
///
/// let s = stream::repeat_with(|| async { 1 });
///
/// pin_utils::pin_mut!(s);
///
/// assert_eq!(s.next().await, Some(1));
/// assert_eq!(s.next().await, Some(1));
/// assert_eq!(s.next().await, Some(1));
/// assert_eq!(s.next().await, Some(1));
/// # })
/// ```
///
/// Going finite:
///
/// ```
/// # async_std::task::block_on(async {
/// #
/// use async_std::prelude::*;
/// use async_std::stream;
///
/// let s = stream::repeat_with(|| async { 1u8 }).take(2);
///
/// pin_utils::pin_mut!(s);
///
/// assert_eq!(s.next().await, Some(1));
/// assert_eq!(s.next().await, Some(1));
/// assert_eq!(s.next().await, None);
/// # })
/// ```
pub fn repeat_with<F, Fut, A>(repeater: F) -> RepeatWith<F, Fut, A>
where
F: FnMut() -> Fut,
Fut: Future<Output = A>,
{
RepeatWith {
f: repeater,
future: None,
__a: PhantomData,
}
}
impl<F, Fut, A> Stream for RepeatWith<F, Fut, A>
where
F: FnMut() -> Fut,
Fut: Future<Output = A>,
{
type Item = A;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
loop {
if this.future.is_some() {
let res = futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx));
this.future.set(None);
return Poll::Ready(Some(res));
} else {
let fut = (this.f)();
this.future.set(Some(fut));
}
}
}
}

@ -1,20 +1,23 @@
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use super::fuse::Fuse; use super::fuse::Fuse;
use crate::prelude::*; use crate::prelude::*;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// Chains two streams one after another. pin_project! {
#[derive(Debug)] /// Chains two streams one after another.
pub struct Chain<S, U> { #[derive(Debug)]
first: Fuse<S>, pub struct Chain<S, U> {
second: Fuse<U>, #[pin]
first: Fuse<S>,
#[pin]
second: Fuse<U>,
}
} }
impl<S: Stream, U: Stream> Chain<S, U> { impl<S: Stream, U: Stream> Chain<S, U> {
pin_utils::unsafe_pinned!(first: Fuse<S>);
pin_utils::unsafe_pinned!(second: Fuse<U>);
pub(super) fn new(first: S, second: U) -> Self { pub(super) fn new(first: S, second: U) -> Self {
Chain { Chain {
first: first.fuse(), first: first.fuse(),
@ -26,22 +29,23 @@ impl<S: Stream, U: Stream> Chain<S, U> {
impl<S: Stream, U: Stream<Item = S::Item>> Stream for Chain<S, U> { impl<S: Stream, U: Stream<Item = S::Item>> Stream for Chain<S, U> {
type Item = S::Item; type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if !self.first.done { let mut this = self.project();
let next = futures_core::ready!(self.as_mut().first().poll_next(cx)); if !this.first.done {
let next = futures_core::ready!(this.first.as_mut().poll_next(cx));
if let Some(next) = next { if let Some(next) = next {
return Poll::Ready(Some(next)); return Poll::Ready(Some(next));
} }
} }
if !self.second.done { if !this.second.done {
let next = futures_core::ready!(self.as_mut().second().poll_next(cx)); let next = futures_core::ready!(this.second.as_mut().poll_next(cx));
if let Some(next) = next { if let Some(next) = next {
return Poll::Ready(Some(next)); return Poll::Ready(Some(next));
} }
} }
if self.first.done && self.second.done { if this.first.done && this.second.done {
return Poll::Ready(None); return Poll::Ready(None);
} }

@ -0,0 +1,93 @@
use std::cmp::Ordering;
use std::pin::Pin;
use pin_project_lite::pin_project;
use super::fuse::Fuse;
use crate::future::Future;
use crate::prelude::*;
use crate::stream::Stream;
use crate::task::{Context, Poll};
pin_project! {
// Lexicographically compares the elements of this `Stream` with those
// of another using `Ord`.
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct CmpFuture<L: Stream, R: Stream> {
#[pin]
l: Fuse<L>,
#[pin]
r: Fuse<R>,
l_cache: Option<L::Item>,
r_cache: Option<R::Item>,
}
}
impl<L: Stream, R: Stream> CmpFuture<L, R> {
pub(super) fn new(l: L, r: R) -> Self {
CmpFuture {
l: l.fuse(),
r: r.fuse(),
l_cache: None,
r_cache: None,
}
}
}
impl<L: Stream, R: Stream> Future for CmpFuture<L, R>
where
L: Stream + Sized,
R: Stream<Item = L::Item> + Sized,
L::Item: Ord,
{
type Output = Ordering;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
loop {
// Stream that completes earliest can be considered Less, etc
let l_complete = this.l.done && this.l_cache.is_none();
let r_complete = this.r.done && this.r_cache.is_none();
if l_complete && r_complete {
return Poll::Ready(Ordering::Equal);
} else if l_complete {
return Poll::Ready(Ordering::Less);
} else if r_complete {
return Poll::Ready(Ordering::Greater);
}
// Get next value if possible and necesary
if !this.l.done && this.l_cache.is_none() {
let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx));
if let Some(item) = l_next {
*this.l_cache = Some(item);
}
}
if !this.r.done && this.r_cache.is_none() {
let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx));
if let Some(item) = r_next {
*this.r_cache = Some(item);
}
}
// Compare if both values are available.
if this.l_cache.is_some() && this.r_cache.is_some() {
let l_value = this.l_cache.take().unwrap();
let r_value = this.r_cache.take().unwrap();
let result = l_value.cmp(&r_value);
if let Ordering::Equal = result {
// Reset cache to prepare for next comparison
*this.l_cache = None;
*this.r_cache = None;
} else {
// Return non equal value
return Poll::Ready(result);
}
}
}
}
}

@ -1,19 +1,21 @@
use crate::task::{Context, Poll};
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll};
#[doc(hidden)] pin_project! {
#[allow(missing_debug_implementations)] #[doc(hidden)]
pub struct Enumerate<S> { #[allow(missing_debug_implementations)]
stream: S, pub struct Enumerate<S> {
i: usize, #[pin]
stream: S,
i: usize,
}
} }
impl<S> Enumerate<S> { impl<S> Enumerate<S> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(i: usize);
pub(super) fn new(stream: S) -> Self { pub(super) fn new(stream: S) -> Self {
Enumerate { stream, i: 0 } Enumerate { stream, i: 0 }
} }
@ -25,13 +27,14 @@ where
{ {
type Item = (usize, S::Item); type Item = (usize, S::Item);
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let this = self.project();
let next = futures_core::ready!(this.stream.poll_next(cx));
match next { match next {
Some(v) => { Some(v) => {
let ret = (self.i, v); let ret = (*this.i, v);
*self.as_mut().i() += 1; *this.i += 1;
Poll::Ready(Some(ret)) Poll::Ready(Some(ret))
} }
None => Poll::Ready(None), None => Poll::Ready(None),

@ -1,21 +1,23 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream to filter elements of another stream with a predicate. pin_project! {
#[derive(Debug)] /// A stream to filter elements of another stream with a predicate.
pub struct Filter<S, P, T> { #[derive(Debug)]
stream: S, pub struct Filter<S, P, T> {
predicate: P, #[pin]
__t: PhantomData<T>, stream: S,
predicate: P,
__t: PhantomData<T>,
}
} }
impl<S, P, T> Filter<S, P, T> { impl<S, P, T> Filter<S, P, T> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(predicate: P);
pub(super) fn new(stream: S, predicate: P) -> Self { pub(super) fn new(stream: S, predicate: P) -> Self {
Filter { Filter {
stream, stream,
@ -32,11 +34,12 @@ where
{ {
type Item = S::Item; type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let this = self.project();
let next = futures_core::ready!(this.stream.poll_next(cx));
match next { match next {
Some(v) if (self.as_mut().predicate())(&v) => Poll::Ready(Some(v)), Some(v) if (this.predicate)(&v) => Poll::Ready(Some(v)),
Some(_) => { Some(_) => {
cx.waker().wake_by_ref(); cx.waker().wake_by_ref();
Poll::Pending Poll::Pending

@ -2,21 +2,23 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
#[doc(hidden)] pin_project! {
#[allow(missing_debug_implementations)] #[doc(hidden)]
pub struct FilterMap<S, F, T, B> { #[allow(missing_debug_implementations)]
stream: S, pub struct FilterMap<S, F, T, B> {
f: F, #[pin]
__from: PhantomData<T>, stream: S,
__to: PhantomData<B>, f: F,
__from: PhantomData<T>,
__to: PhantomData<B>,
}
} }
impl<S, F, T, B> FilterMap<S, F, T, B> { impl<S, F, T, B> FilterMap<S, F, T, B> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(f: F);
pub(crate) fn new(stream: S, f: F) -> Self { pub(crate) fn new(stream: S, f: F) -> Self {
FilterMap { FilterMap {
stream, stream,
@ -34,10 +36,11 @@ where
{ {
type Item = B; type Item = B;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let this = self.project();
let next = futures_core::ready!(this.stream.poll_next(cx));
match next { match next {
Some(v) => match (self.as_mut().f())(v) { Some(v) => match (this.f)(v) {
Some(b) => Poll::Ready(Some(b)), Some(b) => Poll::Ready(Some(b)),
None => { None => {
cx.waker().wake_by_ref(); cx.waker().wake_by_ref();

@ -1,24 +1,25 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
#[doc(hidden)] pin_project! {
#[allow(missing_debug_implementations)] #[doc(hidden)]
pub struct FoldFuture<S, F, T, B> { #[allow(missing_debug_implementations)]
stream: S, pub struct FoldFuture<S, F, T, B> {
f: F, #[pin]
acc: Option<B>, stream: S,
__t: PhantomData<T>, f: F,
acc: Option<B>,
__t: PhantomData<T>,
}
} }
impl<S, F, T, B> FoldFuture<S, F, T, B> { impl<S, F, T, B> FoldFuture<S, F, T, B> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(f: F);
pin_utils::unsafe_unpinned!(acc: Option<B>);
pub(super) fn new(stream: S, init: B, f: F) -> Self { pub(super) fn new(stream: S, init: B, f: F) -> Self {
FoldFuture { FoldFuture {
stream, stream,
@ -36,17 +37,18 @@ where
{ {
type Output = B; type Output = B;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
loop { loop {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
match next { match next {
Some(v) => { Some(v) => {
let old = self.as_mut().acc().take().unwrap(); let old = this.acc.take().unwrap();
let new = (self.as_mut().f())(old, v); let new = (this.f)(old, v);
*self.as_mut().acc() = Some(new); *this.acc = Some(new);
} }
None => return Poll::Ready(self.as_mut().acc().take().unwrap()), None => return Poll::Ready(this.acc.take().unwrap()),
} }
} }
} }

@ -1,22 +1,24 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
#[doc(hidden)] pin_project! {
#[allow(missing_debug_implementations)] #[doc(hidden)]
pub struct ForEachFuture<S, F, T> { #[allow(missing_debug_implementations)]
stream: S, pub struct ForEachFuture<S, F, T> {
f: F, #[pin]
__t: PhantomData<T>, stream: S,
f: F,
__t: PhantomData<T>,
}
} }
impl<S, F, T> ForEachFuture<S, F, T> { impl<S, F, T> ForEachFuture<S, F, T> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(f: F);
pub(super) fn new(stream: S, f: F) -> Self { pub(super) fn new(stream: S, f: F) -> Self {
ForEachFuture { ForEachFuture {
stream, stream,
@ -33,12 +35,13 @@ where
{ {
type Output = (); type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
loop { loop {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
match next { match next {
Some(v) => (self.as_mut().f())(v), Some(v) => (this.f)(v),
None => return Poll::Ready(()), None => return Poll::Ready(()),
} }
} }

@ -1,33 +1,32 @@
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A `Stream` that is permanently closed once a single call to `poll` results in pin_project! {
/// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. /// A `Stream` that is permanently closed once a single call to `poll` results in
#[derive(Clone, Debug)] /// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`.
pub struct Fuse<S> { #[derive(Clone, Debug)]
pub(crate) stream: S, pub struct Fuse<S> {
pub(crate) done: bool, #[pin]
} pub(crate) stream: S,
pub(crate) done: bool,
impl<S: Unpin> Unpin for Fuse<S> {} }
impl<S: Stream> Fuse<S> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(done: bool);
} }
impl<S: Stream> Stream for Fuse<S> { impl<S: Stream> Stream for Fuse<S> {
type Item = S::Item; type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
if self.done { let this = self.project();
if *this.done {
Poll::Ready(None) Poll::Ready(None)
} else { } else {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let next = futures_core::ready!(this.stream.poll_next(cx));
if next.is_none() { if next.is_none() {
*self.as_mut().done() = true; *this.done = true;
} }
Poll::Ready(next) Poll::Ready(next)
} }

@ -0,0 +1,50 @@
use std::cmp::Ordering;
use std::pin::Pin;
use pin_project_lite::pin_project;
use super::partial_cmp::PartialCmpFuture;
use crate::future::Future;
use crate::prelude::*;
use crate::stream::Stream;
use crate::task::{Context, Poll};
pin_project! {
// Determines if the elements of this `Stream` are lexicographically
// greater than or equal to those of another.
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct GeFuture<L: Stream, R: Stream> {
#[pin]
partial_cmp: PartialCmpFuture<L, R>,
}
}
impl<L: Stream, R: Stream> GeFuture<L, R>
where
L::Item: PartialOrd<R::Item>,
{
pub(super) fn new(l: L, r: R) -> Self {
GeFuture {
partial_cmp: l.partial_cmp(r),
}
}
}
impl<L: Stream, R: Stream> Future for GeFuture<L, R>
where
L: Stream,
R: Stream,
L::Item: PartialOrd<R::Item>,
{
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
match result {
Some(Ordering::Greater) | Some(Ordering::Equal) => Poll::Ready(true),
_ => Poll::Ready(false),
}
}
}

@ -0,0 +1,50 @@
use std::cmp::Ordering;
use std::pin::Pin;
use pin_project_lite::pin_project;
use super::partial_cmp::PartialCmpFuture;
use crate::future::Future;
use crate::prelude::*;
use crate::stream::Stream;
use crate::task::{Context, Poll};
pin_project! {
// Determines if the elements of this `Stream` are lexicographically
// greater than those of another.
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct GtFuture<L: Stream, R: Stream> {
#[pin]
partial_cmp: PartialCmpFuture<L, R>,
}
}
impl<L: Stream, R: Stream> GtFuture<L, R>
where
L::Item: PartialOrd<R::Item>,
{
pub(super) fn new(l: L, r: R) -> Self {
GtFuture {
partial_cmp: l.partial_cmp(r),
}
}
}
impl<L: Stream, R: Stream> Future for GtFuture<L, R>
where
L: Stream + Sized,
R: Stream + Sized,
L::Item: PartialOrd<R::Item>,
{
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
match result {
Some(Ordering::Greater) => Poll::Ready(true),
_ => Poll::Ready(false),
}
}
}

@ -1,21 +1,23 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream that does something with each element of another stream. pin_project! {
#[derive(Debug)] /// A stream that does something with each element of another stream.
pub struct Inspect<S, F, T> { #[derive(Debug)]
stream: S, pub struct Inspect<S, F, T> {
f: F, #[pin]
__t: PhantomData<T>, stream: S,
f: F,
__t: PhantomData<T>,
}
} }
impl<S, F, T> Inspect<S, F, T> { impl<S, F, T> Inspect<S, F, T> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(f: F);
pub(super) fn new(stream: S, f: F) -> Self { pub(super) fn new(stream: S, f: F) -> Self {
Inspect { Inspect {
stream, stream,
@ -32,11 +34,12 @@ where
{ {
type Item = S::Item; type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let mut this = self.project();
let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
Poll::Ready(next.and_then(|x| { Poll::Ready(next.and_then(|x| {
(self.as_mut().f())(&x); (this.f)(&x);
Some(x) Some(x)
})) }))
} }

@ -0,0 +1,45 @@
use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future;
use crate::stream::Stream;
use crate::task::{Context, Poll};
pin_project! {
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct LastFuture<S, T> {
#[pin]
stream: S,
last: Option<T>,
}
}
impl<S, T> LastFuture<S, T> {
pub(crate) fn new(stream: S) -> Self {
LastFuture { stream, last: None }
}
}
impl<S> Future for LastFuture<S, S::Item>
where
S: Stream + Unpin + Sized,
S::Item: Copy,
{
type Output = Option<S::Item>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let next = futures_core::ready!(this.stream.poll_next(cx));
match next {
Some(new) => {
cx.waker().wake_by_ref();
*this.last = Some(new);
Poll::Pending
}
None => Poll::Ready(*this.last),
}
}
}

@ -0,0 +1,50 @@
use std::cmp::Ordering;
use std::pin::Pin;
use pin_project_lite::pin_project;
use super::partial_cmp::PartialCmpFuture;
use crate::future::Future;
use crate::prelude::*;
use crate::stream::Stream;
use crate::task::{Context, Poll};
pin_project! {
/// Determines if the elements of this `Stream` are lexicographically
/// less or equal to those of another.
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct LeFuture<L: Stream, R: Stream> {
#[pin]
partial_cmp: PartialCmpFuture<L, R>,
}
}
impl<L: Stream, R: Stream> LeFuture<L, R>
where
L::Item: PartialOrd<R::Item>,
{
pub(super) fn new(l: L, r: R) -> Self {
LeFuture {
partial_cmp: l.partial_cmp(r),
}
}
}
impl<L: Stream, R: Stream> Future for LeFuture<L, R>
where
L: Stream + Sized,
R: Stream + Sized,
L::Item: PartialOrd<R::Item>,
{
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
match result {
Some(Ordering::Less) | Some(Ordering::Equal) => Poll::Ready(true),
_ => Poll::Ready(false),
}
}
}

@ -0,0 +1,50 @@
use std::cmp::Ordering;
use std::pin::Pin;
use pin_project_lite::pin_project;
use super::partial_cmp::PartialCmpFuture;
use crate::future::Future;
use crate::prelude::*;
use crate::stream::Stream;
use crate::task::{Context, Poll};
pin_project! {
// Determines if the elements of this `Stream` are lexicographically
// less than those of another.
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct LtFuture<L: Stream, R: Stream> {
#[pin]
partial_cmp: PartialCmpFuture<L, R>,
}
}
impl<L: Stream, R: Stream> LtFuture<L, R>
where
L::Item: PartialOrd<R::Item>,
{
pub(super) fn new(l: L, r: R) -> Self {
LtFuture {
partial_cmp: l.partial_cmp(r),
}
}
}
impl<L: Stream, R: Stream> Future for LtFuture<L, R>
where
L: Stream + Sized,
R: Stream + Sized,
L::Item: PartialOrd<R::Item>,
{
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
match result {
Some(Ordering::Less) => Poll::Ready(true),
_ => Poll::Ready(false),
}
}
}

@ -1,22 +1,24 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
#[doc(hidden)] pin_project! {
#[allow(missing_debug_implementations)] #[doc(hidden)]
pub struct Map<S, F, T, B> { #[allow(missing_debug_implementations)]
stream: S, pub struct Map<S, F, T, B> {
f: F, #[pin]
__from: PhantomData<T>, stream: S,
__to: PhantomData<B>, f: F,
__from: PhantomData<T>,
__to: PhantomData<B>,
}
} }
impl<S, F, T, B> Map<S, F, T, B> { impl<S, F, T, B> Map<S, F, T, B> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(f: F);
pub(crate) fn new(stream: S, f: F) -> Self { pub(crate) fn new(stream: S, f: F) -> Self {
Map { Map {
stream, stream,
@ -34,8 +36,9 @@ where
{ {
type Item = B; type Item = B;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let this = self.project();
Poll::Ready(next.map(self.as_mut().f())) let next = futures_core::ready!(this.stream.poll_next(cx));
Poll::Ready(next.map(this.f))
} }
} }

@ -2,20 +2,25 @@ use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use futures_core::Stream; use futures_core::Stream;
use pin_project_lite::pin_project;
/// A stream that merges two other streams into a single stream. pin_project! {
/// /// A stream that merges two other streams into a single stream.
/// This stream is returned by [`Stream::merge`]. ///
/// /// This stream is returned by [`Stream::merge`].
/// [`Stream::merge`]: trait.Stream.html#method.merge ///
#[derive(Debug)] /// [`Stream::merge`]: trait.Stream.html#method.merge
pub struct Merge<L, R> { #[cfg(feature = "unstable")]
left: L, #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
right: R, #[derive(Debug)]
pub struct Merge<L, R> {
#[pin]
left: L,
#[pin]
right: R,
}
} }
impl<L, R> Unpin for Merge<L, R> {}
impl<L, R> Merge<L, R> { impl<L, R> Merge<L, R> {
pub(crate) fn new(left: L, right: R) -> Self { pub(crate) fn new(left: L, right: R) -> Self {
Self { left, right } Self { left, right }
@ -24,19 +29,20 @@ impl<L, R> Merge<L, R> {
impl<L, R, T> Stream for Merge<L, R> impl<L, R, T> Stream for Merge<L, R>
where where
L: Stream<Item = T> + Unpin, L: Stream<Item = T>,
R: Stream<Item = T> + Unpin, R: Stream<Item = T>,
{ {
type Item = T; type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if let Poll::Ready(Some(item)) = Pin::new(&mut self.left).poll_next(cx) { let this = self.project();
if let Poll::Ready(Some(item)) = this.left.poll_next(cx) {
// The first stream made progress. The Merge needs to be polled // The first stream made progress. The Merge needs to be polled
// again to check the progress of the second stream. // again to check the progress of the second stream.
cx.waker().wake_by_ref(); cx.waker().wake_by_ref();
Poll::Ready(Some(item)) Poll::Ready(Some(item))
} else { } else {
Pin::new(&mut self.right).poll_next(cx) this.right.poll_next(cx)
} }
} }
} }

@ -1,23 +1,24 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
#[doc(hidden)] pin_project! {
#[allow(missing_debug_implementations)] #[doc(hidden)]
pub struct MinByFuture<S, F, T> { #[allow(missing_debug_implementations)]
stream: S, pub struct MinByFuture<S, F, T> {
compare: F, #[pin]
min: Option<T>, stream: S,
compare: F,
min: Option<T>,
}
} }
impl<S, F, T> MinByFuture<S, F, T> { impl<S, F, T> MinByFuture<S, F, T> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(compare: F);
pin_utils::unsafe_unpinned!(min: Option<T>);
pub(super) fn new(stream: S, compare: F) -> Self { pub(super) fn new(stream: S, compare: F) -> Self {
MinByFuture { MinByFuture {
stream, stream,
@ -35,22 +36,23 @@ where
{ {
type Output = Option<S::Item>; type Output = Option<S::Item>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let this = self.project();
let next = futures_core::ready!(this.stream.poll_next(cx));
match next { match next {
Some(new) => { Some(new) => {
cx.waker().wake_by_ref(); cx.waker().wake_by_ref();
match self.as_mut().min().take() { match this.min.take() {
None => *self.as_mut().min() = Some(new), None => *this.min = Some(new),
Some(old) => match (&mut self.as_mut().compare())(&new, &old) { Some(old) => match (this.compare)(&new, &old) {
Ordering::Less => *self.as_mut().min() = Some(new), Ordering::Less => *this.min = Some(new),
_ => *self.as_mut().min() = Some(old), _ => *this.min = Some(old),
}, },
} }
Poll::Pending Poll::Pending
} }
None => Poll::Ready(self.min), None => Poll::Ready(*this.min),
} }
} }
} }

@ -24,6 +24,7 @@
mod all; mod all;
mod any; mod any;
mod chain; mod chain;
mod cmp;
mod enumerate; mod enumerate;
mod filter; mod filter;
mod filter_map; mod filter_map;
@ -32,7 +33,12 @@ mod find_map;
mod fold; mod fold;
mod for_each; mod for_each;
mod fuse; mod fuse;
mod ge;
mod gt;
mod inspect; mod inspect;
mod last;
mod le;
mod lt;
mod map; mod map;
mod min_by; mod min_by;
mod next; mod next;
@ -44,21 +50,29 @@ mod skip_while;
mod step_by; mod step_by;
mod take; mod take;
mod take_while; mod take_while;
mod try_fold;
mod try_for_each; mod try_for_each;
mod zip; mod zip;
use all::AllFuture; use all::AllFuture;
use any::AnyFuture; use any::AnyFuture;
use cmp::CmpFuture;
use enumerate::Enumerate; use enumerate::Enumerate;
use filter_map::FilterMap; use filter_map::FilterMap;
use find::FindFuture; use find::FindFuture;
use find_map::FindMapFuture; use find_map::FindMapFuture;
use fold::FoldFuture; use fold::FoldFuture;
use for_each::ForEachFuture; use for_each::ForEachFuture;
use ge::GeFuture;
use gt::GtFuture;
use last::LastFuture;
use le::LeFuture;
use lt::LtFuture;
use min_by::MinByFuture; use min_by::MinByFuture;
use next::NextFuture; use next::NextFuture;
use nth::NthFuture; use nth::NthFuture;
use partial_cmp::PartialCmpFuture; use partial_cmp::PartialCmpFuture;
use try_fold::TryFoldFuture;
use try_for_each::TryForEeachFuture; use try_for_each::TryForEeachFuture;
pub use chain::Chain; pub use chain::Chain;
@ -77,32 +91,22 @@ pub use zip::Zip;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::marker::PhantomData; use std::marker::PhantomData;
use cfg_if::cfg_if; cfg_unstable! {
use std::pin::Pin;
use crate::utils::extension_trait; use crate::future::Future;
use crate::stream::FromStream;
cfg_if! { pub use merge::Merge;
if #[cfg(feature = "docs")] {
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll}; mod merge;
}
} }
cfg_if! { extension_trait! {
if #[cfg(any(feature = "unstable", feature = "docs"))] { use std::ops::{Deref, DerefMut};
mod merge;
use std::pin::Pin;
use crate::future::Future; use crate::task::{Context, Poll};
use crate::stream::FromStream;
pub use merge::Merge;
}
}
extension_trait! {
#[doc = r#" #[doc = r#"
An asynchronous stream of values. An asynchronous stream of values.
@ -122,7 +126,7 @@ extension_trait! {
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html
[provided methods]: #provided-methods [provided methods]: #provided-methods
"#] "#]
pub trait Stream [StreamExt: futures_core::stream::Stream] { pub trait Stream {
#[doc = r#" #[doc = r#"
The type of items yielded by this stream. The type of items yielded by this stream.
"#] "#]
@ -180,7 +184,9 @@ extension_trait! {
``` ```
"#] "#]
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
}
pub trait StreamExt: futures_core::stream::Stream {
#[doc = r#" #[doc = r#"
Advances the stream and returns the next value. Advances the stream and returns the next value.
@ -442,6 +448,54 @@ extension_trait! {
Inspect::new(self, f) Inspect::new(self, f)
} }
#[doc = r#"
Returns the last element of the stream.
# Examples
Basic usage:
```
# fn main() { async_std::task::block_on(async {
#
use std::collections::VecDeque;
use async_std::prelude::*;
let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
let last = s.last().await;
assert_eq!(last, Some(3));
#
# }) }
```
An empty stream will return `None:
```
# fn main() { async_std::task::block_on(async {
#
use std::collections::VecDeque;
use async_std::prelude::*;
let s: VecDeque<usize> = vec![].into_iter().collect();
let last = s.last().await;
assert_eq!(last, None);
#
# }) }
```
"#]
fn last(
self,
) -> impl Future<Output = Option<Self::Item>> [LastFuture<Self, Self::Item>]
where
Self: Sized,
{
LastFuture::new(self)
}
#[doc = r#" #[doc = r#"
Transforms this `Stream` into a "fused" `Stream` such that after the first time Transforms this `Stream` into a "fused" `Stream` such that after the first time
`poll` returns `Poll::Ready(None)`, all future calls to `poll` will also return `poll` returns `Poll::Ready(None)`, all future calls to `poll` will also return
@ -1030,6 +1084,46 @@ extension_trait! {
Skip::new(self, n) Skip::new(self, n)
} }
#[doc = r#"
A combinator that applies a function as long as it returns successfully, producing a single, final value.
Immediately returns the error when the function returns unsuccessfully.
# Examples
Basic usage:
```
# fn main() { async_std::task::block_on(async {
#
use async_std::prelude::*;
use std::collections::VecDeque;
let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
let sum = s.try_fold(0, |acc, v| {
if (acc+v) % 2 == 1 {
Ok(v+3)
} else {
Err("fail")
}
}).await;
assert_eq!(sum, Err("fail"));
#
# }) }
```
"#]
fn try_fold<B, F, T, E>(
self,
init: T,
f: F,
) -> impl Future<Output = Result<T, E>> [TryFoldFuture<Self, F, T>]
where
Self: Sized,
F: FnMut(B, Self::Item) -> Result<T, E>,
{
TryFoldFuture::new(self, init, f)
}
#[doc = r#" #[doc = r#"
Applies a falliable function to each element in a stream, stopping at first error and returning it. Applies a falliable function to each element in a stream, stopping at first error and returning it.
@ -1172,7 +1266,7 @@ extension_trait! {
[`stream`]: trait.Stream.html#tymethod.next [`stream`]: trait.Stream.html#tymethod.next
"#] "#]
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"]
fn collect<'a, B>( fn collect<'a, B>(
@ -1211,7 +1305,7 @@ extension_trait! {
# }); # });
``` ```
"#] "#]
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn merge<U>(self, other: U) -> Merge<Self, U> fn merge<U>(self, other: U) -> Merge<Self, U>
where where
@ -1224,23 +1318,26 @@ extension_trait! {
#[doc = r#" #[doc = r#"
Lexicographically compares the elements of this `Stream` with those Lexicographically compares the elements of this `Stream` with those
of another. of another.
# Examples # Examples
``` ```
# fn main() { async_std::task::block_on(async { # fn main() { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::cmp::Ordering; use std::cmp::Ordering;
let s1 = VecDeque::from(vec![1]); let s1 = VecDeque::from(vec![1]);
let s2 = VecDeque::from(vec![1, 2]); let s2 = VecDeque::from(vec![1, 2]);
let s3 = VecDeque::from(vec![1, 2, 3]); let s3 = VecDeque::from(vec![1, 2, 3]);
let s4 = VecDeque::from(vec![1, 2, 4]); let s4 = VecDeque::from(vec![1, 2, 4]);
assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal)); assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal));
assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less)); assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less));
assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater));
assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less)); assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less));
assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater));
# #
# }) } # }) }
``` ```
@ -1256,6 +1353,189 @@ extension_trait! {
{ {
PartialCmpFuture::new(self, other) PartialCmpFuture::new(self, other)
} }
#[doc = r#"
Lexicographically compares the elements of this `Stream` with those
of another using 'Ord'.
# Examples
```
# fn main() { async_std::task::block_on(async {
#
use async_std::prelude::*;
use std::collections::VecDeque;
use std::cmp::Ordering;
let s1 = VecDeque::from(vec![1]);
let s2 = VecDeque::from(vec![1, 2]);
let s3 = VecDeque::from(vec![1, 2, 3]);
let s4 = VecDeque::from(vec![1, 2, 4]);
assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal);
assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less);
assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater);
assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less);
assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater);
#
# }) }
```
"#]
fn cmp<S>(
self,
other: S
) -> impl Future<Output = Ordering> [CmpFuture<Self, S>]
where
Self: Sized + Stream,
S: Stream,
<Self as Stream>::Item: Ord
{
CmpFuture::new(self, other)
}
#[doc = r#"
Determines if the elements of this `Stream` are lexicographically
greater than or equal to those of another.
# Examples
```
# fn main() { async_std::task::block_on(async {
#
use async_std::prelude::*;
use std::collections::VecDeque;
let single: VecDeque<isize> = vec![1].into_iter().collect();
let single_gt: VecDeque<isize> = vec![10].into_iter().collect();
let multi: VecDeque<isize> = vec![1,2].into_iter().collect();
let multi_gt: VecDeque<isize> = vec![1,5].into_iter().collect();
assert_eq!(single.clone().ge(single.clone()).await, true);
assert_eq!(single_gt.clone().ge(single.clone()).await, true);
assert_eq!(multi.clone().ge(single_gt.clone()).await, false);
assert_eq!(multi_gt.clone().ge(multi.clone()).await, true);
#
# }) }
```
"#]
fn ge<S>(
self,
other: S
) -> impl Future<Output = bool> [GeFuture<Self, S>]
where
Self: Sized + Stream,
S: Stream,
<Self as Stream>::Item: PartialOrd<S::Item>,
{
GeFuture::new(self, other)
}
#[doc = r#"
Determines if the elements of this `Stream` are lexicographically
greater than those of another.
# Examples
```
# fn main() { async_std::task::block_on(async {
#
use async_std::prelude::*;
use std::collections::VecDeque;
let single = VecDeque::from(vec![1]);
let single_gt = VecDeque::from(vec![10]);
let multi = VecDeque::from(vec![1,2]);
let multi_gt = VecDeque::from(vec![1,5]);
assert_eq!(single.clone().gt(single.clone()).await, false);
assert_eq!(single_gt.clone().gt(single.clone()).await, true);
assert_eq!(multi.clone().gt(single_gt.clone()).await, false);
assert_eq!(multi_gt.clone().gt(multi.clone()).await, true);
#
# }) }
```
"#]
fn gt<S>(
self,
other: S
) -> impl Future<Output = bool> [GtFuture<Self, S>]
where
Self: Sized + Stream,
S: Stream,
<Self as Stream>::Item: PartialOrd<S::Item>,
{
GtFuture::new(self, other)
}
#[doc = r#"
Determines if the elements of this `Stream` are lexicographically
less or equal to those of another.
# Examples
```
# fn main() { async_std::task::block_on(async {
#
use async_std::prelude::*;
use std::collections::VecDeque;
let single = VecDeque::from(vec![1]);
let single_gt = VecDeque::from(vec![10]);
let multi = VecDeque::from(vec![1,2]);
let multi_gt = VecDeque::from(vec![1,5]);
assert_eq!(single.clone().le(single.clone()).await, true);
assert_eq!(single.clone().le(single_gt.clone()).await, true);
assert_eq!(multi.clone().le(single_gt.clone()).await, true);
assert_eq!(multi_gt.clone().le(multi.clone()).await, false);
#
# }) }
```
"#]
fn le<S>(
self,
other: S
) -> impl Future<Output = bool> [LeFuture<Self, S>]
where
Self: Sized + Stream,
S: Stream,
<Self as Stream>::Item: PartialOrd<S::Item>,
{
LeFuture::new(self, other)
}
#[doc = r#"
Determines if the elements of this `Stream` are lexicographically
less than those of another.
# Examples
```
# fn main() { async_std::task::block_on(async {
#
use async_std::prelude::*;
use std::collections::VecDeque;
let single = VecDeque::from(vec![1]);
let single_gt = VecDeque::from(vec![10]);
let multi = VecDeque::from(vec![1,2]);
let multi_gt = VecDeque::from(vec![1,5]);
assert_eq!(single.clone().lt(single.clone()).await, false);
assert_eq!(single.clone().lt(single_gt.clone()).await, true);
assert_eq!(multi.clone().lt(single_gt.clone()).await, true);
assert_eq!(multi_gt.clone().lt(multi.clone()).await, false);
#
# }) }
```
"#]
fn lt<S>(
self,
other: S
) -> impl Future<Output = bool> [LtFuture<Self, S>]
where
Self: Sized + Stream,
S: Stream,
<Self as Stream>::Item: PartialOrd<S::Item>,
{
LtFuture::new(self, other)
}
} }
impl<S: Stream + Unpin + ?Sized> Stream for Box<S> { impl<S: Stream + Unpin + ?Sized> Stream for Box<S> {

@ -1,29 +1,30 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use super::fuse::Fuse; use super::fuse::Fuse;
use crate::future::Future; use crate::future::Future;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
// Lexicographically compares the elements of this `Stream` with those pin_project! {
// of another. // Lexicographically compares the elements of this `Stream` with those
#[doc(hidden)] // of another.
#[allow(missing_debug_implementations)] #[doc(hidden)]
pub struct PartialCmpFuture<L: Stream, R: Stream> { #[allow(missing_debug_implementations)]
l: Fuse<L>, pub struct PartialCmpFuture<L: Stream, R: Stream> {
r: Fuse<R>, #[pin]
l_cache: Option<L::Item>, l: Fuse<L>,
r_cache: Option<R::Item>, #[pin]
r: Fuse<R>,
l_cache: Option<L::Item>,
r_cache: Option<R::Item>,
}
} }
impl<L: Stream, R: Stream> PartialCmpFuture<L, R> { impl<L: Stream, R: Stream> PartialCmpFuture<L, R> {
pin_utils::unsafe_pinned!(l: Fuse<L>);
pin_utils::unsafe_pinned!(r: Fuse<R>);
pin_utils::unsafe_unpinned!(l_cache: Option<L::Item>);
pin_utils::unsafe_unpinned!(r_cache: Option<R::Item>);
pub(super) fn new(l: L, r: R) -> Self { pub(super) fn new(l: L, r: R) -> Self {
PartialCmpFuture { PartialCmpFuture {
l: l.fuse(), l: l.fuse(),
@ -42,12 +43,13 @@ where
{ {
type Output = Option<Ordering>; type Output = Option<Ordering>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
loop { loop {
// Short circuit logic // Short circuit logic
// Stream that completes earliest can be considered Less, etc // Stream that completes earliest can be considered Less, etc
let l_complete = self.l.done && self.as_mut().l_cache.is_none(); let l_complete = this.l.done && this.l_cache.is_none();
let r_complete = self.r.done && self.as_mut().r_cache.is_none(); let r_complete = this.r.done && this.r_cache.is_none();
if l_complete && r_complete { if l_complete && r_complete {
return Poll::Ready(Some(Ordering::Equal)); return Poll::Ready(Some(Ordering::Equal));
@ -58,30 +60,30 @@ where
} }
// Get next value if possible and necesary // Get next value if possible and necesary
if !self.l.done && self.as_mut().l_cache.is_none() { if !this.l.done && this.l_cache.is_none() {
let l_next = futures_core::ready!(self.as_mut().l().poll_next(cx)); let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx));
if let Some(item) = l_next { if let Some(item) = l_next {
*self.as_mut().l_cache() = Some(item); *this.l_cache = Some(item);
} }
} }
if !self.r.done && self.as_mut().r_cache.is_none() { if !this.r.done && this.r_cache.is_none() {
let r_next = futures_core::ready!(self.as_mut().r().poll_next(cx)); let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx));
if let Some(item) = r_next { if let Some(item) = r_next {
*self.as_mut().r_cache() = Some(item); *this.r_cache = Some(item);
} }
} }
// Compare if both values are available. // Compare if both values are available.
if self.as_mut().l_cache.is_some() && self.as_mut().r_cache.is_some() { if this.l_cache.is_some() && this.r_cache.is_some() {
let l_value = self.as_mut().l_cache().take().unwrap(); let l_value = this.l_cache.as_mut().take().unwrap();
let r_value = self.as_mut().r_cache().take().unwrap(); let r_value = this.r_cache.as_mut().take().unwrap();
let result = l_value.partial_cmp(&r_value); let result = l_value.partial_cmp(&r_value);
if let Some(Ordering::Equal) = result { if let Some(Ordering::Equal) = result {
// Reset cache to prepare for next comparison // Reset cache to prepare for next comparison
*self.as_mut().l_cache() = None; *this.l_cache = None;
*self.as_mut().r_cache() = None; *this.r_cache = None;
} else { } else {
// Return non equal value // Return non equal value
return Poll::Ready(result); return Poll::Ready(result);

@ -1,13 +1,18 @@
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream to maintain state while polling another stream. pin_project! {
#[derive(Debug)] /// A stream to maintain state while polling another stream.
pub struct Scan<S, St, F> { #[derive(Debug)]
stream: S, pub struct Scan<S, St, F> {
state_f: (St, F), #[pin]
stream: S,
state_f: (St, F),
}
} }
impl<S, St, F> Scan<S, St, F> { impl<S, St, F> Scan<S, St, F> {
@ -17,13 +22,8 @@ impl<S, St, F> Scan<S, St, F> {
state_f: (initial_state, f), state_f: (initial_state, f),
} }
} }
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(state_f: (St, F));
} }
impl<S: Unpin, St, F> Unpin for Scan<S, St, F> {}
impl<S, St, F, B> Stream for Scan<S, St, F> impl<S, St, F, B> Stream for Scan<S, St, F>
where where
S: Stream, S: Stream,
@ -31,11 +31,12 @@ where
{ {
type Item = B; type Item = B;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<B>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<B>> {
let poll_result = self.as_mut().stream().poll_next(cx); let mut this = self.project();
let poll_result = this.stream.as_mut().poll_next(cx);
poll_result.map(|item| { poll_result.map(|item| {
item.and_then(|item| { item.and_then(|item| {
let (state, f) = self.as_mut().state_f(); let (state, f) = this.state_f;
f(state, item) f(state, item)
}) })
}) })

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save