Merge branch 'master' into add_future_delay

This commit is contained in:
k-nasa 2019-10-24 08:23:01 +09:00
commit 5c9cfb4fe8
96 changed files with 3740 additions and 1925 deletions

View file

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

View file

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

View file

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

View file

@ -1,7 +1,5 @@
use std::future::Future; use std::future::Future;
use cfg_if::cfg_if;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::blocking;
@ -113,22 +111,13 @@ impl DirBuilder {
} }
} }
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
}
} }
} }
} }

View file

@ -2,8 +2,6 @@ use std::ffi::OsString;
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use cfg_if::cfg_if;
use crate::fs::{FileType, Metadata}; use crate::fs::{FileType, Metadata};
use crate::io; use crate::io;
use crate::path::PathBuf; use crate::path::PathBuf;
@ -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()
}
} }
} }
} }

View file

@ -7,8 +7,6 @@ use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use cfg_if::cfg_if;
use crate::fs::{Metadata, Permissions}; use crate::fs::{Metadata, Permissions};
use crate::future; use crate::future;
use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::io::{self, Read, Seek, SeekFrom, Write};
@ -401,67 +399,54 @@ impl From<std::fs::File> for File {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; impl AsRawFd for File {
} else if #[cfg(unix)] { fn as_raw_fd(&self) -> RawFd {
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; self.file.as_raw_fd()
} else if #[cfg(windows)] { }
use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
} }
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl FromRawFd for File {
cfg_if! { unsafe fn from_raw_fd(fd: RawFd) -> File {
if #[cfg(any(unix, feature = "docs"))] { std::fs::File::from_raw_fd(fd).into()
impl AsRawFd for File {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
} }
}
impl FromRawFd for File { impl IntoRawFd for File {
unsafe fn from_raw_fd(fd: RawFd) -> File { fn into_raw_fd(self) -> RawFd {
std::fs::File::from_raw_fd(fd).into() let file = self.file.clone();
} drop(self);
} Arc::try_unwrap(file)
.expect("cannot acquire ownership of the file handle after drop")
impl IntoRawFd for File { .into_raw_fd()
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)))] cfg_windows! {
cfg_if! { use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] {
impl AsRawHandle for File {
fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle()
}
}
impl FromRawHandle for File { impl AsRawHandle for File {
unsafe fn from_raw_handle(handle: RawHandle) -> File { fn as_raw_handle(&self) -> RawHandle {
std::fs::File::from_raw_handle(handle).into() self.file.as_raw_handle()
}
} }
}
impl IntoRawHandle for File { impl FromRawHandle for File {
fn into_raw_handle(self) -> RawHandle { unsafe fn from_raw_handle(handle: RawHandle) -> File {
let file = self.file.clone(); std::fs::File::from_raw_handle(handle).into()
drop(self); }
Arc::try_unwrap(file) }
.expect("cannot acquire ownership of the file handle after drop")
.into_raw_handle() impl IntoRawHandle for File {
} 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()
} }
} }
} }

View file

@ -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 { unimplemented!()
_private: (),
} }
impl FileType { /// Returns `true` if this file type represents a regular file.
/// Returns `true` if this file type represents a regular directory. ///
/// /// If this file type represents a symbolic link, this method returns `false`.
/// If this file type represents a symbolic link, this method returns `false`. ///
/// /// # Examples
/// # Examples ///
/// /// ```no_run
/// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// #
/// # /// use async_std::fs;
/// use async_std::fs; ///
/// /// let file_type = fs::metadata("a.txt").await?.file_type();
/// let file_type = fs::metadata(".").await?.file_type(); /// println!("{:?}", file_type.is_file());
/// println!("{:?}", file_type.is_dir()); /// #
/// # /// # Ok(()) }) }
/// # Ok(()) }) } /// ```
/// ``` pub fn is_file(&self) -> bool {
pub fn is_dir(&self) -> bool { unimplemented!()
unimplemented!() }
}
/// Returns `true` if this file type represents a 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(()) }) } unimplemented!()
/// ```
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;
} }
} }

View file

@ -1,5 +1,3 @@
use cfg_if::cfg_if;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::blocking;
@ -39,193 +37,193 @@ pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
blocking::spawn(move || std::fs::metadata(path)).await blocking::spawn(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 { unimplemented!()
_private: (),
} }
impl Metadata { /// Returns `true` if this metadata is for a regular directory.
/// Returns the file type from this metadata. ///
/// /// If this metadata is for a symbolic link, this method returns `false`.
/// # Examples ///
/// /// # Examples
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// # /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::fs; /// #
/// /// use async_std::fs;
/// let metadata = fs::metadata("a.txt").await?; ///
/// println!("{:?}", metadata.file_type()); /// let metadata = fs::metadata(".").await?;
/// # /// println!("{:?}", metadata.is_dir());
/// # Ok(()) }) } /// #
/// ``` /// # Ok(()) }) }
pub fn file_type(&self) -> FileType { /// ```
unimplemented!() pub fn is_dir(&self) -> bool {
} unimplemented!()
}
/// Returns `true` if this metadata is for a regular directory.
/// /// 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 {
} unimplemented!()
}
/// 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 { unimplemented!()
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 { unimplemented!()
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> {
/// unimplemented!()
/// # 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> {
/// unimplemented!()
/// # 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> {
/// unimplemented!()
/// # 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;
} }
} }

View file

@ -1,7 +1,5 @@
use std::future::Future; use std::future::Future;
use cfg_if::cfg_if;
use crate::fs::File; use crate::fs::File;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
@ -296,27 +294,18 @@ impl Default for OpenOptions {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::fs::OpenOptionsExt;
use crate::os::unix::fs::OpenOptionsExt;
} else if #[cfg(unix)] {
use std::os::unix::fs::OpenOptionsExt;
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl OpenOptionsExt for OpenOptions {
cfg_if! { fn mode(&mut self, mode: u32) -> &mut Self {
if #[cfg(any(unix, feature = "docs"))] { self.0.mode(mode);
impl OpenOptionsExt for OpenOptions { self
fn mode(&mut self, mode: u32) -> &mut Self { }
self.0.mode(mode);
self
}
fn custom_flags(&mut self, flags: i32) -> &mut Self { fn custom_flags(&mut self, flags: i32) -> &mut Self {
self.0.custom_flags(flags); self.0.custom_flags(flags);
self self
}
} }
} }
} }

View file

@ -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 {
unimplemented!()
} }
impl Permissions { /// Configures the read-only flag.
/// Returns the read-only flag. ///
/// /// [`fs::set_permissions`]: fn.set_permissions.html
/// # Examples ///
/// /// # Examples
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// # /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::fs; /// #
/// /// use async_std::fs;
/// let perm = fs::metadata("a.txt").await?.permissions(); ///
/// println!("{:?}", perm.readonly()); /// let mut perm = fs::metadata("a.txt").await?.permissions();
/// # /// perm.set_readonly(true);
/// # Ok(()) }) } /// fs::set_permissions("a.txt", perm).await?;
/// ``` /// #
pub fn readonly(&self) -> bool { /// # Ok(()) }) }
unimplemented!() /// ```
} pub fn set_readonly(&mut self, readonly: bool) {
unimplemented!()
/// Configures the read-only flag.
///
/// [`fs::set_permissions`]: fn.set_permissions.html
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::fs;
///
/// let mut perm = fs::metadata("a.txt").await?.permissions();
/// perm.set_readonly(true);
/// fs::set_permissions("a.txt", perm).await?;
/// #
/// # Ok(()) }) }
/// ```
pub fn set_readonly(&mut self, readonly: bool) {
unimplemented!()
}
} }
} else {
pub use std::fs::Permissions;
} }
} }

View file

