forked from mirror/async-std
Merge branch 'master' into stream-delay
This commit is contained in:
commit
3b055f364e
255 changed files with 14624 additions and 4042 deletions
67
.github/workflows/ci.yml
vendored
67
.github/workflows/ci.yml
vendored
|
@ -4,9 +4,13 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
|
- master
|
||||||
- staging
|
- staging
|
||||||
- trying
|
- trying
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: -Dwarnings
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_test:
|
build_and_test:
|
||||||
name: Build and test
|
name: Build and test
|
||||||
|
@ -14,7 +18,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||||
rust: [nightly]
|
rust: [nightly, beta, stable]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
@ -29,19 +33,37 @@ 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 --tests
|
||||||
|
|
||||||
- name: check unstable
|
- name: check unstable
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
args: --features unstable --all --benches --bins --examples --tests
|
args: --features unstable --all --bins --examples --tests
|
||||||
|
- name: check bench
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
if: matrix.rust == 'nightly'
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --benches
|
||||||
|
|
||||||
|
- name: check std only
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --no-default-features --features std
|
||||||
|
|
||||||
|
- name: check attributes
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --features attributes
|
||||||
|
|
||||||
- name: tests
|
- name: tests
|
||||||
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 attributes
|
||||||
|
|
||||||
check_fmt_and_docs:
|
check_fmt_and_docs:
|
||||||
name: Checking fmt and docs
|
name: Checking fmt and docs
|
||||||
|
@ -49,15 +71,12 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
- id: component
|
|
||||||
uses: actions-rs/components-nightly@v1
|
|
||||||
with:
|
|
||||||
component: rustfmt
|
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ steps.component.outputs.toolchain }}
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
override: true
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
- name: setup
|
- name: setup
|
||||||
run: |
|
run: |
|
||||||
|
@ -74,20 +93,14 @@ jobs:
|
||||||
- name: Docs
|
- name: Docs
|
||||||
run: cargo doc --features docs
|
run: cargo doc --features docs
|
||||||
|
|
||||||
clippy_check:
|
# clippy_check:
|
||||||
name: Clippy check
|
# name: Clippy check
|
||||||
runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
steps:
|
# steps:
|
||||||
- uses: actions/checkout@v1
|
# - uses: actions/checkout@v1
|
||||||
- id: component
|
# - name: Install rust
|
||||||
uses: actions-rs/components-nightly@v1
|
# run: rustup update beta && rustup default beta
|
||||||
with:
|
# - name: Install clippy
|
||||||
component: clippy
|
# run: rustup component add clippy
|
||||||
- uses: actions-rs/toolchain@v1
|
# - name: clippy
|
||||||
with:
|
# run: cargo clippy --all --features unstable
|
||||||
toolchain: ${{ steps.component.outputs.toolchain }}
|
|
||||||
override: true
|
|
||||||
- run: rustup component add clippy
|
|
||||||
- uses: actions-rs/clippy-check@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
68
.travis.yml
68
.travis.yml
|
@ -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
|
|
335
CHANGELOG.md
335
CHANGELOG.md
|
@ -7,6 +7,333 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
# [1.1.0] - 2019-11-21
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.1.0/async-std)
|
||||||
|
|
||||||
|
This patch introduces a faster scheduler algorithm, `Stream::throttle`, and
|
||||||
|
stabilizes `task::yield_now`. Additionally we're introducing several more stream
|
||||||
|
APIs, bringing us to almost complete parity with the standard library.
|
||||||
|
|
||||||
|
Furthermore our `path` submodule now returns more context in errors. So if
|
||||||
|
opening a file fails, async-std will tell you *which* file was failed to open,
|
||||||
|
making it easier to write and debug programs.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
let mut s = stream::interval(Duration::from_millis(5))
|
||||||
|
.throttle(Duration::from_millis(10))
|
||||||
|
.take(2);
|
||||||
|
|
||||||
|
s.next().await;
|
||||||
|
assert!(start.elapsed().as_millis() >= 5);
|
||||||
|
|
||||||
|
s.next().await;
|
||||||
|
assert!(start.elapsed().as_millis() >= 15);
|
||||||
|
|
||||||
|
s.next().await;
|
||||||
|
assert!(start.elapsed().as_millis() >= 25);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Stream::throttle` as "unstable".
|
||||||
|
- Added `Stream::count` as "unstable".
|
||||||
|
- Added `Stream::max` as "unstable".
|
||||||
|
- Added `Stream::successors` as "unstable".
|
||||||
|
- Added `Stream::by_ref` as "unstable".
|
||||||
|
- Added `Stream::partition` as "unstable".
|
||||||
|
- Added contextual errors to the `path` submodule.
|
||||||
|
- Added `os::windows::symlink_dir` as "unstable".
|
||||||
|
- Added `os::windows::symlink_file` as "unstable".
|
||||||
|
- Stabilized `task::yield_now`.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- We now ignore seek errors when rolling back failed `read` calls on `File`.
|
||||||
|
- Fixed a bug where `Stream::max_by_key` was returning the wrong result.
|
||||||
|
- Fixed a bug where `Stream::min_by_key` was returning the wrong result.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Applied various fixes to the tutorial.
|
||||||
|
- Fixed an issue with Clippy.
|
||||||
|
- Optimized an internal code generation macro, improving compilation speeds.
|
||||||
|
- Removed an `Unpin` bound from `stream::Once`.
|
||||||
|
- Removed various extra internal uses of `pin_mut!`.
|
||||||
|
- Simplified `Stream::any` and `Stream::all`'s internals.
|
||||||
|
- The `surf` example is now enabled again.
|
||||||
|
- Tweaked some streams internals.
|
||||||
|
- Updated `futures-timer` to 2.0.0, improving compilation speed.
|
||||||
|
- Upgraded `async-macros` to 2.0.0.
|
||||||
|
- `Stream::merge` now uses randomized ordering to reduce overall latency.
|
||||||
|
- The scheduler is now more efficient by keeping a slot for the next task to
|
||||||
|
run. This is similar to Go's scheduler, and Tokio's scheduler.
|
||||||
|
- Fixed the documentation of the `channel` types to link back to the `channel`
|
||||||
|
function.
|
||||||
|
|
||||||
|
# [1.0.1] - 2019-11-12
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.0.1/async-std)
|
||||||
|
|
||||||
|
We were seeing a regression in our fs performance, caused by too many
|
||||||
|
long-running tasks. This patch fixes that regression by being more proactive
|
||||||
|
about closing down idle threads.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Improved thread startup/shutdown algorithm in `task::spawn_blocking`.
|
||||||
|
- Fixed a typo in the tutorial.
|
||||||
|
|
||||||
|
# [1.0.0] - 2019-11-11
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/1.0.0/async-std)
|
||||||
|
|
||||||
|
This release marks the `1.0.0` release of async-std; a major milestone for our
|
||||||
|
development. This release itself mostly includes quality of life improvements
|
||||||
|
for all of modules, including more consistent API bounds for a lot of our
|
||||||
|
submodules.
|
||||||
|
|
||||||
|
The biggest change is that we're now using the full semver range,
|
||||||
|
`major.minor.patch`, and any breaking changes to our "stable" APIs will require
|
||||||
|
an update of the `major` number.
|
||||||
|
|
||||||
|
We're excited we've hit this milestone together with you all. Thank you!
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Future::join` as "unstable", replacing `future::join!`.
|
||||||
|
- Added `Future::try_join` as "unstable", replacing `future::try_join!`.
|
||||||
|
- Enabled `stable` and `beta` channel testing on CI.
|
||||||
|
- Implemented `FromIterator` and `Extend` for `PathBuf`.
|
||||||
|
- Implemented `FromStream` for `PathBuf`.
|
||||||
|
- Loosened the trait bounds of `io::copy` on "unstable".
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Added a `Sync` bound to `RwLock`, resolving a memory safety issue.
|
||||||
|
- Fixed a bug in `Stream::take_while` where it could continue after it should've
|
||||||
|
ended.
|
||||||
|
- Fixed a bug where our `attributes` Cargo feature wasn't working as intended.
|
||||||
|
- Improved documentation of `Stream::merge`, documenting ordering guarantees.
|
||||||
|
- Update doc imports in examples to prefer async-std's types.
|
||||||
|
- Various quality of life improvements to the `future` submodule.
|
||||||
|
- Various quality of life improvements to the `path` submodule.
|
||||||
|
- Various quality of life improvements to the `stream` submodule.
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- Removed `future::join!` in favor of `Future::join`.
|
||||||
|
- Removed `future::try_join!` in favor of `Future::try_join`.
|
||||||
|
|
||||||
|
# [0.99.12] - 2019-11-07
|
||||||
|
|
||||||
|
[API Documentation](https://docs.rs/async-std/0.99.12/async-std)
|
||||||
|
|
||||||
|
This patch upgrades us to `futures` 0.3, support for `async/await` on Rust
|
||||||
|
Stable, performance improvements, and brand new module-level documentation.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `Future::flatten` as "unstable".
|
||||||
|
- Added `Future::race` as "unstable" (replaces `future::select!`).
|
||||||
|
- Added `Future::try_race` as "unstable" (replaces `future::try_select!`).
|
||||||
|
- Added `Stderr::lock` as "unstable".
|
||||||
|
- Added `Stdin::lock` as "unstable".
|
||||||
|
- Added `Stdout::lock` as "unstable".
|
||||||
|
- Added `Stream::copied` as "unstable".
|
||||||
|
- Added `Stream::eq` as "unstable".
|
||||||
|
- Added `Stream::max_by_key` as "unstable".
|
||||||
|
- Added `Stream::min` as "unstable".
|
||||||
|
- Added `Stream::ne` as "unstable".
|
||||||
|
- Added `Stream::position` as "unstable".
|
||||||
|
- Added `StreamExt` and `FutureExt` as enumerable in the `prelude`.
|
||||||
|
- Added `TcpListener` and `TcpStream` integration tests.
|
||||||
|
- Added `stream::from_iter`.
|
||||||
|
- Added `sync::WakerSet` for internal use.
|
||||||
|
- Added an example to handle both `IP v4` and `IP v6` connections.
|
||||||
|
- Added the `default` Cargo feature.
|
||||||
|
- Added the `attributes` Cargo feature.
|
||||||
|
- Added the `std` Cargo feature.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Fixed a bug in the blocking threadpool where it didn't spawn more than one thread.
|
||||||
|
- Fixed a bug with `Stream::merge` where sometimes it ended too soon.
|
||||||
|
- Fixed a bug with our GitHub actions setup.
|
||||||
|
- Fixed an issue where our channels could spuriously deadlock.
|
||||||
|
- Refactored the `task` module.
|
||||||
|
- Removed a deprecated GitHub action.
|
||||||
|
- Replaced `futures-preview` with `futures`.
|
||||||
|
- Replaced `lazy_static` with `once_cell`.
|
||||||
|
- Replaced all uses of `VecDequeue` in the examples with `stream::from_iter`.
|
||||||
|
- Simplified `sync::RwLock` using the internal `sync::WakerSet` type.
|
||||||
|
- Updated the `path` submodule documentation to match std.
|
||||||
|
- Updated the mod-level documentation to match std.
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- Removed `future::select!` (replaced by `Future::race`).
|
||||||
|
- Removed `future::try_select!` (replaced by `Future::try_race`).
|
||||||
|
|
||||||
|
# [0.99.11] - 2019-10-29
|
||||||
|
|
||||||
|
This patch introduces `async_std::sync::channel`, a novel asynchronous port of
|
||||||
|
the ultra-fast Crossbeam channels. This has been one of the most anticipated
|
||||||
|
features for async-std, and we're excited to be providing a first version of
|
||||||
|
this!
|
||||||
|
|
||||||
|
In addition to channels, this patch has the regular list of new methods, types,
|
||||||
|
and doc fixes.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
__Send and receive items from a channel__
|
||||||
|
```rust
|
||||||
|
// Create a bounded channel with a max-size of 1
|
||||||
|
let (s, r) = channel(1);
|
||||||
|
|
||||||
|
// This call returns immediately because there is enough space in the channel.
|
||||||
|
s.send(1).await;
|
||||||
|
|
||||||
|
task::spawn(async move {
|
||||||
|
// This call blocks the current task because the channel is full.
|
||||||
|
// It will be able to complete only after the first message is received.
|
||||||
|
s.send(2).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Receive items from the channel
|
||||||
|
task::sleep(Duration::from_secs(1)).await;
|
||||||
|
assert_eq!(r.recv().await, Some(1));
|
||||||
|
assert_eq!(r.recv().await, Some(2));
|
||||||
|
```
|
||||||
|
|
||||||
|
## Added
|
||||||
|
- Added `Future::delay` as "unstable"
|
||||||
|
- Added `Stream::flat_map` as "unstable"
|
||||||
|
- Added `Stream::flatten` as "unstable"
|
||||||
|
- Added `Stream::product` as "unstable"
|
||||||
|
- Added `Stream::sum` as "unstable"
|
||||||
|
- Added `Stream::min_by_key`
|
||||||
|
- Added `Stream::max_by`
|
||||||
|
- Added `Stream::timeout` as "unstable"
|
||||||
|
- Added `sync::channel` as "unstable".
|
||||||
|
- Added doc links from instantiated structs to the methods that create them.
|
||||||
|
- Implemented `Extend` + `FromStream` for `PathBuf`.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- Fixed an issue with `block_on` so it works even when nested.
|
||||||
|
- Fixed issues with our Clippy check on CI.
|
||||||
|
- Replaced our uses of `cfg_if` with our own macros, simplifying the codebase.
|
||||||
|
- Updated the homepage link in `Cargo.toml` to point to [async.rs](https://async.rs).
|
||||||
|
- Updated the module-level documentation for `stream` and `sync`.
|
||||||
|
- Various typos and grammar fixes.
|
||||||
|
- Removed redundant file flushes, improving the performance of `File` operations
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
Nothing was removed in this release.
|
||||||
|
|
||||||
|
# [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 +510,13 @@ 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/v1.1.0...HEAD
|
||||||
|
[1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0
|
||||||
|
[1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1
|
||||||
|
[1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0
|
||||||
|
[0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12
|
||||||
|
[0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11
|
||||||
|
[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
|
||||||
|
|
86
Cargo.toml
86
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "async-std"
|
name = "async-std"
|
||||||
version = "0.99.9"
|
version = "1.1.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Stjepan Glavina <stjepang@gmail.com>",
|
"Stjepan Glavina <stjepang@gmail.com>",
|
||||||
"Yoshua Wuyts <yoshuawuyts@gmail.com>",
|
"Yoshua Wuyts <yoshuawuyts@gmail.com>",
|
||||||
|
@ -9,7 +9,7 @@ authors = [
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0/MIT"
|
license = "Apache-2.0/MIT"
|
||||||
repository = "https://github.com/async-rs/async-std"
|
repository = "https://github.com/async-rs/async-std"
|
||||||
homepage = "https://github.com/async-rs/async-std"
|
homepage = "https://async.rs"
|
||||||
documentation = "https://docs.rs/async-std"
|
documentation = "https://docs.rs/async-std"
|
||||||
description = "Async version of the Rust standard library"
|
description = "Async version of the Rust standard library"
|
||||||
keywords = ["async", "await", "future", "std", "task"]
|
keywords = ["async", "await", "future", "std", "task"]
|
||||||
|
@ -21,35 +21,67 @@ features = ["docs"]
|
||||||
rustdoc-args = ["--cfg", "feature=\"docs\""]
|
rustdoc-args = ["--cfg", "feature=\"docs\""]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
docs = ["broadcaster"]
|
default = [
|
||||||
unstable = ["broadcaster"]
|
"std",
|
||||||
|
"async-task",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"futures-timer",
|
||||||
|
"kv-log-macro",
|
||||||
|
"log",
|
||||||
|
"mio",
|
||||||
|
"mio-uds",
|
||||||
|
"num_cpus",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
docs = ["attributes", "unstable"]
|
||||||
|
unstable = ["default", "broadcaster"]
|
||||||
|
attributes = ["async-attributes"]
|
||||||
|
std = [
|
||||||
|
"async-macros",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"memchr",
|
||||||
|
"once_cell",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-macros = "1.0.0"
|
async-attributes = { version = "1.1.1", optional = true }
|
||||||
async-task = "1.0.0"
|
async-macros = { version = "2.0.0", optional = true }
|
||||||
cfg-if = "0.1.9"
|
async-task = { version = "1.0.0", optional = true }
|
||||||
crossbeam-channel = "0.3.9"
|
|
||||||
crossbeam-deque = "0.7.1"
|
|
||||||
futures-core-preview = "=0.3.0-alpha.19"
|
|
||||||
futures-io-preview = "=0.3.0-alpha.19"
|
|
||||||
futures-timer = "0.4.0"
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
log = { version = "0.4.8", features = ["kv_unstable"] }
|
|
||||||
memchr = "2.2.1"
|
|
||||||
mio = "0.6.19"
|
|
||||||
mio-uds = "0.6.7"
|
|
||||||
num_cpus = "1.10.1"
|
|
||||||
pin-utils = "0.1.0-alpha.4"
|
|
||||||
slab = "0.4.2"
|
|
||||||
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"] }
|
||||||
|
crossbeam-channel = { version = "0.4.0", optional = true }
|
||||||
|
crossbeam-deque = { version = "0.7.2", optional = true }
|
||||||
|
crossbeam-utils = { version = "0.7.0", optional = true }
|
||||||
|
futures-core = { version = "0.3.1", optional = true }
|
||||||
|
futures-io = { version = "0.3.1", optional = true }
|
||||||
|
futures-timer = { version = "2.0.2", optional = true }
|
||||||
|
kv-log-macro = { version = "1.0.4", optional = true }
|
||||||
|
log = { version = "0.4.8", features = ["kv_unstable"], optional = true }
|
||||||
|
memchr = { version = "2.2.1", optional = true }
|
||||||
|
mio = { version = "0.6.19", optional = true }
|
||||||
|
mio-uds = { version = "0.6.7", optional = true }
|
||||||
|
num_cpus = { version = "1.11.1", optional = true }
|
||||||
|
once_cell = { version = "1.2.0", optional = true }
|
||||||
|
pin-project-lite = { version = "0.1.1", optional = true }
|
||||||
|
pin-utils = { version = "0.1.0-alpha.4", optional = true }
|
||||||
|
slab = { version = "0.4.2", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
femme = "1.2.0"
|
femme = "1.3.0"
|
||||||
# surf = "1.0.2"
|
rand = "0.7.2"
|
||||||
|
surf = "1.0.3"
|
||||||
tempdir = "0.3.7"
|
tempdir = "0.3.7"
|
||||||
futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] }
|
futures = "0.3.1"
|
||||||
|
|
||||||
# These are used by the book for examples
|
[[test]]
|
||||||
futures-channel-preview = "=0.3.0-alpha.19"
|
name = "stream"
|
||||||
futures-util-preview = "=0.3.0-alpha.19"
|
required-features = ["unstable"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "tcp-ipv4-and-6-echo"
|
||||||
|
required-features = ["unstable"]
|
||||||
|
|
229
README.md
229
README.md
|
@ -1,142 +1,139 @@
|
||||||
# Async version of the Rust standard library
|
<h1 align="center">async-std</h1>
|
||||||
|
<div align="center">
|
||||||
|
<strong>
|
||||||
|
Async version of the Rust standard library
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
[](https://travis-ci.com/async-rs/async-std)
|
<br />
|
||||||
[](https://github.com/async-rs/async-std)
|
|
||||||
[](https://crates.io/crates/async-std)
|
|
||||||
[](https://docs.rs/async-std)
|
|
||||||
[](https://discord.gg/JvZeVNe)
|
|
||||||
|
|
||||||
This crate provides an async version of [`std`]. It provides all the interfaces you
|
<div align="center">
|
||||||
are used to, but in an async version and ready for Rust's `async`/`await` syntax.
|
<!-- 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 time.
|
||||||
|
- __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`:
|
All examples require the [`"attributes"` feature] to be enabled. This feature
|
||||||
|
is not enabled by default because it significantly impacts compile times. See
|
||||||
|
[`task::block_on`] for an alternative way to start executing tasks.
|
||||||
|
|
||||||
```toml
|
```rust
|
||||||
[dependencies]
|
async fn say_hello() {
|
||||||
async-std = "0.99"
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
say_hello().await;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Or use [cargo add][cargo-add] if you have it installed:
|
More examples, including networking and file access, can be found in our
|
||||||
|
[`examples`] directory and in our [documentation].
|
||||||
|
|
||||||
|
[`examples`]: https://github.com/async-rs/async-std/tree/master/examples
|
||||||
|
[documentation]: https://docs.rs/async-std#examples
|
||||||
|
[`task::block_on`]: task/fn.block_on.html
|
||||||
|
[`"attributes"` feature]: https://docs.rs/async-std/#features
|
||||||
|
|
||||||
|
## Philosophy
|
||||||
|
|
||||||
|
We believe Async Rust should be as easy to pick up as Sync Rust. We also believe
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
With [cargo add][cargo-add] installed run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ cargo add async-std
|
$ cargo add async-std
|
||||||
```
|
```
|
||||||
|
|
||||||
|
We also provide a set of "unstable" features with async-std. See the [features
|
||||||
|
documentation] on how to enable them.
|
||||||
|
|
||||||
[cargo-add]: https://github.com/killercup/cargo-edit
|
[cargo-add]: https://github.com/killercup/cargo-edit
|
||||||
|
[features documentation]: https://docs.rs/async-std/#features
|
||||||
## Hello world
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use async_std::task;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
task::block_on(async {
|
|
||||||
println!("Hello, world!");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Low-Friction Sockets with Built-In Timeouts
|
|
||||||
|
|
||||||
```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
|
|
||||||
|
|
||||||
Clone the repo:
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone git@github.com:async-rs/async-std.git && cd async-std
|
|
||||||
```
|
|
||||||
|
|
||||||
Generate docs:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo +nightly doc --features docs --open
|
|
||||||
```
|
|
||||||
|
|
||||||
Check out the [examples](examples). To run an example:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo +nightly run --example hello-world
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
See [our contribution document][contribution].
|
|
||||||
|
|
||||||
[contribution]: https://async.rs/contribute
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
<sup>
|
||||||
|
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
||||||
|
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
|
||||||
|
</sup>
|
||||||
|
|
||||||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
<br/>
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
at your option.
|
|
||||||
|
|
||||||
#### Contribution
|
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
40
benches/mutex.rs
Normal file
40
benches/mutex.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use async_std::sync::{Arc, Mutex};
|
||||||
|
use async_std::task;
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn create(b: &mut Bencher) {
|
||||||
|
b.iter(|| Mutex::new(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn contention(b: &mut Bencher) {
|
||||||
|
b.iter(|| task::block_on(run(10, 1000)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn no_contention(b: &mut Bencher) {
|
||||||
|
b.iter(|| task::block_on(run(1, 10000)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(task: usize, iter: usize) {
|
||||||
|
let m = Arc::new(Mutex::new(()));
|
||||||
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
|
for _ in 0..task {
|
||||||
|
let m = m.clone();
|
||||||
|
tasks.push(task::spawn(async move {
|
||||||
|
for _ in 0..iter {
|
||||||
|
let _ = m.lock().await;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for t in tasks {
|
||||||
|
t.await;
|
||||||
|
}
|
||||||
|
}
|
11
benches/task.rs
Normal file
11
benches/task.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use async_std::task;
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn block_on(b: &mut Bencher) {
|
||||||
|
b.iter(|| task::block_on(async {}));
|
||||||
|
}
|
|
@ -24,11 +24,7 @@ To sum up: Rust gives us the ability to safely abstract over important propertie
|
||||||
|
|
||||||
## An easy view of computation
|
## An easy view of computation
|
||||||
|
|
||||||
While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us:
|
While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: A sequence of composable operations which can branch based on a decision, run to succession and yield a result or yield an error
|
||||||
|
|
||||||
- computation is a sequence of composable operations
|
|
||||||
- they can branch based on a decision
|
|
||||||
- they either run to succession and yield a result, or they can yield an error
|
|
||||||
|
|
||||||
## Deferring computation
|
## Deferring computation
|
||||||
|
|
||||||
|
@ -136,11 +132,11 @@ When executing 2 or more of these functions at the same time, our runtime system
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
Working from values, we searched for something that expresses *working towards a value available sometime later*. From there, we talked about the concept of polling.
|
Working from values, we searched for something that expresses *working towards a value available later*. From there, we talked about the concept of polling.
|
||||||
|
|
||||||
A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple.
|
A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple.
|
||||||
|
|
||||||
Next, we will introduce you to `tasks`, which we need to actually *run* Futures.
|
Next, we will introduce you to `tasks`, which we will use to actually *run* Futures.
|
||||||
|
|
||||||
[^1]: Two parties reading while it is guaranteed that no one is writing is always safe.
|
[^1]: Two parties reading while it is guaranteed that no one is writing is always safe.
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ Tasks in `async_std` are one of the core abstractions. Much like Rust's `thread`
|
||||||
|
|
||||||
## Blocking
|
## Blocking
|
||||||
|
|
||||||
`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and by itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this:
|
`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and of itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
|
|
||||||
`async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib. But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`.
|
`async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib. But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`.
|
||||||
|
|
||||||
[organization]: https://github.com/async-rs/async-std
|
[organization]: https://github.com/async-rs
|
||||||
|
|
|
@ -31,7 +31,7 @@ In general, this crate will be conservative with respect to the minimum supporte
|
||||||
|
|
||||||
## Security fixes
|
## Security fixes
|
||||||
|
|
||||||
Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give at least _3 month_ of ahead notice.
|
Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give a notice at least _3 months_ ahead.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,11 @@ Rust has two kinds of types commonly referred to as `Future`:
|
||||||
|
|
||||||
|
|
||||||
- the first is `std::future::Future` from Rust’s [standard library](https://doc.rust-lang.org/std/future/trait.Future.html).
|
- the first is `std::future::Future` from Rust’s [standard library](https://doc.rust-lang.org/std/future/trait.Future.html).
|
||||||
- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html), currently released as `futures-preview`.
|
- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html).
|
||||||
|
|
||||||
The future defined in the [futures-rs](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`.
|
The future defined in the [futures-rs](https://docs.rs/futures/0.3/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`.
|
||||||
|
|
||||||
It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features.
|
It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures/0.3/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features.
|
||||||
|
|
||||||
In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures-preview` crate by adding it to your `Cargo.toml` and importing `FuturesExt`.
|
In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures-preview` crate by adding it to your `Cargo.toml` and importing `FuturesExt`.
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,15 @@ At this point, we only need to start the broker to get a fully-functioning (in t
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
use async_std::{
|
use async_std::{
|
||||||
io::{self, BufReader},
|
io::BufReader,
|
||||||
net::{TcpListener, TcpStream, ToSocketAddrs},
|
net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
task,
|
task,
|
||||||
};
|
};
|
||||||
use futures_channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures_util::SinkExt;
|
use futures::sink::SinkExt;
|
||||||
use std::{
|
use std::{
|
||||||
collections::hash_map::{HashMap, Entry},
|
collections::hash_map::{HashMap, Entry},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
|
|
@ -22,16 +22,15 @@ Let's add waiting to the server:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# io::{self, BufReader},
|
# io::{self, BufReader},
|
||||||
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# task,
|
# task,
|
||||||
# };
|
# };
|
||||||
# use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
# use futures_util::SinkExt;
|
# use futures::sink::SinkExt;
|
||||||
# use std::{
|
# use std::{
|
||||||
# collections::hash_map::{HashMap, Entry},
|
# collections::hash_map::{HashMap, Entry},
|
||||||
# sync::Arc,
|
# sync::Arc,
|
||||||
|
@ -156,16 +155,15 @@ And to the broker:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# io::{self, BufReader},
|
# io::{self, BufReader},
|
||||||
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# task,
|
# task,
|
||||||
# };
|
# };
|
||||||
# use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
# use futures_util::SinkExt;
|
# use futures::sink::SinkExt;
|
||||||
# use std::{
|
# use std::{
|
||||||
# collections::hash_map::{HashMap, Entry},
|
# collections::hash_map::{HashMap, Entry},
|
||||||
# sync::Arc,
|
# sync::Arc,
|
||||||
|
|
|
@ -2,25 +2,24 @@
|
||||||
## Connecting Readers and Writers
|
## Connecting Readers and Writers
|
||||||
|
|
||||||
So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`?
|
So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`?
|
||||||
We should somehow maintain an `peers: HashMap<String, Sender<String>>` map which allows a client to find destination channels.
|
We should somehow maintain a `peers: HashMap<String, Sender<String>>` map which allows a client to find destination channels.
|
||||||
However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message.
|
However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message.
|
||||||
|
|
||||||
One trick to make reasoning about state simpler comes from the actor model.
|
One trick to make reasoning about state simpler comes from the actor model.
|
||||||
We can create a dedicated broker tasks which owns the `peers` map and communicates with other tasks by channels.
|
We can create a dedicated broker task which owns the `peers` map and communicates with other tasks using channels.
|
||||||
By hiding `peers` inside such an "actor" task, we remove the need for mutxes and also make serialization point explicit.
|
By hiding `peers` inside such an "actor" task, we remove the need for mutexes and also make the serialization point explicit.
|
||||||
The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue.
|
The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue.
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# net::TcpStream,
|
# net::TcpStream,
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# task,
|
# task,
|
||||||
# };
|
# };
|
||||||
# use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
# use futures_util::sink::SinkExt;
|
# use futures::sink::SinkExt;
|
||||||
# use std::sync::Arc;
|
# use std::sync::Arc;
|
||||||
#
|
#
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
@ -93,9 +92,9 @@ async fn broker_loop(mut events: Receiver<Event>) -> Result<()> {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Broker should handle two types of events: a message or an arrival of a new peer.
|
1. The broker task should handle two types of events: a message or an arrival of a new peer.
|
||||||
2. Internal state of the broker is a `HashMap`.
|
2. The internal state of the broker is a `HashMap`.
|
||||||
Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers
|
Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers
|
||||||
3. To handle a message, we send it over a channel to each destination
|
3. To handle a message, we send it over a channel to each destination
|
||||||
4. To handle new peer, we first register it in the peer's map ...
|
4. To handle a new peer, we first register it in the peer's map ...
|
||||||
5. ... and then spawn a dedicated task to actually write the messages to the socket.
|
5. ... and then spawn a dedicated task to actually write the messages to the socket.
|
||||||
|
|
|
@ -19,11 +19,10 @@ First, let's add a shutdown channel to the `connection_loop`:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::net::TcpStream;
|
# use async_std::net::TcpStream;
|
||||||
# use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
# use futures_util::SinkExt;
|
# use futures::sink::SinkExt;
|
||||||
# use std::sync::Arc;
|
# use std::sync::Arc;
|
||||||
#
|
#
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
@ -61,8 +60,8 @@ async fn connection_loop(mut broker: Sender<Event>, stream: Arc<TcpStream>) -> R
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
1. To enforce that no messages are send along the shutdown channel, we use an uninhabited type.
|
1. To enforce that no messages are sent along the shutdown channel, we use an uninhabited type.
|
||||||
2. We pass the shutdown channel to the writer task
|
2. We pass the shutdown channel to the writer task.
|
||||||
3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped.
|
3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped.
|
||||||
|
|
||||||
In the `connection_writer_loop`, we now need to choose between shutdown and message channels.
|
In the `connection_writer_loop`, we now need to choose between shutdown and message channels.
|
||||||
|
@ -70,17 +69,14 @@ We use the `select` macro for this purpose:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{net::TcpStream, prelude::*};
|
# use async_std::{net::TcpStream, prelude::*};
|
||||||
use futures_channel::mpsc;
|
# use futures::channel::mpsc;
|
||||||
use futures_util::{select, FutureExt};
|
use futures::{select, FutureExt};
|
||||||
# use std::sync::Arc;
|
# use std::sync::Arc;
|
||||||
|
|
||||||
# type Receiver<T> = mpsc::UnboundedReceiver<T>;
|
# type Receiver<T> = mpsc::UnboundedReceiver<T>;
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
# type Sender<T> = mpsc::UnboundedSender<T>;
|
# type Sender<T> = mpsc::UnboundedSender<T>;
|
||||||
|
|
||||||
# #[derive(Debug)]
|
# #[derive(Debug)]
|
||||||
# enum Void {} // 1
|
# enum Void {} // 1
|
||||||
|
|
||||||
|
@ -114,7 +110,7 @@ async fn connection_writer_loop(
|
||||||
|
|
||||||
Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel.
|
Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel.
|
||||||
To not lose these messages completely, we'll return the messages channel back to the broker.
|
To not lose these messages completely, we'll return the messages channel back to the broker.
|
||||||
This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infailable.
|
This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infallible.
|
||||||
|
|
||||||
## Final Code
|
## Final Code
|
||||||
|
|
||||||
|
@ -122,16 +118,16 @@ The final code looks like this:
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
use async_std::{
|
use async_std::{
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
net::{TcpListener, TcpStream, ToSocketAddrs},
|
net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
task,
|
task,
|
||||||
};
|
};
|
||||||
use futures_channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures_util::{select, FutureExt, SinkExt};
|
use futures::sink::SinkExt;
|
||||||
|
use futures::{select, FutureExt};
|
||||||
use std::{
|
use std::{
|
||||||
collections::hash_map::{Entry, HashMap},
|
collections::hash_map::{Entry, HashMap},
|
||||||
future::Future,
|
future::Future,
|
||||||
|
@ -161,7 +157,7 @@ async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
||||||
spawn_and_log_error(connection_loop(broker_sender.clone(), stream));
|
spawn_and_log_error(connection_loop(broker_sender.clone(), stream));
|
||||||
}
|
}
|
||||||
drop(broker_sender);
|
drop(broker_sender);
|
||||||
broker_handle.await;
|
broker_handle.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,14 +16,14 @@ With async, we can just use the `select!` macro.
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_util;
|
# extern crate futures;
|
||||||
use async_std::{
|
use async_std::{
|
||||||
io::{stdin, BufReader},
|
io::{stdin, BufReader},
|
||||||
net::{TcpStream, ToSocketAddrs},
|
net::{TcpStream, ToSocketAddrs},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
task,
|
task,
|
||||||
};
|
};
|
||||||
use futures_util::{select, FutureExt};
|
use futures::{select, FutureExt};
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
# Tutorial: Writing a chat
|
# Tutorial: Writing a chat
|
||||||
|
|
||||||
Nothing is as simple as a chat server, right? Not quite, chat servers
|
Nothing is simpler than creating a chat server, right?
|
||||||
already expose you to all the fun of asynchronous programming: how
|
Not quite, chat servers expose you to all the fun of asynchronous programming:
|
||||||
do you handle clients connecting concurrently. How do you handle them disconnecting?
|
|
||||||
|
|
||||||
How do you distribute the messages?
|
How will the server handle clients connecting concurrently?
|
||||||
|
|
||||||
In this tutorial, we will show you how to write one in `async-std`.
|
How will it handle them disconnecting?
|
||||||
|
|
||||||
|
How will it distribute the messages?
|
||||||
|
|
||||||
|
This tutorial explains how to write a chat server in `async-std`.
|
||||||
|
|
||||||
You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat).
|
You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat).
|
||||||
|
|
|
@ -10,14 +10,18 @@ We need to:
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# io::BufReader,
|
# net::{TcpListener, ToSocketAddrs},
|
||||||
# net::{TcpListener, TcpStream, ToSocketAddrs},
|
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# task,
|
# task,
|
||||||
# };
|
# };
|
||||||
#
|
#
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
#
|
#
|
||||||
|
use async_std::{
|
||||||
|
io::BufReader,
|
||||||
|
net::TcpStream,
|
||||||
|
};
|
||||||
|
|
||||||
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
|
||||||
let listener = TcpListener::bind(addr).await?;
|
let listener = TcpListener::bind(addr).await?;
|
||||||
let mut incoming = listener.incoming();
|
let mut incoming = listener.incoming();
|
||||||
|
@ -46,7 +50,7 @@ async fn connection_loop(stream: TcpStream) -> Result<()> {
|
||||||
Some(idx) => (&line[..idx], line[idx + 1 ..].trim()),
|
Some(idx) => (&line[..idx], line[idx + 1 ..].trim()),
|
||||||
};
|
};
|
||||||
let dest: Vec<String> = dest.split(',').map(|name| name.trim().to_string()).collect();
|
let dest: Vec<String> = dest.split(',').map(|name| name.trim().to_string()).collect();
|
||||||
let msg: String = msg.trim().to_string();
|
let msg: String = msg.to_string();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -130,7 +134,7 @@ So let's use a helper function for this:
|
||||||
# };
|
# };
|
||||||
fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
|
fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
|
||||||
where
|
where
|
||||||
F: Future<Output = io::Result<()>> + Send + 'static,
|
F: Future<Output = Result<()>> + Send + 'static,
|
||||||
{
|
{
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
if let Err(e) = fut.await {
|
if let Err(e) = fut.await {
|
||||||
|
|
|
@ -13,14 +13,13 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the
|
||||||
|
|
||||||
```rust,edition2018
|
```rust,edition2018
|
||||||
# extern crate async_std;
|
# extern crate async_std;
|
||||||
# extern crate futures_channel;
|
# extern crate futures;
|
||||||
# extern crate futures_util;
|
|
||||||
# use async_std::{
|
# use async_std::{
|
||||||
# net::TcpStream,
|
# net::TcpStream,
|
||||||
# prelude::*,
|
# prelude::*,
|
||||||
# };
|
# };
|
||||||
use futures_channel::mpsc; // 1
|
use futures::channel::mpsc; // 1
|
||||||
use futures_util::sink::SinkExt;
|
use futures::sink::SinkExt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
|
|
@ -12,7 +12,7 @@ After that, the client can send messages to other clients using the following sy
|
||||||
login1, login2, ... loginN: message
|
login1, login2, ... loginN: message
|
||||||
```
|
```
|
||||||
|
|
||||||
Each of the specified clients than receives a `from login: message` message.
|
Each of the specified clients then receives a `from login: message` message.
|
||||||
|
|
||||||
A possible session might look like this
|
A possible session might look like this
|
||||||
|
|
||||||
|
@ -38,18 +38,10 @@ $ cargo new a-chat
|
||||||
$ cd a-chat
|
$ cd a-chat
|
||||||
```
|
```
|
||||||
|
|
||||||
At the moment `async-std` requires Rust nightly, so let's add a rustup override for convenience:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ rustup override add nightly
|
|
||||||
$ rustc --version
|
|
||||||
rustc 1.38.0-nightly (c4715198b 2019-08-05)
|
|
||||||
```
|
|
||||||
|
|
||||||
Add the following lines to `Cargo.toml`:
|
Add the following lines to `Cargo.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures-preview = { version = "0.3.0-alpha.19", features = [ "async-await" ] }
|
futures = "0.3.0"
|
||||||
async-std = "0.99"
|
async-std = "1"
|
||||||
```
|
```
|
||||||
|
|
|
@ -8,6 +8,6 @@ fn main() -> Result<()> {
|
||||||
match (args.nth(1).as_ref().map(String::as_str), args.next()) {
|
match (args.nth(1).as_ref().map(String::as_str), args.next()) {
|
||||||
(Some("client"), None) => client::main(),
|
(Some("client"), None) => client::main(),
|
||||||
(Some("server"), None) => server::main(),
|
(Some("server"), None) => server::main(),
|
||||||
_ => Err("Usage: a-chat [client|server]")?,
|
_ => Err("Usage: a-chat [client|server]".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ async fn connection_loop(mut broker: Sender<Event>, stream: TcpStream) -> Result
|
||||||
let mut lines = reader.lines();
|
let mut lines = reader.lines();
|
||||||
|
|
||||||
let name = match lines.next().await {
|
let name = match lines.next().await {
|
||||||
None => Err("peer disconnected immediately")?,
|
None => return Err("peer disconnected immediately".into()),
|
||||||
Some(line) => line?,
|
Some(line) => line?,
|
||||||
};
|
};
|
||||||
let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::<Void>();
|
let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::<Void>();
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
/* TODO: Once the next version of surf released, re-enable this example.
|
|
||||||
//! Sends an HTTP request to the Rust website.
|
|
||||||
|
|
||||||
use async_std::task;
|
use async_std::task;
|
||||||
|
|
||||||
fn main() -> Result<(), surf::Exception> {
|
fn main() -> Result<(), surf::Exception> {
|
||||||
|
@ -18,6 +15,3 @@ fn main() -> Result<(), surf::Exception> {
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
44
examples/tcp-ipv4-and-6-echo.rs
Normal file
44
examples/tcp-ipv4-and-6-echo.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//! TCP echo server, accepting connections both on both ipv4 and ipv6 sockets.
|
||||||
|
//!
|
||||||
|
//! To send messages, do:
|
||||||
|
//!
|
||||||
|
//! ```sh
|
||||||
|
//! $ nc 127.0.0.1 8080
|
||||||
|
//! $ nc ::1 8080
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use async_std::io;
|
||||||
|
use async_std::net::{TcpListener, TcpStream};
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::task;
|
||||||
|
|
||||||
|
async fn process(stream: TcpStream) -> io::Result<()> {
|
||||||
|
println!("Accepted from: {}", stream.peer_addr()?);
|
||||||
|
|
||||||
|
let (reader, writer) = &mut (&stream, &stream);
|
||||||
|
io::copy(reader, writer).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> io::Result<()> {
|
||||||
|
task::block_on(async {
|
||||||
|
let ipv4_listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||||
|
println!("Listening on {}", ipv4_listener.local_addr()?);
|
||||||
|
let ipv6_listener = TcpListener::bind("[::1]:8080").await?;
|
||||||
|
println!("Listening on {}", ipv6_listener.local_addr()?);
|
||||||
|
|
||||||
|
let ipv4_incoming = ipv4_listener.incoming();
|
||||||
|
let ipv6_incoming = ipv6_listener.incoming();
|
||||||
|
|
||||||
|
let mut incoming = ipv4_incoming.merge(ipv6_incoming);
|
||||||
|
|
||||||
|
while let Some(stream) = incoming.next().await {
|
||||||
|
let stream = stream?;
|
||||||
|
task::spawn(async {
|
||||||
|
process(stream).await.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
version = "Two"
|
version = "Two"
|
||||||
|
format_code_in_doc_comments = true
|
||||||
|
|
|
@ -2,10 +2,10 @@ use std::collections::BinaryHeap;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T: Ord> Extend<T> for BinaryHeap<T> {
|
impl<T: Ord> stream::Extend<T> for BinaryHeap<T> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
use std::collections::BinaryHeap;
|
use std::collections::BinaryHeap;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T: Ord> FromStream<T> for BinaryHeap<T> {
|
impl<T: Ord> FromStream<T> for BinaryHeap<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
|
||||||
where
|
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
|
||||||
{
|
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = BinaryHeap::new();
|
let mut out = BinaryHeap::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ use std::collections::BTreeMap;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<K: Ord, V> Extend<(K, V)> for BTreeMap<K, V> {
|
impl<K: Ord, V> stream::Extend<(K, V)> for BTreeMap<K, V> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
fn extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<K: Ord, V> FromStream<(K, V)> for BTreeMap<K, V> {
|
impl<K: Ord, V> FromStream<(K, V)> for BTreeMap<K, V> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = (K, V)>>(
|
fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
|
||||||
where
|
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
|
||||||
{
|
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = BTreeMap::new();
|
let mut out = BTreeMap::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ use std::collections::BTreeSet;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T: Ord> Extend<T> for BTreeSet<T> {
|
impl<T: Ord> stream::Extend<T> for BTreeSet<T> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T: Ord> FromStream<T> for BTreeSet<T> {
|
impl<T: Ord> FromStream<T> for BTreeSet<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
|
||||||
where
|
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
|
||||||
{
|
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = BTreeSet::new();
|
let mut out = BTreeSet::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,14 @@ use std::hash::{BuildHasher, Hash};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<K, V, H> Extend<(K, V)> for HashMap<K, V, H>
|
impl<K, V, H> stream::Extend<(K, V)> for HashMap<K, V, H>
|
||||||
where
|
where
|
||||||
K: Eq + Hash,
|
K: Eq + Hash,
|
||||||
H: BuildHasher + Default,
|
H: BuildHasher + Default,
|
||||||
{
|
{
|
||||||
fn stream_extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
fn extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
||||||
|
|
|
@ -2,7 +2,8 @@ use std::collections::HashMap;
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<K, V, H> FromStream<(K, V)> for HashMap<K, V, H>
|
impl<K, V, H> FromStream<(K, V)> for HashMap<K, V, H>
|
||||||
where
|
where
|
||||||
|
@ -10,19 +11,14 @@ where
|
||||||
H: BuildHasher + Default,
|
H: BuildHasher + Default,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = (K, V)>>(
|
fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
|
||||||
where
|
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
|
||||||
{
|
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = HashMap::with_hasher(Default::default());
|
let mut out = HashMap::with_hasher(Default::default());
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,14 @@ use std::hash::{BuildHasher, Hash};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T, H> Extend<T> for HashSet<T, H>
|
impl<T, H> stream::Extend<T> for HashSet<T, H>
|
||||||
where
|
where
|
||||||
T: Eq + Hash,
|
T: Eq + Hash,
|
||||||
H: BuildHasher + Default,
|
H: BuildHasher + Default,
|
||||||
{
|
{
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
||||||
|
|
|
@ -2,7 +2,8 @@ use std::collections::HashSet;
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T, H> FromStream<T> for HashSet<T, H>
|
impl<T, H> FromStream<T> for HashSet<T, H>
|
||||||
where
|
where
|
||||||
|
@ -10,19 +11,14 @@ where
|
||||||
H: BuildHasher + Default,
|
H: BuildHasher + Default,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
|
||||||
where
|
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
|
||||||
{
|
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = HashSet::with_hasher(Default::default());
|
let mut out = HashSet::with_hasher(Default::default());
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ use std::collections::LinkedList;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T> Extend<T> for LinkedList<T> {
|
impl<T> stream::Extend<T> for LinkedList<T> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
use std::collections::LinkedList;
|
use std::collections::LinkedList;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T> FromStream<T> for LinkedList<T> {
|
impl<T> FromStream<T> for LinkedList<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
|
||||||
where
|
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
|
||||||
{
|
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = LinkedList::new();
|
let mut out = LinkedList::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ use std::collections::VecDeque;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::{Extend, IntoStream};
|
use crate::stream::{self, IntoStream};
|
||||||
|
|
||||||
impl<T> Extend<T> for VecDeque<T> {
|
impl<T> stream::Extend<T> for VecDeque<T> {
|
||||||
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
|
fn extend<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::stream::{Extend, FromStream, IntoStream};
|
use crate::prelude::*;
|
||||||
|
use crate::stream::{self, FromStream, IntoStream};
|
||||||
|
|
||||||
impl<T> FromStream<T> for VecDeque<T> {
|
impl<T> FromStream<T> for VecDeque<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_stream<'a, S: IntoStream<Item = T>>(
|
fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
|
||||||
stream: S,
|
stream: S,
|
||||||
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
|
||||||
where
|
|
||||||
<S as IntoStream>::IntoStream: 'a,
|
|
||||||
{
|
|
||||||
let stream = stream.into_stream();
|
let stream = stream.into_stream();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
pin_utils::pin_mut!(stream);
|
|
||||||
|
|
||||||
let mut out = VecDeque::new();
|
let mut out = VecDeque::new();
|
||||||
out.stream_extend(stream).await;
|
stream::extend(&mut out, stream).await;
|
||||||
out
|
out
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::{Path, PathBuf};
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Returns the canonical form of a path.
|
/// Returns the canonical form of a path.
|
||||||
///
|
///
|
||||||
|
@ -33,5 +33,10 @@ 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) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::canonicalize(&path)
|
||||||
|
.map(Into::into)
|
||||||
|
.context(|| format!("could not canonicalize `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Copies the contents and permissions of a file to a new location.
|
/// Copies the contents and permissions of a file to a new location.
|
||||||
///
|
///
|
||||||
|
@ -42,5 +42,9 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::copy(&from, &to)
|
||||||
|
.context(|| format!("could not copy `{}` to `{}`", from.display(), to.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Creates a new directory.
|
/// Creates a new directory.
|
||||||
///
|
///
|
||||||
|
@ -35,5 +35,9 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::create_dir(&path)
|
||||||
|
.context(|| format!("could not create directory `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Creates a new directory and all of its parents if they are missing.
|
/// Creates a new directory and all of its parents if they are missing.
|
||||||
///
|
///
|
||||||
|
@ -30,5 +30,9 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::create_dir_all(&path)
|
||||||
|
.context(|| format!("could not create directory path `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use std::path::Path;
|
use std::future::Future;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// A builder for creating directories with configurable options.
|
/// A builder for creating directories with configurable options.
|
||||||
///
|
///
|
||||||
|
@ -109,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 { spawn_blocking(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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::PathBuf;
|
|
||||||
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::task::blocking;
|
use crate::path::PathBuf;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// An entry in a directory.
|
/// An entry in a directory.
|
||||||
///
|
///
|
||||||
|
@ -50,7 +48,7 @@ impl DirEntry {
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn path(&self) -> PathBuf {
|
pub fn path(&self) -> PathBuf {
|
||||||
self.0.path()
|
self.0.path().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the metadata for this entry.
|
/// Reads the metadata for this entry.
|
||||||
|
@ -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
|
spawn_blocking(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
|
spawn_blocking(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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
167
src/fs/file.rs
167
src/fs/file.rs
|
@ -3,18 +3,17 @@ use std::cmp;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{Read as _, Seek as _, Write as _};
|
use std::io::{Read as _, Seek as _, Write as _};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::path::Path;
|
|
||||||
use std::pin::Pin;
|
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};
|
||||||
|
use crate::path::Path;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::task::{self, blocking, Context, Poll, Waker};
|
use crate::task::{self, spawn_blocking, Context, Poll, Waker};
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// An open file on the filesystem.
|
/// An open file on the filesystem.
|
||||||
///
|
///
|
||||||
|
@ -68,6 +67,23 @@ pub struct File {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
|
/// Creates an async file handle.
|
||||||
|
pub(crate) fn new(file: std::fs::File, is_flushed: bool) -> File {
|
||||||
|
let file = Arc::new(file);
|
||||||
|
|
||||||
|
File {
|
||||||
|
file: file.clone(),
|
||||||
|
lock: Lock::new(State {
|
||||||
|
file,
|
||||||
|
mode: Mode::Idle,
|
||||||
|
cache: Vec::new(),
|
||||||
|
is_flushed,
|
||||||
|
last_read_err: None,
|
||||||
|
last_write_err: None,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Opens a file in read-only mode.
|
/// Opens a file in read-only mode.
|
||||||
///
|
///
|
||||||
/// See the [`OpenOptions::open`] function for more options.
|
/// See the [`OpenOptions::open`] function for more options.
|
||||||
|
@ -97,8 +113,11 @@ 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 = spawn_blocking(move || {
|
||||||
Ok(file.into())
|
std::fs::File::open(&path).context(|| format!("could not open `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(File::new(file, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opens a file in write-only mode.
|
/// Opens a file in write-only mode.
|
||||||
|
@ -132,8 +151,12 @@ 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 = spawn_blocking(move || {
|
||||||
Ok(file.into())
|
std::fs::File::create(&path)
|
||||||
|
.context(|| format!("could not create `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(File::new(file, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronizes OS-internal buffered contents and metadata to disk.
|
/// Synchronizes OS-internal buffered contents and metadata to disk.
|
||||||
|
@ -165,7 +188,7 @@ impl File {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
blocking::spawn(async move { state.file.sync_all() }).await
|
spawn_blocking(move || state.file.sync_all()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronizes OS-internal buffered contents to disk.
|
/// Synchronizes OS-internal buffered contents to disk.
|
||||||
|
@ -201,7 +224,7 @@ impl File {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
blocking::spawn(async move { state.file.sync_data() }).await
|
spawn_blocking(move || state.file.sync_data()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Truncates or extends the file.
|
/// Truncates or extends the file.
|
||||||
|
@ -234,7 +257,7 @@ impl File {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
blocking::spawn(async move { state.file.set_len(size) }).await
|
spawn_blocking(move || state.file.set_len(size)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the file's metadata.
|
/// Reads the file's metadata.
|
||||||
|
@ -253,7 +276,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
|
spawn_blocking(move || file.metadata()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the permissions on the file.
|
/// Changes the permissions on the file.
|
||||||
|
@ -282,7 +305,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
|
spawn_blocking(move || file.set_permissions(perm)).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,83 +408,58 @@ impl Seek for &File {
|
||||||
|
|
||||||
impl From<std::fs::File> for File {
|
impl From<std::fs::File> for File {
|
||||||
fn from(file: std::fs::File) -> File {
|
fn from(file: std::fs::File) -> File {
|
||||||
let file = Arc::new(file);
|
File::new(file, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File {
|
cfg_unix! {
|
||||||
file: file.clone(),
|
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||||
lock: Lock::new(State {
|
|
||||||
file,
|
impl AsRawFd for File {
|
||||||
mode: Mode::Idle,
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
cache: Vec::new(),
|
self.file.as_raw_fd()
|
||||||
is_flushed: false,
|
}
|
||||||
last_read_err: None,
|
}
|
||||||
last_write_err: None,
|
|
||||||
}),
|
impl FromRawFd for File {
|
||||||
|
unsafe fn from_raw_fd(fd: RawFd) -> File {
|
||||||
|
std::fs::File::from_raw_fd(fd).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for File {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
let file = self.file.clone();
|
||||||
|
drop(self);
|
||||||
|
Arc::try_unwrap(file)
|
||||||
|
.expect("cannot acquire ownership of the file handle after drop")
|
||||||
|
.into_raw_fd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_windows! {
|
||||||
if #[cfg(feature = "docs")] {
|
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
|
||||||
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 AsRawHandle for File {
|
||||||
cfg_if! {
|
fn as_raw_handle(&self) -> RawHandle {
|
||||||
if #[cfg(any(unix, feature = "docs"))] {
|
self.file.as_raw_handle()
|
||||||
impl AsRawFd for File {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.file.as_raw_fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromRawFd for File {
|
|
||||||
unsafe fn from_raw_fd(fd: RawFd) -> File {
|
|
||||||
std::fs::File::from_raw_fd(fd).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoRawFd for File {
|
|
||||||
fn into_raw_fd(self) -> RawFd {
|
|
||||||
let file = self.file.clone();
|
|
||||||
drop(self);
|
|
||||||
Arc::try_unwrap(file)
|
|
||||||
.expect("cannot acquire ownership of the file handle after drop")
|
|
||||||
.into_raw_fd()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(windows)))]
|
impl FromRawHandle for File {
|
||||||
cfg_if! {
|
unsafe fn from_raw_handle(handle: RawHandle) -> File {
|
||||||
if #[cfg(any(windows, feature = "docs"))] {
|
std::fs::File::from_raw_handle(handle).into()
|
||||||
impl AsRawHandle for File {
|
|
||||||
fn as_raw_handle(&self) -> RawHandle {
|
|
||||||
self.file.as_raw_handle()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromRawHandle for File {
|
impl IntoRawHandle for File {
|
||||||
unsafe fn from_raw_handle(handle: RawHandle) -> File {
|
fn into_raw_handle(self) -> RawHandle {
|
||||||
std::fs::File::from_raw_handle(handle).into()
|
let file = self.file.clone();
|
||||||
}
|
drop(self);
|
||||||
}
|
Arc::try_unwrap(file)
|
||||||
|
.expect("cannot acquire ownership of the file handle after drop")
|
||||||
impl IntoRawHandle for File {
|
.into_raw_handle()
|
||||||
fn into_raw_handle(self) -> RawHandle {
|
|
||||||
let file = self.file.clone();
|
|
||||||
drop(self);
|
|
||||||
Arc::try_unwrap(file)
|
|
||||||
.expect("cannot acquire ownership of the file handle after drop")
|
|
||||||
.into_raw_handle()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -702,7 +700,7 @@ impl LockGuard<State> {
|
||||||
self.register(cx);
|
self.register(cx);
|
||||||
|
|
||||||
// Start a read operation asynchronously.
|
// Start a read operation asynchronously.
|
||||||
blocking::spawn(async move {
|
spawn_blocking(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;
|
||||||
|
@ -743,7 +741,10 @@ impl LockGuard<State> {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
// Seek `n` bytes backwards. This call should not block because it only changes
|
// Seek `n` bytes backwards. This call should not block because it only changes
|
||||||
// the internal offset into the file and doesn't touch the actual file on disk.
|
// the internal offset into the file and doesn't touch the actual file on disk.
|
||||||
(&*self.file).seek(SeekFrom::Current(-(n as i64)))?;
|
//
|
||||||
|
// We ignore errors here because special files like `/dev/random` are not
|
||||||
|
// seekable.
|
||||||
|
let _ = (&*self.file).seek(SeekFrom::Current(-(n as i64)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch to idle mode.
|
// Switch to idle mode.
|
||||||
|
@ -811,7 +812,7 @@ impl LockGuard<State> {
|
||||||
self.register(cx);
|
self.register(cx);
|
||||||
|
|
||||||
// Start a write operation asynchronously.
|
// Start a write operation asynchronously.
|
||||||
blocking::spawn(async move {
|
spawn_blocking(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 +845,7 @@ impl LockGuard<State> {
|
||||||
self.register(cx);
|
self.register(cx);
|
||||||
|
|
||||||
// Start a flush operation asynchronously.
|
// Start a flush operation asynchronously.
|
||||||
blocking::spawn(async move {
|
spawn_blocking(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_if! {
|
cfg_docs! {
|
||||||
if #[cfg(feature = "docs")] {
|
/// The type of a file or directory.
|
||||||
/// 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: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileType {
|
||||||
|
/// Returns `true` if this file type represents a regular 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 {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
_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 {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
unimplemented!()
|
}
|
||||||
}
|
|
||||||
|
/// Returns `true` if this file type represents a symbolic link.
|
||||||
/// Returns `true` if this file type represents a regular file.
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// If this file type represents a symbolic link, this method returns `false`.
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// # Examples
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
///
|
/// #
|
||||||
/// ```no_run
|
/// use async_std::fs;
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
///
|
||||||
/// #
|
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
||||||
/// use async_std::fs;
|
/// println!("{:?}", file_type.is_symlink());
|
||||||
///
|
/// #
|
||||||
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
/// # Ok(()) }) }
|
||||||
/// println!("{:?}", file_type.is_file());
|
/// ```
|
||||||
/// #
|
pub fn is_symlink(&self) -> bool {
|
||||||
/// # Ok(()) }) }
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// ```
|
|
||||||
pub fn is_file(&self) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if this file type represents a symbolic link.
|
|
||||||
///
|
|
||||||
/// # 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_symlink());
|
|
||||||
/// #
|
|
||||||
/// # Ok(()) }) }
|
|
||||||
/// ```
|
|
||||||
pub fn is_symlink(&self) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pub use std::fs::FileType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Creates a hard link on the filesystem.
|
/// Creates a hard link on the filesystem.
|
||||||
///
|
///
|
||||||
|
@ -33,5 +33,14 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::hard_link(&from, &to).context(|| {
|
||||||
|
format!(
|
||||||
|
"could not create a hard link from `{}` to `{}`",
|
||||||
|
from.display(),
|
||||||
|
to.display()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// Reads metadata for a path.
|
/// Reads metadata for a path.
|
||||||
///
|
///
|
||||||
|
@ -37,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
|
spawn_blocking(move || std::fs::metadata(path)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_not_docs! {
|
||||||
if #[cfg(feature = "docs")] {
|
pub use std::fs::Metadata;
|
||||||
use std::time::SystemTime;
|
}
|
||||||
|
|
||||||
use crate::fs::{FileType, Permissions};
|
cfg_docs! {
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
/// Metadata for a file or directory.
|
use crate::fs::{FileType, Permissions};
|
||||||
|
|
||||||
|
/// 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 {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
_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 {
|
||||||
}
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
/// Returns `true` if this metadata is for a regular directory.
|
|
||||||
///
|
/// 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
|
///
|
||||||
/// # 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(".").await?;
|
///
|
||||||
/// println!("{:?}", metadata.is_dir());
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
/// #
|
/// println!("{:?}", metadata.is_file());
|
||||||
/// # Ok(()) }) }
|
/// #
|
||||||
/// ```
|
/// # Ok(()) }) }
|
||||||
pub fn is_dir(&self) -> bool {
|
/// ```
|
||||||
unimplemented!()
|
pub fn is_file(&self) -> bool {
|
||||||
}
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
/// Returns `true` if this metadata is for a regular file.
|
|
||||||
///
|
/// Returns the file size in bytes.
|
||||||
/// 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.len());
|
||||||
/// println!("{:?}", metadata.is_file());
|
/// #
|
||||||
/// #
|
/// # Ok(()) }) }
|
||||||
/// # Ok(()) }) }
|
/// ```
|
||||||
/// ```
|
pub fn len(&self) -> u64 {
|
||||||
pub fn is_file(&self) -> bool {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
unimplemented!()
|
}
|
||||||
}
|
|
||||||
|
/// Returns the permissions from this metadata.
|
||||||
/// 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.permissions());
|
||||||
/// println!("{}", metadata.len());
|
/// #
|
||||||
/// #
|
/// # Ok(()) }) }
|
||||||
/// # Ok(()) }) }
|
/// ```
|
||||||
/// ```
|
pub fn permissions(&self) -> Permissions {
|
||||||
pub fn len(&self) -> u64 {
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
unimplemented!()
|
}
|
||||||
}
|
|
||||||
|
/// Returns the last modification time.
|
||||||
/// Returns the permissions from this metadata.
|
///
|
||||||
///
|
/// # Errors
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// This data may not be available on all platforms, in which case an error will be
|
||||||
/// ```no_run
|
/// returned.
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
///
|
||||||
/// #
|
/// # Examples
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// println!("{:?}", metadata.permissions());
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// # Ok(()) }) }
|
///
|
||||||
/// ```
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
pub fn permissions(&self) -> Permissions {
|
/// println!("{:?}", metadata.modified());
|
||||||
unimplemented!()
|
/// #
|
||||||
}
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
/// Returns the last modification time.
|
pub fn modified(&self) -> io::Result<SystemTime> {
|
||||||
///
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// # Errors
|
}
|
||||||
///
|
|
||||||
/// This data may not be available on all platforms, in which case an error will be
|
/// Returns the last access time.
|
||||||
/// returned.
|
///
|
||||||
///
|
/// # Errors
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// This data may not be available on all platforms, in which case an error will be
|
||||||
/// ```no_run
|
/// returned.
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
///
|
||||||
/// #
|
/// # Examples
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// println!("{:?}", metadata.modified());
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// # Ok(()) }) }
|
///
|
||||||
/// ```
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
pub fn modified(&self) -> io::Result<SystemTime> {
|
/// println!("{:?}", metadata.accessed());
|
||||||
unimplemented!()
|
/// #
|
||||||
}
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
/// Returns the last access time.
|
pub fn accessed(&self) -> io::Result<SystemTime> {
|
||||||
///
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// # Errors
|
}
|
||||||
///
|
|
||||||
/// This data may not be available on all platforms, in which case an error will be
|
/// Returns the creation time.
|
||||||
/// returned.
|
///
|
||||||
///
|
/// # Errors
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// This data may not be available on all platforms, in which case an error will be
|
||||||
/// ```no_run
|
/// returned.
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
///
|
||||||
/// #
|
/// # Examples
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// println!("{:?}", metadata.accessed());
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// # Ok(()) }) }
|
///
|
||||||
/// ```
|
/// let metadata = fs::metadata("a.txt").await?;
|
||||||
pub fn accessed(&self) -> io::Result<SystemTime> {
|
/// println!("{:?}", metadata.created());
|
||||||
unimplemented!()
|
/// #
|
||||||
}
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
/// Returns the creation time.
|
pub fn created(&self) -> io::Result<SystemTime> {
|
||||||
///
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// This data may not be available on all platforms, in which case an error will be
|
|
||||||
/// returned.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
||||||
/// #
|
|
||||||
/// use async_std::fs;
|
|
||||||
///
|
|
||||||
/// let metadata = fs::metadata("a.txt").await?;
|
|
||||||
/// println!("{:?}", metadata.created());
|
|
||||||
/// #
|
|
||||||
/// # Ok(()) }) }
|
|
||||||
/// ```
|
|
||||||
pub fn created(&self) -> io::Result<SystemTime> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pub use std::fs::Metadata;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
//! This module is an async version of [`std::fs`].
|
//! This module is an async version of [`std::fs`].
|
||||||
//!
|
//!
|
||||||
//! [`os::unix::fs`]: ../os/unix/fs/index.html
|
//! [`os::unix::fs`]: ../os/unix/fs/index.html
|
||||||
|
//! [`os::windows::fs`]: ../os/windows/fs/index.html
|
||||||
//! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html
|
//! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html
|
||||||
//!
|
//!
|
||||||
//! # Platform-specific extensions
|
//! # Platform-specific extensions
|
||||||
//!
|
//!
|
||||||
//! * Unix: use the [`os::unix::fs`] module.
|
//! * Unix: use the [`os::unix::fs`] module.
|
||||||
|
//! * Windows: use the [`os::windows::fs`] module.
|
||||||
//!
|
//!
|
||||||
//! # Examples
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use std::path::Path;
|
use std::future::Future;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
use crate::fs::File;
|
use crate::fs::File;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// A builder for opening files with configurable options.
|
/// A builder for opening files with configurable options.
|
||||||
///
|
///
|
||||||
|
@ -286,7 +284,10 @@ 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 {
|
||||||
|
let file = spawn_blocking(move || options.open(path)).await?;
|
||||||
|
Ok(File::new(file, true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,27 +297,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_if! {
|
cfg_docs! {
|
||||||
if #[cfg(feature = "docs")] {
|
/// A set of permissions on a file or directory.
|
||||||
/// 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: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Permissions {
|
||||||
|
/// Returns the read-only flag.
|
||||||
///
|
///
|
||||||
/// 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 {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
/// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Reads the entire contents of a file as raw bytes.
|
/// Reads the entire contents of a file as raw bytes.
|
||||||
///
|
///
|
||||||
|
@ -37,5 +37,8 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::read(&path).context(|| format!("could not read file `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use std::path::Path;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::fs::DirEntry;
|
use crate::fs::DirEntry;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
|
use crate::path::Path;
|
||||||
use crate::stream::Stream;
|
use crate::stream::Stream;
|
||||||
use crate::task::{blocking, Context, JoinHandle, Poll};
|
use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Returns a stream of entries in a directory.
|
/// Returns a stream of entries in a directory.
|
||||||
///
|
///
|
||||||
|
@ -45,9 +46,12 @@ 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) })
|
spawn_blocking(move || {
|
||||||
.await
|
std::fs::read_dir(&path)
|
||||||
.map(ReadDir::new)
|
.context(|| format!("could not read directory `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(ReadDir::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A stream of entries in a directory.
|
/// A stream of entries in a directory.
|
||||||
|
@ -91,7 +95,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(spawn_blocking(move || {
|
||||||
let next = inner.next();
|
let next = inner.next();
|
||||||
(inner, next)
|
(inner, next)
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::{Path, PathBuf};
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Reads a symbolic link and returns the path it points to.
|
/// Reads a symbolic link and returns the path it points to.
|
||||||
///
|
///
|
||||||
|
@ -29,5 +29,10 @@ 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) }).await
|
spawn_blocking(move || {
|
||||||
|
std::fs::read_link(&path)
|
||||||
|
.map(Into::into)
|
||||||
|
.context(|| format!("could not read link `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Reads the entire contents of a file as a string.
|
/// Reads the entire contents of a file as a string.
|
||||||
///
|
///
|
||||||
|
@ -38,5 +38,9 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::read_to_string(&path)
|
||||||
|
.context(|| format!("could not read file `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Removes an empty directory.
|
/// Removes an empty directory.
|
||||||
///
|
///
|
||||||
|
@ -30,5 +30,9 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::remove_dir(&path)
|
||||||
|
.context(|| format!("could not remove directory `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Removes a directory and all of its contents.
|
/// Removes a directory and all of its contents.
|
||||||
///
|
///
|
||||||
|
@ -30,5 +30,9 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::remove_dir_all(&path)
|
||||||
|
.context(|| format!("could not remove directory `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Removes a file.
|
/// Removes a file.
|
||||||
///
|
///
|
||||||
|
@ -30,5 +30,9 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::remove_file(&path)
|
||||||
|
.context(|| format!("could not remove file `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Renames a file or directory to a new location.
|
/// Renames a file or directory to a new location.
|
||||||
///
|
///
|
||||||
|
@ -35,5 +35,14 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::rename(&from, &to).context(|| {
|
||||||
|
format!(
|
||||||
|
"could not rename `{}` to `{}`",
|
||||||
|
from.display(),
|
||||||
|
to.display()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::fs::Permissions;
|
use crate::fs::Permissions;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// Changes the permissions of a file or directory.
|
/// Changes the permissions of a file or directory.
|
||||||
///
|
///
|
||||||
|
@ -33,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
|
spawn_blocking(move || std::fs::set_permissions(path, perm)).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::fs::Metadata;
|
use crate::fs::Metadata;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
|
||||||
/// Reads metadata for a path without following symbolic links.
|
/// Reads metadata for a path without following symbolic links.
|
||||||
///
|
///
|
||||||
|
@ -35,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
|
spawn_blocking(move || std::fs::symlink_metadata(path)).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::task::blocking;
|
use crate::path::Path;
|
||||||
|
use crate::task::spawn_blocking;
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Writes a slice of bytes as the new contents of a file.
|
/// Writes a slice of bytes as the new contents of a file.
|
||||||
///
|
///
|
||||||
|
@ -34,5 +34,9 @@ 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
|
spawn_blocking(move || {
|
||||||
|
std::fs::write(&path, contents)
|
||||||
|
.context(|| format!("could not write to file `{}`", path.display()))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
43
src/future/future/delay.rs
Normal file
43
src/future/future/delay.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use futures_timer::Delay;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct DelayFuture<F> {
|
||||||
|
#[pin]
|
||||||
|
future: F,
|
||||||
|
#[pin]
|
||||||
|
delay: Delay,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> DelayFuture<F> {
|
||||||
|
pub fn new(future: F, dur: Duration) -> DelayFuture<F> {
|
||||||
|
let delay = Delay::new(dur);
|
||||||
|
|
||||||
|
DelayFuture { future, delay }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future> Future for DelayFuture<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
match this.delay.poll(cx) {
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(_) => match this.future.poll(cx) {
|
||||||
|
Poll::Ready(v) => Poll::Ready(v),
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
src/future/future/flatten.rs
Normal file
52
src/future/future/flatten.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use crate::future::IntoFuture;
|
||||||
|
use crate::task::{ready, Context, Poll};
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct FlattenFuture<Fut1, Fut2> {
|
||||||
|
state: State<Fut1, Fut2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum State<Fut1, Fut2> {
|
||||||
|
First(Fut1),
|
||||||
|
Second(Fut2),
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut1, Fut2> FlattenFuture<Fut1, Fut2> {
|
||||||
|
pub(crate) fn new(future: Fut1) -> FlattenFuture<Fut1, Fut2> {
|
||||||
|
FlattenFuture {
|
||||||
|
state: State::First(future),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Fut1> Future for FlattenFuture<Fut1, <Fut1::Output as IntoFuture>::Future>
|
||||||
|
where
|
||||||
|
Fut1: Future,
|
||||||
|
Fut1::Output: IntoFuture,
|
||||||
|
{
|
||||||
|
type Output = <Fut1::Output as IntoFuture>::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let Self { state } = unsafe { self.get_unchecked_mut() };
|
||||||
|
loop {
|
||||||
|
match state {
|
||||||
|
State::First(fut1) => {
|
||||||
|
let fut2 = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)).into_future();
|
||||||
|
*state = State::Second(fut2);
|
||||||
|
}
|
||||||
|
State::Second(fut2) => {
|
||||||
|
let v = ready!(unsafe { Pin::new_unchecked(fut2) }.poll(cx));
|
||||||
|
*state = State::Empty;
|
||||||
|
return Poll::Ready(v);
|
||||||
|
}
|
||||||
|
State::Empty => panic!("polled a completed future"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
src/future/future/join.rs
Normal file
60
src/future/future/join.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use async_macros::MaybeDone;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct Join<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>
|
||||||
|
{
|
||||||
|
#[pin] left: MaybeDone<L>,
|
||||||
|
#[pin] right: MaybeDone<R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> Join<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(left: L, right: R) -> Self {
|
||||||
|
Self {
|
||||||
|
left: MaybeDone::new(left),
|
||||||
|
right: MaybeDone::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> Future for Join<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
type Output = (L::Output, R::Output);
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
let mut left = this.left;
|
||||||
|
let mut right = this.right;
|
||||||
|
|
||||||
|
let is_left_ready = Future::poll(Pin::new(&mut left), cx).is_ready();
|
||||||
|
if is_left_ready && right.as_ref().output().is_some() {
|
||||||
|
return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_right_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
|
||||||
|
if is_right_ready && left.as_ref().output().is_some() {
|
||||||
|
return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
395
src/future/future/mod.rs
Normal file
395
src/future/future/mod.rs
Normal file
|
@ -0,0 +1,395 @@
|
||||||
|
cfg_unstable! {
|
||||||
|
mod delay;
|
||||||
|
mod flatten;
|
||||||
|
mod race;
|
||||||
|
mod try_race;
|
||||||
|
mod join;
|
||||||
|
mod try_join;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use delay::DelayFuture;
|
||||||
|
use flatten::FlattenFuture;
|
||||||
|
use crate::future::IntoFuture;
|
||||||
|
use race::Race;
|
||||||
|
use try_race::TryRace;
|
||||||
|
use join::Join;
|
||||||
|
use try_join::TryJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 [provided methods] do not really exist in the trait itself, but they become
|
||||||
|
available when [`FutureExt`] from the [prelude] is imported:
|
||||||
|
|
||||||
|
```
|
||||||
|
# #[allow(unused_imports)]
|
||||||
|
use async_std::prelude::*;
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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
|
||||||
|
[provided methods]: #provided-methods
|
||||||
|
[`FutureExt`]: ../prelude/trait.FutureExt.html
|
||||||
|
[prelude]: ../prelude/index.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>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Extension methods for [`Future`].
|
||||||
|
|
||||||
|
[`Future`]: ../future/trait.Future.html
|
||||||
|
"#]
|
||||||
|
pub trait FutureExt: std::future::Future {
|
||||||
|
/// Returns a Future that delays execution for a specified time.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
/// use async_std::future;
|
||||||
|
/// use std::time::Duration;
|
||||||
|
///
|
||||||
|
/// let a = future::ready(1).delay(Duration::from_millis(2000));
|
||||||
|
/// dbg!(a.await);
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
#[cfg(all(feature = "default", feature = "unstable"))]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn delay(self, dur: Duration) -> impl Future<Output = Self::Output> [DelayFuture<Self>]
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
DelayFuture::new(self, dur)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flatten out the execution of this future when the result itself
|
||||||
|
/// can be converted into another future.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
///
|
||||||
|
/// let nested_future = async { async { 1 } };
|
||||||
|
/// let future = nested_future.flatten();
|
||||||
|
/// assert_eq!(future.await, 1);
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn flatten(
|
||||||
|
self,
|
||||||
|
) -> impl Future<Output = <Self::Output as IntoFuture>::Output>
|
||||||
|
[FlattenFuture<Self, <Self::Output as IntoFuture>::Future>]
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
<Self as Future>::Output: IntoFuture,
|
||||||
|
{
|
||||||
|
FlattenFuture::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for one of two similarly-typed futures to complete.
|
||||||
|
|
||||||
|
Awaits multiple futures simultaneously, returning the output of the
|
||||||
|
first future that completes.
|
||||||
|
|
||||||
|
This function will return a new future which awaits for either one of both
|
||||||
|
futures to complete. If multiple futures are completed at the same time,
|
||||||
|
resolution will occur in the order that they have been passed.
|
||||||
|
|
||||||
|
Note that this function consumes all futures passed, and once a future is
|
||||||
|
completed, all other futures are dropped.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# async_std::task::block_on(async {
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
|
||||||
|
let a = future::pending();
|
||||||
|
let b = future::ready(1u8);
|
||||||
|
let c = future::ready(2u8);
|
||||||
|
|
||||||
|
let f = a.race(b).race(c);
|
||||||
|
assert_eq!(f.await, 1u8);
|
||||||
|
# });
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn race<F>(
|
||||||
|
self,
|
||||||
|
other: F,
|
||||||
|
) -> impl Future<Output = <Self as std::future::Future>::Output> [Race<Self, F>]
|
||||||
|
where
|
||||||
|
Self: std::future::Future + Sized,
|
||||||
|
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
|
||||||
|
{
|
||||||
|
Race::new(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for one of two similarly-typed fallible futures to complete.
|
||||||
|
|
||||||
|
Awaits multiple futures simultaneously, returning all results once complete.
|
||||||
|
|
||||||
|
`try_race` is similar to [`race`], but keeps going if a future
|
||||||
|
resolved to an error until all futures have been resolved. In which case
|
||||||
|
an error is returned.
|
||||||
|
|
||||||
|
The ordering of which value is yielded when two futures resolve
|
||||||
|
simultaneously is intentionally left unspecified.
|
||||||
|
|
||||||
|
[`race`]: #method.race
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
|
||||||
|
let a = future::pending::<Result<_, Error>>();
|
||||||
|
let b = future::ready(Err(Error::from(ErrorKind::Other)));
|
||||||
|
let c = future::ready(Ok(1u8));
|
||||||
|
|
||||||
|
let f = a.try_race(b).try_race(c);
|
||||||
|
assert_eq!(f.await?, 1u8);
|
||||||
|
#
|
||||||
|
# Ok(()) }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn try_race<F, T, E>(
|
||||||
|
self,
|
||||||
|
other: F
|
||||||
|
) -> impl Future<Output = <Self as std::future::Future>::Output> [TryRace<Self, F>]
|
||||||
|
where
|
||||||
|
Self: std::future::Future<Output = Result<T, E>> + Sized,
|
||||||
|
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
|
||||||
|
{
|
||||||
|
TryRace::new(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for two similarly-typed futures to complete.
|
||||||
|
|
||||||
|
Awaits multiple futures simultaneously, returning the output of the
|
||||||
|
futures once both complete.
|
||||||
|
|
||||||
|
This function returns a new future which polls both futures
|
||||||
|
concurrently.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# async_std::task::block_on(async {
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
|
||||||
|
let a = future::ready(1u8);
|
||||||
|
let b = future::ready(2u8);
|
||||||
|
|
||||||
|
let f = a.join(b);
|
||||||
|
assert_eq!(f.await, (1u8, 2u8));
|
||||||
|
# });
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(any(feature = "unstable", feature = "docs"))]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn join<F>(
|
||||||
|
self,
|
||||||
|
other: F
|
||||||
|
) -> impl Future<Output = (<Self as std::future::Future>::Output, <F as std::future::Future>::Output)> [Join<Self, F>]
|
||||||
|
where
|
||||||
|
Self: std::future::Future + Sized,
|
||||||
|
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
|
||||||
|
{
|
||||||
|
Join::new(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Waits for two similarly-typed fallible futures to complete.
|
||||||
|
|
||||||
|
Awaits multiple futures simultaneously, returning all results once
|
||||||
|
complete.
|
||||||
|
|
||||||
|
`try_join` is similar to [`join`], but returns an error immediately
|
||||||
|
if a future resolves to an error.
|
||||||
|
|
||||||
|
[`join`]: #method.join
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::future;
|
||||||
|
|
||||||
|
let a = future::ready(Err("Error"));
|
||||||
|
let b = future::ready(Ok(1u8));
|
||||||
|
|
||||||
|
let f = a.try_join(b);
|
||||||
|
assert_eq!(f.await, Err("Error"));
|
||||||
|
|
||||||
|
let a = future::ready(Ok::<u8, String>(1u8));
|
||||||
|
let b = future::ready(Ok::<u8, String>(2u8));
|
||||||
|
|
||||||
|
let f = a.try_join(b);
|
||||||
|
assert_eq!(f.await, Ok((1u8, 2u8)));
|
||||||
|
#
|
||||||
|
# Ok(()) }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
#[cfg(any(feature = "unstable", feature = "docs"))]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
fn try_join<F, T, E>(
|
||||||
|
self,
|
||||||
|
other: F
|
||||||
|
) -> impl Future<Output = Result<(T, T), E>> [TryJoin<Self, F>]
|
||||||
|
where
|
||||||
|
Self: std::future::Future<Output = Result<T, E>> + Sized,
|
||||||
|
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
|
||||||
|
{
|
||||||
|
TryJoin::new(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/future/future/race.rs
Normal file
57
src/future/future/race.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use async_macros::MaybeDone;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct Race<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>
|
||||||
|
{
|
||||||
|
#[pin] left: MaybeDone<L>,
|
||||||
|
#[pin] right: MaybeDone<R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> Race<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(left: L, right: R) -> Self {
|
||||||
|
Self {
|
||||||
|
left: MaybeDone::new(left),
|
||||||
|
right: MaybeDone::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> Future for Race<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
type Output = L::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
let mut left = this.left;
|
||||||
|
if Future::poll(Pin::new(&mut left), cx).is_ready() {
|
||||||
|
return Poll::Ready(left.take().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut right = this.right;
|
||||||
|
if Future::poll(Pin::new(&mut right), cx).is_ready() {
|
||||||
|
return Poll::Ready(right.take().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
72
src/future/future/try_join.rs
Normal file
72
src/future/future/try_join.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use async_macros::MaybeDone;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct TryJoin<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>
|
||||||
|
{
|
||||||
|
#[pin] left: MaybeDone<L>,
|
||||||
|
#[pin] right: MaybeDone<R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> TryJoin<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(left: L, right: R) -> Self {
|
||||||
|
Self {
|
||||||
|
left: MaybeDone::new(left),
|
||||||
|
right: MaybeDone::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R, T, E> Future for TryJoin<L, R>
|
||||||
|
where
|
||||||
|
L: Future<Output = Result<T, E>>,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
type Output = Result<(T, T), E>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
let mut left = this.left;
|
||||||
|
let mut right = this.right;
|
||||||
|
|
||||||
|
if Future::poll(Pin::new(&mut left), cx).is_ready() {
|
||||||
|
if left.as_ref().output().unwrap().is_err() {
|
||||||
|
return Poll::Ready(Err(left.take().unwrap().err().unwrap()));
|
||||||
|
} else if right.as_ref().output().is_some() {
|
||||||
|
return Poll::Ready(Ok((
|
||||||
|
left.take().unwrap().ok().unwrap(),
|
||||||
|
right.take().unwrap().ok().unwrap(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Future::poll(Pin::new(&mut right), cx).is_ready() {
|
||||||
|
if right.as_ref().output().unwrap().is_err() {
|
||||||
|
return Poll::Ready(Err(right.take().unwrap().err().unwrap()));
|
||||||
|
} else if left.as_ref().output().is_some() {
|
||||||
|
return Poll::Ready(Ok((
|
||||||
|
left.take().unwrap().ok().unwrap(),
|
||||||
|
right.take().unwrap().ok().unwrap(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
66
src/future/future/try_race.rs
Normal file
66
src/future/future/try_race.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use async_macros::MaybeDone;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct TryRace<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>
|
||||||
|
{
|
||||||
|
#[pin] left: MaybeDone<L>,
|
||||||
|
#[pin] right: MaybeDone<R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> TryRace<L, R>
|
||||||
|
where
|
||||||
|
L: Future,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(left: L, right: R) -> Self {
|
||||||
|
Self {
|
||||||
|
left: MaybeDone::new(left),
|
||||||
|
right: MaybeDone::new(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R, T, E> Future for TryRace<L, R>
|
||||||
|
where
|
||||||
|
L: Future<Output = Result<T, E>>,
|
||||||
|
R: Future<Output = L::Output>,
|
||||||
|
{
|
||||||
|
type Output = L::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
let mut left_errored = false;
|
||||||
|
|
||||||
|
// Check if the left future is ready & successful. Continue if not.
|
||||||
|
let mut left = this.left;
|
||||||
|
if Future::poll(Pin::new(&mut left), cx).is_ready() {
|
||||||
|
if left.as_ref().output().unwrap().is_ok() {
|
||||||
|
return Poll::Ready(left.take().unwrap());
|
||||||
|
} else {
|
||||||
|
left_errored = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the right future is ready & successful. Return err if left
|
||||||
|
// future also resolved to err. Continue if not.
|
||||||
|
let mut right = this.right;
|
||||||
|
let is_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
|
||||||
|
if is_ready && (right.as_ref().output().unwrap().is_ok() || left_errored) {
|
||||||
|
return Poll::Ready(right.take().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
53
src/future/into_future.rs
Normal file
53
src/future/into_future.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use std::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
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,63 +4,64 @@
|
||||||
//!
|
//!
|
||||||
//! Often it's desireable to await multiple futures as if it was a single
|
//! Often it's desireable to await multiple futures as if it was a single
|
||||||
//! future. The `join` family of operations converts multiple futures into a
|
//! future. The `join` family of operations converts multiple futures into a
|
||||||
//! single future that returns all of their outputs. The `select` family of
|
//! single future that returns all of their outputs. The `race` family of
|
||||||
//! operations converts multiple future into a single future that returns the
|
//! operations converts multiple future into a single future that returns the
|
||||||
//! first output.
|
//! first output.
|
||||||
//!
|
//!
|
||||||
//! For operating on futures the following macros can be used:
|
//! For operating on futures the following functions can be used:
|
||||||
//!
|
//!
|
||||||
//! | Name | Return signature | When does it return? |
|
//! | Name | Return signature | When does it return? |
|
||||||
//! | --- | --- | --- |
|
//! | --- | --- | --- |
|
||||||
//! | `future::join` | `(T1, T2)` | Wait for all to complete
|
//! | [`Future::join`] | `(T1, T2)` | Wait for all to complete
|
||||||
//! | `future::select` | `T` | Return on first value
|
//! | [`Future::race`] | `T` | Return on first value
|
||||||
//!
|
//!
|
||||||
//! ## Fallible Futures Concurrency
|
//! ## Fallible Futures Concurrency
|
||||||
//!
|
//!
|
||||||
//! For operating on futures that return `Result` additional `try_` variants of
|
//! For operating on futures that return `Result` additional `try_` variants of
|
||||||
//! the macros mentioned before can be used. These macros are aware of `Result`,
|
//! the functions mentioned before can be used. These functions are aware of `Result`,
|
||||||
//! and will behave slightly differently from their base variants.
|
//! and will behave slightly differently from their base variants.
|
||||||
//!
|
//!
|
||||||
//! In the case of `try_join`, if any of the futures returns `Err` all
|
//! In the case of `try_join`, if any of the futures returns `Err` all
|
||||||
//! futures are dropped and an error is returned. This is referred to as
|
//! futures are dropped and an error is returned. This is referred to as
|
||||||
//! "short-circuiting".
|
//! "short-circuiting".
|
||||||
//!
|
//!
|
||||||
//! In the case of `try_select`, instead of returning the first future that
|
//! In the case of `try_race`, instead of returning the first future that
|
||||||
//! completes it returns the first future that _successfully_ completes. This
|
//! completes it returns the first future that _successfully_ completes. This
|
||||||
//! means `try_select` will keep going until any one of the futures returns
|
//! means `try_race` will keep going until any one of the futures returns
|
||||||
//! `Ok`, or _all_ futures have returned `Err`.
|
//! `Ok`, or _all_ futures have returned `Err`.
|
||||||
//!
|
//!
|
||||||
//! However sometimes it can be useful to use the base variants of the macros
|
//! However sometimes it can be useful to use the base variants of the functions
|
||||||
//! even on futures that return `Result`. Here is an overview of operations that
|
//! even on futures that return `Result`. Here is an overview of operations that
|
||||||
//! work on `Result`, and their respective semantics:
|
//! work on `Result`, and their respective semantics:
|
||||||
//!
|
//!
|
||||||
//! | Name | Return signature | When does it return? |
|
//! | Name | Return signature | When does it return? |
|
||||||
//! | --- | --- | --- |
|
//! | --- | --- | --- |
|
||||||
//! | `future::join` | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
|
//! | [`Future::join`] | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
|
||||||
//! | `future::try_join` | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete
|
//! | [`Future::try_join`] | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete
|
||||||
//! | `future::select` | `Result<T, E>` | Return on first value
|
//! | [`Future::race`] | `Result<T, E>` | Return on first value
|
||||||
//! | `future::try_select` | `Result<T, E>` | Return on first `Ok`, reject on last Err
|
//! | [`Future::try_race`] | `Result<T, E>` | Return on first `Ok`, reject on last Err
|
||||||
|
//!
|
||||||
#[doc(inline)]
|
//! [`Future::join`]: trait.Future.html#method.join
|
||||||
pub use std::future::Future;
|
//! [`Future::try_join`]: trait.Future.html#method.try_join
|
||||||
|
//! [`Future::race`]: trait.Future.html#method.race
|
||||||
#[doc(inline)]
|
//! [`Future::try_race`]: trait.Future.html#method.try_race
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
|
||||||
pub use async_macros::{join, select, try_join, try_select};
|
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
|
|
||||||
|
pub use future::Future;
|
||||||
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(crate) mod future;
|
||||||
mod pending;
|
mod pending;
|
||||||
mod poll_fn;
|
mod poll_fn;
|
||||||
mod ready;
|
mod ready;
|
||||||
|
|
||||||
cfg_if! {
|
cfg_default! {
|
||||||
if #[cfg(any(feature = "unstable", feature = "docs"))] {
|
pub use timeout::{timeout, TimeoutError};
|
||||||
mod timeout;
|
mod timeout;
|
||||||
pub use timeout::{timeout, TimeoutError};
|
}
|
||||||
}
|
|
||||||
|
cfg_unstable! {
|
||||||
|
pub use into_future::IntoFuture;
|
||||||
|
mod into_future;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
use std::future::Future;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
/// Never resolves to a value.
|
/// Never resolves to a value.
|
||||||
|
@ -9,7 +9,7 @@ use crate::task::{Context, Poll};
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn main() { async_std::task::block_on(async {
|
/// # async_std::task::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// use std::time::Duration;
|
/// use std::time::Duration;
|
||||||
///
|
///
|
||||||
|
@ -22,7 +22,7 @@ use crate::task::{Context, Poll};
|
||||||
/// let res: io::Result<()> = io::timeout(dur, fut).await;
|
/// let res: io::Result<()> = io::timeout(dur, fut).await;
|
||||||
/// assert!(res.is_err());
|
/// assert!(res.is_err());
|
||||||
/// #
|
/// #
|
||||||
/// # }) }
|
/// # })
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn pending<T>() -> T {
|
pub async fn pending<T>() -> T {
|
||||||
let fut = Pending {
|
let fut = Pending {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
/// Creates a new future wrapping around a function returning [`Poll`].
|
/// Creates a new future wrapping around a function returning [`Poll`].
|
||||||
|
@ -10,7 +10,7 @@ use crate::task::{Context, Poll};
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn main() { async_std::task::block_on(async {
|
/// # async_std::task::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// use async_std::future;
|
/// use async_std::future;
|
||||||
/// use async_std::task::{Context, Poll};
|
/// use async_std::task::{Context, Poll};
|
||||||
|
@ -21,7 +21,7 @@ use crate::task::{Context, Poll};
|
||||||
///
|
///
|
||||||
/// assert_eq!(future::poll_fn(poll_greeting).await, "hello world");
|
/// assert_eq!(future::poll_fn(poll_greeting).await, "hello world");
|
||||||
/// #
|
/// #
|
||||||
/// # }) }
|
/// # })
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn poll_fn<F, T>(f: F) -> T
|
pub async fn poll_fn<F, T>(f: F) -> T
|
||||||
where
|
where
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn main() { async_std::task::block_on(async {
|
/// # async_std::task::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// use async_std::future;
|
/// use async_std::future;
|
||||||
///
|
///
|
||||||
/// assert_eq!(future::ready(10).await, 10);
|
/// assert_eq!(future::ready(10).await, 10);
|
||||||
/// #
|
/// #
|
||||||
/// # }) }
|
/// # })
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn ready<T>(val: T) -> T {
|
pub async fn ready<T>(val: T) -> T {
|
||||||
val
|
val
|
||||||
|
|
|
@ -2,10 +2,11 @@ use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
/// Awaits a future or times out after a duration of time.
|
/// Awaits a future or times out after a duration of time.
|
||||||
|
@ -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()))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,23 @@
|
||||||
mod lines;
|
mod lines;
|
||||||
mod read_line;
|
mod read_line;
|
||||||
mod read_until;
|
mod read_until;
|
||||||
|
mod split;
|
||||||
|
|
||||||
pub use lines::Lines;
|
pub use lines::Lines;
|
||||||
|
pub use split::Split;
|
||||||
|
|
||||||
use read_line::ReadLineFuture;
|
use read_line::ReadLineFuture;
|
||||||
use read_until::ReadUntilFuture;
|
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.
|
||||||
|
|
||||||
|
@ -29,7 +25,7 @@ extension_trait! {
|
||||||
[`std::io::BufRead`].
|
[`std::io::BufRead`].
|
||||||
|
|
||||||
The [provided methods] do not really exist in the trait itself, but they become
|
The [provided methods] do not really exist in the trait itself, but they become
|
||||||
available when the prelude is imported:
|
available when [`BufReadExt`] from the [prelude] is imported:
|
||||||
|
|
||||||
```
|
```
|
||||||
# #[allow(unused_imports)]
|
# #[allow(unused_imports)]
|
||||||
|
@ -38,10 +34,12 @@ extension_trait! {
|
||||||
|
|
||||||
[`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
|
[`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
|
||||||
[`futures::io::AsyncBufRead`]:
|
[`futures::io::AsyncBufRead`]:
|
||||||
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html
|
https://docs.rs/futures/0.3/futures/io/trait.AsyncBufRead.html
|
||||||
[provided methods]: #provided-methods
|
[provided methods]: #provided-methods
|
||||||
|
[`BufReadExt`]: ../io/prelude/trait.BufReadExt.html
|
||||||
|
[prelude]: ../prelude/index.html
|
||||||
"#]
|
"#]
|
||||||
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.
|
||||||
|
@ -64,7 +62,14 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Extension methods for [`BufRead`].
|
||||||
|
|
||||||
|
[`BufRead`]: ../trait.BufRead.html
|
||||||
|
"#]
|
||||||
|
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.
|
||||||
|
|
||||||
|
@ -226,6 +231,57 @@ extension_trait! {
|
||||||
read: 0,
|
read: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Returns a stream over the contents of this reader split on the byte `byte`.
|
||||||
|
|
||||||
|
The stream returned from this function will return instances of
|
||||||
|
[`io::Result`]`<`[`Vec<u8>`]`>`. Each vector returned will *not* have
|
||||||
|
the delimiter byte at the end.
|
||||||
|
|
||||||
|
This function will yield errors whenever [`read_until`] would have
|
||||||
|
also yielded an error.
|
||||||
|
|
||||||
|
[`io::Result`]: type.Result.html
|
||||||
|
[`Vec<u8>`]: ../vec/struct.Vec.html
|
||||||
|
[`read_until`]: #method.read_until
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
[`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In
|
||||||
|
this example, we use [`Cursor`] to iterate over all hyphen delimited
|
||||||
|
segments in a byte slice
|
||||||
|
|
||||||
|
[`Cursor`]: struct.Cursor.html
|
||||||
|
|
||||||
|
```
|
||||||
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
#
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::io;
|
||||||
|
|
||||||
|
let cursor = io::Cursor::new(b"lorem-ipsum-dolor");
|
||||||
|
|
||||||
|
let mut split_iter = cursor.split(b'-').map(|l| l.unwrap());
|
||||||
|
assert_eq!(split_iter.next().await, Some(b"lorem".to_vec()));
|
||||||
|
assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec()));
|
||||||
|
assert_eq!(split_iter.next().await, Some(b"dolor".to_vec()));
|
||||||
|
assert_eq!(split_iter.next().await, None);
|
||||||
|
#
|
||||||
|
# Ok(()) }) }
|
||||||
|
```
|
||||||
|
"#]
|
||||||
|
fn split(self, byte: u8) -> Split<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Split {
|
||||||
|
reader: self,
|
||||||
|
buf: Vec::new(),
|
||||||
|
delim: byte,
|
||||||
|
read: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BufRead + Unpin + ?Sized> BufRead for Box<T> {
|
impl<T: BufRead + Unpin + ?Sized> BufRead for Box<T> {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use super::read_until_internal;
|
use super::read_until_internal;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, BufRead};
|
use crate::io::{self, BufRead};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
@ -37,8 +37,12 @@ impl<T: BufRead + Unpin + ?Sized> Future for ReadLineFuture<'_, T> {
|
||||||
))
|
))
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
debug_assert!(buf.is_empty());
|
#[allow(clippy::debug_assert_with_mut_call)]
|
||||||
debug_assert_eq!(*read, 0);
|
{
|
||||||
|
debug_assert!(buf.is_empty());
|
||||||
|
debug_assert_eq!(*read, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
|
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
|
||||||
mem::swap(unsafe { buf.as_mut_vec() }, bytes);
|
mem::swap(unsafe { buf.as_mut_vec() }, bytes);
|
||||||
Poll::Ready(ret)
|
Poll::Ready(ret)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use super::read_until_internal;
|
use super::read_until_internal;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, BufRead};
|
use crate::io::{self, BufRead};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
51
src/io/buf_read/split.rs
Normal file
51
src/io/buf_read/split.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use std::mem;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use super::read_until_internal;
|
||||||
|
use crate::io::{self, BufRead};
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
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 type is an async version of [`std::io::Split`].
|
||||||
|
///
|
||||||
|
/// [`split`]: trait.BufRead.html#method.lines
|
||||||
|
/// [`BufRead`]: trait.BufRead.html
|
||||||
|
/// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Split<R> {
|
||||||
|
#[pin]
|
||||||
|
pub(crate) reader: R,
|
||||||
|
pub(crate) buf: Vec<u8>,
|
||||||
|
pub(crate) read: usize,
|
||||||
|
pub(crate) delim: u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: BufRead> Stream for Split<R> {
|
||||||
|
type Item = io::Result<Vec<u8>>;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let this = self.project();
|
||||||
|
let n = futures_core::ready!(read_until_internal(
|
||||||
|
this.reader,
|
||||||
|
cx,
|
||||||
|
*this.delim,
|
||||||
|
this.buf,
|
||||||
|
this.read
|
||||||
|
))?;
|
||||||
|
if n == 0 && this.buf.is_empty() {
|
||||||
|
return Poll::Ready(None);
|
||||||
|
}
|
||||||
|
if this.buf[this.buf.len() - 1] == *this.delim {
|
||||||
|
this.buf.pop();
|
||||||
|
}
|
||||||
|
Poll::Ready(Some(Ok(mem::replace(this.buf, vec![]))))
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,51 +2,54 @@ use std::io::{IoSliceMut, Read as _};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::{cmp, fmt};
|
use std::{cmp, fmt};
|
||||||
|
|
||||||
use crate::io::{self, BufRead, Read, Seek, SeekFrom};
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::io::{self, BufRead, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
const DEFAULT_CAPACITY: usize = 8 * 1024;
|
pin_project! {
|
||||||
|
/// Adds buffering to any reader.
|
||||||
/// Adds buffering to any reader.
|
///
|
||||||
///
|
/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader`
|
||||||
/// 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
|
||||||
/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer
|
/// of the incoming byte stream.
|
||||||
/// of the incoming byte stream.
|
///
|
||||||
///
|
/// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to
|
||||||
/// `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
|
||||||
/// the same file or network socket. It does not help when reading very large amounts at once, or
|
/// reading just one or a few times. It also provides no advantage when reading from a source that
|
||||||
/// 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>`.
|
||||||
/// is already in memory, like a `Vec<u8>`.
|
///
|
||||||
///
|
/// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating
|
||||||
/// 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.
|
||||||
/// 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
|
||||||
/// [`Read`]: trait.Read.html
|
/// [`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
|
///
|
||||||
///
|
/// ```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::File;
|
||||||
/// use async_std::fs::File;
|
/// use async_std::io::BufReader;
|
||||||
/// use async_std::io::BufReader;
|
/// use async_std::prelude::*;
|
||||||
/// 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();
|
||||||
/// let mut line = String::new();
|
/// file.read_line(&mut line).await?;
|
||||||
/// file.read_line(&mut line).await?;
|
/// #
|
||||||
/// #
|
/// # Ok(()) }) }
|
||||||
/// # Ok(()) }) }
|
/// ```
|
||||||
/// ```
|
pub struct BufReader<R> {
|
||||||
pub struct BufReader<R> {
|
#[pin]
|
||||||
inner: R,
|
inner: R,
|
||||||
buf: Box<[u8]>,
|
buf: Box<[u8]>,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
cap: usize,
|
cap: usize,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: io::Read> BufReader<R> {
|
impl<R: io::Read> BufReader<R> {
|
||||||
|
@ -67,7 +70,7 @@ impl<R: io::Read> BufReader<R> {
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(inner: R) -> BufReader<R> {
|
pub fn new(inner: R) -> BufReader<R> {
|
||||||
BufReader::with_capacity(DEFAULT_CAPACITY, inner)
|
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new buffered reader with the specified capacity.
|
/// Creates a new buffered reader with the specified capacity.
|
||||||
|
@ -95,10 +98,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 +140,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 +191,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 +208,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 +225,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 +242,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 +308,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))
|
||||||
|
|
|
@ -1,92 +1,121 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use futures_core::ready;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
use crate::io::{self, Seek, SeekFrom, Write};
|
use crate::io::write::WriteExt;
|
||||||
use crate::task::{Context, Poll};
|
use crate::io::{self, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE};
|
||||||
|
use crate::task::{Context, Poll, ready};
|
||||||
|
|
||||||
const DEFAULT_CAPACITY: usize = 8 * 1024;
|
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
|
||||||
|
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
|
||||||
|
/// `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
|
||||||
|
/// help when writing very large amounts at once, or writing just one or a few
|
||||||
|
/// times. It also provides no advantage when writing to a destination that is
|
||||||
|
/// in memory, like a `Vec<u8>`.
|
||||||
|
///
|
||||||
|
/// Unlike the `BufWriter` type in `std`, this type does not write out the
|
||||||
|
/// contents of its buffer when it is dropped. Therefore, it is absolutely
|
||||||
|
/// critical that users explicitly flush the buffer before dropping a
|
||||||
|
/// `BufWriter`.
|
||||||
|
///
|
||||||
|
/// This type is an async version of [`std::io::BufWriter`].
|
||||||
|
///
|
||||||
|
/// [`std::io::BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Let's write the numbers one through ten to a [`TcpStream`]:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// use async_std::net::TcpStream;
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
///
|
||||||
|
/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?;
|
||||||
|
///
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// let arr = [i+1];
|
||||||
|
/// stream.write(&arr).await?;
|
||||||
|
/// }
|
||||||
|
/// #
|
||||||
|
/// # 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
|
||||||
|
/// `BufWriter`:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// use async_std::io::BufWriter;
|
||||||
|
/// 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 arr = [i+1];
|
||||||
|
/// stream.write(&arr).await?;
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// stream.flush().await?;
|
||||||
|
/// #
|
||||||
|
/// # 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
|
||||||
|
/// the `stream` is dropped.
|
||||||
|
///
|
||||||
|
/// [`Write`]: trait.Write.html
|
||||||
|
/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write
|
||||||
|
/// [`TcpStream`]: ../net/struct.TcpStream.html
|
||||||
|
/// [`flush`]: trait.Write.html#tymethod.flush
|
||||||
|
pub struct BufWriter<W> {
|
||||||
|
#[pin]
|
||||||
|
inner: W,
|
||||||
|
buf: Vec<u8>,
|
||||||
|
written: usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wraps a writer and buffers its output.
|
/// An error returned by `into_inner` which combines an error that
|
||||||
///
|
/// happened while writing out the buffer, and the buffered writer object
|
||||||
/// It can be excessively inefficient to work directly with something that
|
/// which may be used to recover from the condition.
|
||||||
/// implements [`Write`]. For example, every call to
|
|
||||||
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
|
|
||||||
/// `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
|
|
||||||
/// help when writing very large amounts at once, or writing just one or a few
|
|
||||||
/// 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 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`].
|
|
||||||
///
|
|
||||||
/// [`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`]:
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
||||||
/// use async_std::net::TcpStream;
|
|
||||||
/// use async_std::prelude::*;
|
|
||||||
///
|
|
||||||
/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?;
|
|
||||||
///
|
|
||||||
/// for i in 0..10 {
|
|
||||||
/// let arr = [i+1];
|
|
||||||
/// stream.write(&arr).await?;
|
|
||||||
/// }
|
|
||||||
/// #
|
|
||||||
/// # 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
|
|
||||||
/// `BufWriter`:
|
|
||||||
///
|
|
||||||
/// ```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::io::BufWriter;
|
/// use async_std::io::BufWriter;
|
||||||
/// use async_std::net::TcpStream;
|
/// use async_std::net::TcpStream;
|
||||||
/// use async_std::prelude::*;
|
|
||||||
///
|
///
|
||||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?);
|
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?);
|
||||||
/// for i in 0..10 {
|
///
|
||||||
/// let arr = [i+1];
|
/// // unwrap the TcpStream and flush the buffer
|
||||||
/// stream.write(&arr).await?;
|
/// let stream = match buf_writer.into_inner().await {
|
||||||
|
/// Ok(s) => s,
|
||||||
|
/// Err(e) => {
|
||||||
|
/// // Here, e is an IntoInnerError
|
||||||
|
/// panic!("An error occurred");
|
||||||
|
/// }
|
||||||
/// };
|
/// };
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
///```
|
||||||
///
|
#[derive(Debug)]
|
||||||
/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped
|
pub struct IntoInnerError<W>(W, crate::io::Error);
|
||||||
/// 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
|
|
||||||
/// [`TcpStream`]: ../net/struct.TcpStream.html
|
|
||||||
/// [`flush`]: trait.Write.html#tymethod.flush
|
|
||||||
pub struct BufWriter<W> {
|
|
||||||
inner: W,
|
|
||||||
buf: Vec<u8>,
|
|
||||||
written: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
||||||
///
|
///
|
||||||
|
@ -103,7 +132,7 @@ impl<W: Write> BufWriter<W> {
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(inner: W) -> BufWriter<W> {
|
pub fn new(inner: W) -> BufWriter<W> {
|
||||||
BufWriter::with_capacity(DEFAULT_CAPACITY, inner)
|
BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `BufWriter` with the specified buffer capacity.
|
/// Creates a new `BufWriter` with the specified buffer capacity.
|
||||||
|
@ -174,14 +203,41 @@ 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.
|
||||||
/// For method that will attempt to write before returning the writer see [`poll_into_inner`]
|
/// For method that will attempt to write before returning the writer see [`poll_into_inner`]
|
||||||
///
|
///
|
||||||
/// [`poll_into_inner`]: #method.poll_into_inner
|
/// [`poll_into_inner`]: #method.poll_into_inner
|
||||||
pub fn into_inner(self) -> W {
|
/// # Examples
|
||||||
self.inner
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// use async_std::io::BufWriter;
|
||||||
|
/// use async_std::net::TcpStream;
|
||||||
|
///
|
||||||
|
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?);
|
||||||
|
///
|
||||||
|
/// // unwrap the TcpStream and flush the buffer
|
||||||
|
/// let stream = buf_writer.into_inner().await.unwrap();
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
pub async fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>>
|
||||||
|
where
|
||||||
|
Self: Unpin,
|
||||||
|
{
|
||||||
|
match self.flush().await {
|
||||||
|
Err(e) => Err(IntoInnerError(self, e)),
|
||||||
|
Ok(()) => Ok(self.inner),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the internally buffered data.
|
/// Returns a reference to the internally buffered data.
|
||||||
|
@ -210,16 +266,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,
|
||||||
|
@ -227,7 +282,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);
|
||||||
|
@ -236,10 +291,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,26 +309,26 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write + fmt::Debug> fmt::Debug for BufWriter<W> {
|
impl<W: Write + fmt::Debug> fmt::Debug for BufWriter<W> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("BufReader")
|
f.debug_struct("BufWriter")
|
||||||
.field("writer", &self.inner)
|
.field("writer", &self.inner)
|
||||||
.field("buf", &self.buf)
|
.field("buf", &self.buf)
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -290,6 +345,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
136
src/io/copy.rs
136
src/io/copy.rs
|
@ -1,8 +1,11 @@
|
||||||
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::future::Future;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
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};
|
||||||
|
use crate::utils::Context as _;
|
||||||
|
|
||||||
/// Copies the entire contents of a reader into a writer.
|
/// Copies the entire contents of a reader into a writer.
|
||||||
///
|
///
|
||||||
|
@ -41,52 +44,44 @@ use crate::task::{Context, Poll};
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg(any(feature = "docs", not(feature = "unstable")))]
|
||||||
pub async fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64>
|
pub async fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64>
|
||||||
where
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,5 +91,92 @@ where
|
||||||
writer,
|
writer,
|
||||||
amt: 0,
|
amt: 0,
|
||||||
};
|
};
|
||||||
future.await
|
future.await.context(|| String::from("io::copy failed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copies the entire contents of a reader into a writer.
|
||||||
|
///
|
||||||
|
/// This function will continuously read data from `reader` and then
|
||||||
|
/// write it into `writer` in a streaming fashion until `reader`
|
||||||
|
/// returns EOF.
|
||||||
|
///
|
||||||
|
/// On success, the total number of bytes that were copied from
|
||||||
|
/// `reader` to `writer` is returned.
|
||||||
|
///
|
||||||
|
/// If you’re wanting to copy the contents of one file to another and you’re
|
||||||
|
/// working with filesystem paths, see the [`fs::copy`] function.
|
||||||
|
///
|
||||||
|
/// This function is an async version of [`std::io::copy`].
|
||||||
|
///
|
||||||
|
/// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
|
||||||
|
/// [`fs::copy`]: ../fs/fn.copy.html
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error immediately if any call to `read` or
|
||||||
|
/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
|
||||||
|
/// handled by this function and the underlying operation is retried.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::io;
|
||||||
|
///
|
||||||
|
/// let mut reader: &[u8] = b"hello";
|
||||||
|
/// let mut writer = io::stdout();
|
||||||
|
///
|
||||||
|
/// io::copy(&mut reader, &mut writer).await?;
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
#[cfg(all(feature = "unstable", not(feature = "docs")))]
|
||||||
|
pub async fn copy<R, W>(reader: R, writer: W) -> io::Result<u64>
|
||||||
|
where
|
||||||
|
R: Read + Unpin,
|
||||||
|
W: Write + Unpin,
|
||||||
|
{
|
||||||
|
pin_project! {
|
||||||
|
struct CopyFuture<R, W> {
|
||||||
|
#[pin]
|
||||||
|
reader: R,
|
||||||
|
#[pin]
|
||||||
|
writer: W,
|
||||||
|
amt: u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, W> Future for CopyFuture<R, W>
|
||||||
|
where
|
||||||
|
R: BufRead,
|
||||||
|
W: Write + Unpin,
|
||||||
|
{
|
||||||
|
type Output = io::Result<u64>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut this = self.project();
|
||||||
|
loop {
|
||||||
|
let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?;
|
||||||
|
if buffer.is_empty() {
|
||||||
|
futures_core::ready!(this.writer.as_mut().poll_flush(cx))?;
|
||||||
|
return Poll::Ready(Ok(*this.amt));
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?;
|
||||||
|
if i == 0 {
|
||||||
|
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
|
||||||
|
}
|
||||||
|
*this.amt += i as u64;
|
||||||
|
this.reader.as_mut().consume(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let future = CopyFuture {
|
||||||
|
reader: BufReader::new(reader),
|
||||||
|
writer,
|
||||||
|
amt: 0,
|
||||||
|
};
|
||||||
|
future.await.context(|| String::from("io::copy failed"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,10 @@ pub fn empty() -> Empty {
|
||||||
|
|
||||||
/// A reader that contains no data.
|
/// A reader that contains no data.
|
||||||
///
|
///
|
||||||
/// This reader is constructed by the [`sink`] function.
|
/// This reader is created by the [`empty`] function. See its
|
||||||
|
/// documentation for more.
|
||||||
///
|
///
|
||||||
/// [`sink`]: fn.sink.html
|
/// [`empty`]: fn.empty.html
|
||||||
pub struct Empty {
|
pub struct Empty {
|
||||||
_private: (),
|
_private: (),
|
||||||
}
|
}
|
||||||
|
|
349
src/io/mod.rs
349
src/io/mod.rs
|
@ -1,59 +1,328 @@
|
||||||
//! 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::fs::File;
|
||||||
|
//! use async_std::prelude::*;
|
||||||
|
//!
|
||||||
//! # 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::io;
|
//! let mut f = File::open("foo.txt").await?;
|
||||||
|
//! let mut buffer = [0; 10];
|
||||||
//!
|
//!
|
||||||
//! let stdin = io::stdin();
|
//! // read up to 10 bytes
|
||||||
//! let mut line = String::new();
|
//! let n = f.read(&mut buffer).await?;
|
||||||
//! stdin.read_line(&mut line).await?;
|
//!
|
||||||
|
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||||
//! #
|
//! #
|
||||||
//! # Ok(()) }) }
|
//! # 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::fs::File;
|
||||||
|
//! use async_std::io::SeekFrom;
|
||||||
|
//! use async_std::prelude::*;
|
||||||
|
//!
|
||||||
|
//! # 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::fs::File;
|
||||||
|
//! use async_std::io::BufReader;
|
||||||
|
//! use async_std::prelude::*;
|
||||||
|
//!
|
||||||
|
//! # 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::fs::File;
|
||||||
|
//! use async_std::io::BufWriter;
|
||||||
|
//! use async_std::io::prelude::*;
|
||||||
|
//!
|
||||||
|
//! # 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;
|
||||||
|
//!
|
||||||
|
//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
//! #
|
||||||
|
//! let mut input = String::new();
|
||||||
|
//!
|
||||||
|
//! io::stdin().read_line(&mut input).await?;
|
||||||
|
//!
|
||||||
|
//! println!("You typed: {}", input.trim());
|
||||||
|
//! #
|
||||||
|
//! # 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::fs::File;
|
||||||
|
//! use async_std::io::BufReader;
|
||||||
|
//! use async_std::prelude::*;
|
||||||
|
//!
|
||||||
|
//! # 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)]
|
const DEFAULT_BUF_SIZE: usize = 8 * 1024;
|
||||||
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
|
|
||||||
|
|
||||||
pub use buf_read::{BufRead, Lines};
|
cfg_std! {
|
||||||
pub use buf_reader::BufReader;
|
#[doc(inline)]
|
||||||
pub use buf_writer::BufWriter;
|
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
|
||||||
pub use copy::copy;
|
|
||||||
pub use cursor::Cursor;
|
|
||||||
pub use empty::{empty, Empty};
|
|
||||||
pub use read::Read;
|
|
||||||
pub use repeat::{repeat, Repeat};
|
|
||||||
pub use seek::Seek;
|
|
||||||
pub use sink::{sink, Sink};
|
|
||||||
pub use stderr::{stderr, Stderr};
|
|
||||||
pub use stdin::{stdin, Stdin};
|
|
||||||
pub use stdout::{stdout, Stdout};
|
|
||||||
pub use timeout::timeout;
|
|
||||||
pub use write::Write;
|
|
||||||
|
|
||||||
pub mod prelude;
|
pub use buf_read::{BufRead, Lines};
|
||||||
|
pub use buf_reader::BufReader;
|
||||||
|
pub use buf_writer::{BufWriter, IntoInnerError};
|
||||||
|
pub use copy::copy;
|
||||||
|
pub use cursor::Cursor;
|
||||||
|
pub use empty::{empty, Empty};
|
||||||
|
pub use read::Read;
|
||||||
|
pub use repeat::{repeat, Repeat};
|
||||||
|
pub use seek::Seek;
|
||||||
|
pub use sink::{sink, Sink};
|
||||||
|
pub use write::Write;
|
||||||
|
|
||||||
pub(crate) mod buf_read;
|
pub mod prelude;
|
||||||
pub(crate) mod read;
|
|
||||||
pub(crate) mod seek;
|
|
||||||
pub(crate) mod write;
|
|
||||||
|
|
||||||
mod buf_reader;
|
pub(crate) mod buf_read;
|
||||||
mod buf_writer;
|
pub(crate) mod read;
|
||||||
mod copy;
|
pub(crate) mod seek;
|
||||||
mod cursor;
|
pub(crate) mod write;
|
||||||
mod empty;
|
pub(crate) mod utils;
|
||||||
mod repeat;
|
|
||||||
mod sink;
|
mod buf_reader;
|
||||||
mod stderr;
|
mod buf_writer;
|
||||||
mod stdin;
|
mod copy;
|
||||||
mod stdout;
|
mod cursor;
|
||||||
mod timeout;
|
mod empty;
|
||||||
|
mod repeat;
|
||||||
|
mod sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_default! {
|
||||||
|
// For use in the print macros.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use stdio::{_eprint, _print};
|
||||||
|
|
||||||
|
pub use stderr::{stderr, Stderr};
|
||||||
|
pub use stdin::{stdin, Stdin};
|
||||||
|
pub use stdout::{stdout, Stdout};
|
||||||
|
pub use timeout::timeout;
|
||||||
|
|
||||||
|
mod timeout;
|
||||||
|
mod stderr;
|
||||||
|
mod stdin;
|
||||||
|
mod stdio;
|
||||||
|
mod stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_unstable! {
|
||||||
|
pub use stderr::StderrLock;
|
||||||
|
pub use stdin::StdinLock;
|
||||||
|
pub use stdout::StdoutLock;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! The async I/O Prelude
|
//! The async I/O prelude.
|
||||||
//!
|
//!
|
||||||
//! The purpose of this module is to alleviate imports of many common I/O traits
|
//! The purpose of this module is to alleviate imports of many common I/O traits
|
||||||
//! by adding a glob import to the top of I/O heavy modules:
|
//! by adding a glob import to the top of I/O heavy modules:
|
||||||
|
@ -17,11 +17,11 @@ pub use crate::io::Seek;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::io::Write;
|
pub use crate::io::Write;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(inline)]
|
||||||
pub use crate::io::buf_read::BufReadExt as _;
|
pub use crate::io::buf_read::BufReadExt;
|
||||||
#[doc(hidden)]
|
#[doc(inline)]
|
||||||
pub use crate::io::read::ReadExt as _;
|
pub use crate::io::read::ReadExt;
|
||||||
#[doc(hidden)]
|
#[doc(inline)]
|
||||||
pub use crate::io::seek::SeekExt as _;
|
pub use crate::io::seek::SeekExt;
|
||||||
#[doc(hidden)]
|
#[doc(inline)]
|
||||||
pub use crate::io::write::WriteExt as _;
|
pub use crate::io::write::WriteExt;
|
||||||
|
|
|
@ -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! {
|
|
||||||
if #[cfg(feature = "docs")] {
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use crate::io;
|
|
||||||
use crate::task::{Context, Poll};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension_trait! {
|
extension_trait! {
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use crate::io;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Allows reading from a byte stream.
|
Allows reading from a byte stream.
|
||||||
|
|
||||||
|
@ -37,7 +31,7 @@ extension_trait! {
|
||||||
[`std::io::Read`].
|
[`std::io::Read`].
|
||||||
|
|
||||||
Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the
|
Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the
|
||||||
trait itself, but they become available when the prelude is imported:
|
trait itself, but they become available when [`ReadExt`] from the [prelude] is imported:
|
||||||
|
|
||||||
```
|
```
|
||||||
# #[allow(unused_imports)]
|
# #[allow(unused_imports)]
|
||||||
|
@ -46,11 +40,13 @@ extension_trait! {
|
||||||
|
|
||||||
[`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
[`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||||
[`futures::io::AsyncRead`]:
|
[`futures::io::AsyncRead`]:
|
||||||
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html
|
https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html
|
||||||
[`poll_read`]: #tymethod.poll_read
|
[`poll_read`]: #tymethod.poll_read
|
||||||
[`poll_read_vectored`]: #method.poll_read_vectored
|
[`poll_read_vectored`]: #method.poll_read_vectored
|
||||||
|
[`ReadExt`]: ../io/prelude/trait.ReadExt.html
|
||||||
|
[prelude]: ../prelude/index.html
|
||||||
"#]
|
"#]
|
||||||
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 +66,14 @@ 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")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Extension methods for [`Read`].
|
||||||
|
|
||||||
|
[`Read`]: ../trait.Read.html
|
||||||
|
"#]
|
||||||
|
pub trait ReadExt: futures_io::AsyncRead {
|
||||||
#[doc = r#"
|
#[doc = r#"
|
||||||
Reads some bytes from the byte stream.
|
Reads some bytes from the byte stream.
|
||||||
|
|
||||||
|
@ -271,7 +274,7 @@ extension_trait! {
|
||||||
This function returns a new instance of `Read` which will read at most
|
This function returns a new instance of `Read` which will read at most
|
||||||
`limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
|
`limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
|
||||||
read errors will not count towards the number of bytes read and future
|
read errors will not count towards the number of bytes read and future
|
||||||
calls to [`read()`] may succeed.
|
calls to [`read`] may succeed.
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
|
@ -279,7 +282,7 @@ extension_trait! {
|
||||||
|
|
||||||
[`File`]: ../fs/struct.File.html
|
[`File`]: ../fs/struct.File.html
|
||||||
[`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok
|
[`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok
|
||||||
[`read()`]: tymethod.read
|
[`read`]: tymethod.read
|
||||||
|
|
||||||
```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 {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, Read};
|
use crate::io::{self, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, Read};
|
use crate::io::{self, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, Read};
|
use crate::io::{self, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use super::read_to_end_internal;
|
use super::read_to_end_internal;
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, Read};
|
use crate::io::{self, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
@ -37,7 +37,11 @@ impl<T: Read + Unpin + ?Sized> Future for ReadToStringFuture<'_, T> {
|
||||||
))
|
))
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
debug_assert!(buf.is_empty());
|
#[allow(clippy::debug_assert_with_mut_call)]
|
||||||
|
{
|
||||||
|
debug_assert!(buf.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
|
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
|
||||||
mem::swap(unsafe { buf.as_mut_vec() }, bytes);
|
mem::swap(unsafe { buf.as_mut_vec() }, bytes);
|
||||||
Poll::Ready(ret)
|
Poll::Ready(ret)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::future::Future;
|
|
||||||
use crate::io::{self, IoSliceMut, Read};
|
use crate::io::{self, IoSliceMut, Read};
|
||||||
use crate::task::{Context, Poll};
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ pub fn repeat(byte: u8) -> Repeat {
|
||||||
|
|
||||||
/// A reader which yields one byte over and over and over and over and over and...
|
/// A reader which yields one byte over and over and over and over and over and...
|
||||||
///
|
///
|
||||||
/// This reader is constructed by the [`repeat`] function.
|
/// This reader is created by the [`repeat`] function. See its
|
||||||
|
/// documentation for more.
|
||||||
///
|
///
|
||||||
/// [`repeat`]: fn.repeat.html
|
/// [`repeat`]: fn.repeat.html
|
||||||
pub struct Repeat {
|
pub struct Repeat {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -21,7 +18,7 @@ extension_trait! {
|
||||||
[`std::io::Seek`].
|
[`std::io::Seek`].
|
||||||
|
|
||||||
The [provided methods] do not really exist in the trait itself, but they become
|
The [provided methods] do not really exist in the trait itself, but they become
|
||||||
available when the prelude is imported:
|
available when [`SeekExt`] the [prelude] is imported:
|
||||||
|
|
||||||
```
|
```
|
||||||
# #[allow(unused_imports)]
|
# #[allow(unused_imports)]
|
||||||
|
@ -30,10 +27,12 @@ extension_trait! {
|
||||||
|
|
||||||
[`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html
|
[`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html
|
||||||
[`futures::io::AsyncSeek`]:
|
[`futures::io::AsyncSeek`]:
|
||||||
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html
|
https://docs.rs/futures/0.3/futures/io/trait.AsyncSeek.html
|
||||||
[provided methods]: #provided-methods
|
[provided methods]: #provided-methods
|
||||||
|
[`SeekExt`]: ../io/prelude/trait.SeekExt.html
|
||||||
|
[prelude]: ../prelude/index.html
|
||||||
"#]
|
"#]
|
||||||
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 +41,14 @@ extension_trait! {
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
pos: SeekFrom,
|
pos: SeekFrom,
|
||||||
) -> Poll<io::Result<u64>>;
|
) -> Poll<io::Result<u64>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Extension methods for [`Seek`].
|
||||||
|
|
||||||
|
[`Seek`]: ../trait.Seek.html
|
||||||
|
"#]
|
||||||
|
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 +76,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 +118,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)
|
|
||||||
}
|
|
||||||
}
|
|
21
src/io/seek/seek.rs
Normal file
21
src/io/seek/seek.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,8 @@ pub fn sink() -> Sink {
|
||||||
|
|
||||||
/// A writer that consumes and drops all data.
|
/// A writer that consumes and drops all data.
|
||||||
///
|
///
|
||||||
/// This writer is constructed by the [`sink`] function.
|
/// This writer is constructed by the [`sink`] function. See its documentation
|
||||||
|
/// for more.
|
||||||
///
|
///
|
||||||
/// [`sink`]: fn.sink.html
|
/// [`sink`]: fn.sink.html
|
||||||
pub struct Sink {
|
pub struct Sink {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue