Files
OpenFUT-Core/src/extractors.rs
T
2026-08-07 12:03:21 -07:00

52 lines
1.5 KiB
Rust

//! Request extractors shared across routes.
use axum::{
async_trait,
extract::FromRequestParts,
http::{request::Parts, HeaderName},
};
use std::convert::Infallible;
/// The game a request belongs to, read from the `X-OpenFUT-Game` header.
///
/// Multi-game support: each game bridge tags its requests with its own id
/// (e.g. `fifa17`, `fifa23`) so core can scope the active profile - and thus all
/// downstream club/card/squad/market state - to that game. Defaults to `fifa23`
/// when the header is absent, so the existing FIFA 23 bridge and the integration
/// tests (which send no header) keep operating on their game unchanged.
///
/// Extraction never fails: a missing or malformed header falls back to the default.
#[derive(Debug, Clone)]
pub struct GameId(pub String);
/// The game assumed when no `X-OpenFUT-Game` header is present.
pub const DEFAULT_GAME: &str = "fifa23";
static HEADER: HeaderName = HeaderName::from_static("x-openfut-game");
impl GameId {
pub fn as_str(&self) -> &str {
&self.0
}
}
#[async_trait]
impl<S> FromRequestParts<S> for GameId
where
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let game = parts
.headers
.get(&HEADER)
.and_then(|v| v.to_str().ok())
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.unwrap_or(DEFAULT_GAME)
.to_string();
Ok(GameId(game))
}
}