@ -1,24 +1,17 @@
use crate::utils::extension_trait; cfg_unstable! {
mod delay;
cfg_if::cfg_if! { use std::time::Duration;
if #[cfg(feature = "docs")] {
use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll}; use delay::DelayFuture;
}
}
cfg_if::cfg_if! {
if #[cfg(any(feature = "unstable", feature = "docs"))] {
mod delay;
use std::time::Duration;
use delay::DelayFuture;
}
} }
extension_trait! { extension_trait! {
use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll};
#[doc = r#" #[doc = r#"
A future represents an asynchronous computation. A future represents an asynchronous computation.

View file

@ -30,7 +30,7 @@ use crate::future::Future;
/// } /// }
/// } /// }
/// ``` /// ```
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub trait IntoFuture { pub trait IntoFuture {
/// The type of value produced on completion. /// The type of value produced on completion.

View file

@ -44,12 +44,6 @@
#[doc(inline)] #[doc(inline)]
pub use async_macros::{join, try_join}; pub use async_macros::{join, try_join};
#[doc(inline)]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub use async_macros::{select, try_select};
use cfg_if::cfg_if;
pub use future::Future; pub use future::Future;
pub use pending::pending; pub use pending::pending;
pub use poll_fn::poll_fn; pub use poll_fn::poll_fn;
@ -62,10 +56,10 @@ mod poll_fn;
mod ready; mod ready;
mod timeout; mod timeout;
cfg_if! { cfg_unstable! {
if #[cfg(any(feature = "unstable", feature = "docs"))] { #[doc(inline)]
mod into_future; pub use async_macros::{select, try_select};
pub use into_future::IntoFuture; pub use into_future::IntoFuture;
} mod into_future;
} }

View file

@ -4,6 +4,7 @@ use std::pin::Pin;
use std::time::Duration; use std::time::Duration;
use futures_timer::Delay; use futures_timer::Delay;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -39,24 +40,24 @@ where
f.await f.await
} }
/// A future that times out after a duration of time. pin_project! {
struct TimeoutFuture<F> { /// A future that times out after a duration of time.
future: F, struct TimeoutFuture<F> {
delay: Delay, #[pin]
} future: F,
#[pin]
impl<F> TimeoutFuture<F> { delay: Delay,
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,
}, },

View file

@ -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()))))
} }
} }

View file

@ -12,19 +12,12 @@ use read_until::ReadUntilFuture;
use std::mem; use std::mem;
use std::pin::Pin; use std::pin::Pin;
use cfg_if::cfg_if;
use crate::io; use crate::io;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use crate::utils::extension_trait;
cfg_if! {
if #[cfg(feature = "docs")] {
use std::ops::{Deref, DerefMut};
}
}
extension_trait! { extension_trait! {
use std::ops::{Deref, DerefMut};
#[doc = r#" #[doc = r#"
Allows reading from a buffered byte stream. Allows reading from a buffered byte stream.

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
} }
} }
} }

View file

@ -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.
@ -309,34 +303,34 @@ extension_trait! {
#[doc = r#" #[doc = r#"
Creates a "by reference" adaptor for this instance of `Read`. Creates a "by reference" adaptor for this instance of `Read`.
The returned adaptor also implements `Read` and will simply borrow this The returned adaptor also implements `Read` and will simply borrow this
current reader. current reader.
# Examples # Examples
[`File`][file]s implement `Read`: [`File`][file]s implement `Read`:
[file]: ../fs/struct.File.html [file]: ../fs/struct.File.html
```no_run ```no_run
# fn main() -> std::io::Result<()> { async_std::task::block_on(async { # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
use async_std::fs::File; use async_std::fs::File;
let mut f = File::open("foo.txt").await?; let mut f = File::open("foo.txt").await?;
let mut buffer = Vec::new(); let mut buffer = Vec::new();
let mut other_buffer = Vec::new(); let mut other_buffer = Vec::new();
{ {
let reference = f.by_ref(); let reference = f.by_ref();
// read at most 5 bytes // read at most 5 bytes
reference.take(5).read_to_end(&mut buffer).await?; reference.take(5).read_to_end(&mut buffer).await?;
} // drop our &mut reference so we can use f again } // drop our &mut reference so we can use f again
// original file still usable, read the rest // original file still usable, read the rest
f.read_to_end(&mut other_buffer).await?; f.read_to_end(&mut other_buffer).await?;
# #
@ -348,27 +342,27 @@ extension_trait! {
#[doc = r#" #[doc = r#"
Transforms this `Read` instance to a `Stream` over its bytes. Transforms this `Read` instance to a `Stream` over its bytes.
The returned type implements `Stream` where the `Item` is The returned type implements `Stream` where the `Item` is
`Result<u8, io::Error>`. `Result<u8, io::Error>`.
The yielded item is `Ok` if a byte was successfully read and `Err` The yielded item is `Ok` if a byte was successfully read and `Err`
otherwise. EOF is mapped to returning `None` from this iterator. otherwise. EOF is mapped to returning `None` from this iterator.
# Examples # Examples
[`File`][file]s implement `Read`: [`File`][file]s implement `Read`:
[file]: ../fs/struct.File.html [file]: ../fs/struct.File.html
```no_run ```no_run
# fn main() -> std::io::Result<()> { async_std::task::block_on(async { # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
use async_std::fs::File; use async_std::fs::File;
let f = File::open("foo.txt").await?; let f = File::open("foo.txt").await?;
let mut s = f.bytes(); let mut s = f.bytes();
while let Some(byte) = s.next().await { while let Some(byte) = s.next().await {
println!("{}", byte.unwrap()); println!("{}", byte.unwrap());
} }
@ -382,29 +376,29 @@ extension_trait! {
#[doc = r#" #[doc = r#"
Creates an adaptor which will chain this stream with another. Creates an adaptor which will chain this stream with another.
The returned `Read` instance will first read all bytes from this object The returned `Read` instance will first read all bytes from this object
until EOF is encountered. Afterwards the output is equivalent to the until EOF is encountered. Afterwards the output is equivalent to the
output of `next`. output of `next`.
# Examples # Examples
[`File`][file]s implement `Read`: [`File`][file]s implement `Read`:
[file]: ../fs/struct.File.html [file]: ../fs/struct.File.html
```no_run ```no_run
# fn main() -> std::io::Result<()> { async_std::task::block_on(async { # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
use async_std::fs::File; use async_std::fs::File;
let f1 = File::open("foo.txt").await?; let f1 = File::open("foo.txt").await?;
let f2 = File::open("bar.txt").await?; let f2 = File::open("bar.txt").await?;
let mut handle = f1.chain(f2); let mut handle = f1.chain(f2);
let mut buffer = String::new(); let mut buffer = String::new();
// read the value into a String. We could use any Read method here, // read the value into a String. We could use any Read method here,
// this is just one example. // this is just one example.
handle.read_to_string(&mut buffer).await?; handle.read_to_string(&mut buffer).await?;

View file

@ -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);
} }
} }

View file

@ -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.
@ -114,19 +111,3 @@ extension_trait! {
} }
} }
} }
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct SeekFuture<'a, T: Unpin + ?Sized> {
seeker: &'a mut T,
pos: SeekFrom,
}
impl<T: SeekExt + Unpin + ?Sized> Future for SeekFuture<'_, T> {
type Output = io::Result<u64>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let pos = self.pos;
Pin::new(&mut *self.seeker).poll_seek(cx, pos)
}
}

21
src/io/seek/seek.rs Normal file
View file

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

View file

