use crate::{
request::{GetUserBest, GetUserRecent},
serde::*,
Osu,
};
use serde::Deserialize;
#[cfg(feature = "serialize")]
use serde::Serialize;
use time::OffsetDateTime;
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct User {
#[serde(deserialize_with = "to_u32")]
pub user_id: u32,
pub username: String,
#[serde(with = "serde_date")]
pub join_date: OffsetDateTime,
#[serde(deserialize_with = "to_u32")]
pub count300: u32,
#[serde(deserialize_with = "to_u32")]
pub count100: u32,
#[serde(deserialize_with = "to_u32")]
pub count50: u32,
#[serde(deserialize_with = "to_u32")]
pub playcount: u32,
#[serde(deserialize_with = "to_u64")]
pub ranked_score: u64,
#[serde(deserialize_with = "to_u64")]
pub total_score: u64,
#[serde(deserialize_with = "to_u32")]
pub pp_rank: u32,
#[serde(deserialize_with = "to_f32")]
pub level: f32,
#[serde(deserialize_with = "to_f32")]
pub pp_raw: f32,
#[serde(deserialize_with = "to_f32")]
pub accuracy: f32,
#[serde(alias = "count_rank_ssh", deserialize_with = "to_u32")]
pub count_ssh: u32,
#[serde(alias = "count_rank_ss", deserialize_with = "to_u32")]
pub count_ss: u32,
#[serde(alias = "count_rank_sh", deserialize_with = "to_u32")]
pub count_sh: u32,
#[serde(alias = "count_rank_s", deserialize_with = "to_u32")]
pub count_s: u32,
#[serde(alias = "count_rank_a", deserialize_with = "to_u32")]
pub count_a: u32,
pub country: String,
#[serde(deserialize_with = "to_u32")]
pub total_seconds_played: u32,
#[serde(deserialize_with = "to_u32")]
pub pp_country_rank: u32,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub events: Vec<Event>,
}
impl User {
pub fn get_top_scores<'o>(&self, osu: &'o Osu) -> GetUserBest<'o> {
osu.top_scores(self.user_id)
}
pub fn get_recent_scores<'o>(&self, osu: &'o Osu) -> GetUserRecent<'o> {
osu.recent_scores(self.user_id)
}
#[inline]
pub fn total_hits(&self) -> u64 {
self.count300 as u64 + self.count100 as u64 + self.count50 as u64
}
}
impl Default for User {
fn default() -> Self {
Self {
user_id: 0,
username: String::default(),
join_date: OffsetDateTime::now_utc(),
count300: 0,
count100: 0,
count50: 0,
playcount: 0,
ranked_score: 0,
total_score: 0,
pp_rank: 0,
level: 0.0,
pp_raw: 0.0,
accuracy: 0.0,
count_ssh: 0,
count_ss: 0,
count_sh: 0,
count_s: 0,
count_a: 0,
country: String::default(),
total_seconds_played: 0,
pp_country_rank: 0,
events: Vec::default(),
}
}
}
impl PartialEq for User {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.user_id == other.user_id
}
}
impl Eq for User {}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct Event {
#[serde(alias = "display_html")]
pub html: String,
#[serde(
deserialize_with = "to_maybe_u32",
skip_serializing_if = "Option::is_none",
default
)]
pub beatmap_id: Option<u32>,
#[serde(
deserialize_with = "to_maybe_u32",
skip_serializing_if = "Option::is_none",
default
)]
pub beatmapset_id: Option<u32>,
#[serde(with = "serde_date")]
pub date: OffsetDateTime,
#[serde(alias = "epicfactor", deserialize_with = "to_u32")]
pub epic_factor: u32,
}
impl Event {
#[inline]
pub fn new(
html: String,
beatmap_id: Option<u32>,
beatmapset_id: Option<u32>,
date: OffsetDateTime,
epic_factor: u32,
) -> Self {
Self {
html,
beatmap_id,
beatmapset_id,
date,
epic_factor,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn user_total_hits() {
let mut user = User::default();
user.count300 = 123;
user.count100 = 50;
user.count50 = 2;
assert_eq!(user.total_hits(), 123 + 50 + 2);
}
}