fix some todos

This commit is contained in:
David Pedersen
2022-07-03 20:19:21 +02:00
parent e03cea82d2
commit 7e30205205
10 changed files with 76 additions and 88 deletions
-2
View File
@@ -22,8 +22,6 @@ use tower_service::Service;
/// that handles errors by converting them into responses.
///
/// See [module docs](self) for more details on axum's error handling model.
// TODO(david): cannot access state, is that bad? It leads to inference issues and one has to
// specify the type manually and risk getting it wrong. So its basically an Extension at that point
pub struct HandleErrorLayer<F, T> {
f: F,
_extractor: PhantomData<fn() -> T>,
+1 -2
View File
@@ -2,8 +2,7 @@ use super::{FromRequest, RequestParts};
use async_trait::async_trait;
use std::convert::Infallible;
/// TODO(david): docs
// TODO(david): document how to extract this from middleware
// document how to extract this from middleware
#[derive(Clone, Copy, Debug, Default)]
pub struct State<S>(pub S);
+2 -13
View File
@@ -1,5 +1,5 @@
use super::Handler;
use crate::{extract::State, response::Response};
use crate::{response::Response, util::extract_state_assume_present};
use http::Request;
use std::{
convert::Infallible,
@@ -59,18 +59,7 @@ where
let handler = self.handler.clone();
// TODO(david): this is duplicated in `axum/src/routing/mod.rs`
// extract into helper function
let State(state) = req
.extensions()
.get::<State<S>>()
.unwrap_or_else(|| {
panic!(
"no state of type `{}` was found. Please file an issue",
std::any::type_name::<State<S>>()
)
})
.clone();
let state = extract_state_assume_present::<S, _>(&req);
let future = Handler::call(handler, state, req);
let future = future.map(Ok as _);
+8 -8
View File
@@ -64,8 +64,7 @@ pub mod future;
/// See the [module docs](crate::handler) for more details.
///
#[doc = include_str!("../docs/debugging_handler_type_errors.md")]
// TODO(david): Add back `B = Body` default
pub trait Handler<S, T, B>: Clone + Send + Sized + 'static {
pub trait Handler<S, T, B = Body>: Clone + Send + Sized + 'static {
/// The type of future calling this handler returns.
type Future: Future<Output = Response> + Send + 'static;
@@ -151,7 +150,6 @@ pub trait Handler<S, T, B>: Clone + Send + Sized + 'static {
/// ```
///
/// [`Router::fallback`]: crate::routing::Router::fallback
// TODO(david): remove this
fn into_service(self, state: S) -> IntoService<Self, S, T, B> {
IntoService::new(self, state)
}
@@ -179,7 +177,6 @@ pub trait Handler<S, T, B>: Clone + Send + Sized + 'static {
/// ```
///
/// [`MakeService`]: tower::make::MakeService
// TODO(david): remove this
fn into_make_service(self, state: S) -> IntoMakeService<IntoService<Self, S, T, B>> {
IntoMakeService::new(self.into_service(state))
}
@@ -212,7 +209,6 @@ pub trait Handler<S, T, B>: Clone + Send + Sized + 'static {
///
/// [`MakeService`]: tower::make::MakeService
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
// TODO(david): remove this
fn into_make_service_with_connect_info<C>(
self,
state: S,
@@ -350,16 +346,20 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::*;
use crate::{extract::State, test_helpers::*};
use http::StatusCode;
#[tokio::test]
async fn handler_into_service() {
async fn handle(body: String) -> impl IntoResponse {
async fn handle(State(state): State<AppState>, body: String) -> impl IntoResponse {
assert_eq!(state.0, 1337);
format!("you said: {}", body)
}
let client = TestClient::new(handle.into_service(()));
#[derive(Clone)]
struct AppState(i32);
let client = TestClient::new(handle.into_service(AppState(1337)));
let res = client.post("/").body("hi there!").send().await;
assert_eq!(res.status(), StatusCode::OK);
+2 -2
View File
@@ -382,8 +382,8 @@
rust_2018_idioms,
future_incompatible,
nonstandard_style,
// missing_debug_implementations,
// missing_docs
missing_debug_implementations,
missing_docs
)]
#![deny(unreachable_pub, private_in_public)]
#![allow(elided_lifetimes_in_paths, clippy::type_complexity)]
+1 -1
View File
@@ -400,7 +400,7 @@ mod tests {
#[tokio::test]
async fn extracting_state() {
async fn access_state<B>(req: Request<B>, next: Next<B>) -> impl IntoResponse {
async fn access_state<B>(req: Request<B>, _next: Next<B>) -> impl IntoResponse {
let State(state) = req.extensions().get::<State<AppState>>().unwrap().clone();
state.value
}
+1 -1
View File
@@ -61,7 +61,7 @@ impl<T> From<T> for Html<T> {
#[cfg(test)]
mod tests {
use crate::extract::Extension;
use crate::{body::Body, routing::get, Router};
use crate::{routing::get, Router};
use axum_core::response::IntoResponse;
use http::HeaderMap;
use http::{StatusCode, Uri};
+31 -36
View File
@@ -76,7 +76,7 @@ macro_rules! top_level_service_fn {
$name:ident, $method:ident
) => {
$(#[$m])+
pub fn $name<T, ReqBody, ResBody, S>(svc: T) -> MethodRouter<S, ReqBody, T::Error, MissingState>
pub fn $name<T, ReqBody, ResBody, S>(svc: T) -> MethodRouter<S, MissingState, ReqBody, T::Error>
where
T: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
T::Future: Send + 'static,
@@ -137,7 +137,7 @@ macro_rules! top_level_handler_fn {
$name:ident, $method:ident
) => {
$(#[$m])+
pub fn $name<H, S, T, B>(handler: H) -> MethodRouter<S, B, Infallible, MissingState>
pub fn $name<H, S, T, B>(handler: H) -> MethodRouter<S, MissingState, B, Infallible>
where
H: Handler<S, T, B>,
B: Send + 'static,
@@ -321,7 +321,7 @@ top_level_service_fn!(trace_service, TRACE);
pub fn on_service<T, ReqBody, ResBody, S>(
filter: MethodFilter,
svc: T,
) -> MethodRouter<S, ReqBody, T::Error, MissingState>
) -> MethodRouter<S, MissingState, ReqBody, T::Error>
where
T: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
T::Future: Send + 'static,
@@ -386,7 +386,7 @@ where
/// ```
pub fn any_service<T, ReqBody, ResBody, S>(
svc: T,
) -> MethodRouter<S, ReqBody, T::Error, MissingState>
) -> MethodRouter<S, MissingState, ReqBody, T::Error>
where
T: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
T::Future: Send + 'static,
@@ -429,7 +429,7 @@ top_level_handler_fn!(trace, TRACE);
pub fn on<H, S, T, B>(
filter: MethodFilter,
handler: H,
) -> MethodRouter<S, B, Infallible, MissingState>
) -> MethodRouter<S, MissingState, B, Infallible>
where
H: Handler<S, T, B>,
B: Send + 'static,
@@ -476,7 +476,7 @@ where
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn any<H, S, T, B>(handler: H) -> MethodRouter<S, B, Infallible, MissingState>
pub fn any<H, S, T, B>(handler: H) -> MethodRouter<S, MissingState, B, Infallible>
where
H: Handler<S, T, B>,
B: Send + 'static,
@@ -490,9 +490,7 @@ where
/// A [`Service`] that accepts requests based on a [`MethodFilter`] and
/// allows chaining additional handlers and services.
// TODO(david): Bring back `B = Body, E = Infallible` defaults
// TODO(david): think about ordering of type params here
pub struct MethodRouter<S, B, E, R> {
pub struct MethodRouter<S, R = MissingState, B = Body, E = Infallible> {
// Invariant: If `R == MissingState` then `state` is `None`
// If `R == WithState` then state is `Some`
// `R` cannot have other values
@@ -520,7 +518,7 @@ enum AllowHeader {
Bytes(BytesMut),
}
impl<S, B, E, R> fmt::Debug for MethodRouter<S, B, E, R>
impl<S, B, E, R> fmt::Debug for MethodRouter<S, R, B, E>
where
S: fmt::Debug,
{
@@ -540,7 +538,7 @@ where
}
}
impl<S, B, E> MethodRouter<S, B, E, MissingState> {
impl<S, B, E> MethodRouter<S, MissingState, B, E> {
/// Create a default `MethodRouter` that will respond with `405 Method Not Allowed` to all
/// requests.
pub fn new() -> Self {
@@ -566,8 +564,7 @@ impl<S, B, E> MethodRouter<S, B, E, MissingState> {
}
}
/// TODO(david): docs
pub fn state(self, state: S) -> MethodRouter<S, B, E, WithState> {
pub fn state(self, state: S) -> MethodRouter<S, WithState, B, E> {
MethodRouter {
state: Some(state),
get: self.get,
@@ -585,21 +582,19 @@ impl<S, B, E> MethodRouter<S, B, E, MissingState> {
}
}
impl<S, B, E> MethodRouter<S, B, E, WithState> {
/// TODO(david): docs
impl<S, B, E> MethodRouter<S, WithState, B, E> {
pub fn with_state(state: S) -> Self {
MethodRouter::new().state(state)
}
}
impl<B, E> MethodRouter<(), B, E, WithState> {
/// TODO(david): docs
impl<B, E> MethodRouter<(), WithState, B, E> {
pub fn without_state() -> Self {
MethodRouter::with_state(())
}
}
impl<S, B, R> MethodRouter<S, B, Infallible, R>
impl<S, B, R> MethodRouter<S, R, B, Infallible>
where
B: Send + 'static,
S: Clone + Send + Sync + 'static,
@@ -645,7 +640,7 @@ where
chained_handler_fn!(trace, TRACE);
#[doc = include_str!("../docs/routing/fallback.md")]
pub fn fallback<H, T>(mut self, handler: H) -> Self
pub fn fallback<H, T>(self, handler: H) -> Self
where
H: Handler<S, T, B>,
T: 'static,
@@ -654,8 +649,8 @@ where
}
}
impl<S, B, R> MethodRouter<S, B, Infallible, R> {
pub(crate) fn change_state_marker<R2>(self) -> MethodRouter<S, B, Infallible, R2> {
impl<S, B, R> MethodRouter<S, R, B, Infallible> {
pub(crate) fn change_state_marker<R2>(self) -> MethodRouter<S, R2, B, Infallible> {
MethodRouter {
state: self.state,
get: self.get,
@@ -673,8 +668,8 @@ impl<S, B, R> MethodRouter<S, B, Infallible, R> {
}
}
impl<S, B> MethodRouter<S, B, Infallible, MissingState> {
pub(crate) fn change_state<S2>(self) -> MethodRouter<S2, B, Infallible, MissingState> {
impl<S, B> MethodRouter<S, MissingState, B, Infallible> {
pub(crate) fn change_state<S2>(self) -> MethodRouter<S2, MissingState, B, Infallible> {
debug_assert!(self.state.is_none());
MethodRouter {
state: None,
@@ -693,7 +688,7 @@ impl<S, B> MethodRouter<S, B, Infallible, MissingState> {
}
}
impl<S, B> MethodRouter<S, B, Infallible, WithState>
impl<S, B> MethodRouter<S, WithState, B, Infallible>
where
B: Send + 'static,
S: Clone + Send + Sync + 'static,
@@ -767,7 +762,7 @@ where
}
}
impl<S, B, E, R> MethodRouter<S, B, E, R> {
impl<S, B, E, R> MethodRouter<S, R, B, E> {
/// Chain an additional service that will accept requests matching the given
/// `MethodFilter`.
///
@@ -837,7 +832,7 @@ impl<S, B, E, R> MethodRouter<S, B, E, R> {
pub fn layer<L, NewReqBody, NewResBody, NewError>(
self,
layer: L,
) -> MethodRouter<S, NewReqBody, NewError, R>
) -> MethodRouter<S, R, NewReqBody, NewError>
where
L: Layer<Route<B, E>>,
L::Service: Service<Request<NewReqBody>, Response = Response<NewResBody>, Error = NewError>
@@ -872,7 +867,7 @@ impl<S, B, E, R> MethodRouter<S, B, E, R> {
}
#[doc = include_str!("../docs/method_routing/route_layer.md")]
pub fn route_layer<L, NewResBody>(self, layer: L) -> MethodRouter<S, B, E, R>
pub fn route_layer<L, NewResBody>(self, layer: L) -> MethodRouter<S, R, B, E>
where
L: Layer<Route<B, E>>,
L::Service: Service<Request<B>, Response = Response<NewResBody>, Error = E>
@@ -907,7 +902,7 @@ impl<S, B, E, R> MethodRouter<S, B, E, R> {
}
#[doc = include_str!("../docs/method_routing/merge.md")]
pub fn merge(self, other: MethodRouter<S, B, E, MissingState>) -> Self {
pub fn merge(self, other: MethodRouter<S, MissingState, B, E>) -> Self {
macro_rules! merge {
( $first:ident, $second:ident ) => {
match ($first, $second) {
@@ -1003,7 +998,7 @@ impl<S, B, E, R> MethodRouter<S, B, E, R> {
/// Apply a [`HandleErrorLayer`].
///
/// This is a convenience method for doing `self.layer(HandleErrorLayer::new(f))`.
pub fn handle_error<F, T>(self, f: F) -> MethodRouter<S, B, Infallible, R>
pub fn handle_error<F, T>(self, f: F) -> MethodRouter<S, R, B, Infallible>
where
F: Clone + Send + 'static,
HandleError<Route<B, E>, F, T>:
@@ -1120,7 +1115,7 @@ fn append_allow_header(allow_header: &mut AllowHeader, method: &'static str) {
}
}
impl<S, B, E, R> Clone for MethodRouter<S, B, E, R>
impl<S, B, E, R> Clone for MethodRouter<S, R, B, E>
where
S: Clone,
{
@@ -1142,7 +1137,7 @@ where
}
}
impl<S, B, E> Default for MethodRouter<S, B, E, MissingState>
impl<S, B, E> Default for MethodRouter<S, MissingState, B, E>
where
B: Send + 'static,
{
@@ -1151,7 +1146,7 @@ where
}
}
impl<S, B, E> Service<Request<B>> for MethodRouter<S, B, E, WithState>
impl<S, B, E> Service<Request<B>> for MethodRouter<S, WithState, B, E>
where
B: HttpBody,
S: Clone + Send + Sync + 'static,
@@ -1430,7 +1425,7 @@ mod tests {
expected = "Overlapping method route. Cannot add two method routes that both handle `GET`"
)]
async fn handler_overlaps() {
let _: MethodRouter<(), Body, Infallible, _> = get(ok).get(ok);
let _: MethodRouter<(), _, Body, Infallible> = get(ok).get(ok);
}
#[tokio::test]
@@ -1438,18 +1433,18 @@ mod tests {
expected = "Overlapping method route. Cannot add two method routes that both handle `POST`"
)]
async fn service_overlaps() {
let _: MethodRouter<(), Body, Infallible, _> =
let _: MethodRouter<(), _, Body, Infallible> =
post_service(ok.into_service(())).post_service(ok.into_service(()));
}
#[tokio::test]
async fn get_head_does_not_overlap() {
let _: MethodRouter<(), Body, Infallible, _> = get(ok).head(ok);
let _: MethodRouter<(), _, Body, Infallible> = get(ok).head(ok);
}
#[tokio::test]
async fn head_get_does_not_overlap() {
let _: MethodRouter<(), Body, Infallible, _> = head(ok).get(ok);
let _: MethodRouter<(), _, Body, Infallible> = head(ok).get(ok);
}
async fn call<S>(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String)
+6 -23
View File
@@ -7,7 +7,7 @@ use crate::{
handler::{Handler, IntoExtensionService},
response::Response,
routing::strip_prefix::StripPrefix,
util::try_downcast,
util::{extract_state_assume_present, try_downcast},
BoxError,
};
use http::Request;
@@ -207,23 +207,9 @@ where
_marker: PhantomData,
}
.layer(MapRequestLayer::new(move |mut req: Request<_>| {
// TODO(david): this is duplicated in `axum/src/handler/into_extension_service.rs`
// extract into helper function
let State(outer_state) = req
.extensions()
.get::<State<OuterState>>()
.unwrap_or_else(|| {
panic!(
"no state of type `{}` was found. Please file an issue",
std::any::type_name::<State<OuterState>>()
)
})
.clone();
let outer_state = extract_state_assume_present::<OuterState, _>(&req);
let inner_state = f(outer_state);
req.extensions_mut().insert(State(inner_state));
req
}))
}
@@ -234,7 +220,6 @@ where
B: HttpBody + Send + 'static,
S: Clone,
{
/// TODO(david): docs
pub fn with_state(state: S) -> Self {
Router::new().state(state)
}
@@ -244,7 +229,6 @@ impl<B> Router<(), WithState, B>
where
B: HttpBody + Send + 'static,
{
/// TODO(david): docs
pub fn without_state() -> Self {
Router::with_state(())
}
@@ -262,7 +246,7 @@ where
path: &str,
// TODO(david): constrain this so it only accepts methods
// routers containing handlers
method_router: MethodRouter<S, B, Infallible, MissingState>,
method_router: MethodRouter<S, MissingState, B, Infallible>,
) -> Self {
validate_path_for_route(path);
@@ -301,7 +285,6 @@ where
}
}
/// TODO(david): docs
pub fn route_service<T>(mut self, path: &str, service: T) -> Self
where
T: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static,
@@ -530,8 +513,9 @@ where
}
}
// TODO(david): update docs
#[doc = include_str!("../docs/routing/fallback.md")]
pub fn fallback<H, T>(mut self, handler: H) -> Self
pub fn fallback<H, T>(self, handler: H) -> Self
where
H: Handler<S, T, B>,
T: 'static,
@@ -540,7 +524,6 @@ where
self.fallback_service(IntoExtensionService::new(handler))
}
/// TODO(david): docs
pub fn fallback_service<T>(mut self, svc: T) -> Self
where
T: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static,
@@ -786,7 +769,7 @@ impl<B, E> Fallback<B, E> {
}
enum Endpoint<S, R, B> {
MethodRouter(MethodRouter<S, B, Infallible, R>),
MethodRouter(MethodRouter<S, R, B, Infallible>),
Route(Route<B>),
}
+24
View File
@@ -1,6 +1,9 @@
use http::Request;
use pin_project_lite::pin_project;
use std::{ops::Deref, sync::Arc};
use crate::extract::State;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct PercentDecodedStr(Arc<str>);
@@ -54,6 +57,27 @@ where
}
}
/// Extract the state from request extensions and panic if its not there.
///
/// This should only be called after `Router::call` or `MethodRouter::call` have been called.
pub(crate) fn extract_state_assume_present<S, B>(req: &Request<B>) -> S
where
S: Clone + Send + Sync + 'static,
{
let State(state) = req
.extensions()
.get::<State<S>>()
.unwrap_or_else(|| {
panic!(
"no state of type `{}` was found. Please file an issue",
std::any::type_name::<State<S>>()
)
})
.clone();
state
}
#[test]
fn test_try_downcast() {
assert_eq!(try_downcast::<i32, _>(5_u32), Err(5_u32));