@ -1,8 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use cfg_if::cfg_if;
use crate::future::Future; use crate::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{blocking, Context, JoinHandle, Poll};
@ -162,35 +160,22 @@ impl Write for Stderr {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::windows::io::{AsRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, RawFd};
} else if #[cfg(windows)] {
use std::os::windows::io::{AsRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for Stderr {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { std::io::stderr().as_raw_fd()
impl AsRawFd for Stderr {
fn as_raw_fd(&self) -> RawFd {
std::io::stderr().as_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(unix)))] cfg_windows! {
cfg_if! { use crate::os::windows::io::{AsRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] {
impl AsRawHandle for Stderr { impl AsRawHandle for Stderr {
fn as_raw_handle(&self) -> RawHandle { fn as_raw_handle(&self) -> RawHandle {
std::io::stderr().as_raw_handle() std::io::stderr().as_raw_handle()
}
} }
} }
} }

View file

@ -1,8 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use cfg_if::cfg_if;
use crate::future::{self, Future}; use crate::future::{self, Future};
use crate::io::{self, Read}; use crate::io::{self, Read};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{blocking, Context, JoinHandle, Poll};
@ -186,35 +184,22 @@ impl Read for Stdin {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::windows::io::{AsRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, RawFd};
} else if #[cfg(windows)] {
use std::os::windows::io::{AsRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for Stdin {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { std::io::stdin().as_raw_fd()
impl AsRawFd for Stdin {
fn as_raw_fd(&self) -> RawFd {
std::io::stdin().as_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(unix)))] cfg_windows! {
cfg_if! { use crate::os::windows::io::{AsRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] {
impl AsRawHandle for Stdin { impl AsRawHandle for Stdin {
fn as_raw_handle(&self) -> RawHandle { fn as_raw_handle(&self) -> RawHandle {
std::io::stdin().as_raw_handle() std::io::stdin().as_raw_handle()
}
} }
} }
} }

View file

@ -1,8 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use cfg_if::cfg_if;
use crate::future::Future; use crate::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{blocking, Context, JoinHandle, Poll};
@ -162,35 +160,22 @@ impl Write for Stdout {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, RawFd};
use crate::os::windows::io::{AsRawHandle, RawHandle};
} else if #[cfg(unix)] {
use std::os::unix::io::{AsRawFd, RawFd};
} else if #[cfg(windows)] {
use std::os::windows::io::{AsRawHandle, RawHandle};
}
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl AsRawFd for Stdout {
cfg_if! { fn as_raw_fd(&self) -> RawFd {
if #[cfg(any(unix, feature = "docs"))] { std::io::stdout().as_raw_fd()
impl AsRawFd for Stdout {
fn as_raw_fd(&self) -> RawFd {
std::io::stdout().as_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(unix)))] cfg_windows! {
cfg_if! { use crate::os::windows::io::{AsRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] {
impl AsRawHandle for Stdout { impl AsRawHandle for Stdout {
fn as_raw_handle(&self) -> RawHandle { fn as_raw_handle(&self) -> RawHandle {
std::io::stdout().as_raw_handle() std::io::stdout().as_raw_handle()
}
} }
} }
} }

View file

@ -3,7 +3,7 @@ use std::task::{Context, Poll};
use std::time::Duration; use std::time::Duration;
use futures_timer::Delay; use futures_timer::Delay;
use pin_utils::unsafe_pinned; use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::io; use crate::io;
@ -43,22 +43,18 @@ where
.await .await
} }
/// Future returned by the `FutureExt::timeout` method. pin_project! {
#[derive(Debug)] /// Future returned by the `FutureExt::timeout` method.
pub struct Timeout<F, T> #[derive(Debug)]
where pub struct Timeout<F, T>
F: Future<Output = io::Result<T>>, where
{ F: Future<Output = io::Result<T>>,
future: F, {
timeout: Delay, #[pin]
} future: F,
#[pin]
impl<F, T> Timeout<F, T> timeout: Delay,
where }
F: Future<Output = io::Result<T>>,
{
unsafe_pinned!(future: F);
unsafe_pinned!(timeout: Delay);
} }
impl<F, T> Future for Timeout<F, T> impl<F, T> Future for Timeout<F, T>
@ -67,14 +63,15 @@ where
{ {
type Output = io::Result<T>; type Output = io::Result<T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.as_mut().future().poll(cx) { let this = self.project();
match this.future.poll(cx) {
Poll::Pending => {} Poll::Pending => {}
other => return other, other => return other,
} }
if self.timeout().poll(cx).is_ready() { if this.timeout.poll(cx).is_ready() {
let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out"));
Poll::Ready(err) Poll::Ready(err)
} else { } else {
Poll::Pending Poll::Pending

View file

@ -10,22 +10,14 @@ use write_all::WriteAllFuture;
use write_fmt::WriteFmtFuture; use write_fmt::WriteFmtFuture;
use write_vectored::WriteVectoredFuture; use write_vectored::WriteVectoredFuture;
use cfg_if::cfg_if; use crate::io::{self, IoSlice};
use crate::io::IoSlice;
use crate::utils::extension_trait;
use crate::io;
cfg_if! {
if #[cfg(feature = "docs")] {
use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll};
}
}
extension_trait! { extension_trait! {
use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll};
#[doc = r#" #[doc = r#"
Allows writing to a byte stream. Allows writing to a byte stream.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,8 +2,6 @@ use std::io::{IoSlice, IoSliceMut, Read as _, Write as _};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use cfg_if::cfg_if;
use crate::future; use crate::future;
use crate::io::{self, Read, Write}; use crate::io::{self, Read, Write};
use crate::net::driver::Watcher; use crate::net::driver::Watcher;
@ -367,59 +365,46 @@ impl From<std::net::TcpStream> for TcpStream {
} }
} }
cfg_if! { cfg_unix! {
if #[cfg(feature = "docs")] { use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; impl AsRawFd for TcpStream {
} else if #[cfg(unix)] { fn as_raw_fd(&self) -> RawFd {
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; self.watcher.get_ref().as_raw_fd()
} else if #[cfg(windows)] { }
// use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
} }
}
#[cfg_attr(feature = "docs", doc(cfg(unix)))] impl FromRawFd for TcpStream {
cfg_if! { unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
if #[cfg(any(unix, feature = "docs"))] { std::net::TcpStream::from_raw_fd(fd).into()
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd()
}
} }
}
impl FromRawFd for TcpStream { impl IntoRawFd for TcpStream {
unsafe fn from_raw_fd(fd: RawFd) -> TcpStream { fn into_raw_fd(self) -> RawFd {
std::net::TcpStream::from_raw_fd(fd).into() self.watcher.into_inner().into_raw_fd()
}
}
impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd()
}
} }
} }
} }
#[cfg_attr(feature = "docs", doc(cfg(windows)))] cfg_windows! {
cfg_if! { // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
if #[cfg(any(windows, feature = "docs"))] { //
// impl AsRawSocket for TcpStream { // impl AsRawSocket for TcpStream {
// fn as_raw_socket(&self) -> RawSocket { // fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket // self.raw_socket
// } // }
// } // }
// //
// impl FromRawSocket for TcpStream { // impl FromRawSocket for TcpStream {
// unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream { // unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream {
// net::TcpStream::from_raw_socket(handle).try_into().unwrap() // net::TcpStream::from_raw_socket(handle).try_into().unwrap()
// } // }
// } // }
// //
// impl IntoRawSocket for TcpListener { // impl IntoRawSocket for TcpListener {
// fn into_raw_socket(self) -> RawSocket { // fn into_raw_socket(self) -> RawSocket {
// self.raw_socket // self.raw_socket
// } // }
// } // }
}
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,20 +1,25 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream that yields elements by calling a closure. pin_project! {
/// /// A stream that yields elements by calling a closure.
/// This stream is constructed by [`from_fn`] function. ///
/// /// This stream is constructed by [`from_fn`] function.
/// [`from_fn`]: fn.from_fn.html ///
#[derive(Debug)] /// [`from_fn`]: fn.from_fn.html
pub struct FromFn<F, Fut, T> { #[derive(Debug)]
f: F, pub struct FromFn<F, Fut, T> {
future: Option<Fut>, f: F,
__t: PhantomData<T>, #[pin]
future: Option<Fut>,
__t: PhantomData<T>,
}
} }
/// Creates a new stream where to produce each new element a provided closure is called. /// Creates a new stream where to produce each new element a provided closure is called.
@ -25,7 +30,7 @@ pub struct FromFn<F, Fut, T> {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # fn main() { async_std::task::block_on(async { /// # async_std::task::block_on(async {
/// # /// #
/// use async_std::prelude::*; /// use async_std::prelude::*;
/// use async_std::sync::Mutex; /// use async_std::sync::Mutex;
@ -53,8 +58,7 @@ pub struct FromFn<F, Fut, T> {
/// assert_eq!(s.next().await, Some(3)); /// assert_eq!(s.next().await, Some(3));
/// assert_eq!(s.next().await, None); /// assert_eq!(s.next().await, None);
/// # /// #
/// # }) } /// # })
///
/// ``` /// ```
pub fn from_fn<T, F, Fut>(f: F) -> FromFn<F, Fut, T> pub fn from_fn<T, F, Fut>(f: F) -> FromFn<F, Fut, T>
where where
@ -68,11 +72,6 @@ where
} }
} }
impl<F, Fut, T> FromFn<F, Fut, T> {
pin_utils::unsafe_unpinned!(f: F);
pin_utils::unsafe_pinned!(future: Option<Fut>);
}
impl<F, Fut, T> Stream for FromFn<F, Fut, T> impl<F, Fut, T> Stream for FromFn<F, Fut, T>
where where
F: FnMut() -> Fut, F: FnMut() -> Fut,
@ -80,20 +79,18 @@ where
{ {
type Item = T; type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
loop { loop {
match &self.future { if this.future.is_some() {
Some(_) => { let next =
let next = futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx));
futures_core::ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); this.future.set(None);
self.as_mut().future().set(None);
return Poll::Ready(next); return Poll::Ready(next);
} } else {
None => { let fut = (this.f)();
let fut = (self.as_mut().f())(); this.future.set(Some(fut));
self.as_mut().future().set(Some(fut));
}
} }
} }
} }

