re-export axum_macros::TypedPath from axum_extra

This commit is contained in:
David Pedersen
2022-02-13 21:31:56 +01:00
parent 9f5dbfd83a
commit 1b7326e9de
6 changed files with 71 additions and 19 deletions
+3 -1
View File
@@ -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"] }
+39
View File
@@ -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<B>: sealed::Sealed {
T: HasRoutes<B>;
/// TODO(david): docs
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_get<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -45,6 +54,8 @@ pub trait RouterExt<B>: sealed::Sealed {
P: TypedPath;
/// TODO(david): docs
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_delete<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -52,6 +63,8 @@ pub trait RouterExt<B>: sealed::Sealed {
P: TypedPath;
/// TODO(david): docs
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_head<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -59,6 +72,8 @@ pub trait RouterExt<B>: sealed::Sealed {
P: TypedPath;
/// TODO(david): docs
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_options<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -66,6 +81,8 @@ pub trait RouterExt<B>: sealed::Sealed {
P: TypedPath;
/// TODO(david): docs
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_patch<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -73,6 +90,8 @@ pub trait RouterExt<B>: sealed::Sealed {
P: TypedPath;
/// TODO(david): docs
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_post<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -80,6 +99,8 @@ pub trait RouterExt<B>: sealed::Sealed {
P: TypedPath;
/// TODO(david): docs
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_put<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -87,6 +108,8 @@ pub trait RouterExt<B>: sealed::Sealed {
P: TypedPath;
/// TODO(david): docs
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_trace<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -105,6 +128,8 @@ where
self.merge(routes.routes())
}
#[cfg(feature = "typed-routing")]
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
fn typed_get<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -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<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -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<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -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<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -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<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -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<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -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<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
@@ -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<H, T, P>(self, handler: H) -> Self
where
H: Handler<T, B>,
+11 -9
View File
@@ -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<axum::body::Body> = app;
/// ```
use super::sealed::Sealed;
pub trait TypedPath: std::fmt::Display {
const PATH: &'static str;
}
+16 -5
View File
@@ -29,11 +29,10 @@ pub(crate) fn expand(item_struct: ItemStruct) -> syn::Result<TokenStream> {
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<TokenStream> {
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 {
+1 -2
View File
@@ -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"] }
+1 -2
View File
@@ -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]