pub mod discord; pub mod models; pub mod request; #[cfg(test)] mod test; use models::*; use request::builders::*; use request::*; use reqwest::Client as HTTPClient; use std::convert::TryInto; use youmubot_prelude::{ratelimit::Ratelimit, *}; /// The number of requests per minute to the osu! server. const REQUESTS_PER_MINUTE: usize = 200; /// Client is the client that will perform calls to the osu! api server. pub struct Client { client: Ratelimit, key: String, } fn vec_try_into>(v: Vec) -> Result, T::Error> { let mut res = Vec::with_capacity(v.len()); for u in v.into_iter() { res.push(u.try_into()?); } Ok(res) } impl Client { /// Create a new client from the given API key. pub fn new(key: String) -> Client { let client = Ratelimit::new( HTTPClient::new(), REQUESTS_PER_MINUTE, std::time::Duration::from_secs(60), ); Client { key, client } } pub(crate) async fn build_request(&self, url: &str) -> Result { Ok(self .client .borrow() .await? .get(url) .query(&[("k", &*self.key)])) } pub async fn beatmaps( &self, kind: BeatmapRequestKind, f: impl FnOnce(&mut BeatmapRequestBuilder) -> &mut BeatmapRequestBuilder, ) -> Result> { let mut r = BeatmapRequestBuilder::new(kind); f(&mut r); let res: Vec = r.build(&self).await?.json().await?; Ok(vec_try_into(res)?) } pub async fn user( &self, 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(&self).await?.json().await?; let res = vec_try_into(res)?; Ok(res.into_iter().next()) } pub async fn scores( &self, beatmap_id: u64, f: impl FnOnce(&mut ScoreRequestBuilder) -> &mut ScoreRequestBuilder, ) -> Result, Error> { let mut r = ScoreRequestBuilder::new(beatmap_id); f(&mut r); let res: Vec = r.build(&self).await?.json().await?; let mut res: Vec = vec_try_into(res)?; // 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 async fn user_best( &self, user: UserID, f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder, ) -> Result, Error> { self.user_scores(UserScoreType::Best, user, f).await } pub async fn user_recent( &self, user: UserID, f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder, ) -> Result, Error> { self.user_scores(UserScoreType::Recent, user, f).await } async fn user_scores( &self, u: UserScoreType, user: UserID, f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder, ) -> Result, Error> { let mut r = UserScoreRequestBuilder::new(u, user); f(&mut r); let res: Vec = r.build(&self).await?.json().await?; let res = vec_try_into(res)?; Ok(res) } }