View file

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

View file

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

View file

@ -4,8 +4,6 @@ use std::time::{Duration, Instant};
use futures_core::future::Future; use futures_core::future::Future;
use futures_core::stream::Stream; use futures_core::stream::Stream;
use pin_utils::unsafe_pinned;
use futures_timer::Delay; use futures_timer::Delay;
/// Creates a new stream that yields at a set interval. /// Creates a new stream that yields at a set interval.
@ -43,8 +41,8 @@ use futures_timer::Delay;
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[doc(inline)]
pub fn interval(dur: Duration) -> Interval { pub fn interval(dur: Duration) -> Interval {
Interval { Interval {
delay: Delay::new(dur), delay: Delay::new(dur),
@ -54,23 +52,19 @@ pub fn interval(dur: Duration) -> Interval {
/// A stream representing notifications at fixed interval /// A stream representing notifications at fixed interval
/// ///
#[derive(Debug)] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[doc(inline)] #[derive(Debug)]
pub struct Interval { pub struct Interval {
delay: Delay, delay: Delay,
interval: Duration, interval: Duration,
} }
impl Interval {
unsafe_pinned!(delay: Delay);
}
impl Stream for Interval { impl Stream for Interval {
type Item = (); type Item = ();
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if Pin::new(&mut *self).delay().poll(cx).is_pending() { if Pin::new(&mut self.delay).poll(cx).is_pending() {
return Poll::Pending; return Poll::Pending;
} }
let when = Instant::now(); let when = Instant::now();

View file

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

View file

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

View file

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

View file

@ -11,8 +11,8 @@ use crate::stream::Stream;
/// [`product`]: trait.Product.html#tymethod.product /// [`product`]: trait.Product.html#tymethod.product
/// [`FromStream`]: trait.FromStream.html /// [`FromStream`]: trait.FromStream.html
/// [`Stream::product`]: trait.Stream.html#method.product /// [`Stream::product`]: trait.Stream.html#method.product
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub trait Product<A = Self>: Sized { pub trait Product<A = Self>: Sized {
/// Method which takes a stream and generates `Self` from the elements by /// Method which takes a stream and generates `Self` from the elements by
/// multiplying the items. /// multiplying the items.

100
src/stream/repeat_with.rs Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

45
src/stream/stream/last.rs Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -36,6 +36,7 @@ mod fuse;
mod ge; mod ge;
mod gt; mod gt;
mod inspect; mod inspect;
mod last;
mod le; mod le;
mod lt; mod lt;
mod map; mod map;
@ -64,6 +65,7 @@ use fold::FoldFuture;
use for_each::ForEachFuture; use for_each::ForEachFuture;
use ge::GeFuture; use ge::GeFuture;
use gt::GtFuture; use gt::GtFuture;
use last::LastFuture;
use le::LeFuture; use le::LeFuture;
use lt::LtFuture; use lt::LtFuture;
use min_by::MinByFuture; use min_by::MinByFuture;
@ -89,32 +91,22 @@ pub use zip::Zip;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::marker::PhantomData; use std::marker::PhantomData;
use cfg_if::cfg_if; cfg_unstable! {
use std::pin::Pin;
use crate::utils::extension_trait; use crate::future::Future;
use crate::stream::FromStream;
cfg_if! { pub use merge::Merge;
if #[cfg(feature = "docs")] {
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll}; mod merge;
}
}
cfg_if! {
if #[cfg(any(feature = "unstable", feature = "docs"))] {
mod merge;
use std::pin::Pin;
use crate::future::Future;
use crate::stream::FromStream;
pub use merge::Merge;
}
} }
extension_trait! { extension_trait! {
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll};
#[doc = r#" #[doc = r#"
An asynchronous stream of values. An asynchronous stream of values.
@ -456,6 +448,54 @@ extension_trait! {
Inspect::new(self, f) Inspect::new(self, f)
} }
#[doc = r#"
Returns the last element of the stream.
# Examples
Basic usage:
```
# fn main() { async_std::task::block_on(async {
#
use std::collections::VecDeque;
use async_std::prelude::*;
let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
let last = s.last().await;
assert_eq!(last, Some(3));
#
# }) }
```
An empty stream will return `None:
```
# fn main() { async_std::task::block_on(async {
#
use std::collections::VecDeque;
use async_std::prelude::*;
let s: VecDeque<usize> = vec![].into_iter().collect();
let last = s.last().await;
assert_eq!(last, None);
#
# }) }
```
"#]
fn last(
self,
) -> impl Future<Output = Option<Self::Item>> [LastFuture<Self, Self::Item>]
where
Self: Sized,
{
LastFuture::new(self)
}
#[doc = r#" #[doc = r#"
Transforms this `Stream` into a "fused" `Stream` such that after the first time Transforms this `Stream` into a "fused" `Stream` such that after the first time
`poll` returns `Poll::Ready(None)`, all future calls to `poll` will also return `poll` returns `Poll::Ready(None)`, all future calls to `poll` will also return
@ -1226,7 +1266,7 @@ extension_trait! {
[`stream`]: trait.Stream.html#tymethod.next [`stream`]: trait.Stream.html#tymethod.next
"#] "#]
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"]
fn collect<'a, B>( fn collect<'a, B>(
@ -1265,7 +1305,7 @@ extension_trait! {
# }); # });
``` ```
"#] "#]
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn merge<U>(self, other: U) -> Merge<Self, U> fn merge<U>(self, other: U) -> Merge<Self, U>
where where
@ -1295,9 +1335,9 @@ extension_trait! {
let s4 = VecDeque::from(vec![1, 2, 4]); let s4 = VecDeque::from(vec![1, 2, 4]);
assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal)); assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal));
assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less)); assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less));
assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater)); assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater));
assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less)); assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less));
assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater)); assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater));
# #
# }) } # }) }
``` ```
@ -1316,7 +1356,7 @@ extension_trait! {
#[doc = r#" #[doc = r#"
Lexicographically compares the elements of this `Stream` with those Lexicographically compares the elements of this `Stream` with those
of another using 'Ord'. of another using 'Ord'.
# Examples # Examples
@ -1333,9 +1373,9 @@ extension_trait! {
let s4 = VecDeque::from(vec![1, 2, 4]); let s4 = VecDeque::from(vec![1, 2, 4]);
assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal); assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal);
assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less); assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less);
assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater); assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater);
assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less); assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less);
assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater); assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater);
# #
# }) } # }) }
``` ```

View file

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

View file

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

View file

@ -1,19 +1,21 @@
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
/// A stream to skip first n elements of another stream. pin_project! {
#[derive(Debug)] /// A stream to skip first n elements of another stream.
pub struct Skip<S> { #[derive(Debug)]
stream: S, pub struct Skip<S> {
n: usize, #[pin]
stream: S,
n: usize,
}
} }
impl<S> Skip<S> { impl<S> Skip<S> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(n: usize);
pub(crate) fn new(stream: S, n: usize) -> Self { pub(crate) fn new(stream: S, n: usize) -> Self {
Skip { stream, n } Skip { stream, n }
} }
@ -25,14 +27,15 @@ where
{ {
type Item = S::Item; type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
loop { loop {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
match next { match next {
Some(v) => match self.n { Some(v) => match *this.n {
0 => return Poll::Ready(Some(v)), 0 => return Poll::Ready(Some(v)),
_ => *self.as_mut().n() -= 1, _ => *this.n -= 1,
}, },
None => return Poll::Ready(None), None => return Poll::Ready(None),
} }

View file

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

View file

@ -1,21 +1,22 @@
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream that steps a given amount of elements of another stream. pin_project! {
#[derive(Debug)] /// A stream that steps a given amount of elements of another stream.
pub struct StepBy<S> { #[derive(Debug)]
stream: S, pub struct StepBy<S> {
step: usize, #[pin]
i: usize, stream: S,
step: usize,
i: usize,
}
} }
impl<S> StepBy<S> { impl<S> StepBy<S> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(step: usize);
pin_utils::unsafe_unpinned!(i: usize);
pub(crate) fn new(stream: S, step: usize) -> Self { pub(crate) fn new(stream: S, step: usize) -> Self {
StepBy { StepBy {
stream, stream,
@ -31,17 +32,18 @@ where
{ {
type Item = S::Item; type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
loop { loop {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
match next { match next {
Some(v) => match self.i { Some(v) => match this.i {
0 => { 0 => {
*self.as_mut().i() = self.step; *this.i = *this.step;
return Poll::Ready(Some(v)); return Poll::Ready(Some(v));
} }
_ => *self.as_mut().i() -= 1, _ => *this.i -= 1,
}, },
None => return Poll::Ready(None), None => return Poll::Ready(None),
} }

View file

@ -1,33 +1,32 @@
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream that yields the first `n` items of another stream. pin_project! {
#[derive(Clone, Debug)] /// A stream that yields the first `n` items of another stream.
pub struct Take<S> { #[derive(Clone, Debug)]
pub(crate) stream: S, pub struct Take<S> {
pub(crate) remaining: usize, #[pin]
} pub(crate) stream: S,
pub(crate) remaining: usize,
impl<S: Unpin> Unpin for Take<S> {} }
impl<S: Stream> Take<S> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(remaining: usize);
} }
impl<S: Stream> Stream for Take<S> { impl<S: Stream> Stream for Take<S> {
type Item = S::Item; type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
if self.remaining == 0 { let this = self.project();
if *this.remaining == 0 {
Poll::Ready(None) Poll::Ready(None)
} else { } else {
let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let next = futures_core::ready!(this.stream.poll_next(cx));
match next { match next {
Some(_) => *self.as_mut().remaining() -= 1, Some(_) => *this.remaining -= 1,
None => *self.as_mut().remaining() = 0, None => *this.remaining = 0,
} }
Poll::Ready(next) Poll::Ready(next)
} }

View file

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

View file

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

View file

@ -1,23 +1,25 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::future::Future; use crate::future::Future;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
#[doc(hidden)] pin_project! {
#[allow(missing_debug_implementations)] #[doc(hidden)]
pub struct TryForEeachFuture<S, F, T, R> { #[allow(missing_debug_implementations)]
stream: S, pub struct TryForEeachFuture<S, F, T, R> {
f: F, #[pin]
__from: PhantomData<T>, stream: S,
__to: PhantomData<R>, f: F,
__from: PhantomData<T>,
__to: PhantomData<R>,
}
} }
impl<S, F, T, R> TryForEeachFuture<S, F, T, R> { impl<S, F, T, R> TryForEeachFuture<S, F, T, R> {
pin_utils::unsafe_pinned!(stream: S);
pin_utils::unsafe_unpinned!(f: F);
pub(crate) fn new(stream: S, f: F) -> Self { pub(crate) fn new(stream: S, f: F) -> Self {
TryForEeachFuture { TryForEeachFuture {
stream, stream,
@ -36,14 +38,15 @@ where
{ {
type Output = Result<(), E>; type Output = Result<(), E>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
loop { loop {
let item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); let item = futures_core::ready!(this.stream.as_mut().poll_next(cx));
match item { match item {
None => return Poll::Ready(Ok(())), None => return Poll::Ready(Ok(())),
Some(v) => { Some(v) => {
let res = (self.as_mut().f())(v); let res = (this.f)(v);
if let Err(e) = res { if let Err(e) = res {
return Poll::Ready(Err(e)); return Poll::Ready(Err(e));
} }

View file

@ -1,14 +1,20 @@
use std::fmt; use std::fmt;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// An iterator that iterates two other iterators simultaneously. pin_project! {
pub struct Zip<A: Stream, B> { /// An iterator that iterates two other iterators simultaneously.
item_slot: Option<A::Item>, pub struct Zip<A: Stream, B> {
first: A, item_slot: Option<A::Item>,
second: B, #[pin]
first: A,
#[pin]
second: B,
}
} }
impl<A: Stream + fmt::Debug, B: fmt::Debug> fmt::Debug for Zip<A, B> { impl<A: Stream + fmt::Debug, B: fmt::Debug> fmt::Debug for Zip<A, B> {
@ -20,8 +26,6 @@ impl<A: Stream + fmt::Debug, B: fmt::Debug> fmt::Debug for Zip<A, B> {
} }
} }
impl<A: Stream + Unpin, B: Unpin> Unpin for Zip<A, B> {}
impl<A: Stream, B> Zip<A, B> { impl<A: Stream, B> Zip<A, B> {
pub(crate) fn new(first: A, second: B) -> Self { pub(crate) fn new(first: A, second: B) -> Self {
Zip { Zip {
@ -30,25 +34,22 @@ impl<A: Stream, B> Zip<A, B> {
second, second,
} }
} }
pin_utils::unsafe_unpinned!(item_slot: Option<A::Item>);
pin_utils::unsafe_pinned!(first: A);
pin_utils::unsafe_pinned!(second: B);
} }
impl<A: Stream, B: Stream> Stream for Zip<A, B> { impl<A: Stream, B: Stream> Stream for Zip<A, B> {
type Item = (A::Item, B::Item); type Item = (A::Item, B::Item);
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if self.as_mut().item_slot().is_none() { let this = self.project();
match self.as_mut().first().poll_next(cx) { if this.item_slot.is_none() {
match this.first.poll_next(cx) {
Poll::Pending => return Poll::Pending, Poll::Pending => return Poll::Pending,
Poll::Ready(None) => return Poll::Ready(None), Poll::Ready(None) => return Poll::Ready(None),
Poll::Ready(Some(item)) => *self.as_mut().item_slot() = Some(item), Poll::Ready(Some(item)) => *this.item_slot = Some(item),
} }
} }
let second_item = futures_core::ready!(self.as_mut().second().poll_next(cx)); let second_item = futures_core::ready!(this.second.poll_next(cx));
let first_item = self.as_mut().item_slot().take().unwrap(); let first_item = this.item_slot.take().unwrap();
Poll::Ready(second_item.map(|second_item| (first_item, second_item))) Poll::Ready(second_item.map(|second_item| (first_item, second_item)))
} }
} }

View file

@ -11,8 +11,8 @@ use crate::stream::Stream;
/// [`sum`]: trait.Sum.html#tymethod.sum /// [`sum`]: trait.Sum.html#tymethod.sum
/// [`FromStream`]: trait.FromStream.html /// [`FromStream`]: trait.FromStream.html
/// [`Stream::sum`]: trait.Stream.html#method.sum /// [`Stream::sum`]: trait.Stream.html#method.sum
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub trait Sum<A = Self>: Sized { pub trait Sum<A = Self>: Sized {
/// Method which takes a stream and generates `Self` from the elements by /// Method which takes a stream and generates `Self` from the elements by
/// "summing up" the items. /// "summing up" the items.

View file

@ -8,7 +8,7 @@ use crate::sync::Mutex;
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # fn main() { async_std::task::block_on(async { /// # async_std::task::block_on(async {
/// # /// #
/// use async_std::sync::{Arc, Barrier}; /// use async_std::sync::{Arc, Barrier};
/// use async_std::task; /// use async_std::task;
@ -30,8 +30,8 @@ use crate::sync::Mutex;
/// handle.await; /// handle.await;
/// } /// }
/// # }); /// # });
/// # }
/// ``` /// ```
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[derive(Debug)] #[derive(Debug)]
pub struct Barrier { pub struct Barrier {
@ -61,6 +61,7 @@ struct BarrierState {
/// let barrier = Barrier::new(1); /// let barrier = Barrier::new(1);
/// let barrier_wait_result = barrier.wait(); /// let barrier_wait_result = barrier.wait();
/// ``` /// ```
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct BarrierWaitResult(bool); pub struct BarrierWaitResult(bool);
@ -118,7 +119,7 @@ impl Barrier {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # fn main() { async_std::task::block_on(async { /// # async_std::task::block_on(async {
/// # /// #
/// use async_std::sync::{Arc, Barrier}; /// use async_std::sync::{Arc, Barrier};
/// use async_std::task; /// use async_std::task;
@ -140,7 +141,6 @@ impl Barrier {
/// handle.await; /// handle.await;
/// } /// }
/// # }); /// # });
/// # }
/// ``` /// ```
pub async fn wait(&self) -> BarrierWaitResult { pub async fn wait(&self) -> BarrierWaitResult {
let mut lock = self.state.lock().await; let mut lock = self.state.lock().await;
@ -188,7 +188,7 @@ impl BarrierWaitResult {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # fn main() { async_std::task::block_on(async { /// # async_std::task::block_on(async {
/// # /// #
/// use async_std::sync::Barrier; /// use async_std::sync::Barrier;
/// ///
@ -196,7 +196,6 @@ impl BarrierWaitResult {
/// let barrier_wait_result = barrier.wait().await; /// let barrier_wait_result = barrier.wait().await;
/// println!("{:?}", barrier_wait_result.is_leader()); /// println!("{:?}", barrier_wait_result.is_leader());
/// # }); /// # });
/// # }
/// ``` /// ```
pub fn is_leader(&self) -> bool { pub fn is_leader(&self) -> bool {
self.0 self.0

1132
src/sync/channel.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -32,14 +32,16 @@
#[doc(inline)] #[doc(inline)]
pub use std::sync::{Arc, Weak}; pub use std::sync::{Arc, Weak};
#[cfg(any(feature = "unstable", feature = "docs"))]
pub use barrier::{Barrier, BarrierWaitResult};
pub use mutex::{Mutex, MutexGuard}; pub use mutex::{Mutex, MutexGuard};
pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
#[cfg(any(feature = "unstable", feature = "docs"))]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
mod barrier;
mod mutex; mod mutex;
mod rwlock; mod rwlock;
cfg_unstable! {
pub use barrier::{Barrier, BarrierWaitResult};
pub use channel::{channel, Sender, Receiver};
mod barrier;
mod channel;
}

View file

@ -1,10 +1,13 @@
use std::cell::UnsafeCell; use std::cell::{Cell, UnsafeCell};
use std::mem::{self, ManuallyDrop}; use std::mem::{self, ManuallyDrop};
use std::panic::{self, AssertUnwindSafe, UnwindSafe}; use std::panic::{self, AssertUnwindSafe, UnwindSafe};
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::task::{RawWaker, RawWakerVTable}; use std::task::{RawWaker, RawWakerVTable};
use std::thread::{self, Thread}; use std::thread;
use crossbeam_utils::sync::Parker;
use pin_project_lite::pin_project;
use super::task; use super::task;
use super::task_local; use super::task_local;
@ -98,19 +101,18 @@ where
} }
} }
struct CatchUnwindFuture<F> { pin_project! {
future: F, struct CatchUnwindFuture<F> {
} #[pin]
future: F,
impl<F> CatchUnwindFuture<F> { }
pin_utils::unsafe_pinned!(future: F);
} }
impl<F: Future + UnwindSafe> Future for CatchUnwindFuture<F> { impl<F: Future + UnwindSafe> Future for CatchUnwindFuture<F> {
type Output = thread::Result<F::Output>; type Output = thread::Result<F::Output>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
panic::catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) panic::catch_unwind(AssertUnwindSafe(|| self.project().future.poll(cx)))?.map(Ok)
} }
} }
@ -119,13 +121,21 @@ where
F: Future<Output = T>, F: Future<Output = T>,
{ {
thread_local! { thread_local! {
static ARC_THREAD: Arc<Thread> = Arc::new(thread::current()); // May hold a pre-allocated parker that can be reused for efficiency.
//
// Note that each invocation of `block` needs its own parker. In particular, if `block`
// recursively calls itself, we must make sure that each recursive call uses a distinct
// parker instance.
static CACHE: Cell<Option<Arc<Parker>>> = Cell::new(None);
} }
pin_utils::pin_mut!(f); pin_utils::pin_mut!(f);
ARC_THREAD.with(|arc_thread: &Arc<Thread>| { CACHE.with(|cache| {
let ptr = (&**arc_thread as *const Thread) as *const (); // Reuse a cached parker or create a new one for this invocation of `block`.
let arc_parker: Arc<Parker> = cache.take().unwrap_or_else(|| Arc::new(Parker::new()));
let ptr = (&*arc_parker as *const Parker) as *const ();
let vt = vtable(); let vt = vtable();
let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, vt))) }; let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, vt))) };
@ -133,32 +143,34 @@ where
loop { loop {
if let Poll::Ready(t) = f.as_mut().poll(cx) { if let Poll::Ready(t) = f.as_mut().poll(cx) {
// Save the parker for the next invocation of `block`.
cache.set(Some(arc_parker));
return t; return t;
} }
thread::park(); arc_parker.park();
} }
}) })
} }
fn vtable() -> &'static RawWakerVTable { fn vtable() -> &'static RawWakerVTable {
unsafe fn clone_raw(ptr: *const ()) -> RawWaker { unsafe fn clone_raw(ptr: *const ()) -> RawWaker {
let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Thread)); let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker));
mem::forget(arc.clone()); mem::forget(arc.clone());
RawWaker::new(ptr, vtable()) RawWaker::new(ptr, vtable())
} }
unsafe fn wake_raw(ptr: *const ()) { unsafe fn wake_raw(ptr: *const ()) {
let arc = Arc::from_raw(ptr as *const Thread); let arc = Arc::from_raw(ptr as *const Parker);
arc.unpark(); arc.unparker().unpark();
} }
unsafe fn wake_by_ref_raw(ptr: *const ()) { unsafe fn wake_by_ref_raw(ptr: *const ()) {
let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Thread)); let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker));
arc.unpark(); arc.unparker().unpark();
} }
unsafe fn drop_raw(ptr: *const ()) { unsafe fn drop_raw(ptr: *const ()) {
drop(Arc::from_raw(ptr as *const Thread)) drop(Arc::from_raw(ptr as *const Parker))
} }
&RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw) &RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw)

