use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Xuid(pub String);
impl Xuid {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for Xuid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for Xuid {
fn from(value: String) -> Self {
Xuid(value)
}
}
impl From<&str> for Xuid {
fn from(value: &str) -> Self {
Xuid(value.to_string())
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct UserToken {
#[serde(rename = "Token")]
pub token: String,
#[serde(rename = "NotAfter")]
pub not_after: DateTime<Utc>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct XuiItem {
pub uhs: String,
pub gtg: Option<String>,
pub xid: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DisplayClaims {
pub xui: Vec<XuiItem>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct XstsToken {
#[serde(rename = "Token")]
pub token: String,
#[serde(rename = "NotAfter")]
pub not_after: DateTime<Utc>,
#[serde(rename = "DisplayClaims")]
pub display_claims: DisplayClaims,
}
impl XstsToken {
pub fn user_hash(&self) -> Option<&str> {
self.display_claims.xui.first().map(|xui| xui.uhs.as_str())
}
pub fn authorization_header(&self) -> Option<String> {
self.user_hash()
.map(|uhs| format!("XBL3.0 x={uhs};{}", self.token))
}
pub fn gamertag(&self) -> Option<&str> {
self.display_claims
.xui
.first()
.and_then(|xui| xui.gtg.as_deref())
}
pub fn xuid(&self) -> Option<Xuid> {
self.display_claims
.xui
.first()
.and_then(|xui| xui.xid.clone())
.map(Xuid)
}
}