Skip to content

Commit

Permalink
Add when_read
Browse files Browse the repository at this point in the history
  • Loading branch information
TehPers committed Aug 15, 2024
1 parent c2b7682 commit 5c06788
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ async fn get_cat_url(id: u32) -> String {
| `nth` | gets nth item |
| `as_utf8` | parses as utf8 |

### Readers

| Modifier | Description |
| ----------- | ---------------------- |
| `when_read` | reads into byte buffer |

### Futures

| Modifier | Description | Requires feature |
Expand Down
1 change: 1 addition & 0 deletions src/assertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ pub mod futures;
pub mod general;
pub mod iterators;
pub mod options;
pub mod read;
pub mod results;
pub mod strings;

Expand Down
7 changes: 7 additions & 0 deletions src/assertions/read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Modifiers related to the [`Read`](std::io::Read) trait.
mod extensions;
mod modifiers;

pub use extensions::*;
pub use modifiers::*;
57 changes: 57 additions & 0 deletions src/assertions/read/extensions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::io::Read;

use crate::assertions::AssertionBuilder;

use super::WhenReadAsBytesModifier;

/// Modifiers for types that implement [`Read`].
pub trait ReadExtensions<T, M>
where
T: Read,
{
/// Reads the subject into a buffer, then executes the assertion on it.
///
/// ```
/// # use expecters::prelude::*;
/// use std::io::Cursor;
/// expect!(
/// Cursor::new("Hello, world!"),
/// when_read,
/// as_utf8,
/// to_equal("Hello, world!"),
/// );
/// ```
///
/// The assertion fails if reading the subject fails:
///
/// ```should_panic
/// # use expecters::prelude::*;
/// use std::io::{Error, ErrorKind, Read};
///
/// struct MyReader;
///
/// impl Read for MyReader {
/// fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
/// Err(Error::new(ErrorKind::Other, "always fail"))
/// }
/// }
///
/// expect!(
/// MyReader,
/// when_read,
/// count,
/// to_be_greater_than_or_equal_to(0),
/// );
/// ```
fn when_read(self) -> AssertionBuilder<Vec<u8>, WhenReadAsBytesModifier<M>>;
}

impl<T, M> ReadExtensions<T, M> for AssertionBuilder<T, M>
where
T: Read,
{
#[inline]
fn when_read(self) -> AssertionBuilder<Vec<u8>, WhenReadAsBytesModifier<M>> {
AssertionBuilder::modify(self, WhenReadAsBytesModifier::new)
}
}
3 changes: 3 additions & 0 deletions src/assertions/read/modifiers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod as_bytes;

pub use as_bytes::*;
56 changes: 56 additions & 0 deletions src/assertions/read/modifiers/as_bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::io::Read;

use crate::assertions::{
general::IntoInitializableOutput, Assertion, AssertionContext, AssertionContextBuilder,
AssertionModifier,
};

/// Reads a subject into a buffer.
#[derive(Clone, Debug)]
pub struct WhenReadAsBytesModifier<M> {
prev: M,
}

impl<M> WhenReadAsBytesModifier<M> {
#[inline]
pub(crate) fn new(prev: M) -> Self {
WhenReadAsBytesModifier { prev }
}
}

impl<M, A> AssertionModifier<A> for WhenReadAsBytesModifier<M>
where
M: AssertionModifier<WhenReadAsBytesAssertion<A>>,
{
type Output = M::Output;

fn apply(self, cx: AssertionContextBuilder, next: A) -> Self::Output {
self.prev.apply(cx, WhenReadAsBytesAssertion { next })
}
}

/// Reads the subject into a buffer and executes the inner assertion on it.
#[derive(Clone, Debug)]
pub struct WhenReadAsBytesAssertion<A> {
next: A,
}

impl<T, A> Assertion<T> for WhenReadAsBytesAssertion<A>
where
T: Read,
A: Assertion<Vec<u8>, Output: IntoInitializableOutput>,
{
type Output = <A::Output as IntoInitializableOutput>::Initialized;

fn execute(self, mut cx: AssertionContext, subject: T) -> Self::Output {
let bytes = match subject.bytes().collect::<Result<Vec<_>, _>>() {
Ok(bytes) => bytes,
Err(error) => {
cx.annotate("error", &error);
return cx.fail("failed to read");
}
};

self.next.execute(cx, bytes).into_initialized()
}
}
1 change: 1 addition & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub use crate::{
general::GeneralAssertions,
iterators::IteratorAssertions,
options::OptionAssertions,
read::ReadExtensions,
results::ResultAssertions,
strings::{DebugAssertions, DisplayAssertions, StringAssertions},
},
Expand Down

0 comments on commit 5c06788

Please sign in to comment.