Skip to content

Commit

Permalink
Merge pull request #397 from tirr-c/more-generic-endpoint
Browse files Browse the repository at this point in the history
Make Endpoint::call generic over lifetime
  • Loading branch information
tirr-c authored Jan 31, 2020
2 parents b429110 + cd975d8 commit 935498f
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 65 deletions.
7 changes: 3 additions & 4 deletions examples/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@ fn main() -> io::Result<()> {
task::block_on(async {
let mut app = tide::new();

app.at("/submit").post(|mut req: tide::Request<()>| {
async move {
app.at("/submit")
.post(|mut req: tide::Request<()>| async move {
let cat: Cat = req.body_json().await.unwrap();
println!("cat name: {}", cat.name);

let cat = Cat {
name: "chashu".into(),
};
tide::Response::new(200).body_json(&cat).unwrap()
}
});
});

app.listen("127.0.0.1:8080").await?;
Ok(())
Expand Down
11 changes: 3 additions & 8 deletions src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,19 @@ use crate::{response::IntoResponse, Request, Response};
///
/// Tide routes will also accept endpoints with `Fn` signatures of this form, but using the `async` keyword has better ergonomics.
pub trait Endpoint<State>: Send + Sync + 'static {
/// The async result of `call`.
type Fut: Future<Output = Response> + Send + 'static;

/// Invoke the endpoint within the given context
fn call(&self, req: Request<State>) -> Self::Fut;
fn call<'a>(&'a self, req: Request<State>) -> BoxFuture<'a, Response>;
}

pub(crate) type DynEndpoint<State> =
dyn (Fn(Request<State>) -> BoxFuture<'static, Response>) + 'static + Send + Sync;
pub(crate) type DynEndpoint<State> = dyn Endpoint<State>;

impl<State, F: Send + Sync + 'static, Fut> Endpoint<State> for F
where
F: Fn(Request<State>) -> Fut,
Fut: Future + Send + 'static,
Fut::Output: IntoResponse,
{
type Fut = BoxFuture<'static, Response>;
fn call(&self, req: Request<State>) -> Self::Fut {
fn call<'a>(&'a self, req: Request<State>) -> BoxFuture<'a, Response> {
let fut = (self)(req);
Box::pin(async move { fut.await.into_response() })
}
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl<'a, State: 'static> Next<'a, State> {
self.next_middleware = next;
current.handle(req, self)
} else {
(self.endpoint)(req)
self.endpoint.call(req)
}
}
}
26 changes: 4 additions & 22 deletions src/redirect.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use async_std::future;
use async_std::task::{Context, Poll};

use std::pin::Pin;

use crate::utils::BoxFuture;
use crate::{Endpoint, Request, Response};

/// Redirect a route to another route.
Expand All @@ -21,7 +17,7 @@ use crate::{Endpoint, Request, Response};
/// app.listen("127.0.0.1:8080").await?;
/// #
/// # Ok(()) }) }
/// ````
/// ```
pub fn redirect<State>(location: impl AsRef<str>) -> impl Endpoint<State> {
let location = location.as_ref().to_owned();
Redirect { location }
Expand All @@ -33,22 +29,8 @@ pub struct Redirect {
}

impl<State> Endpoint<State> for Redirect {
type Fut = Future;

fn call(&self, _req: Request<State>) -> Self::Fut {
fn call<'a>(&'a self, _req: Request<State>) -> BoxFuture<'a, Response> {
let res = Response::new(307).set_header("Location", &self.location);
Future { res: Some(res) }
}
}

/// Future returned from `redirect`.
pub struct Future {
res: Option<Response>,
}

impl future::Future for Future {
type Output = Response;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(self.res.take().unwrap())
Box::pin(async move { res })
}
}
5 changes: 2 additions & 3 deletions src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ impl<State: 'static> Router<State> {
self.method_map
.entry(method)
.or_insert_with(MethodRouter::new)
.add(path, Box::new(move |cx| Box::pin(ep.call(cx))))
.add(path, Box::new(ep))
}

pub(crate) fn add_all(&mut self, path: &str, ep: impl Endpoint<State>) {
self.all_method_router
.add(path, Box::new(move |cx| Box::pin(ep.call(cx))))
self.all_method_router.add(path, Box::new(ep))
}

