1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
use std::future::Future; use std::sync::Arc; use log; use reqwest::Client; use crate::Result; use crate::ResponseInfo; use crate::RiotApiConfig; use crate::RiotApiError; use crate::req::RegionalRequester; use crate::util::InsertOnlyCHashMap; /// For retrieving data from the Riot Games API. /// /// # Usage /// /// Construct an instance using [`with_key(api_key)`](RiotApi::with_key) or /// [`with_config(config)`](RiotApi::with_config). /// /// An instance provides access to "endpoint handles" which in turn provide access /// to individual API method calls. For example, getting a summoner by name: /// ```ignore /// riot_api.summoner_v4().get_by_summoner_name(Region::NA, "LugnutsK") /// ``` /// /// # Rate Limiting /// /// The Riot Game API enforces _dynamic_ rate limiting, meaning that rate limits are /// specified in response headers and (theoretically) could change at any time. /// Riven keeps track of changing rate limits seamlessly, preventing you from /// getting blacklisted. /// /// Riven's rate limiting is highly efficient, meaning that it can reach the limits /// of your rate limit without going over. /// /// To adjust rate limiting, see [RiotApiConfig](crate::RiotApiConfig) and use /// [`with_config(config)`](RiotApi::with_config) to construct an instance. pub struct RiotApi { /// Configuration settings. config: RiotApiConfig, /// Client for making requests. client: Client, /// Per-region requesters. regional_requesters: InsertOnlyCHashMap<&'static str, RegionalRequester>, } impl RiotApi { /// 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."); Self { config: config, client: client_builder.build().expect("Failed to create client from builder."), regional_requesters: InsertOnlyCHashMap::new(), } } /// Constructs a new instance from the given API key, using default configuration. /// /// `api_key` should be a Riot Games API key from /// [https://developer.riotgames.com/](https://developer.riotgames.com/), /// and should look like `"RGAPI-01234567-89ab-cdef-0123-456789abcdef"`. pub fn with_key<T: Into<String>>(api_key: T) -> Self { Self::with_config(RiotApiConfig::with_key(api_key)) } /// This method is not meant to be used directly. /// /// This sends a GET request based on the given parameters and returns an optional parsed result. /// /// # Parameters /// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting. /// * `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. /// /// # Returns /// A future resolving to a `Result` containg either a `Option<T>` (success) or a `RiotApiError` (failure). pub async fn get_optional<'a, T: serde::de::DeserializeOwned + 'a>(&'a self, method_id: &'static str, region_platform: &'static str, path: String, query: Option<String>) -> Result<Option<T>> { let rinfo = self.get_raw_response(method_id, region_platform, path, query).await?; if rinfo.status_none { return Ok(None); } let retries = rinfo.retries; let status = rinfo.response.status(); let value = rinfo.response.json::<Option<T>>().await; value.map_err(|e| RiotApiError::new(e, retries, None, Some(status))) } /// This method is not meant to be used directly. /// /// This sends a GET request based on the given parameters and returns a parsed result. /// /// # Parameters /// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting. /// * `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. /// /// # Returns /// A future resolving to a `Result` containg either a `T` (success) or a `RiotApiError` (failure). pub async fn get<'a, T: serde::de::DeserializeOwned + 'a>(&'a self, method_id: &'static str, region_platform: &'static str, path: String, query: Option<String>) -> Result<T> { let rinfo = self.get_raw_response(method_id, region_platform, path, query).await?; let retries = rinfo.retries; let status = rinfo.response.status(); let value = rinfo.response.json::<T>().await; value.map_err(|e| RiotApiError::new(e, retries, None, Some(status))) } /// This method is not meant to be used directly. /// /// This sends a GET request based on the given parameters and returns a raw `ResponseInfo`. /// /// This can be used to implement a Riot API proxy without needing to deserialize and reserialize JSON responses. /// /// # Parameters /// * `method_id` - A unique string id representing the endpoint method for per-method rate limiting. /// * `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. /// /// # Returns /// A future resolving to a `Result` containg either a `ResponseInfo` (success) or a `RiotApiError` (failure). pub fn get_raw_response<'a>(&'a self, method_id: &'static str, region_platform: &'static str, path: String, query: Option<String>) -> impl Future<Output = Result<ResponseInfo>> + 'a { self.regional_requester(region_platform) .get(&self.config, &self.client, method_id, region_platform, path, query) } /// Get or create the RegionalRequester for the given region. fn regional_requester(&self, region_platform: &'static str) -> Arc<RegionalRequester> { self.regional_requesters.get_or_insert_with(region_platform, || { log::debug!("Creating requester for region platform {}.", region_platform); RegionalRequester::new() }) } }