use chrono::{DateTime, Utc};
use serde::Deserialize;
use serde::Serialize;
use super::common::{Activity, Language, Platform, SubscriptionTier, Theme, UserRole, UserStatus};
#[derive(Debug, Clone, Deserialize)]
pub struct User {
pub id: String,
#[serde(rename = "ingameName")]
pub ingame_name: String,
#[serde(default)]
pub avatar: Option<String>,
#[serde(default)]
pub reputation: i32,
#[serde(default)]
pub locale: Option<String>,
pub platform: Platform,
#[serde(default)]
pub crossplay: bool,
#[serde(rename = "status", default)]
pub status: UserStatus,
#[serde(default)]
pub activity: Option<Activity>,
#[serde(rename = "lastSeen", default)]
pub last_seen: Option<DateTime<Utc>>,
}
impl User {
pub fn is_available(&self) -> bool {
self.status.is_available()
}
pub fn avatar_url(&self) -> Option<&str> {
self.avatar.as_deref()
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct FullUser {
pub id: String,
#[serde(rename = "ingameName")]
pub ingame_name: String,
#[serde(default)]
pub slug: Option<String>,
#[serde(default)]
pub avatar: Option<String>,
#[serde(default)]
pub background: Option<String>,
#[serde(default)]
pub about: Option<String>,
#[serde(default)]
pub reputation: i32,
#[serde(rename = "masteryLevel", default)]
pub mastery_level: Option<u32>,
pub platform: Platform,
#[serde(default)]
pub crossplay: bool,
#[serde(default)]
pub locale: Option<String>,
#[serde(rename = "status", default)]
pub status: UserStatus,
#[serde(default)]
pub activity: Option<Activity>,
#[serde(rename = "lastSeen", default)]
pub last_seen: Option<DateTime<Utc>>,
#[serde(default)]
pub banned: Option<bool>,
#[serde(rename = "banUntil", default)]
pub ban_until: Option<DateTime<Utc>>,
#[serde(rename = "banMessage", default)]
pub ban_message: Option<String>,
#[serde(default)]
pub warned: Option<bool>,
#[serde(rename = "warnMessage", default)]
pub warn_message: Option<String>,
#[serde(default)]
pub email: Option<String>,
#[serde(rename = "emailVerified", default)]
pub email_verified: Option<bool>,
#[serde(rename = "ingameVerified", default)]
pub ingame_verified: Option<bool>,
}
impl FullUser {
pub fn is_available(&self) -> bool {
self.status.is_available()
}
pub fn is_banned(&self) -> bool {
self.banned.unwrap_or(false)
}
pub fn has_warning(&self) -> bool {
self.warned.unwrap_or(false)
}
pub fn to_user(&self) -> User {
User {
id: self.id.clone(),
ingame_name: self.ingame_name.clone(),
avatar: self.avatar.clone(),
reputation: self.reputation,
locale: self.locale.clone(),
platform: self.platform,
crossplay: self.crossplay,
status: self.status,
activity: self.activity.clone(),
last_seen: self.last_seen,
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct UserProfile {
pub id: String,
#[serde(rename = "ingameName")]
pub ingame_name: String,
pub slug: String,
#[serde(default)]
pub avatar: Option<String>,
#[serde(default)]
pub background: Option<String>,
#[serde(default)]
pub about: Option<String>,
#[serde(default)]
pub reputation: i32,
#[serde(rename = "masteryLevel", default)]
pub mastery_level: Option<u32>,
pub platform: Platform,
#[serde(default)]
pub crossplay: bool,
#[serde(default)]
pub locale: Option<String>,
#[serde(rename = "status", default)]
pub status: UserStatus,
#[serde(default)]
pub activity: Option<Activity>,
#[serde(rename = "lastSeen", default)]
pub last_seen: Option<DateTime<Utc>>,
#[serde(rename = "achievementShowcase", default)]
pub achievement_showcase: Vec<Achievement>,
#[serde(default)]
pub banned: Option<bool>,
#[serde(rename = "banUntil", default)]
pub ban_until: Option<DateTime<Utc>>,
#[serde(rename = "banMessage", default)]
pub ban_message: Option<String>,
#[serde(default)]
pub warned: Option<bool>,
#[serde(rename = "warnMessage", default)]
pub warn_message: Option<String>,
}
impl UserProfile {
pub fn is_available(&self) -> bool {
self.status.is_available()
}
pub fn is_banned(&self) -> bool {
self.banned.unwrap_or(false)
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Achievement {
pub id: String,
pub icon: String,
#[serde(default)]
pub thumb: Option<String>,
#[serde(rename = "type")]
pub achievement_type: AchievementType,
#[serde(default)]
pub i18n: Option<AchievementTranslation>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AchievementType {
Patreon,
Github,
Custom,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AchievementTranslation {
pub name: String,
pub description: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct UserPrivate {
pub id: String,
#[serde(rename = "ingameName")]
pub ingame_name: String,
pub slug: String,
#[serde(default)]
pub avatar: Option<String>,
#[serde(default)]
pub background: Option<String>,
#[serde(default)]
pub about: Option<String>,
#[serde(default)]
pub reputation: i32,
#[serde(rename = "masteryLevel", default)]
pub mastery_level: Option<u32>,
pub platform: Platform,
#[serde(default)]
pub crossplay: bool,
#[serde(default)]
pub locale: Option<String>,
#[serde(rename = "achievementShowcase", default)]
pub achievement_showcase: Vec<Achievement>,
#[serde(rename = "status", default)]
pub status: UserStatus,
#[serde(default)]
pub activity: Option<Activity>,
#[serde(rename = "lastSeen", default)]
pub last_seen: Option<DateTime<Utc>>,
#[serde(default)]
pub banned: Option<bool>,
#[serde(rename = "banUntil", default)]
pub ban_until: Option<DateTime<Utc>>,
#[serde(rename = "banMessage", default)]
pub ban_message: Option<String>,
#[serde(default)]
pub warned: Option<bool>,
#[serde(rename = "warnMessage", default)]
pub warn_message: Option<String>,
#[serde(default)]
pub email: Option<String>,
#[serde(default)]
pub role: UserRole,
#[serde(default)]
pub tier: Option<SubscriptionTier>,
#[serde(default)]
pub theme: Option<Theme>,
#[serde(rename = "syncLocale", default)]
pub sync_locale: Option<bool>,
#[serde(rename = "syncTheme", default)]
pub sync_theme: Option<bool>,
#[serde(default)]
pub verified: Option<bool>,
}
impl UserPrivate {
pub fn is_available(&self) -> bool {
self.status.is_available()
}
pub fn is_banned(&self) -> bool {
self.banned.unwrap_or(false)
}
pub fn has_warning(&self) -> bool {
self.warned.unwrap_or(false)
}
pub fn is_verified(&self) -> bool {
self.verified.unwrap_or(false)
}
pub fn is_staff(&self) -> bool {
matches!(self.role, UserRole::Moderator | UserRole::Admin)
}
pub fn to_profile(&self) -> UserProfile {
UserProfile {
id: self.id.clone(),
ingame_name: self.ingame_name.clone(),
slug: self.slug.clone(),
avatar: self.avatar.clone(),
background: self.background.clone(),
about: self.about.clone(),
reputation: self.reputation,
mastery_level: self.mastery_level,
platform: self.platform,
crossplay: self.crossplay,
locale: self.locale.clone(),
status: self.status,
activity: self.activity.clone(),
last_seen: self.last_seen,
achievement_showcase: self.achievement_showcase.clone(),
banned: self.banned,
ban_until: self.ban_until,
ban_message: self.ban_message.clone(),
warned: self.warned,
warn_message: self.warn_message.clone(),
}
}
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateProfile {
#[serde(skip_serializing_if = "Option::is_none")]
pub about: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub platform: Option<Platform>,
#[serde(skip_serializing_if = "Option::is_none")]
pub crossplay: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<Language>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<Theme>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_locale: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_theme: Option<bool>,
}
impl UpdateProfile {
pub fn new() -> Self {
Self::default()
}
pub fn about(mut self, about: impl Into<String>) -> Self {
self.about = Some(about.into());
self
}
pub fn platform(mut self, platform: Platform) -> Self {
self.platform = Some(platform);
self
}
pub fn crossplay(mut self, crossplay: bool) -> Self {
self.crossplay = Some(crossplay);
self
}
pub fn locale(mut self, locale: Language) -> Self {
self.locale = Some(locale);
self
}
pub fn theme(mut self, theme: Theme) -> Self {
self.theme = Some(theme);
self
}
pub fn sync_locale(mut self, sync: bool) -> Self {
self.sync_locale = Some(sync);
self
}
pub fn sync_theme(mut self, sync: bool) -> Self {
self.sync_theme = Some(sync);
self
}
pub fn is_empty(&self) -> bool {
self.about.is_none()
&& self.platform.is_none()
&& self.crossplay.is_none()
&& self.locale.is_none()
&& self.theme.is_none()
&& self.sync_locale.is_none()
&& self.sync_theme.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_is_available() {
let user = User {
id: "test".to_string(),
ingame_name: "TestUser".to_string(),
avatar: None,
reputation: 0,
locale: None,
platform: Platform::Pc,
crossplay: true,
status: UserStatus::Online,
activity: None,
last_seen: None,
};
assert!(user.is_available());
}
#[test]
fn test_user_offline_not_available() {
let user = User {
id: "test".to_string(),
ingame_name: "TestUser".to_string(),
avatar: None,
reputation: 0,
locale: None,
platform: Platform::Pc,
crossplay: true,
status: UserStatus::Offline,
activity: None,
last_seen: None,
};
assert!(!user.is_available());
}
}