View file

@ -1,26 +1,122 @@
//! Asynchronous tasks. //! Types and traits for working with asynchronous tasks.
//! //!
//! This module is similar to [`std::thread`], except it uses asynchronous tasks in place of //! This module is similar to [`std::thread`], except it uses asynchronous tasks in place of
//! threads. //! threads.
//! //!
//! [`std::thread`]: https://doc.rust-lang.org/std/thread/index.html //! [`std::thread`]: https://doc.rust-lang.org/std/thread
//! //!
//! # Examples //! ## The task model
//! //!
//! Spawn a task and await its result: //! An executing asynchronous Rust program consists of a collection of native OS threads, on top of
//! which multiple stackless coroutines are multiplexed. We refer to these as "tasks". Tasks can
//! be named, and provide some built-in support for synchronization.
//! //!
//! ``` //! Communication between tasks can be done through channels, Rust's message-passing types, along
//! # async_std::task::block_on(async { //! with [other forms of tasks synchronization](../sync/index.html) and shared-memory data
//! # //! structures. In particular, types that are guaranteed to be threadsafe are easily shared between
//! tasks using the atomically-reference-counted container, [`Arc`].
//!
//! Fatal logic errors in Rust cause *thread panic*, during which a thread will unwind the stack,
//! running destructors and freeing owned resources. If a panic occurs inside a task, there is no
//! meaningful way of recovering, so the panic will propagate through any thread boundaries all the
//! way to the root task. This is also known as a "panic = abort" model.
//!
//! ## Spawning a task
//!
//! A new task can be spawned using the [`task::spawn`][`spawn`] function:
//!
//! ```no_run
//! use async_std::task; //! use async_std::task;
//! //!
//! let handle = task::spawn(async { //! task::spawn(async {
//! 1 + 2 //! // some work here
//! }); //! });
//! assert_eq!(handle.await, 3); //! ```
//!
//! In this example, the spawned task is "detached" from the current task. This means that it can
//! outlive its parent (the task that spawned it), unless this parent is the root task.
//!
//! The root task can also wait on the completion of the child task; a call to [`spawn`] produces a
//! [`JoinHandle`], which provides implements `Future` and can be `await`ed:
//!
//! ```
//! use async_std::task;
//!
//! # async_std::task::block_on(async {
//! #
//! let child = task::spawn(async {
//! // some work here
//! });
//! // some work here
//! let res = child.await;
//! # //! #
//! # }) //! # })
//! ``` //! ```
//!
//! The `await` operator returns the final value produced by the child task.
//!
//! ## Configuring tasks
//!
//! A new task can be configured before it is spawned via the [`Builder`] type,
//! which currently allows you to set the name and stack size for the child task:
//!
//! ```
//! # #![allow(unused_must_use)]
//! use async_std::task;
//!
//! # async_std::task::block_on(async {
//! #
//! task::Builder::new().name("child1".to_string()).spawn(async {
//! println!("Hello, world!");
//! });
//! #
//! # })
//! ```
//!
//! ## The `Task` type
//!
//! Tasks are represented via the [`Task`] type, which you can get in one of
//! two ways:
//!
//! * By spawning a new task, e.g., using the [`task::spawn`][`spawn`]
//! function, and calling [`task`][`JoinHandle::task`] on the [`JoinHandle`].
//! * By requesting the current task, using the [`task::current`] function.
//!
//! ## Task-local storage
//!
//! This module also provides an implementation of task-local storage for Rust
//! programs. Task-local storage is a method of storing data into a global
//! variable that each task in the program will have its own copy of.
//! Tasks do not share this data, so accesses do not need to be synchronized.
//!
//! A task-local key owns the value it contains and will destroy the value when the
//! task exits. It is created with the [`task_local!`] macro and can contain any
//! value that is `'static` (no borrowed pointers). It provides an accessor function,
//! [`with`], that yields a shared reference to the value to the specified
//! closure. Task-local keys allow only shared access to values, as there would be no
//! way to guarantee uniqueness if mutable borrows were allowed.
//!
//! ## Naming tasks
//!
//! Tasks are able to have associated names for identification purposes. By default, spawned
//! tasks are unnamed. To specify a name for a task, build the task with [`Builder`] and pass
//! the desired task name to [`Builder::name`]. To retrieve the task name from within the
//! task, use [`Task::name`].
//!
//! [`Arc`]: ../gsync/struct.Arc.html
//! [`spawn`]: fn.spawn.html
//! [`JoinHandle`]: struct.JoinHandle.html
//! [`JoinHandle::task`]: struct.JoinHandle.html#method.task
//! [`join`]: struct.JoinHandle.html#method.join
//! [`panic!`]: https://doc.rust-lang.org/std/macro.panic.html
//! [`Builder`]: struct.Builder.html
//! [`Builder::stack_size`]: struct.Builder.html#method.stack_size
//! [`Builder::name`]: struct.Builder.html#method.name
//! [`task::current`]: fn.current.html
//! [`Task`]: struct.Thread.html
//! [`Task::name`]: struct.Task.html#method.name
//! [`task_local!`]: ../macro.task_local.html
//! [`with`]: struct.LocalKey.html#method.with
#[doc(inline)] #[doc(inline)]
pub use std::task::{Context, Poll, Waker}; pub use std::task::{Context, Poll, Waker};
@ -47,11 +143,9 @@ mod worker;
pub(crate) mod blocking; pub(crate) mod blocking;
cfg_if::cfg_if! { cfg_unstable! {
if #[cfg(any(feature = "unstable", feature = "docs"))] { mod yield_now;
mod yield_now; pub use yield_now::yield_now;
pub use yield_now::yield_now;
}
} }
/// Spawns a blocking task. /// Spawns a blocking task.
@ -82,7 +176,7 @@ cfg_if::cfg_if! {
/// ``` /// ```
// Once this function stabilizes we should merge `blocking::spawn` into this so // Once this function stabilizes we should merge `blocking::spawn` into this so
// all code in our crate uses `task::blocking` too. // all code in our crate uses `task::blocking` too.
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[inline] #[inline]
pub fn spawn_blocking<F, R>(f: F) -> task::JoinHandle<R> pub fn spawn_blocking<F, R>(f: F) -> task::JoinHandle<R>

View file

@ -18,15 +18,15 @@ use std::pin::Pin;
/// Basic usage: /// Basic usage:
/// ///
/// ``` /// ```
/// # fn main() { async_std::task::block_on(async { /// # async_std::task::block_on(async {
/// # /// #
/// use async_std::task; /// use async_std::task;
/// ///
/// task::yield_now().await; /// task::yield_now().await;
/// # /// #
/// # }) } /// # })
/// ``` /// ```
#[cfg(any(feature = "unstable", feature = "docs"))] #[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[inline] #[inline]
pub async fn yield_now() { pub async fn yield_now() {

View file

@ -20,6 +20,64 @@ pub fn abort_on_panic<T>(f: impl FnOnce() -> T) -> T {
t t
} }
/// Declares unstable items.
#[doc(hidden)]
macro_rules! cfg_unstable {
($($item:item)*) => {
$(
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
$item
)*
}
}
/// Declares Unix-specific items.
#[doc(hidden)]
macro_rules! cfg_unix {
($($item:item)*) => {
$(
#[cfg(any(unix, feature = "docs"))]
#[cfg_attr(feature = "docs", doc(cfg(unix)))]
$item
)*
}
}
/// Declares Windows-specific items.
#[doc(hidden)]
macro_rules! cfg_windows {
($($item:item)*) => {
$(
#[cfg(any(windows, feature = "docs"))]
#[cfg_attr(feature = "docs", doc(cfg(windows)))]
$item
)*
}
}
/// Declares items when the "docs" feature is enabled.
#[doc(hidden)]
macro_rules! cfg_docs {
($($item:item)*) => {
$(
#[cfg(feature = "docs")]
$item
)*
}
}
/// Declares items when the "docs" feature is disabled.
#[doc(hidden)]
macro_rules! cfg_not_docs {
($($item:item)*) => {
$(
#[cfg(not(feature = "docs"))]
$item
)*
}
}
/// Defines an extension trait for a base trait. /// Defines an extension trait for a base trait.
/// ///
/// In generated docs, the base trait will contain methods from the extension trait. In actual /// In generated docs, the base trait will contain methods from the extension trait. In actual
@ -29,7 +87,6 @@ pub fn abort_on_panic<T>(f: impl FnOnce() -> T) -> T {
/// Inside invocations of this macro, we write a definitions that looks similar to the final /// Inside invocations of this macro, we write a definitions that looks similar to the final
/// rendered docs, and the macro then generates all the boilerplate for us. /// rendered docs, and the macro then generates all the boilerplate for us.
#[doc(hidden)] #[doc(hidden)]
#[macro_export]
macro_rules! extension_trait { macro_rules! extension_trait {
( (
// Interesting patterns: // Interesting patterns:
@ -113,6 +170,12 @@ macro_rules! extension_trait {
// Handle the end of the token list. // Handle the end of the token list.
(@doc ($($head:tt)*)) => { $($head)* }; (@doc ($($head:tt)*)) => { $($head)* };
(@ext ($($head:tt)*)) => { $($head)* }; (@ext ($($head:tt)*)) => { $($head)* };
}
pub use crate::extension_trait; // Parse imports at the beginning of the macro.
($import:item $($tail:tt)*) => {
#[cfg(feature = "docs")]
$import
extension_trait!($($tail)*);
};
}

View file

@ -48,13 +48,13 @@ fn test_buffered_writer() {
} }
#[test] #[test]
fn test_buffered_writer_inner_into_inner_does_not_flush() { fn test_buffered_writer_inner_into_inner_flushes() {
task::block_on(async { task::block_on(async {
let mut w = BufWriter::with_capacity(3, Vec::new()); let mut w = BufWriter::with_capacity(3, Vec::new());
w.write(&[0, 1]).await.unwrap(); w.write(&[0, 1]).await.unwrap();
assert_eq!(*w.get_ref(), []); assert_eq!(*w.get_ref(), []);
let w = w.into_inner().await.unwrap(); let w = w.into_inner().await.unwrap();
assert_eq!(w, []); assert_eq!(w, [0, 1]);
}) })
} }

350
tests/channel.rs Normal file
View file

@ -0,0 +1,350 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use async_std::sync::channel;
use async_std::task;
use rand::{thread_rng, Rng};
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn smoke() {
task::block_on(async {
let (s, r) = channel(1);
s.send(7).await;
assert_eq!(r.recv().await, Some(7));
s.send(8).await;
assert_eq!(r.recv().await, Some(8));
drop(s);
assert_eq!(r.recv().await, None);
})
}
#[test]
fn capacity() {
for i in 1..10 {
let (s, r) = channel::<()>(i);
assert_eq!(s.capacity(), i);
assert_eq!(r.capacity(), i);
}
}
#[test]
fn len_empty_full() {
task::block_on(async {
let (s, r) = channel(2);
assert_eq!(s.len(), 0);
assert_eq!(s.is_empty(), true);
assert_eq!(s.is_full(), false);
assert_eq!(r.len(), 0);
assert_eq!(r.is_empty(), true);
assert_eq!(r.is_full(), false);
s.send(()).await;
assert_eq!(s.len(), 1);
assert_eq!(s.is_empty(), false);
assert_eq!(s.is_full(), false);
assert_eq!(r.len(), 1);
assert_eq!(r.is_empty(), false);
assert_eq!(r.is_full(), false);
s.send(()).await;
assert_eq!(s.len(), 2);
assert_eq!(s.is_empty(), false);
assert_eq!(s.is_full(), true);
assert_eq!(r.len(), 2);
assert_eq!(r.is_empty(), false);
assert_eq!(r.is_full(), true);
r.recv().await;
assert_eq!(s.len(), 1);
assert_eq!(s.is_empty(), false);
assert_eq!(s.is_full(), false);
assert_eq!(r.len(), 1);
assert_eq!(r.is_empty(), false);
assert_eq!(r.is_full(), false);
})
}
#[test]
fn recv() {
task::block_on(async {
let (s, r) = channel(100);
task::spawn(async move {
assert_eq!(r.recv().await, Some(7));
task::sleep(ms(1000)).await;
assert_eq!(r.recv().await, Some(8));
task::sleep(ms(1000)).await;
assert_eq!(r.recv().await, Some(9));
assert_eq!(r.recv().await, None);
});
task::sleep(ms(1500)).await;
s.send(7).await;
s.send(8).await;
s.send(9).await;
})
}
#[test]
fn send() {
task::block_on(async {
let (s, r) = channel(1);
task::spawn(async move {
s.send(7).await;
task::sleep(ms(1000)).await;
s.send(8).await;
task::sleep(ms(1000)).await;
s.send(9).await;
task::sleep(ms(1000)).await;
s.send(10).await;
});
task::sleep(ms(1500)).await;
assert_eq!(r.recv().await, Some(7));
assert_eq!(r.recv().await, Some(8));
assert_eq!(r.recv().await, Some(9));
})
}
#[test]
fn recv_after_disconnect() {
task::block_on(async {
let (s, r) = channel(100);
s.send(1).await;
s.send(2).await;
s.send(3).await;
drop(s);
assert_eq!(r.recv().await, Some(1));
assert_eq!(r.recv().await, Some(2));
assert_eq!(r.recv().await, Some(3));
assert_eq!(r.recv().await, None);
})
}
#[test]
fn len() {
const COUNT: usize = 25_000;
const CAP: usize = 1000;
task::block_on(async {
let (s, r) = channel(CAP);
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
for _ in 0..CAP / 10 {
for i in 0..50 {
s.send(i).await;
assert_eq!(s.len(), i + 1);
}
for i in 0..50 {
r.recv().await;
assert_eq!(r.len(), 50 - i - 1);
}
}
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
for i in 0..CAP {
s.send(i).await;
assert_eq!(s.len(), i + 1);
}
for _ in 0..CAP {
r.recv().await.unwrap();
}
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
let child = task::spawn({
let r = r.clone();
async move {
for i in 0..COUNT {
assert_eq!(r.recv().await, Some(i));
let len = r.len();
assert!(len <= CAP);
}
}
});
for i in 0..COUNT {
s.send(i).await;
let len = s.len();
assert!(len <= CAP);
}
child.await;
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
})
}
#[test]
fn disconnect_wakes_receiver() {
task::block_on(async {
let (s, r) = channel::<()>(1);
let child = task::spawn(async move {
assert_eq!(r.recv().await, None);
});
task::sleep(ms(1000)).await;
drop(s);
child.await;
})
}
#[test]
fn spsc() {
const COUNT: usize = 100_000;
task::block_on(async {
let (s, r) = channel(3);
let child = task::spawn(async move {
for i in 0..COUNT {
assert_eq!(r.recv().await, Some(i));
}
assert_eq!(r.recv().await, None);
});
for i in 0..COUNT {
s.send(i).await;
}
drop(s);
child.await;
})
}
#[test]
fn mpmc() {
const COUNT: usize = 25_000;
const TASKS: usize = 4;
task::block_on(async {
let (s, r) = channel::<usize>(3);
let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::<Vec<_>>();
let v = Arc::new(v);
let mut tasks = Vec::new();
for _ in 0..TASKS {
let r = r.clone();
let v = v.clone();
tasks.push(task::spawn(async move {
for _ in 0..COUNT {
let n = r.recv().await.unwrap();
v[n].fetch_add(1, Ordering::SeqCst);
}
}));
}
for _ in 0..TASKS {
let s = s.clone();
tasks.push(task::spawn(async move {
for i in 0..COUNT {
s.send(i).await;
}
}));
}
for t in tasks {
t.await;
}
for c in v.iter() {
assert_eq!(c.load(Ordering::SeqCst), TASKS);
}
});
}
#[test]
fn oneshot() {
const COUNT: usize = 10_000;
task::block_on(async {
for _ in 0..COUNT {
let (s, r) = channel(1);
let c1 = task::spawn(async move { r.recv().await.unwrap() });
let c2 = task::spawn(async move { s.send(0).await });
c1.await;
c2.await;
}
})
}
#[test]
fn drops() {
const RUNS: usize = 100;
static DROPS: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, PartialEq)]
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::SeqCst);
}
}
let mut rng = thread_rng();
for _ in 0..RUNS {
task::block_on(async {
let steps = rng.gen_range(0, 10_000);
let additional = rng.gen_range(0, 50);
DROPS.store(0, Ordering::SeqCst);
let (s, r) = channel::<DropCounter>(50);
let child = task::spawn({
let r = r.clone();
async move {
for _ in 0..steps {
r.recv().await.unwrap();
}
}
});
for _ in 0..steps {
s.send(DropCounter).await;
}
child.await;
for _ in 0..additional {
s.send(DropCounter).await;
}
assert_eq!(DROPS.load(Ordering::SeqCst), steps);
drop(s);
drop(r);
assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional);
})
}
}