Skip to content
This repository has been archived by the owner on Jan 18, 2020. It is now read-only.

Commit

Permalink
tweak rejection APIs
Browse files Browse the repository at this point in the history
* add combinator: OrReject and OrRejectWith
* make Fixed and reject() deprecated
  • Loading branch information
ubnt-intrepid committed Sep 1, 2018
1 parent 29aa64e commit 308d6a3
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 40 deletions.
8 changes: 7 additions & 1 deletion src/endpoint/fixed.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(deprecated)]

use std::pin::PinMut;

use futures_core::future::{Future, TryFuture};
Expand All @@ -8,7 +10,11 @@ use pin_utils::unsafe_unpinned;
use crate::endpoint::{Context, Endpoint, EndpointError, EndpointResult};
use crate::error::Error;

#[allow(missing_docs)]
#[doc(hidden)]
#[deprecated(
since = "0.12.0-alpha.3",
note = "This struct is going to remove before releasing 0.12.0."
)]
#[derive(Debug, Copy, Clone)]
pub struct Fixed<E> {
pub(super) endpoint: E,
Expand Down
30 changes: 29 additions & 1 deletion src/endpoint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod fixed;
mod lazy;
mod map;
mod or;
mod or_reject;
mod recover;
mod reject;
mod then;
Expand All @@ -24,13 +25,18 @@ pub use self::error::{EndpointError, EndpointResult};
pub use self::and::And;
pub use self::and_then::AndThen;
pub use self::boxed::{Boxed, BoxedLocal};
#[allow(deprecated)]
#[doc(hidden)]
pub use self::fixed::Fixed;
pub use self::map::Map;
pub use self::or::Or;
pub use self::or_reject::{OrReject, OrRejectWith};
pub use self::recover::Recover;
pub use self::then::Then;

pub use self::lazy::{lazy, Lazy};
#[allow(deprecated)]
#[doc(hidden)]
pub use self::reject::{reject, Reject};
pub use self::unit::{unit, Unit};
pub use self::value::{value, Value};
Expand Down Expand Up @@ -173,6 +179,23 @@ pub trait EndpointExt<'a>: Endpoint<'a> + Sized {
(AndThen { endpoint: self, f }).output::<(<F::Out as TryFuture>::Ok,)>()
}

/// Creates an endpoint which returns the error value returned from
/// `Endpoint::apply()` as the return value from the associated `Future`.
fn or_reject(self) -> OrReject<Self> {
(OrReject { endpoint: self }).output::<Self::Output>()
}

/// Creates an endpoint which converts the error value returned from
/// `Endpoint::apply()` to the specified type and returns it as
/// the return value from the associated `Future`.
fn or_reject_with<F, R>(self, f: F) -> OrRejectWith<Self, F>
where
F: Fn(EndpointError, &mut Context<'_>) -> R + 'a,
R: Into<Error> + 'a,
{
(OrRejectWith { endpoint: self, f }).output::<Self::Output>()
}

#[allow(missing_docs)]
fn recover<F, R>(self, f: F) -> Recover<Self, F>
where
Expand All @@ -182,7 +205,12 @@ pub trait EndpointExt<'a>: Endpoint<'a> + Sized {
(Recover { endpoint: self, f }).output::<(self::recover::Recovered<Self::Output, R::Ok>,)>()
}

#[allow(missing_docs)]
#[doc(hidden)]
#[deprecated(
since = "0.12.0-alpha.3",
note = "this method is going to remove before releasing 0.12.0."
)]
#[allow(deprecated)]
fn fixed(self) -> Fixed<Self> {
Fixed { endpoint: self }
}
Expand Down
87 changes: 87 additions & 0 deletions src/endpoint/or_reject.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::pin::PinMut;

use futures_core::future::{Future, TryFuture};
use futures_core::task;
use futures_core::task::Poll;
use pin_utils::unsafe_unpinned;

use crate::endpoint::{Context, Endpoint, EndpointError, EndpointResult};
use crate::error::Error;

#[allow(missing_docs)]
#[derive(Debug, Copy, Clone)]
pub struct OrReject<E> {
pub(super) endpoint: E,
}

impl<'a, E: Endpoint<'a>> Endpoint<'a> for OrReject<E> {
type Output = E::Output;
type Future = OrRejectFuture<E::Future>;

fn apply(&'a self, ecx: &mut Context<'_>) -> EndpointResult<Self::Future> {
match self.endpoint.apply(ecx) {
Ok(future) => Ok(OrRejectFuture { inner: Ok(future) }),
Err(err) => {
while let Some(..) = ecx.next_segment() {}
Ok(OrRejectFuture {
inner: Err(Some(err.into())),
})
}
}
}
}

#[derive(Debug)]
pub struct OrRejectFuture<F> {
inner: Result<F, Option<Error>>,
}

impl<F> OrRejectFuture<F> {
unsafe_unpinned!(inner: Result<F, Option<Error>>);
}

impl<F> Future for OrRejectFuture<F>
where
F: TryFuture<Error = Error>,
{
type Output = Result<F::Ok, Error>;

fn poll(mut self: PinMut<'_, Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
match self.inner() {
Ok(ref mut f) => unsafe { PinMut::new_unchecked(f).try_poll(cx) },
Err(ref mut err) => Poll::Ready(Err(err.take().unwrap())),
}
}
}

// ==== OrRejectWith ====

#[allow(missing_docs)]
#[derive(Debug, Copy, Clone)]
pub struct OrRejectWith<E, F> {
pub(super) endpoint: E,
pub(super) f: F,
}

