Skip to content

Commit 915d6d5

Browse files
authored
feat(util): add InspectErr, InspectFrame combinators (#161)
fixes #160. the standard library has introduced conventions for combinators that accept callbacks that "inspect" a value, upon being provided a reference to a yielded value. for example, `Result::inspect()` and `Result::inspect_err()` allow callers to e.g. log an `Ok(_)` or `Err(_)` value. `Iterator::inspect()` similarly allows callers to apply a layer of functionality to inspect items as they are yielded by the inner iterator. this commit introduces a pair of combinators to `http_body_util`, called `InspectErr<B, F>` and `InspectFrame<B, F>`. these allow callers to inspect errors and frames yielded by an inner body. these are constructed by calling `BodyExt::inspect_err()` and `BodyExt::inspect_frame()`, respectively. Signed-off-by: katelyn martin <[email protected]>
1 parent 0fc0a94 commit 915d6d5

File tree

4 files changed

+206
-0
lines changed

4 files changed

+206
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use http_body::{Body, Frame};
2+
use pin_project_lite::pin_project;
3+
use std::{
4+
any::type_name,
5+
fmt,
6+
pin::Pin,
7+
task::{Context, Poll},
8+
};
9+
10+
pin_project! {
11+
/// Body returned by the [`inspect_err()`] combinator.
12+
///
13+
/// [`inspect_err()`]: crate::BodyExt::inspect_err
14+
#[derive(Clone, Copy)]
15+
pub struct InspectErr<B, F> {
16+
#[pin]
17+
inner: B,
18+
f: F
19+
}
20+
}
21+
22+
impl<B, F> InspectErr<B, F> {
23+
#[inline]
24+
pub(crate) fn new(body: B, f: F) -> Self {
25+
Self { inner: body, f }
26+
}
27+
28+
/// Get a reference to the inner body
29+
pub fn get_ref(&self) -> &B {
30+
&self.inner
31+
}
32+
33+
/// Get a mutable reference to the inner body
34+
pub fn get_mut(&mut self) -> &mut B {
35+
&mut self.inner
36+
}
37+
38+
/// Get a pinned mutable reference to the inner body
39+
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut B> {
40+
self.project().inner
41+
}
42+
43+
/// Consume `self`, returning the inner body
44+
pub fn into_inner(self) -> B {
45+
self.inner
46+
}
47+
}
48+
49+
impl<B, F> Body for InspectErr<B, F>
50+
where
51+
B: Body,
52+
F: FnMut(&B::Error),
53+
{
54+
type Data = B::Data;
55+
type Error = B::Error;
56+
57+
fn poll_frame(
58+
self: Pin<&mut Self>,
59+
cx: &mut Context<'_>,
60+
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
61+
let this = self.project();
62+
match this.inner.poll_frame(cx) {
63+
Poll::Pending => Poll::Pending,
64+
Poll::Ready(None) => Poll::Ready(None),
65+
Poll::Ready(Some(Ok(frame))) => Poll::Ready(Some(Ok(frame))),
66+
Poll::Ready(Some(Err(err))) => {
67+
(this.f)(&err);
68+
Poll::Ready(Some(Err(err)))
69+
}
70+
}
71+
}
72+
73+
fn size_hint(&self) -> http_body::SizeHint {
74+
self.inner.size_hint()
75+
}
76+
77+
fn is_end_stream(&self) -> bool {
78+
self.inner.is_end_stream()
79+
}
80+
}
81+
82+
impl<B, F> fmt::Debug for InspectErr<B, F>
83+
where
84+
B: fmt::Debug,
85+
{
86+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87+
f.debug_struct("InspectErr")
88+
.field("inner", &self.inner)
89+
.field("f", &type_name::<F>())
90+
.finish()
91+
}
92+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use http_body::{Body, Frame};
2+
use pin_project_lite::pin_project;
3+
use std::{
4+
any::type_name,
5+
fmt,
6+
pin::Pin,
7+
task::{Context, Poll},
8+
};
9+
10+
pin_project! {
11+
/// Body returned by the [`inspect_frame()`] combinator.
12+
///
13+
/// [`inspect_frame()`]: crate::BodyExt::inspect_frame
14+
#[derive(Clone, Copy)]
15+
pub struct InspectFrame<B, F> {
16+
#[pin]
17+
inner: B,
18+
f: F
19+
}
20+
}
21+
22+
impl<B, F> InspectFrame<B, F> {
23+
#[inline]
24+
pub(crate) fn new(body: B, f: F) -> Self {
25+
Self { inner: body, f }
26+
}
27+
28+
/// Get a reference to the inner body
29+
pub fn get_ref(&self) -> &B {
30+
&self.inner
31+
}
32+
33+
/// Get a mutable reference to the inner body
34+
pub fn get_mut(&mut self) -> &mut B {
35+
&mut self.inner
36+
}
37+
38+
/// Get a pinned mutable reference to the inner body
39+
pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut B> {
40+
self.project().inner
41+
}
42+
43+
/// Consume `self`, returning the inner body
44+
pub fn into_inner(self) -> B {
45+
self.inner
46+
}
47+
}
48+
49+
impl<B, F> Body for InspectFrame<B, F>
50+
where
51+
B: Body,
52+
F: FnMut(&Frame<B::Data>),
53+
{
54+
type Data = B::Data;
55+
type Error = B::Error;
56+
57+
fn poll_frame(
58+
self: Pin<&mut Self>,
59+
cx: &mut Context<'_>,
60+
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
61+
let this = self.project();
62+
match this.inner.poll_frame(cx) {
63+
Poll::Pending => Poll::Pending,
64+
Poll::Ready(None) => Poll::Ready(None),
65+
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(err))),
66+
Poll::Ready(Some(Ok(frame))) => {
67+
(this.f)(&frame);
68+
Poll::Ready(Some(Ok(frame)))
69+
}
70+
}
71+
}
72+
73+
fn size_hint(&self) -> http_body::SizeHint {
74+
self.inner.size_hint()
75+
}
76+
77+
fn is_end_stream(&self) -> bool {
78+
self.inner.is_end_stream()
79+
}
80+
}
81+
82+
impl<B, F> fmt::Debug for InspectFrame<B, F>
83+
where
84+
B: fmt::Debug,
85+
{
86+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87+
f.debug_struct("InspectFrame")
88+
.field("inner", &self.inner)
89+
.field("f", &type_name::<F>())
90+
.finish()
91+
}
92+
}

