1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use axum::{
    async_trait,
    extract::{rejection::MatchedPathRejection, FromRequestParts, MatchedPath},
    http::request::Parts,
    RequestPartsExt,
};

/// Extracts matched path of the request
///
/// # Usage
///
/// ```
/// # use axum::{response::IntoResponse, Server, Router, routing::get};
/// # use axum_template::Key;
/// async fn handler(
///     Key(key): Key
/// ) -> impl IntoResponse
/// {
///     key
/// }
///
/// let router = Router::new()
///     // key == "/some/route"
///     .route("/some/route", get(handler))
///     // key == "/:dynamic"
///     .route("/:dynamic", get(handler));
///
/// # async {
/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
///     .serve(router.into_make_service())
///     .await
///     .expect("server failed");
/// # };
/// ```
///
/// # Additional resources
///
/// - [`MatchedPath`]
/// - Example: [`custom_key.rs`]
///
/// [`MatchedPath`]: axum::extract::MatchedPath
/// [`custom_key.rs`]: https://github.com/Altair-Bueno/axum-template/blob/main/examples/custom_key.rs
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Key(pub String);

#[async_trait]
impl<S> FromRequestParts<S> for Key
where
    S: Send + Sync,
{
    type Rejection = MatchedPathRejection;

    async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
        let path = parts.extract::<MatchedPath>().await?.as_str().to_owned();
        Ok(Key(path))
    }
}

impl AsRef<str> for Key {
    fn as_ref(&self) -> &str {
        self.0.as_str()
    }
}

impl From<String> for Key {
    fn from(s: String) -> Self {
        Self(s)
    }
}