impl<'a, E, F, R> Endpoint<'a> for OrRejectWith<E, F>
where
E: Endpoint<'a>,
F: Fn(EndpointError, &mut Context<'_>) -> R + 'a,
R: Into<Error> + 'a,
{
type Output = E::Output;
type Future = OrRejectFuture<E::Future>;

fn apply(&'a self, ecx: &mut Context<'_>) -> EndpointResult<Self::Future> {
match self.endpoint.apply(ecx) {
Ok(future) => Ok(OrRejectFuture { inner: Ok(future) }),
Err(err) => {
while let Some(..) = ecx.next_segment() {}
let err = (self.f)(err, ecx).into();
Ok(OrRejectFuture {
inner: Err(Some(err)),
})
}
}
}
}
14 changes: 12 additions & 2 deletions src/endpoint/reject.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(deprecated)]

use std::marker::PhantomData;
use std::pin::PinMut;

Expand All @@ -7,7 +9,11 @@ use crate::endpoint::{Context, Endpoint, EndpointExt, EndpointResult};
use crate::error::Error;
use crate::input::Input;

/// Creates an endpoint which always rejects the request with the specified error.
#[doc(hidden)]
#[deprecated(
since = "0.12.0-alpha.3",
note = "This endpoint is going to remove before releasing 0.12.0."
)]
pub fn reject<F, E>(f: F) -> Reject<F, E>
where
F: Fn(PinMut<'_, Input>) -> E,
Expand All @@ -19,7 +25,11 @@ where
}).output::<()>()
}

#[allow(missing_docs)]
#[doc(hidden)]
#[deprecated(
since = "0.12.0-alpha.3",
note = "This endpoint is going to remove before releasing 0.12.0."
)]
#[derive(Debug)]
pub struct Reject<F, E> {
f: F,
Expand Down
3 changes: 1 addition & 2 deletions src/endpoints/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,10 @@ where
/// ```
/// # use finchers::endpoint::EndpointExt;
/// # use finchers::endpoints::header;
/// use finchers::endpoint::reject;
/// use finchers::error;
///
/// let endpoint = header::matches("origin", "www.example.com")
/// .or(reject(|_| error::bad_request("The value of Origin is invalid")));
/// .or_reject_with(|_, _| error::bad_request("invalid header value"));
/// # drop(endpoint);
/// ```
pub fn matches<K, V>(name: K, value: V) -> Matches<V>
Expand Down
27 changes: 1 addition & 26 deletions tests/endpoint/and.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use failure::format_err;
use finchers::endpoint::{reject, unit, value, EndpointExt};
use finchers::error::bad_request;
use finchers::endpoint::{unit, value, EndpointExt};
use finchers::local;

use http::StatusCode;
use matches::assert_matches;

#[test]
Expand All @@ -13,28 +10,6 @@ fn test_and_all_ok() {
assert_matches!(local::get("/").apply(&endpoint), Ok(("Hello", "world")));
}

#[test]
fn test_and_with_err_1() {
let endpoint = value("Hello").and(reject(|_| bad_request(format_err!(""))).output::<()>());

assert_matches!(
local::get("/").apply(&endpoint),
Err(ref e) if e.status_code() == StatusCode::BAD_REQUEST
);
}

#[test]
fn test_and_with_err_2() {
let endpoint = reject(|_| bad_request(format_err!("")))
.output::<()>()
.and(value("Hello"));

assert_matches!(
local::get("/").apply(&endpoint),
Err(ref e) if e.status_code() == StatusCode::BAD_REQUEST
);
}

#[test]
fn test_and_flatten() {
let endpoint = value("Hello")
Expand Down
6 changes: 3 additions & 3 deletions tests/endpoint/or.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use failure::format_err;
use finchers::endpoint::{reject, value, EndpointExt};
use finchers::endpoint::{value, EndpointExt};
use finchers::endpoints::path::path;
use finchers::error::bad_request;
use finchers::local;
Expand Down Expand Up @@ -28,10 +28,10 @@ fn test_or_choose_longer_segments() {
}

#[test]
fn test_or_with_rejection_path() {
fn test_or_with_rejection() {
let endpoint = path("foo")
.or(path("bar"))
.or(reject(|_| bad_request(format_err!("custom rejection"))));
.or_reject_with(|_err, _cx| bad_request(format_err!("custom rejection")));

assert_matches!(local::get("/foo").apply(&endpoint), Ok(..));

Expand Down
2 changes: 1 addition & 1 deletion tests/endpoint/recover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn test_recover() {
let endpoint = method::get(path::path("posts").and(path::param::<u32>()))
.map(|id: u32| format!("param={}", id));

let recovered = endpoint.fixed().recover(|err| {
let recovered = endpoint.or_reject().recover(|err| {
if err.is::<EndpointError>() {
ready(Ok(Response::builder()
.status(err.status_code())
Expand Down
7 changes: 3 additions & 4 deletions tests/endpoints/header.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use finchers::endpoint::{reject, EndpointExt};
use finchers::endpoint::EndpointExt;
use finchers::endpoints::header;
use finchers::error;
use finchers::local;
Expand Down Expand Up @@ -71,9 +71,8 @@ fn test_header_optional() {

#[test]
fn test_header_matches_with_rejection() {
let endpoint = header::matches("origin", "www.example.com").or(reject(|_| {
error::bad_request("The value of Origin is invalid")
}));
let endpoint = header::matches("origin", "www.example.com")
.or_reject_with(|_, _| error::bad_request("The value of Origin is invalid"));

assert_matches!(
local::get("/")
Expand Down

0 comments on commit 308d6a3

Please sign in to comment.