mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-21 00:00:14 +02:00
docs
This commit is contained in:
@@ -7,7 +7,6 @@ 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;
|
||||
@@ -44,7 +43,12 @@ pub trait RouterExt<B>: sealed::Sealed {
|
||||
where
|
||||
T: HasRoutes<B>;
|
||||
|
||||
/// TODO(david): docs
|
||||
/// Add a typed `GET` route to the router.
|
||||
///
|
||||
/// The path will be inferred from the first argument to the handler function which must
|
||||
/// implement [`TypedPath`].
|
||||
///
|
||||
/// See [`TypedPath`] for more details and examples.
|
||||
#[cfg(feature = "typed-routing")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
|
||||
fn typed_get<H, T, P>(self, handler: H) -> Self
|
||||
@@ -53,7 +57,12 @@ pub trait RouterExt<B>: sealed::Sealed {
|
||||
T: FirstElementIs<P> + 'static,
|
||||
P: TypedPath;
|
||||
|
||||
/// TODO(david): docs
|
||||
/// Add a typed `DELETE` route to the router.
|
||||
///
|
||||
/// The path will be inferred from the first argument to the handler function which must
|
||||
/// implement [`TypedPath`].
|
||||
///
|
||||
/// See [`TypedPath`] for more details and examples.
|
||||
#[cfg(feature = "typed-routing")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
|
||||
fn typed_delete<H, T, P>(self, handler: H) -> Self
|
||||
@@ -62,7 +71,12 @@ pub trait RouterExt<B>: sealed::Sealed {
|
||||
T: FirstElementIs<P> + 'static,
|
||||
P: TypedPath;
|
||||
|
||||
/// TODO(david): docs
|
||||
/// Add a typed `HEAD` route to the router.
|
||||
///
|
||||
/// The path will be inferred from the first argument to the handler function which must
|
||||
/// implement [`TypedPath`].
|
||||
///
|
||||
/// See [`TypedPath`] for more details and examples.
|
||||
#[cfg(feature = "typed-routing")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
|
||||
fn typed_head<H, T, P>(self, handler: H) -> Self
|
||||
@@ -71,7 +85,12 @@ pub trait RouterExt<B>: sealed::Sealed {
|
||||
T: FirstElementIs<P> + 'static,
|
||||
P: TypedPath;
|
||||
|
||||
/// TODO(david): docs
|
||||
/// Add a typed `OPTIONS` route to the router.
|
||||
///
|
||||
/// The path will be inferred from the first argument to the handler function which must
|
||||
/// implement [`TypedPath`].
|
||||
///
|
||||
/// See [`TypedPath`] for more details and examples.
|
||||
#[cfg(feature = "typed-routing")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
|
||||
fn typed_options<H, T, P>(self, handler: H) -> Self
|
||||
@@ -80,7 +99,12 @@ pub trait RouterExt<B>: sealed::Sealed {
|
||||
T: FirstElementIs<P> + 'static,
|
||||
P: TypedPath;
|
||||
|
||||
/// TODO(david): docs
|
||||
/// Add a typed `PATCH` route to the router.
|
||||
///
|
||||
/// The path will be inferred from the first argument to the handler function which must
|
||||
/// implement [`TypedPath`].
|
||||
///
|
||||
/// See [`TypedPath`] for more details and examples.
|
||||
#[cfg(feature = "typed-routing")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
|
||||
fn typed_patch<H, T, P>(self, handler: H) -> Self
|
||||
@@ -89,7 +113,12 @@ pub trait RouterExt<B>: sealed::Sealed {
|
||||
T: FirstElementIs<P> + 'static,
|
||||
P: TypedPath;
|
||||
|
||||
/// TODO(david): docs
|
||||
/// Add a typed `POST` route to the router.
|
||||
///
|
||||
/// The path will be inferred from the first argument to the handler function which must
|
||||
/// implement [`TypedPath`].
|
||||
///
|
||||
/// See [`TypedPath`] for more details and examples.
|
||||
#[cfg(feature = "typed-routing")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
|
||||
fn typed_post<H, T, P>(self, handler: H) -> Self
|
||||
@@ -98,7 +127,12 @@ pub trait RouterExt<B>: sealed::Sealed {
|
||||
T: FirstElementIs<P> + 'static,
|
||||
P: TypedPath;
|
||||
|
||||
/// TODO(david): docs
|
||||
/// Add a typed `PUT` route to the router.
|
||||
///
|
||||
/// The path will be inferred from the first argument to the handler function which must
|
||||
/// implement [`TypedPath`].
|
||||
///
|
||||
/// See [`TypedPath`] for more details and examples.
|
||||
#[cfg(feature = "typed-routing")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
|
||||
fn typed_put<H, T, P>(self, handler: H) -> Self
|
||||
@@ -107,7 +141,12 @@ pub trait RouterExt<B>: sealed::Sealed {
|
||||
T: FirstElementIs<P> + 'static,
|
||||
P: TypedPath;
|
||||
|
||||
/// TODO(david): docs
|
||||
/// Add a typed `TRACE` route to the router.
|
||||
///
|
||||
/// The path will be inferred from the first argument to the handler function which must
|
||||
/// implement [`TypedPath`].
|
||||
///
|
||||
/// See [`TypedPath`] for more details and examples.
|
||||
#[cfg(feature = "typed-routing")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typed-routing")))]
|
||||
fn typed_trace<H, T, P>(self, handler: H) -> Self
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
//! Type safe routing.
|
||||
//!
|
||||
//! See [`TypedPath`] for more details.
|
||||
|
||||
use super::sealed::Sealed;
|
||||
|
||||
/// TODO(david): more docs
|
||||
/// A type safe path.
|
||||
///
|
||||
/// This is used to statically connect a path to its corresponding handler using
|
||||
/// [`RouterExt::typed_get`], [`RouterExt::typed_post`], etc.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -18,14 +17,14 @@ use super::sealed::Sealed;
|
||||
/// };
|
||||
///
|
||||
/// // A type safe route with `/users/:id` as its associated path.
|
||||
/// #[derive(Deserialize, TypedPath)]
|
||||
/// #[derive(TypedPath, Deserialize)]
|
||||
/// #[typed_path("/users/:id")]
|
||||
/// struct UsersMember {
|
||||
/// id: u32,
|
||||
/// }
|
||||
///
|
||||
/// // A regular handler function that takes `UsersMember` as the first argument
|
||||
/// // and thus creates a typed connection between this handler and the `/users/:id` route.
|
||||
/// // and thus creates a typed connection between this handler and the `/users/:id` path.
|
||||
/// //
|
||||
/// // The `TypedPath` must be the first argument to the function.
|
||||
/// async fn users_show(
|
||||
@@ -63,11 +62,68 @@ use super::sealed::Sealed;
|
||||
/// #
|
||||
/// # let app: Router<axum::body::Body> = app;
|
||||
/// ```
|
||||
///
|
||||
/// # Using `#[derive(TypedPath)]`
|
||||
///
|
||||
/// While `TypedPath` can be implemented manually its _highly_ recommended to derive it:
|
||||
///
|
||||
/// ```
|
||||
/// # use serde::Deserialize;
|
||||
/// #
|
||||
/// #[derive(TypedPath, Deserialize)]
|
||||
/// #[typed_path("/users/:id")]
|
||||
/// struct UsersMember {
|
||||
/// id: u32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The macro expands to:
|
||||
///
|
||||
/// - A `TypedPath` implementation.
|
||||
/// - A [`Display`] implementation that interpolates the captures. This can be used to, among other
|
||||
/// things, create links to known paths and have them verified statically.
|
||||
/// - A [`FromRequest`] implementation compatible with [`RouterExt::typed_get`],
|
||||
/// [`RouterExt::typed_post`], etc. This implementation uses [`Path`] and thus your struct must
|
||||
/// also implement [`serde::Deserialize`], unless its a unit struct.
|
||||
///
|
||||
/// Additionally the macro will verify the captures in the path matches the fields of the struct.
|
||||
/// For example this fails to compile since the struct doesn't have a `team_id` field:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use serde::Deserialize;
|
||||
/// #
|
||||
/// #[derive(TypedPath, Deserialize)]
|
||||
/// #[typed_path("/users/:id/teams/:team_id")]
|
||||
/// struct UsersMember {
|
||||
/// id: u32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Unit and tuple structs are also supported:
|
||||
///
|
||||
/// ```
|
||||
/// # use serde::Deserialize;
|
||||
/// #
|
||||
/// #[derive(TypedPath)]
|
||||
/// #[typed_path("/users")]
|
||||
/// struct UsersCollection;
|
||||
///
|
||||
/// #[derive(TypedPath, Deserialize)]
|
||||
/// #[typed_path("/users/:id")]
|
||||
/// struct UsersMember(u32);
|
||||
/// ```
|
||||
///
|
||||
/// [`FromRequest`]: axum::extract::FromRequest
|
||||
/// [`RouterExt::typed_get`]: super::RouterExt::typed_get
|
||||
/// [`RouterExt::typed_post`]: super::RouterExt::typed_post
|
||||
/// [`Path`]: axum::extract::Path
|
||||
/// [`Display`]: std::fmt::Display
|
||||
pub trait TypedPath: std::fmt::Display {
|
||||
/// The path with optional captures such as `/users/:id`.
|
||||
const PATH: &'static str;
|
||||
}
|
||||
|
||||
/// Utility trait used with [`TypedRouter`] to ensure the first element of a tuple type is a
|
||||
/// Utility trait used with [`RouterExt`] to ensure the first element of a tuple type is a
|
||||
/// given type.
|
||||
///
|
||||
/// If you see it in type errors its most likely because the first argument to your handler doesn't
|
||||
@@ -76,13 +132,21 @@ pub trait TypedPath: std::fmt::Display {
|
||||
/// You normally shouldn't have to use this trait directly.
|
||||
///
|
||||
/// It is sealed such that it cannot be implemented outside this crate.
|
||||
///
|
||||
/// [`RouterExt`]: super::RouterExt
|
||||
pub trait FirstElementIs<P>: Sealed {}
|
||||
|
||||
macro_rules! impl_first_element_is {
|
||||
( $($ty:ident),* $(,)? ) => {
|
||||
impl<P, $($ty,)*> FirstElementIs<P> for (P, $($ty,)*) {}
|
||||
impl<P, $($ty,)*> FirstElementIs<P> for (P, $($ty,)*)
|
||||
where
|
||||
P: TypedPath
|
||||
{}
|
||||
|
||||
impl<P, $($ty,)*> Sealed for (P, $($ty,)*) {}
|
||||
impl<P, $($ty,)*> Sealed for (P, $($ty,)*)
|
||||
where
|
||||
P: TypedPath
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ syn = { version = "1.0", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
axum = { path = "../axum", version = "0.4", features = ["headers"] }
|
||||
axum-extra = { path = "../axum-extra", version = "0.1" }
|
||||
rustversion = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
@@ -386,7 +386,9 @@ pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
return expand_attr_with(_attr, input, debug_handler::expand);
|
||||
}
|
||||
|
||||
/// TODO
|
||||
/// Derive an implementation of [`axum_extra::routing::TypedPath`].
|
||||
///
|
||||
/// See that trait for more details.
|
||||
#[proc_macro_derive(TypedPath, attributes(typed_path))]
|
||||
pub fn derive_typed_path(input: TokenStream) -> TokenStream {
|
||||
expand_with(input, typed_path::expand)
|
||||
|
||||
Reference in New Issue
Block a user