From c6ec62f33ba4e0de5ba1d261ef4076a61e09e568 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Wed, 25 Dec 2019 11:21:56 -0500 Subject: [PATCH] Add library for user requests --- youmubot-osu/src/lib.rs | 12 ++++++++ youmubot-osu/src/models/deser.rs | 48 ++++++++++++++++++++++++++++++++ youmubot-osu/src/models/mod.rs | 2 +- youmubot-osu/src/models/raw.rs | 35 +++++++++++++++++++++++ youmubot-osu/src/request.rs | 45 ++++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 1 deletion(-) diff --git a/youmubot-osu/src/lib.rs b/youmubot-osu/src/lib.rs index 089dbce..47420f7 100644 --- a/youmubot-osu/src/lib.rs +++ b/youmubot-osu/src/lib.rs @@ -35,4 +35,16 @@ impl Client { let res = r.build(client).query(&[("k", &self.key)]).send()?.json()?; Ok(res) } + + pub fn user( + &self, + client: &HTTPClient, + user: UserID, + f: impl FnOnce(&mut UserRequestBuilder) -> &mut UserRequestBuilder, + ) -> Result, Error> { + let mut r = UserRequestBuilder::new(user); + f(&mut r); + let res: Vec<_> = r.build(client).query(&[("k", &self.key)]).send()?.json()?; + Ok(res.into_iter().next()) + } } diff --git a/youmubot-osu/src/models/deser.rs b/youmubot-osu/src/models/deser.rs index 0e7381a..a667946 100644 --- a/youmubot-osu/src/models/deser.rs +++ b/youmubot-osu/src/models/deser.rs @@ -6,6 +6,44 @@ use chrono::{ use serde::{de, Deserialize, Deserializer}; use std::str::FromStr; +impl<'de> Deserialize<'de> for User { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let raw: raw::User = raw::User::deserialize(deserializer)?; + Ok(User { + id: parse_from_str(&raw.user_id)?, + username: raw.username, + joined: parse_date(&raw.join_date)?, + country: raw.country, + count_300: parse_from_str(&raw.count300)?, + count_100: parse_from_str(&raw.count100)?, + count_50: parse_from_str(&raw.count50)?, + play_count: parse_from_str(&raw.playcount)?, + played_time: parse_duration(&raw.total_seconds_played)?, + ranked_score: parse_from_str(&raw.ranked_score)?, + total_score: parse_from_str(&raw.total_score)?, + count_ss: parse_from_str(&raw.count_rank_ss)?, + count_ssh: parse_from_str(&raw.count_rank_ssh)?, + count_s: parse_from_str(&raw.count_rank_s)?, + count_sh: parse_from_str(&raw.count_rank_sh)?, + count_a: parse_from_str(&raw.count_rank_a)?, + rank: parse_from_str(&raw.pp_rank)?, + level: parse_from_str(&raw.level)?, + pp: Some(parse_from_str(&raw.pp_raw)?).filter(|v| *v != 0.0), + accuracy: parse_from_str(&raw.accuracy)?, + events: { + let mut v = Vec::new(); + for e in raw.events.into_iter() { + v.push(parse_user_event(e)?); + } + v + }, + }) + } +} + impl<'de> Deserialize<'de> for Beatmap { fn deserialize(deserializer: D) -> Result where @@ -55,6 +93,16 @@ impl<'de> Deserialize<'de> for Beatmap { } } +fn parse_user_event(s: raw::UserEvent) -> Result { + Ok(UserEvent { + display_html: s.display_html, + beatmap_id: parse_from_str(&s.beatmap_id)?, + beatmapset_id: parse_from_str(&s.beatmapset_id)?, + date: parse_date(&s.date)?, + epic_factor: parse_from_str(&s.epicfactor)?, + }) +} + fn parse_mode(s: impl AsRef) -> Result { let t: u8 = parse_from_str(s)?; use Mode::*; diff --git a/youmubot-osu/src/models/mod.rs b/youmubot-osu/src/models/mod.rs index 1c8262b..5432f73 100644 --- a/youmubot-osu/src/models/mod.rs +++ b/youmubot-osu/src/models/mod.rs @@ -176,7 +176,7 @@ pub struct User { // Rankings pub rank: u64, pub level: f64, - pub pp: Option, + pub pp: Option, pub accuracy: f64, } diff --git a/youmubot-osu/src/models/raw.rs b/youmubot-osu/src/models/raw.rs index 7b80308..39242e0 100644 --- a/youmubot-osu/src/models/raw.rs +++ b/youmubot-osu/src/models/raw.rs @@ -40,3 +40,38 @@ pub(crate) struct Beatmap { pub download_unavailable: String, pub audio_unavailable: String, } + +#[derive(Debug, Deserialize)] +pub(crate) struct User { + pub user_id: String, + pub username: String, + pub join_date: String, + pub count300: String, + pub count100: String, + pub count50: String, + pub playcount: String, + pub ranked_score: String, + pub total_score: String, + pub pp_rank: String, + pub level: String, + pub pp_raw: String, + pub accuracy: String, + pub count_rank_ss: String, + pub count_rank_ssh: String, + pub count_rank_s: String, + pub count_rank_sh: String, + pub count_rank_a: String, + pub country: String, + pub total_seconds_played: String, + pub pp_country_rank: String, + pub events: Vec, +} + +#[derive(Debug, Deserialize)] +pub(crate) struct UserEvent { + pub display_html: String, + pub beatmap_id: String, + pub beatmapset_id: String, + pub date: String, + pub epicfactor: String, +} diff --git a/youmubot-osu/src/request.rs b/youmubot-osu/src/request.rs index a17415a..4b7be84 100644 --- a/youmubot-osu/src/request.rs +++ b/youmubot-osu/src/request.rs @@ -15,6 +15,12 @@ impl ToQuery for Option { } } +impl ToQuery for Mode { + fn to_query(&self) -> Vec<(&'static str, String)> { + vec![("m", (*self as u8).to_string())] + } +} + impl ToQuery for (Mode, bool) { fn to_query(&self) -> Vec<(&'static str, String)> { vec![ @@ -106,6 +112,45 @@ pub mod builders { .query(&self.mode.to_query()) } } + + pub struct UserRequestBuilder { + user: UserID, + mode: Option, + event_days: Option, + } + + impl UserRequestBuilder { + pub(crate) fn new(user: UserID) -> Self { + UserRequestBuilder { + user, + mode: None, + event_days: None, + } + } + + pub fn mode(&mut self, mode: Mode) -> &mut Self { + self.mode = Some(mode); + self + } + + pub fn event_days(&mut self, event_days: u8) -> &mut Self { + self.event_days = Some(event_days).filter(|&v| v <= 31).or(self.event_days); + self + } + + pub(crate) fn build(&self, client: &Client) -> RequestBuilder { + client + .get("https://osu.ppy.sh/api/get_user") + .query(&self.user.to_query()) + .query(&self.mode.to_query()) + .query( + &self + .event_days + .map(|v| ("event_days", v.to_string())) + .to_query(), + ) + } + } } pub struct UserRequest {