user_agent_parser/
request_guards_axum.rs1use std::{borrow::Cow, convert::Infallible, sync::Arc};
2
3use axum::{
4 extract::FromRequestParts,
5 http::{header::USER_AGENT, request::Parts},
6};
7
8use crate::{UserAgentParser, models::*};
9
10#[inline]
14fn user_agent_str(parts: &Parts) -> Option<&str> {
15 parts.headers.get(USER_AGENT).and_then(|value| value.to_str().ok())
16}
17
18#[inline]
19fn parser(parts: &Parts) -> &Arc<UserAgentParser> {
20 parts.extensions.get::<Arc<UserAgentParser>>().expect(
21 "a `UserAgentParser` is not shared with axum; add an `Extension<Arc<UserAgentParser>>` \
22 layer to the router",
23 )
24}
25
26impl<S: Send + Sync> FromRequestParts<S> for UserAgent<'static> {
27 type Rejection = Infallible;
28
29 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
30 let user_agent = user_agent_str(parts).map(|ua| Cow::from(ua.to_string()));
31
32 Ok(UserAgent {
33 user_agent,
34 })
35 }
36}
37
38macro_rules! impl_from_request_parts {
39 ($model:ident, $parse:ident) => {
40 impl<S: Send + Sync> FromRequestParts<S> for $model<'static> {
41 type Rejection = Infallible;
42
43 async fn from_request_parts(
44 parts: &mut Parts,
45 _state: &S,
46 ) -> Result<Self, Self::Rejection> {
47 let user_agent_parser = parser(parts);
49
50 let result = match user_agent_str(parts) {
52 Some(user_agent) => user_agent_parser.$parse(user_agent).into_owned(),
53 None => $model::default(),
54 };
55
56 Ok(result)
57 }
58 }
59 };
60}
61
62impl_from_request_parts!(Product, parse_product);
63impl_from_request_parts!(OS, parse_os);
64impl_from_request_parts!(Device, parse_device);
65impl_from_request_parts!(CPU, parse_cpu);
66impl_from_request_parts!(Engine, parse_engine);