From 02f9ebaee914fa514d97342e641baaf3bfe679e8 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 3 Jul 2022 21:07:29 +0200 Subject: [PATCH] checkpoint --- Cargo.toml | 4 +- axum-extra/src/extract/cached.rs | 14 ++-- axum-extra/src/extract/cookie/mod.rs | 19 +++-- axum-extra/src/extract/cookie/private.rs | 7 +- axum-extra/src/extract/cookie/signed.rs | 7 +- axum-extra/src/extract/form.rs | 17 ++--- axum-extra/src/extract/query.rs | 15 ++-- axum-extra/src/json_lines.rs | 61 +++++++-------- axum-extra/src/routing/mod.rs | 76 ++++++++++++------- axum-extra/src/routing/resource.rs | 67 +++++++--------- axum-extra/src/routing/spa.rs | 29 +++---- axum/src/routing/method_routing.rs | 7 ++ .../src/main.rs | 10 +-- 13 files changed, 172 insertions(+), 161 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a84f6fdd..a221fbc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,8 @@ members = [ "axum", "axum-core", - # "axum-extra", - # "axum-macros", + "axum-extra", + "axum-macros", # internal crate used to bump the minimum versions we # get for some dependencies which otherwise wouldn't build diff --git a/axum-extra/src/extract/cached.rs b/axum-extra/src/extract/cached.rs index 0ada78a8..e526aa9c 100644 --- a/axum-extra/src/extract/cached.rs +++ b/axum-extra/src/extract/cached.rs @@ -88,14 +88,15 @@ pub struct Cached(pub T); struct CachedEntry(T); #[async_trait] -impl FromRequest for Cached +impl FromRequest for Cached where + S: Send, B: Send, - T: FromRequest + Clone + Send + Sync + 'static, + T: FromRequest + Clone + Send + Sync + 'static, { type Rejection = T::Rejection; - async fn from_request(req: &mut RequestParts) -> Result { + async fn from_request(req: &mut RequestParts) -> Result { match Extension::>::from_request(req).await { Ok(Extension(CachedEntry(value))) => Ok(Self(value)), Err(_) => { @@ -139,19 +140,20 @@ mod tests { struct Extractor(Instant); #[async_trait] - impl FromRequest for Extractor + impl FromRequest for Extractor where + S: Send, B: Send, { type Rejection = Infallible; - async fn from_request(_req: &mut RequestParts) -> Result { + async fn from_request(_req: &mut RequestParts) -> Result { COUNTER.fetch_add(1, Ordering::SeqCst); Ok(Self(Instant::now())) } } - let mut req = RequestParts::new(Request::new(())); + let mut req = RequestParts::new((), Request::new(())); let first = Cached::::from_request(&mut req).await.unwrap().0; assert_eq!(COUNTER.load(Ordering::SeqCst), 1); diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 86a841cb..f1a51d1a 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -88,13 +88,14 @@ pub struct CookieJar { } #[async_trait] -impl FromRequest for CookieJar +impl FromRequest for CookieJar where + S: Send, B: Send, { type Rejection = Infallible; - async fn from_request(req: &mut RequestParts) -> Result { + async fn from_request(req: &mut RequestParts) -> Result { let mut jar = cookie_lib::CookieJar::new(); for cookie in cookies_from_request(req) { jar.add_original(cookie); @@ -103,8 +104,8 @@ where } } -fn cookies_from_request( - req: &mut RequestParts, +fn cookies_from_request( + req: &mut RequestParts, ) -> impl Iterator> + '_ { req.headers() .get_all(COOKIE) @@ -226,13 +227,12 @@ mod tests { jar.remove(Cookie::named("key")) } - let app = Router::<_, Body, _>::new() + let app = Router::without_state() .route("/set", get(set_cookie)) .route("/get", get(get_cookie)) .route("/remove", get(remove_cookie)) .layer(Extension(Key::generate())) - .layer(Extension(CustomKey(Key::generate()))) - .state(()); + .layer(Extension(CustomKey(Key::generate()))); let res = app .clone() @@ -295,10 +295,9 @@ mod tests { format!("{:?}", jar.get("key")) } - let app = Router::<_, Body, _>::new() + let app = Router::without_state() .route("/get", get(get_cookie)) - .layer(Extension(Key::generate())) - .state(()); + .layer(Extension(Key::generate())); let res = app .clone() diff --git a/axum-extra/src/extract/cookie/private.rs b/axum-extra/src/extract/cookie/private.rs index a583a3c9..57adb74e 100644 --- a/axum-extra/src/extract/cookie/private.rs +++ b/axum-extra/src/extract/cookie/private.rs @@ -74,14 +74,15 @@ impl fmt::Debug for PrivateCookieJar { } #[async_trait] -impl FromRequest for PrivateCookieJar +impl FromRequest for PrivateCookieJar where B: Send, + S: Send, K: Into + Clone + Send + Sync + 'static, { - type Rejection = as FromRequest>::Rejection; + type Rejection = as FromRequest>::Rejection; - async fn from_request(req: &mut RequestParts) -> Result { + async fn from_request(req: &mut RequestParts) -> Result { let key = Extension::::from_request(req).await?.0.into(); let mut jar = cookie_lib::CookieJar::new(); diff --git a/axum-extra/src/extract/cookie/signed.rs b/axum-extra/src/extract/cookie/signed.rs index 51fb865c..cab8d6af 100644 --- a/axum-extra/src/extract/cookie/signed.rs +++ b/axum-extra/src/extract/cookie/signed.rs @@ -92,14 +92,15 @@ impl fmt::Debug for SignedCookieJar { } #[async_trait] -impl FromRequest for SignedCookieJar +impl FromRequest for SignedCookieJar where B: Send, + S: Send, K: Into + Clone + Send + Sync + 'static, { - type Rejection = as FromRequest>::Rejection; + type Rejection = as FromRequest>::Rejection; - async fn from_request(req: &mut RequestParts) -> Result { + async fn from_request(req: &mut RequestParts) -> Result { let key = Extension::::from_request(req).await?.0.into(); let mut jar = cookie_lib::CookieJar::new(); diff --git a/axum-extra/src/extract/form.rs b/axum-extra/src/extract/form.rs index 238f409f..fdce04ba 100644 --- a/axum-extra/src/extract/form.rs +++ b/axum-extra/src/extract/form.rs @@ -54,16 +54,17 @@ impl Deref for Form { } #[async_trait] -impl FromRequest for Form +impl FromRequest for Form where T: DeserializeOwned, B: HttpBody + Send, B::Data: Send, B::Error: Into, + S: Send, { type Rejection = FormRejection; - async fn from_request(req: &mut RequestParts) -> Result { + async fn from_request(req: &mut RequestParts) -> Result { if req.method() == Method::GET { let query = req.uri().query().unwrap_or_default(); let value = serde_html_form::from_str(query) @@ -84,7 +85,7 @@ where } // this is duplicated in `axum/src/extract/mod.rs` -fn has_content_type(req: &RequestParts, expected_content_type: &mime::Mime) -> bool { +fn has_content_type(req: &RequestParts, expected_content_type: &mime::Mime) -> bool { let content_type = if let Some(content_type) = req.headers().get(header::CONTENT_TYPE) { content_type } else { @@ -116,12 +117,10 @@ mod tests { values: Vec, } - let app = Router::new() - .route( - "/", - post(|Form(data): Form| async move { data.values.join(",") }), - ) - .state(()); + let app = Router::without_state().route( + "/", + post(|Form(data): Form| async move { data.values.join(",") }), + ); let client = TestClient::new(app); diff --git a/axum-extra/src/extract/query.rs b/axum-extra/src/extract/query.rs index 8638e8ad..38522fa8 100644 --- a/axum-extra/src/extract/query.rs +++ b/axum-extra/src/extract/query.rs @@ -58,14 +58,15 @@ use std::ops::Deref; pub struct Query(pub T); #[async_trait] -impl FromRequest for Query +impl FromRequest for Query where T: DeserializeOwned, B: Send, + S: Send, { type Rejection = QueryRejection; - async fn from_request(req: &mut RequestParts) -> Result { + async fn from_request(req: &mut RequestParts) -> Result { let query = req.uri().query().unwrap_or_default(); let value = serde_html_form::from_str(query) .map_err(FailedToDeserializeQueryString::__private_new::)?; @@ -97,12 +98,10 @@ mod tests { values: Vec, } - let app = Router::new() - .route( - "/", - post(|Query(data): Query| async move { data.values.join(",") }), - ) - .state(()); + let app = Router::without_state().route( + "/", + post(|Query(data): Query| async move { data.values.join(",") }), + ); let client = TestClient::new(app); diff --git a/axum-extra/src/json_lines.rs b/axum-extra/src/json_lines.rs index f704f581..e16b6695 100644 --- a/axum-extra/src/json_lines.rs +++ b/axum-extra/src/json_lines.rs @@ -98,16 +98,17 @@ impl JsonLines { } #[async_trait] -impl FromRequest for JsonLines +impl FromRequest for JsonLines where B: HttpBody + Send + 'static, B::Data: Into, B::Error: Into, T: DeserializeOwned, + S: Send, { type Rejection = BodyAlreadyExtracted; - async fn from_request(req: &mut RequestParts) -> Result { + async fn from_request(req: &mut RequestParts) -> Result { // `Stream::lines` isn't a thing so we have to convert it into an `AsyncRead` // so we can call `AsyncRead::lines` and then convert it back to a `Stream` @@ -217,24 +218,22 @@ mod tests { #[tokio::test] async fn extractor() { - let app = Router::new() - .route( - "/", - post(|mut stream: JsonLines| async move { - assert_eq!(stream.next().await.unwrap().unwrap(), User { id: 1 }); - assert_eq!(stream.next().await.unwrap().unwrap(), User { id: 2 }); - assert_eq!(stream.next().await.unwrap().unwrap(), User { id: 3 }); + let app = Router::without_state().route( + "/", + post(|mut stream: JsonLines| async move { + assert_eq!(stream.next().await.unwrap().unwrap(), User { id: 1 }); + assert_eq!(stream.next().await.unwrap().unwrap(), User { id: 2 }); + assert_eq!(stream.next().await.unwrap().unwrap(), User { id: 3 }); - // sources are downcastable to `serde_json::Error` - let err = stream.next().await.unwrap().unwrap_err(); - let _: &serde_json::Error = err - .source() - .unwrap() - .downcast_ref::() - .unwrap(); - }), - ) - .state(()); + // sources are downcastable to `serde_json::Error` + let err = stream.next().await.unwrap().unwrap_err(); + let _: &serde_json::Error = err + .source() + .unwrap() + .downcast_ref::() + .unwrap(); + }), + ); let client = TestClient::new(app); @@ -257,19 +256,17 @@ mod tests { #[tokio::test] async fn response() { - let app = Router::new() - .route( - "/", - get(|| async { - let values = futures_util::stream::iter(vec![ - Ok::<_, Infallible>(User { id: 1 }), - Ok::<_, Infallible>(User { id: 2 }), - Ok::<_, Infallible>(User { id: 3 }), - ]); - JsonLines::new(values) - }), - ) - .state(()); + let app = Router::without_state().route( + "/", + get(|| async { + let values = futures_util::stream::iter(vec![ + Ok::<_, Infallible>(User { id: 1 }), + Ok::<_, Infallible>(User { id: 2 }), + Ok::<_, Infallible>(User { id: 3 }), + ]); + JsonLines::new(values) + }), + ); let client = TestClient::new(app); diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index e78f3276..fbbcab99 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -4,6 +4,7 @@ use axum::{ handler::Handler, http::Request, response::{Redirect, Response}, + routing::{MethodRouter, MissingState}, Router, }; use std::{convert::Infallible, future::ready}; @@ -29,7 +30,7 @@ pub use self::typed::{FirstElementIs, TypedPath}; pub use self::spa::SpaRouter; /// Extension trait that adds additional methods to [`Router`]. -pub trait RouterExt: sealed::Sealed { +pub trait RouterExt: sealed::Sealed { /// Add a typed `GET` route to the router. /// /// The path will be inferred from the first argument to the handler function which must @@ -39,7 +40,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_get(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath; @@ -52,7 +53,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_delete(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath; @@ -65,7 +66,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_head(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath; @@ -78,7 +79,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_options(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath; @@ -91,7 +92,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_patch(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath; @@ -104,7 +105,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_post(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath; @@ -117,7 +118,7 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_put(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath; @@ -130,10 +131,18 @@ pub trait RouterExt: sealed::Sealed { #[cfg(feature = "typed-routing")] fn typed_trace(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath; + fn route_with_tsr( + self, + path: &str, + // TODO(david): constrain this so it only accepts methods + // routers containing handlers + method_router: MethodRouter, + ) -> Self; + /// Add another route to the router with an additional "trailing slash redirect" route. /// /// If you add a route _without_ a trailing slash, such as `/foo`, this method will also add a @@ -159,23 +168,23 @@ pub trait RouterExt: sealed::Sealed { /// .route_with_tsr("/bar/", get(|| async {})); /// # let _: Router = app; /// ``` - fn route_with_tsr(self, path: &str, service: T) -> Self + fn route_service_with_tsr(self, path: &str, service: T) -> Self where T: Service, Response = Response, Error = Infallible> + Clone + Send + 'static, T::Future: Send + 'static, Self: Sized; } -impl RouterExt for Router +impl RouterExt for Router where B: axum::body::HttpBody + Send + 'static, + S: Clone + Send + Sync + 'static, R: 'static, - S: 'static, { #[cfg(feature = "typed-routing")] fn typed_get(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath, { @@ -185,7 +194,7 @@ where #[cfg(feature = "typed-routing")] fn typed_delete(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath, { @@ -195,7 +204,7 @@ where #[cfg(feature = "typed-routing")] fn typed_head(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath, { @@ -205,7 +214,7 @@ where #[cfg(feature = "typed-routing")] fn typed_options(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath, { @@ -215,7 +224,7 @@ where #[cfg(feature = "typed-routing")] fn typed_patch(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath, { @@ -225,7 +234,7 @@ where #[cfg(feature = "typed-routing")] fn typed_post(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath, { @@ -235,7 +244,7 @@ where #[cfg(feature = "typed-routing")] fn typed_put(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath, { @@ -245,32 +254,42 @@ where #[cfg(feature = "typed-routing")] fn typed_trace(self, handler: H) -> Self where - H: Handler, + H: Handler, T: FirstElementIs

+ 'static, P: TypedPath, { self.route(P::PATH, axum::routing::trace(handler)) } - fn route_with_tsr(mut self, path: &str, service: T) -> Self + fn route_with_tsr( + mut self, + path: &str, + // TODO(david): constrain this so it only accepts methods + // routers containing handlers + method_router: MethodRouter, + ) -> Self { + todo!() + } + + fn route_service_with_tsr(mut self, path: &str, service: T) -> Self where T: Service, Response = Response, Error = Infallible> + Clone + Send + 'static, T::Future: Send + 'static, Self: Sized, { - self = self.route(path, service); + self = self.route_service(path, service); let redirect = Redirect::permanent(path); if let Some(path_without_trailing_slash) = path.strip_suffix('/') { - self.route( + self.route_service( path_without_trailing_slash, - (move || ready(redirect.clone())).into_service(), + (move || ready(redirect.clone())).into_service(()), ) } else { - self.route( + self.route_service( &format!("{}/", path), - (move || ready(redirect.clone())).into_service(), + (move || ready(redirect.clone())).into_service(()), ) } } @@ -289,10 +308,9 @@ mod tests { #[tokio::test] async fn test_tsr() { - let app = Router::new() + let app = Router::without_state() .route_with_tsr("/foo", get(|| async {})) - .route_with_tsr("/bar/", get(|| async {})) - .state(()); + .route_with_tsr("/bar/", get(|| async {})); let client = TestClient::new(app); diff --git a/axum-extra/src/routing/resource.rs b/axum-extra/src/routing/resource.rs index d67baa71..2d6d8eba 100644 --- a/axum-extra/src/routing/resource.rs +++ b/axum-extra/src/routing/resource.rs @@ -3,7 +3,7 @@ use axum::{ handler::Handler, http::Request, response::Response, - routing::{delete, get, on, post, MethodFilter, MissingState, WithState}, + routing::{delete, get, on, post, MethodFilter, MethodRouter, MissingState, WithState}, Router, }; use std::{convert::Infallible, fmt}; @@ -47,12 +47,12 @@ use tower_service::Service; /// let app = Router::new().merge(users); /// # let _: Router = app; /// ``` -pub struct Resource { +pub struct Resource { pub(crate) name: String, - pub(crate) router: Router, + pub(crate) router: Router, } -impl fmt::Debug for Resource +impl fmt::Debug for Resource where S: fmt::Debug, { @@ -64,7 +64,7 @@ where } } -impl Resource +impl Resource where B: axum::body::HttpBody + Send + 'static, { @@ -77,29 +77,18 @@ where router: Default::default(), } } - - /// TODO(david): docs - pub fn state(self, state: S) -> Resource - where - S: Clone, - { - Resource { - name: self.name, - router: self.router.state(state), - } - } } -impl Resource +impl Resource where B: axum::body::HttpBody + Send + 'static, - S: 'static, + S: Clone + Send + Sync + 'static, R: 'static, { /// Add a handler at `GET /{resource_name}`. pub fn index(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.index_create_path(); @@ -109,7 +98,7 @@ where /// Add a handler at `POST /{resource_name}`. pub fn create(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.index_create_path(); @@ -119,7 +108,7 @@ where /// Add a handler at `GET /{resource_name}/new`. pub fn new(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = format!("/{}/new", self.name); @@ -129,7 +118,7 @@ where /// Add a handler at `GET /{resource_name}/:{resource_name}_id`. pub fn show(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.show_update_destroy_path(); @@ -139,7 +128,7 @@ where /// Add a handler at `GET /{resource_name}/:{resource_name}_id/edit`. pub fn edit(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = format!("/{0}/:{0}_id/edit", self.name); @@ -149,7 +138,7 @@ where /// Add a handler at `PUT or PATCH /resource_name/:{resource_name}_id`. pub fn update(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.show_update_destroy_path(); @@ -159,7 +148,7 @@ where /// Add a handler at `DELETE /{resource_name}/:{resource_name}_id`. pub fn destroy(self, handler: H) -> Self where - H: Handler, + H: Handler, T: 'static, { let path = self.show_update_destroy_path(); @@ -169,7 +158,7 @@ where /// Nest another router at the "member level". /// /// The routes will be nested at `/{resource_name}/:{resource_name}_id`. - pub fn nest(mut self, router: Router) -> Self { + pub fn nest(mut self, router: Router) -> Self { let path = self.show_update_destroy_path(); self.router = self.router.nest(&path, router); self @@ -178,7 +167,7 @@ where /// Nest another router at the "collection level". /// /// The routes will be nested at `/{resource_name}`. - pub fn nest_collection(mut self, router: Router) -> Self { + pub fn nest_collection(mut self, router: Router) -> Self { let path = self.index_create_path(); self.router = self.router.nest(&path, router); self @@ -192,18 +181,18 @@ where format!("/{0}/:{0}_id", self.name) } - fn route(mut self, path: &str, svc: T) -> Self - where - T: Service, Response = Response, Error = Infallible> + Clone + Send + 'static, - T::Future: Send + 'static, - { - self.router = self.router.route(path, svc); + fn route( + mut self, + path: &str, + method_router: MethodRouter, + ) -> Self { + self.router = self.router.route(path, method_router); self } } -impl From> for Router { - fn from(resource: Resource) -> Self { +impl From> for Router { + fn from(resource: Resource) -> Self { resource.router } } @@ -233,7 +222,7 @@ mod tests { Router::new().route("/featured", get(|| async move { "users#featured" })), ); - let mut app = Router::new().merge(users).state(()); + let mut app = Router::without_state().merge(users); assert_eq!( call_route(&mut app, Method::GET, "/users").await, @@ -286,11 +275,7 @@ mod tests { ); } - async fn call_route( - app: &mut Router<(), Body, axum::routing::WithState>, - method: Method, - uri: &str, - ) -> String { + async fn call_route(app: &mut Router<(), WithState>, method: Method, uri: &str) -> String { let res = app .ready() .await diff --git a/axum-extra/src/routing/spa.rs b/axum-extra/src/routing/spa.rs index 68a50c5c..2e4a4158 100644 --- a/axum-extra/src/routing/spa.rs +++ b/axum-extra/src/routing/spa.rs @@ -147,7 +147,7 @@ impl SpaRouter { } } -impl From> for Router +impl From> for Router where F: Clone + Send + 'static, HandleError, F, T>: @@ -155,17 +155,22 @@ where , F, T> as Service>>::Future: Send, B: HttpBody + Send + 'static, T: 'static, - S: 'static, + S: Clone + Send + Sync + 'static, { fn from(spa: SpaRouter) -> Self { let assets_service = get_service(ServeDir::new(&spa.paths.assets_dir)) - .handle_error(spa.handle_error.clone()); + .handle_error(spa.handle_error.clone()) + // TODO(david): having to do this is annoying + .state(()); + + let fallback_service = get_service(ServeFile::new(&spa.paths.index_file)) + .handle_error(spa.handle_error) + // TODO(david): having to do this is annoying + .state(()); Router::new() .nest_service(&spa.paths.assets_path, assets_service) - .fallback( - get_service(ServeFile::new(&spa.paths.index_file)).handle_error(spa.handle_error), - ) + .fallback_service(fallback_service) } } @@ -213,10 +218,9 @@ mod tests { #[tokio::test] async fn basic() { - let app = Router::new() + let app = Router::without_state() .route("/foo", get(|| async { "GET /foo" })) - .merge(SpaRouter::new("/assets", "test_files")) - .state(()); + .merge(SpaRouter::new("/assets", "test_files")); let client = TestClient::new(app); let res = client.get("/").send().await; @@ -241,9 +245,8 @@ mod tests { #[tokio::test] async fn setting_index_file() { - let app = Router::new() - .merge(SpaRouter::new("/assets", "test_files").index_file("index_2.html")) - .state(()); + let app = Router::without_state() + .merge(SpaRouter::new("/assets", "test_files").index_file("index_2.html")); let client = TestClient::new(app); let res = client.get("/").send().await; @@ -267,6 +270,6 @@ mod tests { let spa = SpaRouter::new("/assets", "test_files").handle_error(handle_error); - Router::<(), Body, _>::new().merge(spa); + Router::<()>::new().merge(spa); } } diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 5a93c420..9b46e308 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -1469,4 +1469,11 @@ mod tests { async fn created() -> (StatusCode, &'static str) { (StatusCode::CREATED, "created") } + + #[test] + fn service_constructors_have_state() { + // shouldn't have to do + // get_service(...).state(...) + todo!() + } } diff --git a/examples/routes-and-handlers-close-together/src/main.rs b/examples/routes-and-handlers-close-together/src/main.rs index 5e52ad7b..2e6510d0 100644 --- a/examples/routes-and-handlers-close-together/src/main.rs +++ b/examples/routes-and-handlers-close-together/src/main.rs @@ -12,7 +12,7 @@ use std::net::SocketAddr; #[tokio::main] async fn main() { - let app = Router::new() + let app = Router::without_state() .merge(root()) .merge(get_foo()) .merge(post_foo()); @@ -25,7 +25,7 @@ async fn main() { .unwrap(); } -fn root() -> Router { +fn root() -> Router<()> { async fn handler() -> &'static str { "Hello, World!" } @@ -33,7 +33,7 @@ fn root() -> Router { route("/", get(handler)) } -fn get_foo() -> Router { +fn get_foo() -> Router<()> { async fn handler() -> &'static str { "Hi from `GET /foo`" } @@ -41,7 +41,7 @@ fn get_foo() -> Router { route("/foo", get(handler)) } -fn post_foo() -> Router { +fn post_foo() -> Router<()> { async fn handler() -> &'static str { "Hi from `POST /foo`" } @@ -49,6 +49,6 @@ fn post_foo() -> Router { route("/foo", post(handler)) } -fn route(path: &str, method_router: MethodRouter) -> Router { +fn route(path: &str, method_router: MethodRouter<()>) -> Router<()> { Router::new().route(path, method_router) }