Implement score request

This commit is contained in:
Natsu Kagami 2020-01-08 12:55:27 +09:00
parent 3e951554d7
commit f1742664c4
5 changed files with 117 additions and 12 deletions

View file

@ -24,6 +24,16 @@ impl Client {
}
}
fn build_request(
&self,
c: &HTTPClient,
r: reqwest::RequestBuilder,
) -> Result<reqwest::Response, Error> {
let v = r.query(&[("k", &self.key)]).build()?;
// println!("{}", v.url());
Ok(c.execute(v)?)
}
pub fn beatmaps(
&self,
client: &HTTPClient,
@ -32,7 +42,7 @@ impl Client {
) -> Result<Vec<Beatmap>, Error> {
let mut r = BeatmapRequestBuilder::new(kind);
f(&mut r);
let res = r.build(client).query(&[("k", &self.key)]).send()?.json()?;
let res = self.build_request(client, r.build(client))?.json()?;
Ok(res)
}
@ -44,7 +54,7 @@ impl Client {
) -> Result<Option<User>, Error> {
let mut r = UserRequestBuilder::new(user);
f(&mut r);
let res: Vec<_> = r.build(client).query(&[("k", &self.key)]).send()?.json()?;
let res: Vec<_> = self.build_request(client, r.build(client))?.json()?;
Ok(res.into_iter().next())
}
@ -56,7 +66,43 @@ impl Client {
) -> Result<Vec<Score>, Error> {
let mut r = ScoreRequestBuilder::new(beatmap_id);
f(&mut r);
let res = r.build(client).query(&[("k", &self.key)]).send()?.json()?;
let mut res: Vec<Score> = self.build_request(client, r.build(client))?.json()?;
// with a scores request you need to fill the beatmap ids yourself
res.iter_mut().for_each(|v| {
v.beatmap_id = beatmap_id;
});
Ok(res)
}
pub fn user_best(
&self,
client: &HTTPClient,
user: UserID,
f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder,
) -> Result<Vec<Score>, Error> {
self.user_scores(UserScoreType::Best, client, user, f)
}
pub fn user_recent(
&self,
client: &HTTPClient,
user: UserID,
f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder,
) -> Result<Vec<Score>, Error> {
self.user_scores(UserScoreType::Recent, client, user, f)
}
fn user_scores(
&self,
u: UserScoreType,
client: &HTTPClient,
user: UserID,
f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder,
) -> Result<Vec<Score>, Error> {
let mut r = UserScoreRequestBuilder::new(u, user);
f(&mut r);
let res = self.build_request(client, r.build(client))?.json()?;
Ok(res)
}
}

View file

@ -13,15 +13,18 @@ impl<'de> Deserialize<'de> for Score {
{
let raw: raw::Score = raw::Score::deserialize(deserializer)?;
Ok(Score {
id: parse_from_str(&raw.score_id)?,
username: raw.username,
id: raw.score_id.map(parse_from_str).transpose()?,
user_id: parse_from_str(&raw.user_id)?,
date: parse_date(&raw.date)?,
beatmap_id: raw.beatmap_id.map(parse_from_str).transpose()?.unwrap_or(0),
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)?,
mods: {
let v: u64 = parse_from_str(&raw.enabled_mods)?;
Mods::from_bits(v).unwrap_or(Mods::NOMOD)
},
count_300: parse_from_str(&raw.count300)?,
count_100: parse_from_str(&raw.count100)?,
count_50: parse_from_str(&raw.count50)?,

View file

@ -149,6 +149,18 @@ pub struct Beatmap {
pub pass_count: u64,
}
const NEW_MODE_NAMES: [&'static str; 4] = ["osu", "taiko", "fruits", "mania"];
impl Beatmap {
/// Gets a link pointing to the beatmap, in the new format.
pub fn link(&self) -> String {
format!(
"https://osu.ppy.sh/beatmapsets/{}#{}/{}",
self.beatmapset_id, NEW_MODE_NAMES[self.mode as usize], self.beatmap_id
)
}
}
#[derive(Debug)]
pub struct UserEvent {
pub display_html: String,
@ -203,8 +215,8 @@ 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,
"SS" | "X" => Rank::SS,
"SSH" | "XH" => Rank::SSH,
"S" => Rank::S,
"SH" => Rank::SH,
"A" => Rank::A,
@ -225,11 +237,11 @@ impl fmt::Display for Rank {
#[derive(Debug)]
pub struct Score {
pub id: u64,
pub username: String,
pub id: Option<u64>, // No id if you fail
pub user_id: u64,
pub date: DateTime<Utc>,
pub replay_available: bool,
pub beatmap_id: u64,
pub score: u64,
pub pp: f64,

View file

@ -78,9 +78,9 @@ pub(crate) struct UserEvent {
#[derive(Deserialize, Debug)]
pub(crate) struct Score {
pub score_id: String,
pub score_id: Option<String>,
pub beatmap_id: Option<String>,
pub score: String,
pub username: String,
pub count300: String,
pub count100: String,
pub count50: String,

View file

@ -207,6 +207,50 @@ pub mod builders {
.query(&self.limit.map(|v| ("limit", v.to_string())).to_query())
}
}
pub(crate) enum UserScoreType {
Recent,
Best,
}
pub struct UserScoreRequestBuilder {
score_type: UserScoreType,
user: UserID,
mode: Option<Mode>,
limit: Option<u8>,
}
impl UserScoreRequestBuilder {
pub(crate) fn new(score_type: UserScoreType, user: UserID) -> Self {
UserScoreRequestBuilder {
score_type,
user,
mode: None,
limit: None,
}
}
pub fn mode(&mut self, m: Mode) -> &mut Self {
self.mode = Some(m);
self
}
pub fn limit(&mut self, limit: u8) -> &mut Self {
self.limit = Some(limit).filter(|&v| v <= 100).or(self.limit);
self
}
pub(crate) fn build(&self, client: &Client) -> RequestBuilder {
client
.get(match self.score_type {
UserScoreType::Best => "https://osu.ppy.sh/api/get_user_best",
UserScoreType::Recent => "https://osu.ppy.sh/api/get_user_recent"
})
.query(&self.user.to_query())
.query(&self.mode.to_query())
.query(&self.limit.map(|v| ("limit", v.to_string())).to_query())
}
}
}
pub struct UserBestRequest {