http-body-util/src/combinators/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ mod box_body;
44
mod collect;
55
mod frame;
66
mod fuse;
7+
mod inspect_err;
8+
mod inspect_frame;
79
mod map_err;
810
mod map_frame;
911
mod with_trailers;
@@ -13,6 +15,8 @@ pub use self::{
1315
collect::Collect,
1416
frame::Frame,
1517
fuse::Fuse,
18+
inspect_err::InspectErr,
19+
inspect_frame::InspectFrame,
1620
map_err::MapErr,
1721
map_frame::MapFrame,
1822
with_trailers::WithTrailers,

http-body-util/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ pub trait BodyExt: http_body::Body {
5555
MapFrame::new(self, f)
5656
}
5757

58+
/// A body that calls a function with a reference to each frame before yielding it.
59+
fn inspect_frame<F>(self, f: F) -> combinators::InspectFrame<Self, F>
60+
where
61+
Self: Sized,
62+
F: FnMut(&http_body::Frame<Self::Data>),
63+
{
64+
combinators::InspectFrame::new(self, f)
65+
}
66+
5867
/// Maps this body's error value to a different value.
5968
fn map_err<F, E>(self, f: F) -> MapErr<Self, F>
6069
where
@@ -64,6 +73,15 @@ pub trait BodyExt: http_body::Body {
6473
MapErr::new(self, f)
6574
}
6675

76+
/// A body that calls a function with a reference to an error before yielding it.
77+
fn inspect_err<F>(self, f: F) -> combinators::InspectErr<Self, F>
78+
where
79+
Self: Sized,
80+
F: FnMut(&Self::Error),
81+
{
82+
combinators::InspectErr::new(self, f)
83+
}
84+
6785
/// Turn this body into a boxed trait object.
6886
fn boxed(self) -> BoxBody<Self::Data, Self::Error>
6987
where

0 commit comments

Comments
 (0)