use std::borrow::Cow;
use rocket::{
outcome::Outcome,
request::{FromRequest, Outcome as OutcomeResult, Request},
};
use crate::{UserAgentParser, models::*};
const NOT_MANAGED: &str = "a `UserAgentParser` is not managed by Rocket; add \
`.manage(UserAgentParser::from_path(..))` when building the instance";
fn from_request_user_agent<'r>(request: &'r Request<'_>) -> UserAgent<'r> {
let user_agent: Option<Cow<'r, str>> =
request.headers().get("user-agent").next().map(Cow::from);
UserAgent {
user_agent,
}
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for UserAgent<'r> {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> OutcomeResult<Self, Self::Error> {
Outcome::Success(from_request_user_agent(request))
}
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for &UserAgent<'r> {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> OutcomeResult<Self, Self::Error> {
let cache = request.local_cache(|| from_request_user_agent(request).into_owned());
Outcome::Success(cache)
}
}
macro_rules! impl_from_request {
($model:ident, $parse:ident) => {
#[rocket::async_trait]
impl<'r> FromRequest<'r> for $model<'r> {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> OutcomeResult<Self, Self::Error> {
Outcome::Success(parse(request))
}
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for &$model<'r> {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> OutcomeResult<Self, Self::Error> {
let cache = request.local_cache_async(async { parse(request).into_owned() }).await;
Outcome::Success(cache)
}
}
fn parse<'r>(request: &'r Request<'_>) -> $model<'r> {
let user_agent_parser = request.rocket().state::<UserAgentParser>().expect(NOT_MANAGED);
match request.headers().get("user-agent").next() {
Some(user_agent) => user_agent_parser.$parse(user_agent),
None => $model::default(),
}
}
};
}
mod product {
use super::*;
impl_from_request!(Product, parse_product);
}
mod os {
use super::*;
impl_from_request!(OS, parse_os);
}
mod device {
use super::*;
impl_from_request!(Device, parse_device);
}
mod cpu {
use super::*;
impl_from_request!(CPU, parse_cpu);
}
mod engine {
use super::*;
impl_from_request!(Engine, parse_engine);
}