forked from mirror/async-std
Merge branch 'master' into add_stdin_lock
commit
48b255897e
@ -1,68 +0,0 @@
|
|||||||
language: rust
|
|
||||||
|
|
||||||
env:
|
|
||||||
- RUSTFLAGS="-D warnings"
|
|
||||||
|
|
||||||
# Cache the whole `~/.cargo` directory to keep `~/cargo/.crates.toml`.
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- /home/travis/.cargo
|
|
||||||
|
|
||||||
# Don't cache the cargo registry because it's too big.
|
|
||||||
before_cache:
|
|
||||||
- rm -rf /home/travis/.cargo/registry
|
|
||||||
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
- trying
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
include:
|
|
||||||
- rust: nightly
|
|
||||||
os: linux
|
|
||||||
|
|
||||||
- rust: nightly
|
|
||||||
os: osx
|
|
||||||
osx_image: xcode9.2
|
|
||||||
|
|
||||||
- rust: nightly-x86_64-pc-windows-msvc
|
|
||||||
os: windows
|
|
||||||
|
|
||||||
- name: fmt
|
|
||||||
rust: nightly
|
|
||||||
os: linux
|
|
||||||
before_script: |
|
|
||||||
if ! rustup component add rustfmt; then
|
|
||||||
target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`;
|
|
||||||
echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead";
|
|
||||||
rustup toolchain install nightly-$target;
|
|
||||||
rustup default nightly-$target;
|
|
||||||
rustup component add rustfmt;
|
|
||||||
fi
|
|
||||||
script:
|
|
||||||
- cargo fmt --all -- --check
|
|
||||||
|
|
||||||
- name: docs
|
|
||||||
rust: nightly
|
|
||||||
os: linux
|
|
||||||
script:
|
|
||||||
- cargo doc --features docs
|
|
||||||
|
|
||||||
- name: book
|
|
||||||
rust: nightly
|
|
||||||
os: linux
|
|
||||||
before_script:
|
|
||||||
- test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh
|
|
||||||
- cargo build # to find 'extern crate async_std' by `mdbook test`
|
|
||||||
script:
|
|
||||||
- mdbook build docs
|
|
||||||
- mdbook test -L ./target/debug/deps docs
|
|
||||||
|
|
||||||
script:
|
|
||||||
- cargo check --all --benches --bins --examples --tests
|
|
||||||
- cargo check --features unstable --all --benches --bins --examples --tests
|
|
||||||
- cargo test --all --doc --features unstable
|
|
@ -1,86 +1,84 @@
|
|||||||
use cfg_if::cfg_if;
|
cfg_not_docs! {
|
||||||
|
pub use std::fs::FileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_docs! {
|
||||||
|
/// The type of a file or directory.
|
||||||
|
///
|
||||||
|
/// A file type is returned by [`Metadata::file_type`].
|
||||||
|
///
|
||||||
|
/// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`],
|
||||||
|
/// [`is_file`], and [`is_symlink`] can return `true`.
|
||||||
|
///
|
||||||
|
/// This type is a re-export of [`std::fs::FileType`].
|
||||||
|
///
|
||||||
|
/// [`Metadata::file_type`]: struct.Metadata.html#method.file_type
|
||||||
|
/// [`is_dir`]: #method.is_dir
|
||||||
|
/// [`is_file`]: #method.is_file
|
||||||
|
/// [`is_symlink`]: #method.is_symlink
|
||||||
|
/// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct FileType {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
cfg_if! {
|
impl FileType {
|
||||||
if #[cfg(feature = "docs")] {
|
/// Returns `true` if this file type represents a regular directory.
|
||||||
/// The type of a file or directory.
|
|
||||||
///
|
///
|
||||||
/// A file type is returned by [`Metadata::file_type`].
|
/// If this file type represents a symbolic link, this method returns `false`.
|
||||||
///
|
///
|
||||||
/// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`],
|
/// # Examples
|
||||||
/// [`is_file`], and [`is_symlink`] can return `true`.
|
|
||||||
///
|
///
|
||||||
/// This type is a re-export of [`std::fs::FileType`].
|
/// ```no_run
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::fs;
|
||||||
///
|
///
|
||||||
/// [`Metadata::file_type`]: struct.Metadata.html#method.file_type
|
/// let file_type = fs::metadata(".").await?.file_type();
|
||||||
/// [`is_dir`]: #method.is_dir
|
/// println!("{:?}", file_type.is_dir());
|
||||||
/// [`is_file`]: #method.is_file
|
/// #
|
||||||
/// [`is_symlink`]: #method.is_symlink
|
/// # Ok(()) }) }
|
||||||
/// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html
|
/// ```
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
pub fn is_dir(&self) -> bool {
|
||||||
pub struct FileType {
|
unimplemented!()
|
||||||
_private: (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileType {
|
/// Returns `true` if this file type represents a regular file.
|
||||||
/// Returns `true` if this file type represents a regular directory.
|
///
|
||||||
///
|
/// If this file type represents a symbolic link, this method returns `false`.
|
||||||
/// If this file type represents a symbolic link, this method returns `false`.
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// ```no_run
|
||||||
/// ```no_run
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// #
|
||||||
/// #
|
/// use async_std::fs;
|
||||||
/// use async_std::fs;
|
///
|
||||||
///
|
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
||||||
/// let file_type = fs::metadata(".").await?.file_type();
|
/// println!("{:?}", file_type.is_file());
|
||||||
/// println!("{:?}", file_type.is_dir());
|
/// #
|
||||||
/// #
|
/// # Ok(()) }) }
|
||||||
/// # Ok(()) }) }
|
/// ```
|
||||||
/// ```
|
pub fn is_file(&self) -> bool {
|
||||||
pub fn is_dir(&self) -> bool {
|
unimplemented!()
|
||||||
unimplemented!()
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if this file type represents a regular file.
|
|
||||||
///
|
|
||||||
/// If this file type represents a symbolic link, this method returns `false`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
||||||
/// #
|
|
||||||
/// use async_std::fs;
|
|
||||||
///
|
|
||||||
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
|
||||||
/// println!("{:?}", file_type.is_file());
|
|
||||||
/// #
|
|
||||||
/// # Ok(()) }) }
|
|
||||||
/// ```
|
|
||||||
pub fn is_file(&self) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if this file type represents a symbolic link.
|
/// Returns `true` if this file type represents a symbolic link.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// #
|
/// #
|
||||||
/// use async_std::fs;
|
/// use async_std::fs;
|
||||||
///
|
///
|
||||||
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
/// let file_type = fs::metadata("a.txt").await?.file_type();
|
||||||
/// println!("{:?}", file_type.is_symlink());
|
/// println!("{:?}", file_type.is_symlink());
|
||||||
/// #
|
/// #
|
||||||
/// # Ok(()) }) }
|
/// # Ok(()) }) }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn is_symlink(&self) -> bool {
|
pub fn is_symlink(&self) -> bool {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pub use std::fs::FileType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,58 +1,56 @@
|
|||||||
use cfg_if::cfg_if;
|
cfg_not_docs! {
|
||||||
|
pub use std::fs::Permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_docs! {
|
||||||
|
/// A set of permissions on a file or directory.
|
||||||
|
///
|
||||||
|
/// This type is a re-export of [`std::fs::Permissions`].
|
||||||
|
///
|
||||||
|
/// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub struct Permissions {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
cfg_if! {
|
impl Permissions {
|
||||||
if #[cfg(feature = "docs")] {
|
/// Returns the read-only flag.
|
||||||
/// A set of permissions on a file or directory.
|
|
||||||
///
|
///
|
||||||
/// This type is a re-export of [`std::fs::Permissions`].
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html
|
/// ```no_run
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
pub struct Permissions {
|
/// #
|
||||||
_private: (),
|
/// use async_std::fs;
|
||||||
|
///
|
||||||
|
/// let perm = fs::metadata("a.txt").await?.permissions();
|
||||||
|
/// println!("{:?}", perm.readonly());
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
pub fn readonly(&self) -> bool {
|
||||||
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Permissions {
|
/// Configures the read-only flag.
|
||||||
/// Returns the read-only flag.
|
///
|
||||||
///
|
/// [`fs::set_permissions`]: fn.set_permissions.html
|
||||||
/// # Examples
|
///
|
||||||
///
|
/// # Examples
|
||||||
/// ```no_run
|
///
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
/// ```no_run
|
||||||
/// #
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
/// use async_std::fs;
|
/// #
|
||||||
///
|
/// use async_std::fs;
|
||||||
/// let perm = fs::metadata("a.txt").await?.permissions();
|
///
|
||||||
/// println!("{:?}", perm.readonly());
|
/// let mut perm = fs::metadata("a.txt").await?.permissions();
|
||||||
/// #
|
/// perm.set_readonly(true);
|
||||||
/// # Ok(()) }) }
|
/// fs::set_permissions("a.txt", perm).await?;
|
||||||
/// ```
|
/// #
|
||||||
pub fn readonly(&self) -> bool {
|
/// # Ok(()) }) }
|
||||||
unimplemented!()
|
/// ```
|
||||||
}
|
pub fn set_readonly(&mut self, readonly: bool) {
|
||||||
|
unimplemented!()
|
||||||
/// Configures the read-only flag.
|
|
||||||
///
|
|
||||||
/// [`fs::set_permissions`]: fn.set_permissions.html
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
||||||
/// #
|
|
||||||
/// use async_std::fs;
|
|
||||||
///
|
|
||||||
/// let mut perm = fs::metadata("a.txt").await?.permissions();
|
|
||||||
/// perm.set_readonly(true);
|
|
||||||
/// fs::set_permissions("a.txt", perm).await?;
|
|
||||||
/// #
|
|
||||||
/// # Ok(()) }) }
|
|
||||||
/// ```
|
|
||||||
pub fn set_readonly(&mut self, readonly: bool) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pub use std::fs::Permissions;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,139 @@
|
|||||||
|
extension_trait! {
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
A future represents an asynchronous computation.
|
||||||
|
|
||||||
|
A future is a value that may not have finished computing yet. This kind of
|
||||||
|
"asynchronous value" makes it possible for a thread to continue doing useful
|
||||||
|
work while it waits for the value to become available.
|
||||||
|
|
||||||
|
# The `poll` method
|
||||||
|
|
||||||
|
The core method of future, `poll`, *attempts* to resolve the future into a
|
||||||
|
final value. This method does not block if the value is not ready. Instead,
|
||||||
|
the current task is scheduled to be woken up when it's possible to make
|
||||||
|
further progress by `poll`ing again. The `context` passed to the `poll`
|
||||||
|
method can provide a [`Waker`], which is a handle for waking up the current
|
||||||
|
task.
|
||||||
|
|
||||||
|
When using a future, you generally won't call `poll` directly, but instead
|
||||||
|
`.await` the value.
|
||||||
|
|
||||||
|
[`Waker`]: ../task/struct.Waker.html
|
||||||
|
"#]
|
||||||
|
pub trait Future {
|
||||||
|
#[doc = r#"
|
||||||
|
The type of value produced on completion.
|
||||||
|
"#]
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
#[doc = r#"
|
||||||
|
Attempt to resolve the future to a final value, registering
|
||||||
|
the current task for wakeup if the value is not yet available.
|
||||||
|
|
||||||
|
# Return value
|
||||||
|
|
||||||
|
This function returns:
|
||||||
|
|
||||||
|
- [`Poll::Pending`] if the future is not ready yet
|
||||||
|
- [`Poll::Ready(val)`] with the result `val` of this future if it
|
||||||
|
finished successfully.
|
||||||
|
|
||||||
|
Once a future has finished, clients should not `poll` it again.
|
||||||
|
|
||||||
|
When a future is not ready yet, `poll` returns `Poll::Pending` and
|
||||||
|
stores a clone of the [`Waker`] copied from the current [`Context`].
|
||||||
|
This [`Waker`] is then woken once the future can make progress.
|
||||||
|
For example, a future waiting for a socket to become
|
||||||
|
readable would call `.clone()` on the [`Waker`] and store it.
|
||||||
|
When a signal arrives elsewhere indicating that the socket is readable,
|
||||||
|
[`Waker::wake`] is called and the socket future's task is awoken.
|
||||||
|
Once a task has been woken up, it should attempt to `poll` the future
|
||||||
|
again, which may or may not produce a final value.
|
||||||
|
|
||||||
|
Note that on multiple calls to `poll`, only the [`Waker`] from the
|
||||||
|
[`Context`] passed to the most recent call should be scheduled to
|
||||||
|
receive a wakeup.
|
||||||
|
|
||||||
|
# Runtime characteristics
|
||||||
|
|
||||||
|
Futures alone are *inert*; they must be *actively* `poll`ed to make
|
||||||
|
progress, meaning that each time the current task is woken up, it should
|
||||||
|
actively re-`poll` pending futures that it still has an interest in.
|
||||||
|
|
||||||
|
The `poll` function is not called repeatedly in a tight loop -- instead,
|
||||||
|
it should only be called when the future indicates that it is ready to
|
||||||
|
make progress (by calling `wake()`). If you're familiar with the
|
||||||
|
`poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures
|
||||||
|
typically do *not* suffer the same problems of "all wakeups must poll
|
||||||
|
all events"; they are more like `epoll(4)`.
|
||||||
|
|
||||||
|
An implementation of `poll` should strive to return quickly, and should
|
||||||
|
not block. Returning quickly prevents unnecessarily clogging up
|
||||||
|
threads or event loops. If it is known ahead of time that a call to
|
||||||
|
`poll` may end up taking awhile, the work should be offloaded to a
|
||||||
|
thread pool (or something similar) to ensure that `poll` can return
|
||||||
|
quickly.
|
||||||
|
|
||||||
|
# Panics
|
||||||
|
|
||||||
|
Once a future has completed (returned `Ready` from `poll`), calling its
|
||||||
|
`poll` method again may panic, block forever, or cause other kinds of
|
||||||
|
problems; the `Future` trait places no requirements on the effects of
|
||||||
|
such a call. However, as the `poll` method is not marked `unsafe`,
|
||||||
|
Rust's usual rules apply: calls must never cause undefined behavior
|
||||||
|
(memory corruption, incorrect use of `unsafe` functions, or the like),
|
||||||
|
regardless of the future's state.
|
||||||
|
|
||||||
|
[`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending
|
||||||
|
[`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready
|
||||||
|
[`Context`]: ../task/struct.Context.html
|
||||||
|
[`Waker`]: ../task/struct.Waker.html
|
||||||
|
[`Waker::wake`]: ../task/struct.Waker.html#method.wake
|
||||||
|
"#]
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FutureExt: std::future::Future {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future + Unpin + ?Sized> Future for Box<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future + Unpin + ?Sized> Future for &mut F {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> Future for Pin<P>
|
||||||
|
where
|
||||||
|
P: DerefMut + Unpin,
|
||||||
|
<P as Deref>::Target: Future,
|
||||||
|
{
|
||||||
|
type Output = <<P as Deref>::Target as Future>::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future> Future for std::panic::AssertUnwindSafe<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
unreachable!("this impl only appears in the rendered docs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
use crate::future::Future;
|
||||||
|
|
||||||
|
/// Convert a type into a `Future`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use async_std::future::{Future, IntoFuture};
|
||||||
|
/// use async_std::io;
|
||||||
|
/// use async_std::pin::Pin;
|
||||||
|
///
|
||||||
|
/// struct Client;
|
||||||
|
///
|
||||||
|
/// impl Client {
|
||||||
|
/// pub async fn send(self) -> io::Result<()> {
|
||||||
|
/// // Send a request
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl IntoFuture for Client {
|
||||||
|
/// type Output = io::Result<()>;
|
||||||
|
///
|
||||||
|
/// type Future = Pin<Box<dyn Future<Output = Self::Output>>>;
|
||||||
|
///
|
||||||
|
/// fn into_future(self) -> Self::Future {
|
||||||
|
/// Box::pin(async {
|
||||||
|
/// self.send().await
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
pub trait IntoFuture {
|
||||||
|
/// The type of value produced on completion.
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
/// Which kind of future are we turning this into?
|
||||||
|
type Future: Future<Output = Self::Output>;
|
||||||
|
|
||||||
|
/// Create a future from a value
|
||||||
|
fn into_future(self) -> Self::Future;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Future> IntoFuture for T {
|
||||||
|
type Output = T::Output;
|
||||||
|
|
||||||
|
type Future = T;
|
||||||
|
|
||||||
|
fn into_future(self) -> Self::Future {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
@ -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![]))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
//! OS-specific extensions.
|
//! OS-specific extensions.
|
||||||
|
|
||||||
#[cfg(any(unix, feature = "docs"))]
|
cfg_unix! {
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(unix)))]
|
pub mod unix;
|
||||||
pub mod unix;
|
}
|
||||||
|
|
||||||
#[cfg(any(windows, feature = "docs"))]
|
cfg_windows! {
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(windows)))]
|
pub mod windows;
|
||||||
pub mod windows;
|
}
|
||||||
|
@ -1,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};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::future::Future;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
/// A stream that yields elements by calling a closure.
|
||||||
|
///
|
||||||
|
/// This stream is constructed by [`from_fn`] function.
|
||||||
|
///
|
||||||
|
/// [`from_fn`]: fn.from_fn.html
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FromFn<F, Fut, T> {
|
||||||
|
f: F,
|
||||||
|
#[pin]
|
||||||
|
future: Option<Fut>,
|
||||||
|
__t: PhantomData<T>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new stream where to produce each new element a provided closure is called.
|
||||||
|
///
|
||||||
|
/// This allows creating a custom stream with any behaviour without using the more verbose
|
||||||
|
/// syntax of creating a dedicated type and implementing a `Stream` trait for it.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
/// use async_std::sync::Mutex;
|
||||||
|
/// use std::sync::Arc;
|
||||||
|
/// use async_std::stream;
|
||||||
|
///
|
||||||
|
/// let count = Arc::new(Mutex::new(0u8));
|
||||||
|
/// let s = stream::from_fn(|| {
|
||||||
|
/// let count = Arc::clone(&count);
|
||||||
|
///
|
||||||
|
/// async move {
|
||||||
|
/// *count.lock().await += 1;
|
||||||
|
///
|
||||||
|
/// if *count.lock().await > 3 {
|
||||||
|
/// None
|
||||||
|
/// } else {
|
||||||
|
/// Some(*count.lock().await)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// pin_utils::pin_mut!(s);
|
||||||
|
/// assert_eq!(s.next().await, Some(1));
|
||||||
|
/// assert_eq!(s.next().await, Some(2));
|
||||||
|
/// assert_eq!(s.next().await, Some(3));
|
||||||
|
/// assert_eq!(s.next().await, None);
|
||||||
|
/// #
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
pub fn from_fn<T, F, Fut>(f: F) -> FromFn<F, Fut, T>
|
||||||
|
where
|
||||||
|
F: FnMut() -> Fut,
|
||||||
|
Fut: Future<Output = Option<T>>,
|
||||||
|
{
|
||||||
|
FromFn {
|
||||||
|
f,
|
||||||
|
future: None,
|
||||||
|
__t: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, Fut, T> Stream for FromFn<F, Fut, T>
|
||||||
|
where
|
||||||
|
F: FnMut() -> Fut,
|
||||||
|
Fut: Future<Output = Option<T>>,
|
||||||
|
{
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let mut this = self.project();
|
||||||
|
loop {
|
||||||
|
if this.future.is_some() {
|
||||||
|
let next =
|
||||||
|
futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx));
|
||||||
|
this.future.set(None);
|
||||||
|
|
||||||
|
return Poll::Ready(next);
|
||||||
|
} else {
|
||||||
|
let fut = (this.f)();
|
||||||
|
this.future.set(Some(fut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,192 @@
|
|||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use futures_core::future::Future;
|
||||||
|
use futures_core::stream::Stream;
|
||||||
|
use futures_timer::Delay;
|
||||||
|
|
||||||
|
/// Creates a new stream that yields at a set interval.
|
||||||
|
///
|
||||||
|
/// The stream first yields after `dur`, and continues to yield every
|
||||||
|
/// `dur` after that. The stream accounts for time elapsed between calls, and
|
||||||
|
/// will adjust accordingly to prevent time skews.
|
||||||
|
///
|
||||||
|
/// Each interval may be slightly longer than the specified duration, but never
|
||||||
|
/// less.
|
||||||
|
///
|
||||||
|
/// Note that intervals are not intended for high resolution timers, but rather
|
||||||
|
/// they will likely fire some granularity after the exact instant that they're
|
||||||
|
/// otherwise indicated to fire at.
|
||||||
|
///
|
||||||
|
/// See also: [`task::sleep`].
|
||||||
|
///
|
||||||
|
/// [`task::sleep`]: ../task/fn.sleep.html
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Basic example:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
/// use async_std::stream;
|
||||||
|
/// use std::time::Duration;
|
||||||
|
///
|
||||||
|
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// let mut interval = stream::interval(Duration::from_secs(4));
|
||||||
|
/// while let Some(_) = interval.next().await {
|
||||||
|
/// println!("prints every four seconds");
|
||||||
|
/// }
|
||||||
|
/// #
|
||||||
|
/// # Ok(()) }) }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
pub fn interval(dur: Duration) -> Interval {
|
||||||
|
Interval {
|
||||||
|
delay: Delay::new(dur),
|
||||||
|
interval: dur,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A stream representing notifications at fixed interval
|
||||||
|
///
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Interval {
|
||||||
|
delay: Delay,
|
||||||
|
interval: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for Interval {
|
||||||
|
type Item = ();
|
||||||
|
|
||||||
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
if Pin::new(&mut self.delay).poll(cx).is_pending() {
|
||||||
|
return Poll::Pending;
|
||||||
|
}
|
||||||
|
let when = Instant::now();
|
||||||
|
let next = next_interval(when, Instant::now(), self.interval);
|
||||||
|
self.delay.reset(next);
|
||||||
|
Poll::Ready(Some(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts Duration object to raw nanoseconds if possible
|
||||||
|
///
|
||||||
|
/// This is useful to divide intervals.
|
||||||
|
///
|
||||||
|
/// While technically for large duration it's impossible to represent any
|
||||||
|
/// duration as nanoseconds, the largest duration we can represent is about
|
||||||
|
/// 427_000 years. Large enough for any interval we would use or calculate in
|
||||||
|
/// tokio.
|
||||||
|
fn duration_to_nanos(dur: Duration) -> Option<u64> {
|
||||||
|
dur.as_secs()
|
||||||
|
.checked_mul(1_000_000_000)
|
||||||
|
.and_then(|v| v.checked_add(u64::from(dur.subsec_nanos())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_interval(prev: Instant, now: Instant, interval: Duration) -> Instant {
|
||||||
|
let new = prev + interval;
|
||||||
|
if new > now {
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
let spent_ns = duration_to_nanos(now.duration_since(prev)).expect("interval should be expired");
|
||||||
|
let interval_ns =
|
||||||
|
duration_to_nanos(interval).expect("interval is less that 427 thousand years");
|
||||||
|
let mult = spent_ns / interval_ns + 1;
|
||||||
|
assert!(
|
||||||
|
mult < (1 << 32),
|
||||||
|
"can't skip more than 4 billion intervals of {:?} \
|
||||||
|
(trying to skip {})",
|
||||||
|
interval,
|
||||||
|
mult
|
||||||
|
);
|
||||||
|
prev + interval * (mult as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::next_interval;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
struct Timeline(Instant);
|
||||||
|
|
||||||
|
impl Timeline {
|
||||||
|
fn new() -> Timeline {
|
||||||
|
Timeline(Instant::now())
|
||||||
|
}
|
||||||
|
fn at(&self, millis: u64) -> Instant {
|
||||||
|
self.0 + Duration::from_millis(millis)
|
||||||
|
}
|
||||||
|
fn at_ns(&self, sec: u64, nanos: u32) -> Instant {
|
||||||
|
self.0 + Duration::new(sec, nanos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dur(millis: u64) -> Duration {
|
||||||
|
Duration::from_millis(millis)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The math around Instant/Duration isn't 100% precise due to rounding
|
||||||
|
// errors, see #249 for more info
|
||||||
|
fn almost_eq(a: Instant, b: Instant) -> bool {
|
||||||
|
if a == b {
|
||||||
|
true
|
||||||
|
} else if a > b {
|
||||||
|
a - b < Duration::from_millis(1)
|
||||||
|
} else {
|
||||||
|
b - a < Duration::from_millis(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn norm_next() {
|
||||||
|
let tm = Timeline::new();
|
||||||
|
assert!(almost_eq(
|
||||||
|
next_interval(tm.at(1), tm.at(2), dur(10)),
|
||||||
|
tm.at(11)
|
||||||
|
));
|
||||||
|
assert!(almost_eq(
|
||||||
|
next_interval(tm.at(7777), tm.at(7788), dur(100)),
|
||||||
|
tm.at(7877)
|
||||||
|
));
|
||||||
|
assert!(almost_eq(
|
||||||
|
next_interval(tm.at(1), tm.at(1000), dur(2100)),
|
||||||
|
tm.at(2101)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fast_forward() {
|
||||||
|
let tm = Timeline::new();
|
||||||
|
assert!(almost_eq(
|
||||||
|
next_interval(tm.at(1), tm.at(1000), dur(10)),
|
||||||
|
tm.at(1001)
|
||||||
|
));
|
||||||
|
assert!(almost_eq(
|
||||||
|
next_interval(tm.at(7777), tm.at(8888), dur(100)),
|
||||||
|
tm.at(8977)
|
||||||
|
));
|
||||||
|
assert!(almost_eq(
|
||||||
|
next_interval(tm.at(1), tm.at(10000), dur(2100)),
|
||||||
|
tm.at(10501)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: this test actually should be successful, but since we can't
|
||||||
|
/// multiply Duration on anything larger than u32 easily we decided
|
||||||
|
/// to allow it to fail for now
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "can't skip more than 4 billion intervals")]
|
||||||
|
fn large_skip() {
|
||||||
|
let tm = Timeline::new();
|
||||||
|
assert_eq!(
|
||||||
|
next_interval(tm.at_ns(0, 1), tm.at_ns(25, 0), Duration::new(0, 2)),
|
||||||
|
tm.at_ns(25, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
use crate::future::Future;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
|
||||||
|
/// Trait to represent types that can be created by productming up a stream.
|
||||||
|
///
|
||||||
|
/// This trait is used to implement the [`product`] method on streams. Types which
|
||||||
|
/// implement the trait can be generated by the [`product`] method. Like
|
||||||
|
/// [`FromStream`] this trait should rarely be called directly and instead
|
||||||
|
/// interacted with through [`Stream::product`].
|
||||||
|
///
|
||||||
|
/// [`product`]: trait.Product.html#tymethod.product
|
||||||
|
/// [`FromStream`]: trait.FromStream.html
|
||||||
|
/// [`Stream::product`]: trait.Stream.html#method.product
|
||||||
|
#[cfg(feature = "unstable")]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
|
||||||
|
pub trait Product<A = Self>: Sized {
|
||||||
|
/// Method which takes a stream and generates `Self` from the elements by
|
||||||
|
/// multiplying the items.
|
||||||
|
fn product<S, F>(stream: S) -> F
|
||||||
|
where
|
||||||
|
S: Stream<Item = A>,
|
||||||
|
F: Future<Output = Self>;
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::future::Future;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
/// A stream that repeats elements of type `T` endlessly by applying a provided closure.
|
||||||
|
///
|
||||||
|
/// This stream is constructed by the [`repeat_with`] function.
|
||||||
|
///
|
||||||
|
/// [`repeat_with`]: fn.repeat_with.html
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RepeatWith<F, Fut, A> {
|
||||||
|
f: F,
|
||||||
|
#[pin]
|
||||||
|
future: Option<Fut>,
|
||||||
|
__a: PhantomData<A>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Basic usage:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
/// use async_std::stream;
|
||||||
|
///
|
||||||
|
/// let s = stream::repeat_with(|| async { 1 });
|
||||||
|
///
|
||||||
|
/// pin_utils::pin_mut!(s);
|
||||||
|
///
|
||||||
|
/// assert_eq!(s.next().await, Some(1));
|
||||||
|
/// assert_eq!(s.next().await, Some(1));
|
||||||
|
/// assert_eq!(s.next().await, Some(1));
|
||||||
|
/// assert_eq!(s.next().await, Some(1));
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Going finite:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// #
|
||||||
|
/// use async_std::prelude::*;
|
||||||
|
/// use async_std::stream;
|
||||||
|
///
|
||||||
|
/// let s = stream::repeat_with(|| async { 1u8 }).take(2);
|
||||||
|
///
|
||||||
|
/// pin_utils::pin_mut!(s);
|
||||||
|
///
|
||||||
|
/// assert_eq!(s.next().await, Some(1));
|
||||||
|
/// assert_eq!(s.next().await, Some(1));
|
||||||
|
/// assert_eq!(s.next().await, None);
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
pub fn repeat_with<F, Fut, A>(repeater: F) -> RepeatWith<F, Fut, A>
|
||||||
|
where
|
||||||
|
F: FnMut() -> Fut,
|
||||||
|
Fut: Future<Output = A>,
|
||||||
|
{
|
||||||
|
RepeatWith {
|
||||||
|
f: repeater,
|
||||||
|
future: None,
|
||||||
|
__a: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, Fut, A> Stream for RepeatWith<F, Fut, A>
|
||||||
|
where
|
||||||
|
F: FnMut() -> Fut,
|
||||||
|
Fut: Future<Output = A>,
|
||||||
|
{
|
||||||
|
type Item = A;
|
||||||
|
|
||||||
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let mut this = self.project();
|
||||||
|
loop {
|
||||||
|
if this.future.is_some() {
|
||||||
|
let res = futures_core::ready!(this.future.as_mut().as_pin_mut().unwrap().poll(cx));
|
||||||
|
|
||||||
|
this.future.set(None);
|
||||||
|
|
||||||
|
return Poll::Ready(Some(res));
|
||||||
|
} else {
|
||||||
|
let fut = (this.f)();
|
||||||
|
|
||||||
|
this.future.set(Some(fut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use super::fuse::Fuse;
|
||||||
|
use crate::future::Future;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
// Lexicographically compares the elements of this `Stream` with those
|
||||||
|
// of another using `Ord`.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct CmpFuture<L: Stream, R: Stream> {
|
||||||
|
#[pin]
|
||||||
|
l: Fuse<L>,
|
||||||
|
#[pin]
|
||||||
|
r: Fuse<R>,
|
||||||
|
l_cache: Option<L::Item>,
|
||||||
|
r_cache: Option<R::Item>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> CmpFuture<L, R> {
|
||||||
|
pub(super) fn new(l: L, r: R) -> Self {
|
||||||
|
CmpFuture {
|
||||||
|
l: l.fuse(),
|
||||||
|
r: r.fuse(),
|
||||||
|
l_cache: None,
|
||||||
|
r_cache: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> Future for CmpFuture<L, R>
|
||||||
|
where
|
||||||
|
L: Stream + Sized,
|
||||||
|
R: Stream<Item = L::Item> + Sized,
|
||||||
|
L::Item: Ord,
|
||||||
|
{
|
||||||
|
type Output = Ordering;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut this = self.project();
|
||||||
|
loop {
|
||||||
|
// Stream that completes earliest can be considered Less, etc
|
||||||
|
let l_complete = this.l.done && this.l_cache.is_none();
|
||||||
|
let r_complete = this.r.done && this.r_cache.is_none();
|
||||||
|
|
||||||
|
if l_complete && r_complete {
|
||||||
|
return Poll::Ready(Ordering::Equal);
|
||||||
|
} else if l_complete {
|
||||||
|
return Poll::Ready(Ordering::Less);
|
||||||
|
} else if r_complete {
|
||||||
|
return Poll::Ready(Ordering::Greater);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get next value if possible and necesary
|
||||||
|
if !this.l.done && this.l_cache.is_none() {
|
||||||
|
let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx));
|
||||||
|
if let Some(item) = l_next {
|
||||||
|
*this.l_cache = Some(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !this.r.done && this.r_cache.is_none() {
|
||||||
|
let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx));
|
||||||
|
if let Some(item) = r_next {
|
||||||
|
*this.r_cache = Some(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare if both values are available.
|
||||||
|
if this.l_cache.is_some() && this.r_cache.is_some() {
|
||||||
|
let l_value = this.l_cache.take().unwrap();
|
||||||
|
let r_value = this.r_cache.take().unwrap();
|
||||||
|
let result = l_value.cmp(&r_value);
|
||||||
|
|
||||||
|
if let Ordering::Equal = result {
|
||||||
|
// Reset cache to prepare for next comparison
|
||||||
|
*this.l_cache = None;
|
||||||
|
*this.r_cache = None;
|
||||||
|
} else {
|
||||||
|
// Return non equal value
|
||||||
|
return Poll::Ready(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use super::partial_cmp::PartialCmpFuture;
|
||||||
|
use crate::future::Future;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
// Determines if the elements of this `Stream` are lexicographically
|
||||||
|
// greater than or equal to those of another.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct GeFuture<L: Stream, R: Stream> {
|
||||||
|
#[pin]
|
||||||
|
partial_cmp: PartialCmpFuture<L, R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> GeFuture<L, R>
|
||||||
|
where
|
||||||
|
L::Item: PartialOrd<R::Item>,
|
||||||
|
{
|
||||||
|
pub(super) fn new(l: L, r: R) -> Self {
|
||||||
|
GeFuture {
|
||||||
|
partial_cmp: l.partial_cmp(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> Future for GeFuture<L, R>
|
||||||
|
where
|
||||||
|
L: Stream,
|
||||||
|
R: Stream,
|
||||||
|
L::Item: PartialOrd<R::Item>,
|
||||||
|
{
|
||||||
|
type Output = bool;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Some(Ordering::Greater) | Some(Ordering::Equal) => Poll::Ready(true),
|
||||||
|
_ => Poll::Ready(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use super::partial_cmp::PartialCmpFuture;
|
||||||
|
use crate::future::Future;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
// Determines if the elements of this `Stream` are lexicographically
|
||||||
|
// greater than those of another.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct GtFuture<L: Stream, R: Stream> {
|
||||||
|
#[pin]
|
||||||
|
partial_cmp: PartialCmpFuture<L, R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> GtFuture<L, R>
|
||||||
|
where
|
||||||
|
L::Item: PartialOrd<R::Item>,
|
||||||
|
{
|
||||||
|
pub(super) fn new(l: L, r: R) -> Self {
|
||||||
|
GtFuture {
|
||||||
|
partial_cmp: l.partial_cmp(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> Future for GtFuture<L, R>
|
||||||
|
where
|
||||||
|
L: Stream + Sized,
|
||||||
|
R: Stream + Sized,
|
||||||
|
L::Item: PartialOrd<R::Item>,
|
||||||
|
{
|
||||||
|
type Output = bool;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Some(Ordering::Greater) => Poll::Ready(true),
|
||||||
|
_ => Poll::Ready(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use crate::future::Future;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct LastFuture<S, T> {
|
||||||
|
#[pin]
|
||||||
|
stream: S,
|
||||||
|
last: Option<T>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, T> LastFuture<S, T> {
|
||||||
|
pub(crate) fn new(stream: S) -> Self {
|
||||||
|
LastFuture { stream, last: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Future for LastFuture<S, S::Item>
|
||||||
|
where
|
||||||
|
S: Stream + Unpin + Sized,
|
||||||
|
S::Item: Copy,
|
||||||
|
{
|
||||||
|
type Output = Option<S::Item>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
let next = futures_core::ready!(this.stream.poll_next(cx));
|
||||||
|
|
||||||
|
match next {
|
||||||
|
Some(new) => {
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
*this.last = Some(new);
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
None => Poll::Ready(*this.last),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use super::partial_cmp::PartialCmpFuture;
|
||||||
|
use crate::future::Future;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
/// Determines if the elements of this `Stream` are lexicographically
|
||||||
|
/// less or equal to those of another.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct LeFuture<L: Stream, R: Stream> {
|
||||||
|
#[pin]
|
||||||
|
partial_cmp: PartialCmpFuture<L, R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> LeFuture<L, R>
|
||||||
|
where
|
||||||
|
L::Item: PartialOrd<R::Item>,
|
||||||
|
{
|
||||||
|
pub(super) fn new(l: L, r: R) -> Self {
|
||||||
|
LeFuture {
|
||||||
|
partial_cmp: l.partial_cmp(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> Future for LeFuture<L, R>
|
||||||
|
where
|
||||||
|
L: Stream + Sized,
|
||||||
|
R: Stream + Sized,
|
||||||
|
L::Item: PartialOrd<R::Item>,
|
||||||
|
{
|
||||||
|
type Output = bool;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Some(Ordering::Less) | Some(Ordering::Equal) => Poll::Ready(true),
|
||||||
|
_ => Poll::Ready(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
use super::partial_cmp::PartialCmpFuture;
|
||||||
|
use crate::future::Future;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::stream::Stream;
|
||||||
|
use crate::task::{Context, Poll};
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
// Determines if the elements of this `Stream` are lexicographically
|
||||||
|
// less than those of another.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct LtFuture<L: Stream, R: Stream> {
|
||||||
|
#[pin]
|
||||||
|
partial_cmp: PartialCmpFuture<L, R>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> LtFuture<L, R>
|
||||||
|
where
|
||||||
|
L::Item: PartialOrd<R::Item>,
|
||||||
|
{
|
||||||
|
pub(super) fn new(l: L, r: R) -> Self {
|
||||||
|
LtFuture {
|
||||||
|
partial_cmp: l.partial_cmp(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Stream, R: Stream> Future for LtFuture<L, R>
|
||||||
|
where
|
||||||
|
L: Stream + Sized,
|
||||||
|
R: Stream + Sized,
|
||||||
|
L::Item: PartialOrd<R::Item>,
|
||||||
|
{
|
||||||
|
type Output = bool;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Some(Ordering::Less) => Poll::Ready(true),
|
||||||
|
_ => Poll::Ready(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue