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

Commit

Permalink
fix(endpoint): reimplement combinators
Browse files Browse the repository at this point in the history
  • Loading branch information
ubnt-intrepid committed Apr 8, 2018
1 parent f767db8 commit 31da4e9
Show file tree
Hide file tree
Showing 25 changed files with 543 additions and 402 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use finchers::output::Display;
use finchers::runtime::Server;
fn main() {
let endpoint = endpoint("api/v1").with(choice![
let endpoint = endpoint("api/v1").right(choice![
get(path::<u64>()).map(|id| format!("GET: id={}", id)),
post(body::<String>()).map(|body| format!("POST: body={}", body)),
]);
Expand Down
2 changes: 1 addition & 1 deletion examples/form/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl fmt::Display for FormParam {

fn main() {
let endpoint = endpoint("search")
.with(choice![get(queries()), post(form_body()),])
.right(choice![get(queries()), post(form_body()),])
.map(|param: FormParam| {
println!("Received: {:#}", param);
Debug::new(param)
Expand Down
21 changes: 11 additions & 10 deletions examples/todo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,41 +30,42 @@ fn main() {
let app = application::new();

let endpoint = {
use finchers::endpoint::ok;
use finchers::endpoint::prelude::*;

let find_todo = get(path())
.and_then({
.try_abort({
let app = app.clone();
move |id| app.find_todo(id)
})
.and_then(|todo| todo.map(TheTodo).ok_or_else(|| NoRoute::new()));
.try_abort(|todo: Option<_>| todo.map(TheTodo).ok_or_else(|| NoRoute::new()));

let list_todos = get(()).and_then({
let list_todos = get(ok(())).try_abort({
let app = app.clone();
move |_| app.list_todos().map(Todos)
});

let add_todo = post(json_body()).and_then({
let add_todo = post(json_body()).try_abort({
let app = app.clone();
move |new_todo| app.add_todo(new_todo).map(NewTodo)
});

let patch_todo = patch((path(), json_body()))
.and_then({
let patch_todo = patch(path().and(json_body()))
.try_abort({
let app = app.clone();
move |(id, patch)| app.patch_todo(id, patch)
})
.and_then(|todo| todo.map(TheTodo).ok_or_else(|| NoRoute::new()));
.try_abort(|todo: Option<_>| todo.map(TheTodo).ok_or_else(|| NoRoute::new()));

let delete_todo = delete(path())
.and_then({
.try_abort({
let app = app.clone();
move |id| app.delete_todo(id)
})
.and_then(|todo| todo.map(|_| Deleted).ok_or_else(|| NoRoute::new()));
.try_abort(|todo: Option<_>| todo.map(|_| Deleted).ok_or_else(|| NoRoute::new()));

endpoint("api/v1/todos")
.with(choice![find_todo, list_todos, add_todo, patch_todo, delete_todo,])
.right(choice![find_todo, list_todos, add_todo, patch_todo, delete_todo,])
.map(JsonOutput::new)
};

Expand Down
60 changes: 60 additions & 0 deletions finchers-core/src/caller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use std::rc::Rc;
use std::sync::Arc;

pub trait Caller<T> {
type Output;
fn call(self, arg: T) -> Self::Output;
}

impl<F, T, R> Caller<T> for F
where
F: FnOnce(T) -> R,
{
type Output = R;

fn call(self, arg: T) -> Self::Output {
(self)(arg)
}
}

impl<F, T, R> Caller<T> for Rc<F>
where
F: Fn(T) -> R,
{
type Output = R;

fn call(self, arg: T) -> Self::Output {
(*self)(arg)
}
}

impl<F, T, R> Caller<T> for Arc<F>
where
F: Fn(T) -> R,
{
type Output = R;

fn call(self, arg: T) -> Self::Output {
(*self)(arg)
}
}

pub struct BoxedCaller<T: ?Sized>(Box<T>);

impl<T: ?Sized> BoxedCaller<T> {
pub fn new(caller: Box<T>) -> Self {
BoxedCaller(caller)
}
}

// TODO: FnBox?
impl<F, T, R> Caller<T> for BoxedCaller<F>
where
F: FnMut(T) -> R,
{
type Output = R;

fn call(mut self, arg: T) -> Self::Output {
(*self.0)(arg)
}
}
2 changes: 2 additions & 0 deletions finchers-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern crate mime;
#[cfg(feature = "from_hyper")]
extern crate hyper;

mod caller;
mod never;
mod string;

Expand All @@ -17,6 +18,7 @@ pub mod input;
pub mod output;

pub use bytes::Bytes;
pub use caller::{BoxedCaller, Caller};
pub use error::Error;
pub use input::Input;
pub use never::Never;
Expand Down
6 changes: 6 additions & 0 deletions finchers-core/src/never.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ impl HttpError for Never {
match *self {}
}
}

impl HttpError for ! {
fn status_code(&self) -> StatusCode {
unreachable!()
}
}
24 changes: 5 additions & 19 deletions finchers-endpoint/src/join_all.rs → finchers-endpoint/src/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,24 @@

use finchers_core::Input;
use futures::future;
use std::fmt;
use {Context, Endpoint, IntoEndpoint};

pub fn join_all<I>(iter: I) -> JoinAll<<I::Item as IntoEndpoint>::Endpoint>
pub fn all<I>(iter: I) -> All<<I::Item as IntoEndpoint>::Endpoint>
where
I: IntoIterator,
I::Item: IntoEndpoint,
{
JoinAll {
All {
inner: iter.into_iter().map(IntoEndpoint::into_endpoint).collect(),
}
}

pub struct JoinAll<E: Endpoint> {
#[derive(Clone, Debug)]
pub struct All<E> {
inner: Vec<E>,
}

impl<E: Endpoint + Clone> Clone for JoinAll<E> {
fn clone(&self) -> Self {
JoinAll {
inner: self.inner.clone(),
}
}
}

impl<E: Endpoint + fmt::Debug> fmt::Debug for JoinAll<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("JoinAll").field(&self.inner).finish()
}
}

impl<E: Endpoint> Endpoint for JoinAll<E> {
impl<E: Endpoint> Endpoint for All<E> {
type Item = Vec<E::Item>;
type Future = future::JoinAll<Vec<E::Future>>;

Expand Down
27 changes: 3 additions & 24 deletions finchers-endpoint/src/endpoint.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use super::*;
use Context;
use finchers_core::{Error, Input};
use futures::Future;
Expand All @@ -7,14 +6,14 @@ use std::sync::Arc;

/// Abstruction of an endpoint.
pub trait Endpoint {
/// The type *on success*.
/// The *internal* type of this endpoint.
type Item;

/// The type of returned value from `apply`.
/// The type of future returned from `apply`.
type Future: Future<Item = Self::Item, Error = Error>;

/// Validates the incoming HTTP request,
/// and returns the instance of `Task` if matched.
/// and returns the instance of `Future` if matched.
fn apply(&self, input: &Input, ctx: &mut Context) -> Option<Self::Future>;
}

Expand Down Expand Up @@ -75,26 +74,6 @@ impl<E: Endpoint> IntoEndpoint for E {
}
}

impl IntoEndpoint for () {
type Item = ();
type Endpoint = EndpointOk<()>;

#[inline]
fn into_endpoint(self) -> Self::Endpoint {
ok(())
}
}

impl<E: IntoEndpoint> IntoEndpoint for Vec<E> {
type Item = Vec<E::Item>;
type Endpoint = JoinAll<E::Endpoint>;

#[inline]
fn into_endpoint(self) -> Self::Endpoint {
join_all(self)
}
}

/// A shortcut of `IntoEndpoint::into_endpoint()`
#[inline]
pub fn endpoint<E: IntoEndpoint>(endpoint: E) -> E::Endpoint {
Expand Down
52 changes: 52 additions & 0 deletions finchers-endpoint/src/ext/abort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use finchers_core::error::HttpError;
use finchers_core::{Error, Input};
use futures::{Future, Poll};
use {Context, Endpoint, IntoEndpoint};

pub fn new<E>(endpoint: E) -> Abort<E::Endpoint>
where
E: IntoEndpoint,
E::Item: HttpError,
{
Abort {
endpoint: endpoint.into_endpoint(),
}
}

#[derive(Clone, Copy, Debug)]
pub struct Abort<E> {
endpoint: E,
}

impl<E> Endpoint for Abort<E>
where
E: Endpoint,
E::Item: HttpError,
{
type Item = !;
type Future = AbortFuture<E::Future>;

fn apply(&self, input: &Input, ctx: &mut Context) -> Option<Self::Future> {
let fut = self.endpoint.apply(input, ctx)?;
Some(AbortFuture { fut })
}
}

#[derive(Debug)]
pub struct AbortFuture<T> {
fut: T,
}

impl<T> Future for AbortFuture<T>
where
T: Future<Error = Error>,
T::Item: HttpError,
{
type Item = !;
type Error = Error;

fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let item = try_ready!(self.fut.poll());
Err(Error::from(item).into())
}
}
62 changes: 62 additions & 0 deletions finchers-endpoint/src/ext/abort_with.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use finchers_core::error::HttpError;
use finchers_core::{Caller, Error, Input};
use futures::{Future, Poll};
use {Context, Endpoint, IntoEndpoint};

pub fn new<E, F>(endpoint: E, f: F) -> AbortWith<E::Endpoint, F>
where
E: IntoEndpoint,
F: Caller<E::Item> + Clone,
F::Output: HttpError,
{
AbortWith {
endpoint: endpoint.into_endpoint(),
f,
}
}

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

impl<E, F> Endpoint for AbortWith<E, F>
where
E: Endpoint,
F: Caller<E::Item> + Clone,
F::Output: HttpError,
{
type Item = !;
type Future = AbortWithFuture<E::Future, F>;

fn apply(&self, input: &Input, ctx: &mut Context) -> Option<Self::Future> {
let fut = self.endpoint.apply(input, ctx)?;
Some(AbortWithFuture {
fut,
f: Some(self.f.clone()),
})
}
}

#[derive(Debug)]
pub struct AbortWithFuture<T, F> {
fut: T,
f: Option<F>,
}

impl<T, F> Future for AbortWithFuture<T, F>
where
T: Future<Error = Error>,
F: Caller<T::Item>,
F::Output: HttpError,
{
type Item = !;
type Error = Error;

fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let item = try_ready!(self.fut.poll());
let f = self.f.take().expect("cannot resolve twice");
Err(f.call(item).into())
}
}
Loading

0 comments on commit 31da4e9

Please sign in to comment.