Implement get_scores requests

This commit is contained in:
Natsu Kagami 2019-12-28 10:45:07 +09:00
parent 71041533bf
commit 3e951554d7
9 changed files with 282 additions and 13 deletions

View file

@ -6,6 +6,34 @@ use chrono::{
use serde::{de, Deserialize, Deserializer};
use std::str::FromStr;
impl<'de> Deserialize<'de> for Score {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let raw: raw::Score = raw::Score::deserialize(deserializer)?;
Ok(Score {
id: parse_from_str(&raw.score_id)?,
username: raw.username,
user_id: parse_from_str(&raw.user_id)?,
date: parse_date(&raw.date)?,
replay_available: parse_bool(&raw.replay_available)?,
score: parse_from_str(&raw.score)?,
pp: parse_from_str(&raw.pp)?,
rank: parse_from_str(&raw.rank)?,
mods: parse_from_str(&raw.enabled_mods)?,
count_300: parse_from_str(&raw.count300)?,
count_100: parse_from_str(&raw.count100)?,
count_50: parse_from_str(&raw.count50)?,
count_miss: parse_from_str(&raw.countmiss)?,
count_katu: parse_from_str(&raw.countkatu)?,
count_geki: parse_from_str(&raw.countgeki)?,
max_combo: parse_from_str(&raw.maxcombo)?,
perfect: parse_bool(&raw.perfect)?,
})
}
}
impl<'de> Deserialize<'de> for User {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where

View file

@ -2,8 +2,11 @@ use chrono::{DateTime, Duration, Utc};
use std::fmt;
pub mod deser;
pub mod mods;
pub(crate) mod raw;
pub use mods::Mods;
#[derive(Debug)]
pub enum ApprovalStatus {
Loved,
@ -146,6 +149,7 @@ pub struct Beatmap {
pub pass_count: u64,
}
#[derive(Debug)]
pub struct UserEvent {
pub display_html: String,
pub beatmap_id: u64,
@ -154,6 +158,7 @@ pub struct UserEvent {
pub epic_factor: u8,
}
#[derive(Debug)]
pub struct User {
pub id: u64,
pub username: String,
@ -181,6 +186,7 @@ pub struct User {
pub accuracy: f64,
}
#[derive(Debug)]
pub enum Rank {
SS,
SSH,
@ -193,6 +199,31 @@ pub enum Rank {
F,
}
impl std::str::FromStr for Rank {
type Err = String;
fn from_str(a: &str) -> Result<Self, Self::Err> {
Ok(match &a.to_uppercase()[..] {
"SS" => Rank::SS,
"SSH" => Rank::SSH,
"S" => Rank::S,
"SH" => Rank::SH,
"A" => Rank::A,
"B" => Rank::B,
"C" => Rank::C,
"D" => Rank::D,
"F" => Rank::F,
t => return Err(format!("Invalid value {}", t)),
})
}
}
impl fmt::Display for Rank {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug)]
pub struct Score {
pub id: u64,
pub username: String,
@ -203,7 +234,7 @@ pub struct Score {
pub score: u64,
pub pp: f64,
pub rank: Rank,
pub mods: u64, // Later
pub mods: Mods, // Later
pub count_300: u64,
pub count_100: u64,

View file

@ -0,0 +1,129 @@
use std::fmt;
bitflags::bitflags! {
/// The mods available to osu!
#[derive(std::default::Default)]
pub struct Mods: u64 {
const NOMOD = 0;
const NF = 1 << 0;
const EZ = 1 << 1;
const TD = 1 << 2;
const HD = 1 << 3;
const HR = 1 << 4;
const SD = 1 << 5;
const DT = 1 << 6;
const RX = 1 << 7;
const HT = 1 << 8;
const NC = 1 << 9;
const FL = 1 << 10;
const AT = 1 << 11;
const SO = 1 << 12;
const AP = 1 << 13;
const PF = 1 << 14;
const KEY4 = 1 << 15; /* TODO: what are these abbreviated to? */
const KEY5 = 1 << 16;
const KEY6 = 1 << 17;
const KEY7 = 1 << 18;
const KEY8 = 1 << 19;
const FADEIN = 1 << 20;
const RANDOM = 1 << 21;
const CINEMA = 1 << 22;
const TARGET = 1 << 23;
const KEY9 = 1 << 24;
const KEYCOOP = 1 << 25;
const KEY1 = 1 << 26;
const KEY3 = 1 << 27;
const KEY2 = 1 << 28;
const SCOREV2 = 1 << 29;
const TOUCH_DEVICE = Self::TD.bits;
const NOVIDEO = Self::TD.bits; /* never forget */
const SPEED_CHANGING = Self::DT.bits | Self::HT.bits | Self::NC.bits;
const MAP_CHANGING = Self::HR.bits | Self::EZ.bits | Self::SPEED_CHANGING.bits;
}
}
const MODS_WITH_NAMES: &[(Mods, &'static str)] = &[
(Mods::NF, "NF"),
(Mods::EZ, "EZ"),
(Mods::TD, "TD"),
(Mods::HD, "HD"),
(Mods::HR, "HR"),
(Mods::SD, "SD"),
(Mods::DT, "DT"),
(Mods::RX, "RX"),
(Mods::HT, "HT"),
(Mods::NC, "NC"),
(Mods::FL, "FL"),
(Mods::AT, "AT"),
(Mods::SO, "SO"),
(Mods::AP, "AP"),
(Mods::PF, "PF"),
(Mods::KEY1, "1K"),
(Mods::KEY2, "2K"),
(Mods::KEY3, "3K"),
(Mods::KEY4, "4K"),
(Mods::KEY5, "5K"),
(Mods::KEY6, "6K"),
(Mods::KEY7, "7K"),
(Mods::KEY8, "8K"),
(Mods::KEY9, "9K"),
];
impl std::str::FromStr for Mods {
type Err = String;
fn from_str(mut s: &str) -> Result<Self, Self::Err> {
let mut res = Self::default();
while s.len() >= 2 {
let (m, nw) = s.split_at(2);
s = nw;
match &m.to_uppercase()[..] {
"NF" => res |= Mods::NF,
"EZ" => res |= Mods::EZ,
"TD" => res |= Mods::TD,
"HD" => res |= Mods::HD,
"HR" => res |= Mods::HR,
"SD" => res |= Mods::SD,
"DT" => res |= Mods::DT,
"RX" => res |= Mods::RX,
"HT" => res |= Mods::HT,
"NC" => res |= Mods::NC,
"FL" => res |= Mods::FL,
"AT" => res |= Mods::AT,
"SO" => res |= Mods::SO,
"AP" => res |= Mods::AP,
"PF" => res |= Mods::PF,
"1K" => res |= Mods::KEY1,
"2K" => res |= Mods::KEY2,
"3K" => res |= Mods::KEY3,
"4K" => res |= Mods::KEY4,
"5K" => res |= Mods::KEY5,
"6K" => res |= Mods::KEY6,
"7K" => res |= Mods::KEY7,
"8K" => res |= Mods::KEY8,
"9K" => res |= Mods::KEY9,
v => return Err(format!("{} is not a valid mod", v)),
}
}
if s.len() > 0 {
Err("String of odd length is not a mod string".to_owned())
} else {
Ok(res)
}
}
}
impl fmt::Display for Mods {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_empty() {
// Return an empty string
return Ok(());
}
write!(f, "+")?;
for p in MODS_WITH_NAMES.iter() {
if self.contains(p.0) {
write!(f, "{}", p.1)?;
}
}
Ok(())
}
}

View file

@ -75,3 +75,24 @@ pub(crate) struct UserEvent {
pub date: String,
pub epicfactor: String,
}
#[derive(Deserialize, Debug)]
pub(crate) struct Score {
pub score_id: String,
pub score: String,
pub username: String,
pub count300: String,
pub count100: String,
pub count50: String,
pub countmiss: String,
pub maxcombo: String,
pub countkatu: String,
pub countgeki: String,
pub perfect: String,
pub enabled_mods: String,
pub user_id: String,
pub date: String,
pub rank: String,
pub pp: String,
pub replay_available: String,
}