From 5c06788204d212c744ba9f2cc636de16d6df154a Mon Sep 17 00:00:00 2001 From: TehPers Date: Wed, 14 Aug 2024 20:26:12 -0700 Subject: [PATCH] Add when_read --- README.md | 6 +++ src/assertions.rs | 1 + src/assertions/read.rs | 7 +++ src/assertions/read/extensions.rs | 57 +++++++++++++++++++++++ src/assertions/read/modifiers.rs | 3 ++ src/assertions/read/modifiers/as_bytes.rs | 56 ++++++++++++++++++++++ src/prelude.rs | 1 + 7 files changed, 131 insertions(+) create mode 100644 src/assertions/read.rs create mode 100644 src/assertions/read/extensions.rs create mode 100644 src/assertions/read/modifiers.rs create mode 100644 src/assertions/read/modifiers/as_bytes.rs diff --git a/README.md b/README.md index 64f7dca..57259e8 100644 --- a/README.md +++ b/README.md @@ -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 | diff --git a/src/assertions.rs b/src/assertions.rs index 1e010bf..588d978 100644 --- a/src/assertions.rs +++ b/src/assertions.rs @@ -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; diff --git a/src/assertions/read.rs b/src/assertions/read.rs new file mode 100644 index 0000000..ee0115f --- /dev/null +++ b/src/assertions/read.rs @@ -0,0 +1,7 @@ +//! Modifiers related to the [`Read`](std::io::Read) trait. + +mod extensions; +mod modifiers; + +pub use extensions::*; +pub use modifiers::*; diff --git a/src/assertions/read/extensions.rs b/src/assertions/read/extensions.rs new file mode 100644 index 0000000..b7f7ae0 --- /dev/null +++ b/src/assertions/read/extensions.rs @@ -0,0 +1,57 @@ +use std::io::Read; + +use crate::assertions::AssertionBuilder; + +use super::WhenReadAsBytesModifier; + +/// Modifiers for types that implement [`Read`]. +pub trait ReadExtensions +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 { + /// 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, WhenReadAsBytesModifier>; +} + +impl ReadExtensions for AssertionBuilder +where + T: Read, +{ + #[inline] + fn when_read(self) -> AssertionBuilder, WhenReadAsBytesModifier> { + AssertionBuilder::modify(self, WhenReadAsBytesModifier::new) + } +} diff --git a/src/assertions/read/modifiers.rs b/src/assertions/read/modifiers.rs new file mode 100644 index 0000000..d1aff2f --- /dev/null +++ b/src/assertions/read/modifiers.rs @@ -0,0 +1,3 @@ +mod as_bytes; + +pub use as_bytes::*; diff --git a/src/assertions/read/modifiers/as_bytes.rs b/src/assertions/read/modifiers/as_bytes.rs new file mode 100644 index 0000000..91fb092 --- /dev/null +++ b/src/assertions/read/modifiers/as_bytes.rs @@ -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 { + prev: M, +} + +impl WhenReadAsBytesModifier { + #[inline] + pub(crate) fn new(prev: M) -> Self { + WhenReadAsBytesModifier { prev } + } +} + +impl AssertionModifier for WhenReadAsBytesModifier +where + M: AssertionModifier>, +{ + 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 { + next: A, +} + +impl Assertion for WhenReadAsBytesAssertion +where + T: Read, + A: Assertion, Output: IntoInitializableOutput>, +{ + type Output = ::Initialized; + + fn execute(self, mut cx: AssertionContext, subject: T) -> Self::Output { + let bytes = match subject.bytes().collect::, _>>() { + Ok(bytes) => bytes, + Err(error) => { + cx.annotate("error", &error); + return cx.fail("failed to read"); + } + }; + + self.next.execute(cx, bytes).into_initialized() + } +} diff --git a/src/prelude.rs b/src/prelude.rs index 93f2754..1a14fdb 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -16,6 +16,7 @@ pub use crate::{ general::GeneralAssertions, iterators::IteratorAssertions, options::OptionAssertions, + read::ReadExtensions, results::ResultAssertions, strings::{DebugAssertions, DisplayAssertions, StringAssertions}, },