From 1b7326e9dedfa3632395d64e3cf8bc103cf2d0f0 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 13 Feb 2022 21:31:56 +0100 Subject: [PATCH] re-export `axum_macros::TypedPath` from `axum_extra` --- axum-extra/Cargo.toml | 4 +++- axum-extra/src/routing/mod.rs | 39 ++++++++++++++++++++++++++++++++ axum-extra/src/routing/typed.rs | 20 ++++++++-------- axum-macros/src/typed_path.rs | 21 +++++++++++++---- examples/hello-world/Cargo.toml | 3 +-- examples/hello-world/src/main.rs | 3 +-- 6 files changed, 71 insertions(+), 19 deletions(-) diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 43093109..fdfb9b49 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -11,7 +11,9 @@ repository = "https://github.com/tokio-rs/axum" version = "0.1.2" [features] +default = [] erased-json = ["serde_json"] +typed-routing = ["axum-macros"] [dependencies] axum = { path = "../axum", version = "0.4" } @@ -26,10 +28,10 @@ tower-layer = "0.3" tower-service = "0.3" # optional dependencies +axum-macros = { path = "../axum-macros", version = "0.1", optional = true } serde_json = { version = "1.0.71", optional = true } [dev-dependencies] -axum-macros = { path = "../axum-macros", version = "0.1" } hyper = "0.14" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.14", features = ["full"] } diff --git a/axum-extra/src/routing/mod.rs b/axum-extra/src/routing/mod.rs index 60a21882..4c573a5f 100644 --- a/axum-extra/src/routing/mod.rs +++ b/axum-extra/src/routing/mod.rs @@ -3,8 +3,15 @@ use axum::{body::Body, handler::Handler, Router}; mod resource; + +#[cfg(feature = "typed-routing")] mod typed; +/// TODO(david): docs +#[cfg(feature = "typed-routing")] +#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] +pub use axum_macros::TypedPath; + pub use self::{ resource::Resource, typed::{FirstElementIs, TypedPath}, @@ -38,6 +45,8 @@ pub trait RouterExt: sealed::Sealed { T: HasRoutes; /// TODO(david): docs + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_get(self, handler: H) -> Self where H: Handler, @@ -45,6 +54,8 @@ pub trait RouterExt: sealed::Sealed { P: TypedPath; /// TODO(david): docs + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_delete(self, handler: H) -> Self where H: Handler, @@ -52,6 +63,8 @@ pub trait RouterExt: sealed::Sealed { P: TypedPath; /// TODO(david): docs + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_head(self, handler: H) -> Self where H: Handler, @@ -59,6 +72,8 @@ pub trait RouterExt: sealed::Sealed { P: TypedPath; /// TODO(david): docs + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_options(self, handler: H) -> Self where H: Handler, @@ -66,6 +81,8 @@ pub trait RouterExt: sealed::Sealed { P: TypedPath; /// TODO(david): docs + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_patch(self, handler: H) -> Self where H: Handler, @@ -73,6 +90,8 @@ pub trait RouterExt: sealed::Sealed { P: TypedPath; /// TODO(david): docs + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_post(self, handler: H) -> Self where H: Handler, @@ -80,6 +99,8 @@ pub trait RouterExt: sealed::Sealed { P: TypedPath; /// TODO(david): docs + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_put(self, handler: H) -> Self where H: Handler, @@ -87,6 +108,8 @@ pub trait RouterExt: sealed::Sealed { P: TypedPath; /// TODO(david): docs + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_trace(self, handler: H) -> Self where H: Handler, @@ -105,6 +128,8 @@ where self.merge(routes.routes()) } + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_get(self, handler: H) -> Self where H: Handler, @@ -114,6 +139,8 @@ where self.route(P::PATH, axum::routing::get(handler)) } + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_delete(self, handler: H) -> Self where H: Handler, @@ -123,6 +150,8 @@ where self.route(P::PATH, axum::routing::delete(handler)) } + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_head(self, handler: H) -> Self where H: Handler, @@ -132,6 +161,8 @@ where self.route(P::PATH, axum::routing::head(handler)) } + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_options(self, handler: H) -> Self where H: Handler, @@ -141,6 +172,8 @@ where self.route(P::PATH, axum::routing::options(handler)) } + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_patch(self, handler: H) -> Self where H: Handler, @@ -150,6 +183,8 @@ where self.route(P::PATH, axum::routing::patch(handler)) } + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_post(self, handler: H) -> Self where H: Handler, @@ -159,6 +194,8 @@ where self.route(P::PATH, axum::routing::post(handler)) } + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_put(self, handler: H) -> Self where H: Handler, @@ -168,6 +205,8 @@ where self.route(P::PATH, axum::routing::put(handler)) } + #[cfg(feature = "typed-routing")] + #[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))] fn typed_trace(self, handler: H) -> Self where H: Handler, diff --git a/axum-extra/src/routing/typed.rs b/axum-extra/src/routing/typed.rs index 9aa953de..c6f07575 100644 --- a/axum-extra/src/routing/typed.rs +++ b/axum-extra/src/routing/typed.rs @@ -1,4 +1,10 @@ -/// Type safe routing. +//! Type safe routing. +//! +//! See [`TypedPath`] for more details. + +use super::sealed::Sealed; + +/// TODO(david): more docs /// /// # Example /// @@ -8,7 +14,7 @@ /// use axum::{Router, extract::Json}; /// use axum_extra::routing::{ /// typed, -/// RouterExt, // for `Router::with` +/// RouterExt, // for `Router::typed_*` /// }; /// /// // A type safe route with `/users/:id` as its associated path. @@ -33,11 +39,9 @@ /// // /// // The path will be inferred to `/users/:id` since `users_show`'s /// // first argument is `UsersMember` which implements `TypedPath` -/// .with(typed::get(users_show)) -/// // Add multiple handlers for `/users` depending on the HTTP method. -/// .with(typed::post(users_create).delete(users_destroy)) -/// // We can still add regular routes. -/// .route("/foo", get(|| async { /* ... */ })); +/// .typed_get(users_show) +/// .typed_post(users_create) +/// .typed_delete(users_destroy); /// /// #[derive(TypedPath)] /// #[typed_path("/users")] @@ -59,8 +63,6 @@ /// # /// # let app: Router = app; /// ``` -use super::sealed::Sealed; - pub trait TypedPath: std::fmt::Display { const PATH: &'static str; } diff --git a/axum-macros/src/typed_path.rs b/axum-macros/src/typed_path.rs index 9d526370..ed2264f4 100644 --- a/axum-macros/src/typed_path.rs +++ b/axum-macros/src/typed_path.rs @@ -29,11 +29,10 @@ pub(crate) fn expand(item_struct: ItemStruct) -> syn::Result { let segments = parse_path(&path); expand_unnamed_fields(fields, ident, path, &segments) } - syn::Fields::Unit => Ok(expand_unit_fields(ident, path)), + syn::Fields::Unit => Ok(expand_unit_fields(ident, path)?), } } -#[derive(Debug)] struct Attrs { path: LitStr, } @@ -180,8 +179,20 @@ fn simple_pluralize(count: usize, word: &str) -> String { } } -fn expand_unit_fields(ident: &syn::Ident, path: LitStr) -> TokenStream { - quote_spanned! {path.span()=> +fn expand_unit_fields(ident: &syn::Ident, path: LitStr) -> syn::Result { + for segment in parse_path(&path) { + match segment { + Segment::Capture(_, span) => { + return Err(syn::Error::new( + span, + "Typed paths for unit structs cannot contain captures", + )); + } + Segment::Static(_) => {} + } + } + + Ok(quote_spanned! {path.span()=> #[automatically_derived] impl ::axum_extra::routing::TypedPath for #ident { const PATH: &'static str = #path; @@ -210,7 +221,7 @@ fn expand_unit_fields(ident: &syn::Ident, path: LitStr) -> TokenStream { } } } - } + }) } fn format_str_from_path(segments: &[Segment]) -> String { diff --git a/examples/hello-world/Cargo.toml b/examples/hello-world/Cargo.toml index 10c814c5..54a2fd98 100644 --- a/examples/hello-world/Cargo.toml +++ b/examples/hello-world/Cargo.toml @@ -8,6 +8,5 @@ publish = false axum = { path = "../../axum" } tokio = { version = "1.0", features = ["full"] } -axum-macros = { path = "../../axum-macros" } -axum-extra = { path = "../../axum-extra" } +axum-extra = { path = "../../axum-extra", features = ["typed-routing"] } serde = { version = "1.0", features = ["derive"] } diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index cdb93b84..07105679 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -7,8 +7,7 @@ // Just using this file for manual testing. Will be cleaned up before an eventual merge use axum::{response::IntoResponse, Router}; -use axum_extra::routing::RouterExt; -use axum_macros::TypedPath; +use axum_extra::routing::{RouterExt, TypedPath}; use serde::Deserialize; #[tokio::main]