pub(crate) fn route(&self, path: &str, method: http::Method) -> Selection<'_, State> {
Expand Down
19 changes: 13 additions & 6 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,14 +321,23 @@ impl<State: Send + Sync + 'static> Server<State> {
///
/// This type is useful only in conjunction with the [`HttpService`] trait,
/// i.e. for hosting a Tide app within some custom HTTP server.
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct Service<State> {
router: Arc<Router<State>>,
state: Arc<State>,
middleware: Arc<Vec<Arc<dyn Middleware<State>>>>,
}

impl<State> Clone for Service<State> {
fn clone(&self) -> Self {
Self {
router: self.router.clone(),
state: self.state.clone(),
middleware: self.middleware.clone(),
}
}
}

#[derive(Debug)]
pub struct ReadyFuture;

Expand All @@ -351,17 +360,15 @@ impl<State: Sync + Send + 'static> HttpService for Service<State> {

fn respond(&self, _conn: &mut (), req: http_service::Request) -> Self::ResponseFuture {
let req = Request::new(self.state.clone(), req, Vec::new());
let fut = self.call(req);
Box::pin(async move { Ok(fut.await.into()) })
let service = self.clone();
Box::pin(async move { Ok(service.call(req).await.into()) })
}
}

impl<State: Sync + Send + 'static, InnerState: Sync + Send + 'static> Endpoint<State>
for Service<InnerState>
{
type Fut = BoxFuture<'static, Response>;

fn call(&self, req: Request<State>) -> Self::Fut {
fn call<'a>(&'a self, req: Request<State>) -> BoxFuture<'a, Response> {
let Request {
request: req,
mut route_params,
Expand Down
7 changes: 3 additions & 4 deletions src/server/route.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{router::Router, Endpoint};
use crate::utils::BoxFuture;
use crate::{router::Router, Endpoint, Response};

/// A handle to a route.
///
Expand Down Expand Up @@ -172,9 +173,7 @@ impl<E> Clone for StripPrefixEndpoint<E> {
}

impl<State, E: Endpoint<State>> Endpoint<State> for StripPrefixEndpoint<E> {
type Fut = E::Fut;

fn call(&self, mut req: crate::Request<State>) -> Self::Fut {
fn call<'a>(&'a self, mut req: crate::Request<State>) -> BoxFuture<'a, Response> {
let rest = req.rest().unwrap_or("");
let mut path_and_query = format!("/{}", rest);
let uri = req.uri();
Expand Down
8 changes: 3 additions & 5 deletions tests/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,9 @@ fn nested_middleware() {
fn nested_with_different_state() {
let mut outer = tide::new();
let mut inner = tide::with_state(42);
inner.at("/").get(|req: tide::Request<i32>| {
async move {
let num = req.state();
format!("the number is {}", num)
}
inner.at("/").get(|req: tide::Request<i32>| async move {
let num = req.state();
format!("the number is {}", num)
});
outer.at("/").get(|_| async move { "Hello, world!" });
outer.at("/foo").nest(inner);
Expand Down
20 changes: 8 additions & 12 deletions tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ fn hello_world() -> Result<(), surf::Exception> {
task::block_on(async {
let server = task::spawn(async {
let mut app = tide::new();
app.at("/").get(|mut req: tide::Request<()>| {
async move {
assert_eq!(req.body_string().await.unwrap(), "nori".to_string());
tide::Response::new(200).body_string("says hello".to_string())
}
app.at("/").get(|mut req: tide::Request<()>| async move {
assert_eq!(req.body_string().await.unwrap(), "nori".to_string());
tide::Response::new(200).body_string("says hello".to_string())
});
app.listen("localhost:8080").await?;
Result::<(), surf::Exception>::Ok(())
Expand Down Expand Up @@ -67,13 +65,11 @@ fn json() -> Result<(), surf::Exception> {
task::block_on(async {
let server = task::spawn(async {
let mut app = tide::new();
app.at("/").get(|mut req: tide::Request<()>| {
async move {
let mut counter: Counter = req.body_json().await.unwrap();
assert_eq!(counter.count, 0);
counter.count = 1;
tide::Response::new(200).body_json(&counter).unwrap()
}
app.at("/").get(|mut req: tide::Request<()>| async move {
let mut counter: Counter = req.body_json().await.unwrap();
assert_eq!(counter.count, 0);
counter.count = 1;
tide::Response::new(200).body_json(&counter).unwrap()
});
app.listen("localhost:8082").await?;
Result::<(), surf::Exception>::Ok(())
Expand Down

0 comments on commit 935498f

Please sign in to comment.