forked from mirror/Riven
1
0
Fork 0

Compare commits

...

3 Commits

Author SHA1 Message Date
Mingwei Samuel 41ef6d9c2e Big generic client compiling 2020-04-21 14:50:30 -07:00
Mingwei Samuel 5d2925c0fa Adding Client, Response traits for reqwest 2020-04-21 13:45:07 -07:00
Mingwei Samuel f716a30e31 Standardize re-export of reqwest 2020-04-21 00:48:57 -07:00
12 changed files with 248 additions and 156 deletions

26
src/client/client.rs Normal file
View File

@ -0,0 +1,26 @@
use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use serde::de::DeserializeOwned;
pub type BoxFut<T> = Pin<Box<dyn Future<Output = T> + Send>>;
pub trait Client {
type Resp: Response;
type Err: Debug;
fn new() -> Self;
fn get(&self, url_base: String, url_path: &str, url_query: Option<&str>,
headers: Vec<(&'static str, &str)>) -> BoxFut<Result<Self::Resp, <Self::Resp as Response>::Err>>;
}
// TODO: custom/generic HeaderValue trait? And for keys?
pub trait Response {
type Err: Debug;
fn status(&self) -> u16;
fn verison(&self) -> String;
fn header(&self, key: &str) -> Option<String>;
fn headers_all(&self, key: &str) -> Vec<String>;
fn into_body(self) -> BoxFut<Result<String, Self::Err>>;
fn into_json<T: DeserializeOwned + 'static>(self) -> BoxFut<Result<T, Self::Err>>;
}

View File

@ -0,0 +1,62 @@
use std::iter::FromIterator;
use std::convert::TryInto;
use serde::de::DeserializeOwned;
use super::{ Client, Response, BoxFut };
impl Client for reqwest::Client {
type Resp = reqwest::Response;
type Err = reqwest::Error;
fn new() -> Self {
Self::new()
}
fn get(&self, url_base: String, url_path: &str, url_query: Option<&str>,
headers: Vec<(&'static str, &str)>) -> BoxFut<Result<reqwest::Response, reqwest::Error>>
{
let mut url = reqwest::Url::parse(&*url_base)
.unwrap_or_else(|_| panic!("Failed to parse url_base: \"{}\".", url_base));
url.set_path(url_path);
url.set_query(url_query);
let header_iter = headers.into_iter()
.map(|(key, value)| (
key.try_into().unwrap_or_else(|_| panic!("Invalid header key: \"{}\".", key)),
value.try_into().unwrap_or_else(|_| panic!("Invalid header value: \"{}\".", value)),
));
let header_map = reqwest::header::HeaderMap::from_iter(header_iter);
let fut = self.get(url)
.headers(header_map)
.send();
return Box::pin(fut);
}
}
impl Response for reqwest::Response {
type Err = reqwest::Error;
fn status(&self) -> u16 {
self.status().as_u16()
}
fn verison(&self) -> String {
format!("{:?}", self.version())
}
fn header(&self, key: &str) -> Option<String> {
self.headers().get(key)
.and_then(|value| value.to_str().ok())
.map(|value| value.to_owned())
}
fn headers_all(&self, key: &str) -> Vec<String> {
self.headers().get_all(key).iter()
.filter_map(|value| value.to_str().ok())
.map(|value| value.to_owned())
.collect()
}
fn into_body(self) -> BoxFut<Result<String, reqwest::Error>> {
//buf: Vec<u8> = Vec::with_capacity(self.content_length());
Box::pin(self.text())
}
fn into_json<T: DeserializeOwned + 'static>(self) -> BoxFut<Result<T, reqwest::Error>> {
Box::pin(self.json())
}
}

7
src/client/mod.rs Normal file
View File

@ -0,0 +1,7 @@
///! Contains client support for `reqwest` and `surf`.
mod client;
pub use client::*;
mod client_reqwest;
pub use client_reqwest::*;

View File

@ -7,7 +7,7 @@
///////////////////////////////////////////////
// http://www.mingweisamuel.com/riotapi-schema/tool/
// Version 71bb788ab92c0b03d5dd284402d9514b625fe2a4
// Version 64f3c82d132f12808d8eb8f483d1d2182386c432
//! Automatically generated endpoint handles.
@ -19,17 +19,18 @@ use std::vec::Vec;
use url::form_urlencoded::Serializer;
use crate::Result;
use crate::client::Client;
use crate::consts::Region;
use crate::riot_api::RiotApi;
impl RiotApi {
impl<C: Client> RiotApi<C> {
/// Returns a handle for accessing [ChampionMasteryV4](crate::endpoints::ChampionMasteryV4) endpoints.
/// # Riot Developer API Reference
/// <a href="https://developer.riotgames.com/apis#champion-mastery-v4" target="_blank">`champion-mastery-v4`</a>
///
/// Note: this method is automatically generated.
#[inline]
pub fn champion_mastery_v4(&self) -> ChampionMasteryV4 {
pub fn champion_mastery_v4(&self) -> ChampionMasteryV4<C> {
ChampionMasteryV4 { base: self }
}
/// Returns a handle for accessing [ChampionV3](crate::endpoints::ChampionV3) endpoints.
@ -38,7 +39,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn champion_v3(&self) -> ChampionV3 {
pub fn champion_v3(&self) -> ChampionV3<C> {
ChampionV3 { base: self }
}
/// Returns a handle for accessing [ClashV1](crate::endpoints::ClashV1) endpoints.
@ -47,7 +48,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn clash_v1(&self) -> ClashV1 {
pub fn clash_v1(&self) -> ClashV1<C> {
ClashV1 { base: self }
}
/// Returns a handle for accessing [LeagueExpV4](crate::endpoints::LeagueExpV4) endpoints.
@ -56,7 +57,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn league_exp_v4(&self) -> LeagueExpV4 {
pub fn league_exp_v4(&self) -> LeagueExpV4<C> {
LeagueExpV4 { base: self }
}
/// Returns a handle for accessing [LeagueV4](crate::endpoints::LeagueV4) endpoints.
@ -65,7 +66,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn league_v4(&self) -> LeagueV4 {
pub fn league_v4(&self) -> LeagueV4<C> {
LeagueV4 { base: self }
}
/// Returns a handle for accessing [LolStatusV3](crate::endpoints::LolStatusV3) endpoints.
@ -74,7 +75,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn lol_status_v3(&self) -> LolStatusV3 {
pub fn lol_status_v3(&self) -> LolStatusV3<C> {
LolStatusV3 { base: self }
}
/// Returns a handle for accessing [LorRankedV1](crate::endpoints::LorRankedV1) endpoints.
@ -83,7 +84,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn lor_ranked_v1(&self) -> LorRankedV1 {
pub fn lor_ranked_v1(&self) -> LorRankedV1<C> {
LorRankedV1 { base: self }
}
/// Returns a handle for accessing [MatchV4](crate::endpoints::MatchV4) endpoints.
@ -92,7 +93,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn match_v4(&self) -> MatchV4 {
pub fn match_v4(&self) -> MatchV4<C> {
MatchV4 { base: self }
}
/// Returns a handle for accessing [SpectatorV4](crate::endpoints::SpectatorV4) endpoints.
@ -101,7 +102,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn spectator_v4(&self) -> SpectatorV4 {
pub fn spectator_v4(&self) -> SpectatorV4<C> {
SpectatorV4 { base: self }
}
/// Returns a handle for accessing [SummonerV4](crate::endpoints::SummonerV4) endpoints.
@ -110,7 +111,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn summoner_v4(&self) -> SummonerV4 {
pub fn summoner_v4(&self) -> SummonerV4<C> {
SummonerV4 { base: self }
}
/// Returns a handle for accessing [TftLeagueV1](crate::endpoints::TftLeagueV1) endpoints.
@ -119,7 +120,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn tft_league_v1(&self) -> TftLeagueV1 {
pub fn tft_league_v1(&self) -> TftLeagueV1<C> {
TftLeagueV1 { base: self }
}
/// Returns a handle for accessing [TftMatchV1](crate::endpoints::TftMatchV1) endpoints.
@ -128,7 +129,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn tft_match_v1(&self) -> TftMatchV1 {
pub fn tft_match_v1(&self) -> TftMatchV1<C> {
TftMatchV1 { base: self }
}
/// Returns a handle for accessing [TftSummonerV1](crate::endpoints::TftSummonerV1) endpoints.
@ -137,7 +138,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn tft_summoner_v1(&self) -> TftSummonerV1 {
pub fn tft_summoner_v1(&self) -> TftSummonerV1<C> {
TftSummonerV1 { base: self }
}
/// Returns a handle for accessing [ThirdPartyCodeV4](crate::endpoints::ThirdPartyCodeV4) endpoints.
@ -146,7 +147,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn third_party_code_v4(&self) -> ThirdPartyCodeV4 {
pub fn third_party_code_v4(&self) -> ThirdPartyCodeV4<C> {
ThirdPartyCodeV4 { base: self }
}
/// Returns a handle for accessing [TournamentStubV4](crate::endpoints::TournamentStubV4) endpoints.
@ -155,7 +156,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn tournament_stub_v4(&self) -> TournamentStubV4 {
pub fn tournament_stub_v4(&self) -> TournamentStubV4<C> {
TournamentStubV4 { base: self }
}
/// Returns a handle for accessing [TournamentV4](crate::endpoints::TournamentV4) endpoints.
@ -164,7 +165,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn tournament_v4(&self) -> TournamentV4 {
pub fn tournament_v4(&self) -> TournamentV4<C> {
TournamentV4 { base: self }
}
}
@ -174,10 +175,10 @@ impl RiotApi {
/// <a href="https://developer.riotgames.com/apis#champion-mastery-v4" target="_blank">`champion-mastery-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct ChampionMasteryV4<'a> {
base: &'a RiotApi,
pub struct ChampionMasteryV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> ChampionMasteryV4<'a> {
impl<'a, C: Client> ChampionMasteryV4<'a, C> {
/// Get all champion mastery entries sorted by number of champion points descending,
/// # Parameters
/// * `region` - Region to query.
@ -231,10 +232,10 @@ impl<'a> ChampionMasteryV4<'a> {
/// <a href="https://developer.riotgames.com/apis#champion-v3" target="_blank">`champion-v3`</a>
///
/// Note: this struct is automatically generated.
pub struct ChampionV3<'a> {
base: &'a RiotApi,
pub struct ChampionV3<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> ChampionV3<'a> {
impl<'a, C: Client> ChampionV3<'a, C> {
/// Returns champion rotations, including free-to-play and low-level free-to-play rotations (REST)
/// # Parameters
/// * `region` - Region to query.
@ -256,11 +257,13 @@ impl<'a> ChampionV3<'a> {
/// <a href="https://developer.riotgames.com/apis#clash-v1" target="_blank">`clash-v1`</a>
///
/// Note: this struct is automatically generated.
pub struct ClashV1<'a> {
base: &'a RiotApi,
pub struct ClashV1<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> ClashV1<'a> {
impl<'a, C: Client> ClashV1<'a, C> {
/// Get players by summoner ID.
/// ## Implementation Notes
/// This endpoint returns a list of active Clash players for a given summoner ID. If a summoner registers for multiple tournaments at the same time (e.g., Saturday and Sunday) then both registrations would appear in this list.
/// # Parameters
/// * `region` - Region to query.
/// * `summonerId`
@ -341,10 +344,10 @@ impl<'a> ClashV1<'a> {
/// <a href="https://developer.riotgames.com/apis#league-exp-v4" target="_blank">`league-exp-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct LeagueExpV4<'a> {
base: &'a RiotApi,
pub struct LeagueExpV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> LeagueExpV4<'a> {
impl<'a, C: Client> LeagueExpV4<'a, C> {
/// Get all the league entries.
/// # Parameters
/// * `region` - Region to query.
@ -373,10 +376,10 @@ impl<'a> LeagueExpV4<'a> {
/// <a href="https://developer.riotgames.com/apis#league-v4" target="_blank">`league-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct LeagueV4<'a> {
base: &'a RiotApi,
pub struct LeagueV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> LeagueV4<'a> {
impl<'a, C: Client> LeagueV4<'a, C> {
/// Get the challenger league for given queue.
/// # Parameters
/// * `region` - Region to query.
@ -480,10 +483,10 @@ impl<'a> LeagueV4<'a> {
/// <a href="https://developer.riotgames.com/apis#lol-status-v3" target="_blank">`lol-status-v3`</a>
///
/// Note: this struct is automatically generated.
pub struct LolStatusV3<'a> {
base: &'a RiotApi,
pub struct LolStatusV3<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> LolStatusV3<'a> {
impl<'a, C: Client> LolStatusV3<'a, C> {
/// Get League of Legends status for the given shard.
/// ## Rate Limit Notes
/// Requests to this API are not counted against the application Rate Limits.
@ -507,10 +510,10 @@ impl<'a> LolStatusV3<'a> {
/// <a href="https://developer.riotgames.com/apis#lor-ranked-v1" target="_blank">`lor-ranked-v1`</a>
///
/// Note: this struct is automatically generated.
pub struct LorRankedV1<'a> {
base: &'a RiotApi,
pub struct LorRankedV1<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> LorRankedV1<'a> {
impl<'a, C: Client> LorRankedV1<'a, C> {
/// Get the players in Master tier.
/// # Parameters
/// * `region` - Region to query.
@ -532,10 +535,10 @@ impl<'a> LorRankedV1<'a> {
/// <a href="https://developer.riotgames.com/apis#match-v4" target="_blank">`match-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct MatchV4<'a> {
base: &'a RiotApi,
pub struct MatchV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> MatchV4<'a> {
impl<'a, C: Client> MatchV4<'a, C> {
/// Get match IDs by tournament code.
/// # Parameters
/// * `region` - Region to query.
@ -643,10 +646,10 @@ impl<'a> MatchV4<'a> {
/// <a href="https://developer.riotgames.com/apis#spectator-v4" target="_blank">`spectator-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct SpectatorV4<'a> {
base: &'a RiotApi,
pub struct SpectatorV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> SpectatorV4<'a> {
impl<'a, C: Client> SpectatorV4<'a, C> {
/// Get current game information for the given summoner ID.
/// # Parameters
/// * `region` - Region to query.
@ -683,10 +686,10 @@ impl<'a> SpectatorV4<'a> {
/// <a href="https://developer.riotgames.com/apis#summoner-v4" target="_blank">`summoner-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct SummonerV4<'a> {
base: &'a RiotApi,
pub struct SummonerV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> SummonerV4<'a> {
impl<'a, C: Client> SummonerV4<'a, C> {
/// Get a summoner by account ID.
/// # Parameters
/// * `region` - Region to query.
@ -754,10 +757,10 @@ impl<'a> SummonerV4<'a> {
/// <a href="https://developer.riotgames.com/apis#tft-league-v1" target="_blank">`tft-league-v1`</a>
///
/// Note: this struct is automatically generated.
pub struct TftLeagueV1<'a> {
base: &'a RiotApi,
pub struct TftLeagueV1<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> TftLeagueV1<'a> {
impl<'a, C: Client> TftLeagueV1<'a, C> {
/// Get the challenger league.
/// # Parameters
/// * `region` - Region to query.
@ -857,10 +860,10 @@ impl<'a> TftLeagueV1<'a> {
/// <a href="https://developer.riotgames.com/apis#tft-match-v1" target="_blank">`tft-match-v1`</a>
///
/// Note: this struct is automatically generated.
pub struct TftMatchV1<'a> {
base: &'a RiotApi,
pub struct TftMatchV1<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> TftMatchV1<'a> {
impl<'a, C: Client> TftMatchV1<'a, C> {
/// Get a list of match ids by PUUID.
/// # Parameters
/// * `region` - Region to query.
@ -902,10 +905,10 @@ impl<'a> TftMatchV1<'a> {
/// <a href="https://developer.riotgames.com/apis#tft-summoner-v1" target="_blank">`tft-summoner-v1`</a>
///
/// Note: this struct is automatically generated.
pub struct TftSummonerV1<'a> {
base: &'a RiotApi,
pub struct TftSummonerV1<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> TftSummonerV1<'a> {
impl<'a, C: Client> TftSummonerV1<'a, C> {
/// Get a summoner by account ID.
/// # Parameters
/// * `region` - Region to query.
@ -973,10 +976,10 @@ impl<'a> TftSummonerV1<'a> {
/// <a href="https://developer.riotgames.com/apis#third-party-code-v4" target="_blank">`third-party-code-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct ThirdPartyCodeV4<'a> {
base: &'a RiotApi,
pub struct ThirdPartyCodeV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> ThirdPartyCodeV4<'a> {
impl<'a, C: Client> ThirdPartyCodeV4<'a, C> {
/// Get third party code for a given summoner ID.
/// # Parameters
/// * `region` - Region to query.
@ -999,10 +1002,10 @@ impl<'a> ThirdPartyCodeV4<'a> {
/// <a href="https://developer.riotgames.com/apis#tournament-stub-v4" target="_blank">`tournament-stub-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct TournamentStubV4<'a> {
base: &'a RiotApi,
pub struct TournamentStubV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> TournamentStubV4<'a> {
impl<'a, C: Client> TournamentStubV4<'a, C> {
/// Gets a mock list of lobby events by tournament code.
/// # Parameters
/// * `region` - Region to query.
@ -1025,10 +1028,10 @@ impl<'a> TournamentStubV4<'a> {
/// <a href="https://developer.riotgames.com/apis#tournament-v4" target="_blank">`tournament-v4`</a>
///
/// Note: this struct is automatically generated.
pub struct TournamentV4<'a> {
base: &'a RiotApi,
pub struct TournamentV4<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> TournamentV4<'a> {
impl<'a, C: Client> TournamentV4<'a, C> {
/// Returns the tournament code DTO associated with a tournament code string.
/// # Parameters
/// * `region` - Region to query.

View File

@ -1,13 +1,7 @@
use std::error::Error as StdError;
use std::fmt;
/// Re-exported `reqwest` types.
pub mod reqwest {
pub use reqwest::{
Error, Response, StatusCode, Url
};
}
use ::reqwest::*;
use crate::reqwest::*;
/// Result containing RiotApiError on failure.
pub type Result<T> = std::result::Result<T, RiotApiError>;

View File

@ -6,6 +6,11 @@
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
#![cfg_attr(not(feature = "nightly"), doc = "See [README.md](https://github.com/MingweiSamuel/Riven#readme).")]
/// Re-exported `reqwest` types.
pub use reqwest;
pub mod client;
mod config;
pub use config::RiotApiConfig;

View File

@ -7,7 +7,7 @@
///////////////////////////////////////////////
// http://www.mingweisamuel.com/riotapi-schema/tool/
// Version 71bb788ab92c0b03d5dd284402d9514b625fe2a4
// Version 64f3c82d132f12808d8eb8f483d1d2182386c432
//! Data transfer structs.
//!
@ -466,7 +466,7 @@ pub mod match_v4 {
pub struct ParticipantIdentity {
#[serde(rename = "participantId")]
pub participant_id: i32,
/// Player information.
/// Player information not included in the response for custom matches. Custom matches are considered private unless a tournament code was used to create the match.
#[serde(rename = "player")]
pub player: Player,
}

View File

@ -1,4 +1,4 @@
//! Module containing rate limiting and requesting types.
//! Contains rate limiting and requesting types.
mod rate_limit;
pub use rate_limit::*;

View File

@ -3,10 +3,10 @@ use std::time::{ Duration, Instant };
use log;
use parking_lot::{ RwLock, RwLockUpgradableReadGuard };
use reqwest::{ StatusCode, Response };
use scan_fmt::scan_fmt;
use crate::RiotApiConfig;
use crate::client::Response;
use super::{ TokenBucket, VectorTokenBucket };
use super::RateLimitType;
@ -68,25 +68,25 @@ impl RateLimit {
self.retry_after.read().and_then(|i| Instant::now().checked_duration_since(i))
}
pub fn on_response(&self, config: &RiotApiConfig, response: &Response) {
pub fn on_response<R: Response>(&self, config: &RiotApiConfig, response: &R) {
self.on_response_retry_after(response);
self.on_response_rate_limits(config, response);
}
/// `on_response` helper for retry after check.
#[inline]
fn on_response_retry_after(&self, response: &Response) {
fn on_response_retry_after<R: Response>(&self, response: &R) {
if let Some(retry_after) = || -> Option<Instant> {
// Only care about 429s.
if StatusCode::TOO_MANY_REQUESTS != response.status() {
// Only care about 429 Too Many Requests.
if 429 != response.status() {
return None;
}
{
// Only care if the header that indicates the relevant RateLimit is present.
let type_name_header = response.headers()
.get(RateLimit::HEADER_XRATELIMITTYPE)?.to_str()
.expect("Failed to read x-rate-limit-type header as string.");
let type_name_header = response
.header(RateLimit::HEADER_XRATELIMITTYPE)?;
// .expect("Failed to read x-rate-limit-type header as string.");
// Only care if that header's value matches us.
if self.rate_limit_type.type_name() != type_name_header.to_lowercase() {
return None;
@ -94,9 +94,9 @@ impl RateLimit {
}
// Get retry after header. Only care if it exists.
let retry_after_header = response.headers()
.get(RateLimit::HEADER_RETRYAFTER)?.to_str()
.expect("Failed to read retry-after header as string.");
let retry_after_header = response
.header(RateLimit::HEADER_RETRYAFTER)?;
// .expect("Failed to read retry-after header as string.");
log::debug!("Hit 429, retry-after {} secs.", retry_after_header);
@ -112,26 +112,26 @@ impl RateLimit {
}
#[inline]
fn on_response_rate_limits(&self, config: &RiotApiConfig, response: &Response) {
fn on_response_rate_limits<R: Response>(&self, config: &RiotApiConfig, response: &R) {
// Check if rate limits changed.
let headers = response.headers();
let limit_header_opt = headers.get(self.rate_limit_type.limit_header())
.map(|h| h.to_str().expect("Failed to read limit header as string."));
let count_header_opt = headers.get(self.rate_limit_type.count_header())
.map(|h| h.to_str().expect("Failed to read count header as string."));
// let headers = response.headers();
let limit_header_opt = response.header(self.rate_limit_type.limit_header());
// .map(|h| h.to_str().expect("Failed to read limit header as string."));
let count_header_opt = response.header(self.rate_limit_type.count_header());
// .map(|h| h.to_str().expect("Failed to read count header as string."));
// https://github.com/rust-lang/rust/issues/53667
if let Some(limit_header) = limit_header_opt {
if let Some(count_header) = count_header_opt {
let buckets = self.buckets.upgradable_read();
if !buckets_require_updating(limit_header, &*buckets) {
if !buckets_require_updating(&limit_header, &*buckets) {
return;
}
// Buckets require updating. Upgrade to write lock.
let mut buckets = RwLockUpgradableReadGuard::upgrade(buckets);
*buckets = buckets_from_header(config, limit_header, count_header)
*buckets = buckets_from_header(config, &limit_header, &count_header)
}}
}
}

View File

@ -2,9 +2,9 @@ use std::future::Future;
use std::sync::Arc;
use log;
use reqwest::{ Client, StatusCode, Url };
use tokio::time::delay_for;
use crate::client::{ Client, Response };
use crate::Result;
use crate::RiotApiError;
use crate::RiotApiConfig;
@ -25,9 +25,9 @@ impl RegionalRequester {
const RIOT_KEY_HEADER: &'static str = "X-Riot-Token";
/// HTTP status codes which are considered a success but will results in `None`.
const NONE_STATUS_CODES: [StatusCode; 2] = [
StatusCode::NO_CONTENT, // 204
StatusCode::NOT_FOUND, // 404
const NONE_STATUS_CODES: [u16; 2] = [
204, // No Content.
404, // Not Found.
];
pub fn new() -> Self {
@ -37,27 +37,28 @@ impl RegionalRequester {
}
}
pub fn get_optional<'a, T: serde::de::DeserializeOwned>(self: Arc<Self>,
config: &'a RiotApiConfig, client: &'a Client,
pub fn get_optional<'a, C: Client, T: serde::de::DeserializeOwned + 'static>(self: Arc<Self>,
config: &'a RiotApiConfig, client: &'a C,
method_id: &'static str, region_platform: &'a str, path: String, query: Option<String>)
-> impl Future<Output = Result<Option<T>>> + 'a
{
async move {
let response_result = self.get(config, client,
method_id, region_platform, path, query).await;
response_result.map(|value| Some(value))
.or_else(|e| {
if let Some(status) = e.status_code() {
if Self::NONE_STATUS_CODES.contains(&status) {
return Ok(None);
}}
Err(e)
})
panic!("FIXME");
// let response_result = self.get(config, client,
// method_id, region_platform, path, query).await;
// response_result.map(|value| Some(value))
// .or_else(|e| {
// if let Some(status) = e.status_code() {
// if Self::NONE_STATUS_CODES.contains(&status) {
// return Ok(None);
// }}
// Err(e)
// })
}
}
pub fn get<'a, T: serde::de::DeserializeOwned>(self: Arc<Self>,
config: &'a RiotApiConfig, client: &'a Client,
pub fn get<'a, C: Client, T: serde::de::DeserializeOwned + 'static>(self: Arc<Self>,
config: &'a RiotApiConfig, client: &'a C,
method_id: &'static str, region_platform: &'a str, path: String, query: Option<String>)
-> impl Future<Output = Result<T>> + 'a
{
@ -77,16 +78,10 @@ impl RegionalRequester {
// Send request.
let url_base = format!("https://{}.api.riotgames.com", region_platform);
let mut url = Url::parse(&*url_base)
.unwrap_or_else(|_| panic!("Failed to parse url_base: \"{}\".", url_base));
url.set_path(&*path);
url.set_query(query);
let response = client.get(url)
.header(Self::RIOT_KEY_HEADER, &*config.api_key)
.send()
let response = client.get(url_base, &path, query, vec![( Self::RIOT_KEY_HEADER, &config.api_key )])
.await
.map_err(|e| RiotApiError::new(e, retries, None, None))?;
.unwrap(); // FIXME
// .map_err(|e| RiotApiError::new(e, retries, None, None))?;
// Maybe update rate limits (based on response headers).
self.app_rate_limit.on_response(&config, &response);
@ -94,27 +89,20 @@ impl RegionalRequester {
let status = response.status();
// Handle normal success / failure cases.
match response.error_for_status_ref() {
if 200 == status {
// Success.
Ok(_response) => {
log::trace!("Response {} (retried {} times), parsed result.", status, retries);
let value = response.json::<T>().await;
break value.map_err(|e| RiotApiError::new(e, retries, None, Some(status)));
},
// Failure, may or may not be retryable.
Err(err) => {
// Not-retryable: no more retries or 4xx or ? (3xx, redirects exceeded).
// Retryable: retries remaining, and 429 or 5xx.
if retries >= config.retries ||
(StatusCode::TOO_MANY_REQUESTS != status
&& !status.is_server_error())
{
log::debug!("Response {} (retried {} times), returning.", status, retries);
break Err(RiotApiError::new(err, retries, Some(response), Some(status)));
}
log::debug!("Response {} (retried {} times), retrying.", status, retries);
},
};
log::trace!("Response {} (retried {} times), parsed result.", status, retries);
let value = response.into_json::<T>().await;
break value.map_err(|e| panic!("FIXME")); //RiotApiError::new(Some(e), retries, None, Some(status)));
}
// Not-retryable: no more retries or 4xx or ? (3xx, redirects exceeded).
// Retryable: retries remaining, and 429 or 5xx.
if retries >= config.retries || (429 != status && 500 > status)
{
log::debug!("Response {} (retried {} times), returning.", status, retries);
panic!("FIXME"); // FIXME break Err(RiotApiError::new(None, retries, Some(response), Some(status)));
}
log::debug!("Response {} (retried {} times), retrying.", status, retries);
retries += 1;
}

View File

@ -2,9 +2,9 @@ use std::future::Future;
use std::sync::Arc;
use log;
use reqwest::Client;
use crate::Result;
use crate::client::Client;
use crate::RiotApiConfig;
use crate::req::RegionalRequester;
use crate::util::InsertOnlyCHashMap;
@ -34,24 +34,30 @@ use crate::util::InsertOnlyCHashMap;
///
/// To adjust rate limiting, see [RiotApiConfig](crate::RiotApiConfig) and use
/// [`with_config(config)`](RiotApi::with_config) to construct an instance.
pub struct RiotApi {
pub struct RiotApi<C: Client> {
/// Configuration settings.
config: RiotApiConfig,
/// Client for making requests.
client: Client,
client: C,
/// Per-region requesters.
regional_requesters: InsertOnlyCHashMap<&'static str, RegionalRequester>,
}
impl RiotApi {
impl<C: Client> RiotApi<C> {
/// Constructs a new instance from the given [RiotApiConfig](crate::RiotApiConfig), consuming it.
pub fn with_config(mut config: RiotApiConfig) -> Self {
let client_builder = config.client_builder.take()
.expect("!NONE CLIENT_BUILDER IN CONFIG.");
//FIXME
// let client_builder = config.client_builder.take()
// .expect("!NONE CLIENT_BUILDER IN CONFIG.");
// Self {
// config: config,
// client: client_builder.build().expect("Failed to create client from builder."),
// regional_requesters: InsertOnlyCHashMap::new(),
// }
Self {
config: config,
client: client_builder.build().expect("Failed to create client from builder."),
client: C::new(),
regional_requesters: InsertOnlyCHashMap::new(),
}
}
@ -74,7 +80,7 @@ impl RiotApi {
/// * `region_platform` - The stringified platform, prepended to `.api.riotgames.com` to create the hostname.
/// * `path` - The path relative to the hostname.
/// * `query` - An optional query string.
pub fn get_optional<'a, T: serde::de::DeserializeOwned + 'a>(&'a self,
pub fn get_optional<'a, T: serde::de::DeserializeOwned + 'static>(&'a self,
method_id: &'static str, region_platform: &'static str, path: String, query: Option<String>)
-> impl Future<Output = Result<Option<T>>> + 'a
{
@ -91,7 +97,7 @@ impl RiotApi {
/// * `region_platform` - The stringified platform, prepended to `.api.riotgames.com` to create the hostname.
/// * `path` - The path relative to the hostname.
/// * `query` - An optional query string.
pub fn get<'a, T: serde::de::DeserializeOwned + 'a>(&'a self,
pub fn get<'a, T: serde::de::DeserializeOwned + 'static>(&'a self,
method_id: &'static str, region_platform: &'static str, path: String, query: Option<String>)
-> impl Future<Output = Result<T>> + 'a
{

View File

@ -16,6 +16,7 @@ use std::vec::Vec;
use url::form_urlencoded::Serializer;
use crate::Result;
use crate::client::Client;
use crate::consts::Region;
use crate::riot_api::RiotApi;
@ -27,7 +28,7 @@ use crate::riot_api::RiotApi;
endpointGroups[ep].push(path);
}
}}
impl RiotApi {
impl<C: Client> RiotApi<C> {
{{
for (const endpointName of Object.keys(endpointGroups)) {
const method = dotUtils.changeCase.snakeCase(endpointName);
@ -39,7 +40,7 @@ impl RiotApi {
///
/// Note: this method is automatically generated.
#[inline]
pub fn {{= method }}(&self) -> {{= type }} {
pub fn {{= method }}(&self) -> {{= type }}<C> {
{{= type }} { base: self }
}
{{
@ -57,10 +58,10 @@ impl RiotApi {
/// <a href="https://developer.riotgames.com/apis#{{= endpointName }}" target="_blank">`{{= endpointName }}`</a>
///
/// Note: this struct is automatically generated.
pub struct {{= endpoint }}<'a> {
base: &'a RiotApi,
pub struct {{= endpoint }}<'a, C: Client> {
base: &'a RiotApi<C>,
}
impl<'a> {{= endpoint }}<'a> {
impl<'a, C: Client> {{= endpoint }}<'a, C> {
{{
for (let [ route, path ] of endpointMethods)
{