-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add when_read_async, cleanup when_read
- Loading branch information
Showing
12 changed files
with
258 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
//! Modifiers for types that can be read asynchronously. | ||
mod extensions; | ||
mod modifiers; | ||
mod outputs; | ||
|
||
pub use extensions::*; | ||
pub use modifiers::*; | ||
pub use outputs::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
use futures::AsyncRead; | ||
|
||
use crate::assertions::AssertionBuilder; | ||
|
||
use super::WhenReadAsyncModifier; | ||
|
||
/// Modifiers for types that implement [`futures::AsyncRead`]. | ||
pub trait AsyncReadAssertions<T, M> | ||
where | ||
T: AsyncRead, | ||
{ | ||
/// Asynchronously reads the subject into a buffer, then executes the | ||
/// assertion on it. | ||
/// | ||
/// ``` | ||
/// # use expecters::prelude::*; | ||
/// use futures::io::Cursor; | ||
/// # #[tokio::main(flavor = "current_thread")] | ||
/// # async fn main() { | ||
/// expect!( | ||
/// Cursor::new("Hello, world!"), | ||
/// when_read_async, | ||
/// as_utf8, | ||
/// to_equal("Hello, world!"), | ||
/// ) | ||
/// .await; | ||
/// # } | ||
/// ``` | ||
/// | ||
/// The assertion fails if reading the subject fails: | ||
/// | ||
/// ```should_panic | ||
/// # use expecters::prelude::*; | ||
/// use std::{ | ||
/// pin::Pin, | ||
/// task::{Context, Poll}, | ||
/// }; | ||
/// | ||
/// use futures::io::{Error, ErrorKind, AsyncRead}; | ||
/// | ||
/// struct MyReader; | ||
/// | ||
/// impl AsyncRead for MyReader { | ||
/// fn poll_read( | ||
/// self: Pin<&mut Self>, | ||
/// _cx: &mut Context, | ||
/// _buf: &mut [u8], | ||
/// ) -> Poll<std::io::Result<usize>> { | ||
/// Poll::Ready(Err(Error::new(ErrorKind::Other, "always fail"))) | ||
/// } | ||
/// } | ||
/// | ||
/// # #[tokio::main(flavor = "current_thread")] | ||
/// # async fn main() { | ||
/// expect!( | ||
/// MyReader, | ||
/// when_read_async, | ||
/// count, | ||
/// to_be_greater_than_or_equal_to(0), | ||
/// ) | ||
/// .await; | ||
/// # } | ||
/// ``` | ||
fn when_read_async(self) -> AssertionBuilder<Vec<u8>, WhenReadAsyncModifier<M>>; | ||
} | ||
|
||
impl<T, M> AsyncReadAssertions<T, M> for AssertionBuilder<T, M> | ||
where | ||
T: AsyncRead, | ||
{ | ||
#[inline] | ||
fn when_read_async(self) -> AssertionBuilder<Vec<u8>, WhenReadAsyncModifier<M>> { | ||
AssertionBuilder::modify(self, WhenReadAsyncModifier::new) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod when_read; | ||
|
||
pub use when_read::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
use futures::AsyncRead; | ||
|
||
use crate::assertions::{ | ||
async_read::WhenReadAsyncFuture, general::IntoInitializableOutput, Assertion, AssertionContext, | ||
AssertionContextBuilder, AssertionModifier, | ||
}; | ||
|
||
/// Reads a subject into a buffer asynchronously. | ||
#[derive(Clone, Debug)] | ||
pub struct WhenReadAsyncModifier<M> { | ||
prev: M, | ||
} | ||
|
||
impl<M> WhenReadAsyncModifier<M> { | ||
#[inline] | ||
pub(crate) fn new(prev: M) -> Self { | ||
Self { prev } | ||
} | ||
} | ||
|
||
impl<M, A> AssertionModifier<A> for WhenReadAsyncModifier<M> | ||
where | ||
M: AssertionModifier<WhenReadAsyncAssertion<A>>, | ||
{ | ||
type Output = M::Output; | ||
|
||
#[inline] | ||
fn apply(self, cx: AssertionContextBuilder, next: A) -> Self::Output { | ||
self.prev.apply(cx, WhenReadAsyncAssertion { next }) | ||
} | ||
} | ||
|
||
/// Reads the subject into a buffer asynchronously and executes the inner | ||
/// assertion on it. | ||
#[derive(Clone, Debug)] | ||
pub struct WhenReadAsyncAssertion<A> { | ||
next: A, | ||
} | ||
|
||
impl<A, T> Assertion<T> for WhenReadAsyncAssertion<A> | ||
where | ||
A: Assertion<Vec<u8>, Output: IntoInitializableOutput>, | ||
T: AsyncRead, | ||
{ | ||
type Output = WhenReadAsyncFuture<T, A>; | ||
|
||
#[inline] | ||
fn execute(self, cx: AssertionContext, subject: T) -> Self::Output { | ||
WhenReadAsyncFuture::new(cx, subject, self.next) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod when_read; | ||
|
||
pub use when_read::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
use std::{ | ||
future::Future, | ||
pin::Pin, | ||
task::{ready, Context, Poll}, | ||
}; | ||
|
||
use futures::AsyncRead; | ||
use pin_project_lite::pin_project; | ||
|
||
use crate::assertions::{general::IntoInitializableOutput, Assertion, AssertionContext}; | ||
|
||
pin_project! { | ||
/// Asynchronously reads a subject and executes an assertion on it. | ||
#[derive(Clone, Debug)] | ||
pub struct WhenReadAsyncFuture<T, A> { | ||
#[pin] | ||
subject: T, | ||
buffer: Vec<u8>, | ||
result: Vec<u8>, | ||
next: Option<(AssertionContext, A)> | ||
} | ||
} | ||
|
||
impl<T, A> WhenReadAsyncFuture<T, A> { | ||
#[inline] | ||
pub(crate) fn new(cx: AssertionContext, subject: T, next: A) -> Self { | ||
WhenReadAsyncFuture { | ||
subject, | ||
buffer: vec![0; 32], | ||
result: Vec::new(), | ||
next: Some((cx, next)), | ||
} | ||
} | ||
} | ||
|
||
impl<T, A> Future for WhenReadAsyncFuture<T, A> | ||
where | ||
T: AsyncRead, | ||
A: Assertion<Vec<u8>, Output: IntoInitializableOutput>, | ||
{ | ||
type Output = <A::Output as IntoInitializableOutput>::Initialized; | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> { | ||
let mut projected = self.project(); | ||
|
||
// Read the subject | ||
loop { | ||
let result = ready!(projected.subject.as_mut().poll_read(cx, projected.buffer)); | ||
match result { | ||
Ok(0) => break, | ||
Ok(n) => { | ||
projected.result.extend(&projected.buffer[..n]); | ||
|
||
// Check if we can grow the buffer for the next read | ||
if n == projected.buffer.len() { | ||
projected.buffer.reserve(32); | ||
projected.buffer.resize(projected.buffer.capacity(), 0); | ||
} | ||
} | ||
Err(error) => { | ||
let (mut cx, _) = projected.next.take().expect("poll after ready"); | ||
cx.annotate("error", error); | ||
return Poll::Ready(cx.fail("failed to read")); | ||
} | ||
}; | ||
} | ||
|
||
let (mut cx, next) = projected.next.take().expect("poll after ready"); | ||
cx.annotate("read bytes", projected.result.len()); | ||
Poll::Ready( | ||
next.execute(cx, std::mem::take(projected.result)) | ||
.into_initialized(), | ||
) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use futures::io::Cursor; | ||
|
||
use crate::prelude::*; | ||
|
||
#[tokio::test] | ||
async fn long_data() { | ||
let subject = "Hello, world! ".repeat(100); | ||
expect!( | ||
Cursor::new(subject.clone()), | ||
when_read_async, | ||
as_utf8, | ||
to_equal(subject), | ||
) | ||
.await; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
mod as_bytes; | ||
mod when_read; | ||
|
||
pub use as_bytes::*; | ||
pub use when_read::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters