mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-18 00:00:15 +02:00
re-export axum_macros::TypedPath from axum_extra
This commit is contained in:
@@ -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"] }
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user