chore: replace `lazy_static` with std `OnceLock`

pull/63/head
Mingwei Samuel 2024-02-24 09:46:11 -08:00
parent 101633bb47
commit 092ea34358
18 changed files with 106 additions and 97 deletions

View File

@ -39,7 +39,6 @@ deny-unknown-enum-variants-strings = []
deny-unknown-enum-variants-integers = [] deny-unknown-enum-variants-integers = []
[dependencies] [dependencies]
lazy_static = "1.4"
log = "0.4" log = "0.4"
num_enum = "0.5" num_enum = "0.5"
parking_lot = "0.12" parking_lot = "0.12"

View File

@ -1,28 +1,31 @@
// #![deny(warnings)] // #![deny(warnings)]
use std::convert::Infallible; use std::convert::Infallible;
use std::sync::OnceLock;
use http::{Method, Request, Response, StatusCode}; use http::{Method, Request, Response, StatusCode};
use hyper::header::HeaderValue; use hyper::header::HeaderValue;
use hyper::service::{make_service_fn, service_fn}; use hyper::service::{make_service_fn, service_fn};
use hyper::{http, Body, Server}; use hyper::{http, Body, Server};
use lazy_static::lazy_static;
use riven::consts::Route; use riven::consts::Route;
use riven::{RiotApi, RiotApiConfig}; use riven::{RiotApi, RiotApiConfig};
use tracing as log; use tracing as log;
lazy_static! { static RIOT_API: OnceLock<RiotApi> = OnceLock::new();
/// Create lazy static RiotApi instance. pub fn riot_api() -> &'static RiotApi {
/// Easier than passing it around. RIOT_API.get_or_init(|| {
pub static ref RIOT_API: RiotApi = { let api_key = std::env::var("RGAPI_KEY")
let api_key = std::env::var("RGAPI_KEY").ok() .ok()
.or_else(|| { .or_else(|| {
let path: std::path::PathBuf = [ env!("CARGO_MANIFEST_DIR"), "../../apikey.txt" ].iter().collect(); use std::iter::FromIterator;
let path =
std::path::PathBuf::from_iter([env!("CARGO_MANIFEST_DIR"), "../apikey.txt"]);
std::fs::read_to_string(path).ok() std::fs::read_to_string(path).ok()
}) })
.expect("Failed to find RGAPI_KEY env var or apikey.txt."); .expect("Failed to find RGAPI_KEY env var or apikey.txt.");
RiotApi::new(RiotApiConfig::with_key(api_key.trim()).preconfig_burst()) RiotApi::new(RiotApiConfig::with_key(api_key.trim()).preconfig_burst())
}; })
} }
/// Helper to create JSON error responses. /// Helper to create JSON error responses.
@ -73,10 +76,12 @@ async fn handle_request(req: Request<Body>) -> Result<Response<Body>, Infallible
Ok(bytes) => bytes, Ok(bytes) => bytes,
}; };
let req_builder = RIOT_API.request(method, route.into(), req_path).body(body); let req_builder = riot_api()
.request(method, route.into(), req_path)
.body(body);
// Send request to Riot API. // Send request to Riot API.
let resp_result = RIOT_API let resp_result = riot_api()
.execute_raw(method_id, route.into(), req_builder) .execute_raw(method_id, route.into(), req_builder)
.await; .await;
let resp_info = match resp_result { let resp_info = match resp_result {
@ -169,8 +174,8 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// env_logger::init(); // env_logger::init();
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
// Trigger lazy_static. // Trigger initialization.
let _ = &*RIOT_API; let _ = riot_api();
// For every connection, we must make a `Service` to handle all // For every connection, we must make a `Service` to handle all
// incoming HTTP requests on said connection. // incoming HTTP requests on said connection.

View File

@ -7,18 +7,14 @@ mod token_bucket {
include!("token_bucket.rs"); include!("token_bucket.rs");
mod tests { mod tests {
use lazy_static::lazy_static;
use super::*; use super::*;
lazy_static! { pub const ZERO: Duration = Duration::new(0, 0);
pub static ref ZERO: Duration = Duration::new(0, 0);
}
#[test] #[test]
fn test_basic() { fn test_basic() {
Instant::set_time(50_000); Instant::set_time(50_000);
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 0.95, 1.0); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 0.95, 1.0);
assert!(bucket.get_tokens(50), "Should have not violated limit."); assert!(bucket.get_tokens(50), "Should have not violated limit.");
assert_eq!(None, bucket.get_delay(), "Can get stuff."); assert_eq!(None, bucket.get_delay(), "Can get stuff.");
assert!(!bucket.get_tokens(51), "Should have violated limit."); assert!(!bucket.get_tokens(51), "Should have violated limit.");
@ -26,20 +22,20 @@ mod token_bucket {
#[test] #[test]
fn test_internal_constructor() { fn test_internal_constructor() {
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 1.0, 1.0); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 1.0, 1.0);
assert_eq!(100, bucket.burst_limit); assert_eq!(100, bucket.burst_limit);
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 1e-6, 1.0); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 1e-6, 1.0);
assert_eq!(1, bucket.burst_limit); assert_eq!(1, bucket.burst_limit);
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 1.0, 1e-6); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 1.0, 1e-6);
assert_eq!(1, bucket.total_limit); assert_eq!(1, bucket.total_limit);
assert_eq!(1, bucket.burst_limit); assert_eq!(1, bucket.burst_limit);
} }
#[test] #[test]
fn test_saturated_100_burst() { fn test_saturated_100_burst() {
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 1.00, 1.0); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 1.00, 1.0);
Instant::set_time(50_000); Instant::set_time(50_000);
assert!( assert!(
@ -58,7 +54,7 @@ mod token_bucket {
#[test] #[test]
fn test_saturated_95_burst() { fn test_saturated_95_burst() {
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 0.95, 1.0); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 0.95, 1.0);
Instant::set_time(50_000); Instant::set_time(50_000);
assert!( assert!(
@ -85,7 +81,7 @@ mod token_bucket {
#[test] #[test]
fn test_violated_50_burst() { fn test_violated_50_burst() {
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 0.50, 1.0); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 0.50, 1.0);
Instant::set_time(50_000); Instant::set_time(50_000);
assert!(!bucket.get_tokens(90), "Burst should be violated."); assert!(!bucket.get_tokens(90), "Burst should be violated.");
@ -94,7 +90,7 @@ mod token_bucket {
#[test] #[test]
fn test_saturated_50_burst() { fn test_saturated_50_burst() {
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 0.50, 1.0); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 0.50, 1.0);
Instant::set_time(50_000); Instant::set_time(50_000);
assert!( assert!(
@ -128,7 +124,7 @@ mod token_bucket {
let bucket = VectorTokenBucket::new( let bucket = VectorTokenBucket::new(
Duration::from_millis(1000), Duration::from_millis(1000),
100, 100,
*ZERO, ZERO,
0.90, 0.90,
rate_usage_factor, rate_usage_factor,
); );
@ -159,7 +155,7 @@ mod token_bucket {
#[test] #[test]
fn test_many() { fn test_many() {
Instant::set_time(50_000); Instant::set_time(50_000);
let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, *ZERO, 0.5, 1.0); let bucket = VectorTokenBucket::new(Duration::from_millis(1000), 100, ZERO, 0.5, 1.0);
assert!( assert!(
bucket.get_tokens(50), bucket.get_tokens(50),
"Should have not violated limit. i=-1." "Should have not violated limit. i=-1."

View File

@ -6,7 +6,7 @@ const ROUTE: PlatformRoute = PlatformRoute::RU;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn summoner_leagues() -> Result<(), String> { async fn summoner_leagues() -> Result<(), String> {
let sum = RIOT_API let sum = riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_name(ROUTE, "d3atomiz3d"); .get_by_summoner_name(ROUTE, "d3atomiz3d");
let sum = sum let sum = sum
@ -14,7 +14,7 @@ async fn summoner_leagues() -> Result<(), String> {
.map_err(|e| format!("Error getting summoner: {}", e))? .map_err(|e| format!("Error getting summoner: {}", e))?
.ok_or_else(|| "Failed to find summoner".to_owned())?; .ok_or_else(|| "Failed to find summoner".to_owned())?;
let p = RIOT_API let p = riot_api()
.league_v4() .league_v4()
.get_league_entries_for_summoner(ROUTE, &sum.id); .get_league_entries_for_summoner(ROUTE, &sum.id);
let s = p let s = p

View File

@ -15,14 +15,14 @@ static MATCHES: &[&str] = &[
async fn account_v1_getbyriotid_getbypuuid() -> Result<(), String> { async fn account_v1_getbyriotid_getbypuuid() -> Result<(), String> {
// Game name is case and whitespace insensitive. // Game name is case and whitespace insensitive.
// But tag cannot have spaces. (Is it case sensitive?). // But tag cannot have spaces. (Is it case sensitive?).
let account_tag = RIOT_API let account_tag = riot_api()
.account_v1() .account_v1()
.get_by_riot_id(ROUTE, "Lug nuts K", "000") .get_by_riot_id(ROUTE, "Lug nuts K", "000")
.await .await
.map_err(|e| format!("Failed to get account by riot ID: {}", e))? .map_err(|e| format!("Failed to get account by riot ID: {}", e))?
.ok_or("Riot account not found!".to_owned())?; .ok_or("Riot account not found!".to_owned())?;
let account_puuid = RIOT_API let account_puuid = riot_api()
.account_v1() .account_v1()
.get_by_puuid(ROUTE, &account_tag.puuid) .get_by_puuid(ROUTE, &account_tag.puuid)
.await .await
@ -36,7 +36,7 @@ async fn account_v1_getbyriotid_getbypuuid() -> Result<(), String> {
/// Tournament stub test. /// Tournament stub test.
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn tournamentstub() -> Result<(), String> { async fn tournamentstub() -> Result<(), String> {
let ts = RIOT_API.tournament_stub_v5(); let ts = riot_api().tournament_stub_v5();
let provider_id = ts let provider_id = ts
.register_provider_data( .register_provider_data(
ROUTE, ROUTE,

View File

@ -25,7 +25,7 @@ static MATCHES: &[&str] = &[
/// Summoner tests. /// Summoner tests.
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn summoner_get_kanjikana() -> Result<(), String> { async fn summoner_get_kanjikana() -> Result<(), String> {
let p = RIOT_API let p = riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_name(ROUTE, "私の 頭が かたい"); .get_by_summoner_name(ROUTE, "私の 頭が かたい");
let s = p let s = p
@ -41,7 +41,7 @@ async fn summoner_get_kanjikana() -> Result<(), String> {
// /// Make sure get_raw_response(...) with invalid path fails as expected. // /// Make sure get_raw_response(...) with invalid path fails as expected.
// #[tokio_shared_rt::test] // #[tokio_shared_rt::test]
// async fn raw_response_invalid -> Result<(), String> { // async fn raw_response_invalid -> Result<(), String> {
// let p = RIOT_API.get_raw_response("summoner-v4.getBySummonerName", Region::JP.into(), "INVALID/PATH".to_owned(), None); // let p = riot_api().get_raw_response("summoner-v4.getBySummonerName", Region::JP.into(), "INVALID/PATH".to_owned(), None);
// let r = p.await; // let r = p.await;
// rassert!(r.is_err()); // rassert!(r.is_err());
// Ok(()) // Ok(())
@ -55,8 +55,8 @@ async fn get_nonoptional_invalid() -> Result<(), String> {
"/lol/summoner/v4/summoners/by-name/{}", "/lol/summoner/v4/summoners/by-name/{}",
"SUMMONER THAT DOES NOT EXIST" "SUMMONER THAT DOES NOT EXIST"
); );
let request = RIOT_API.request(reqwest::Method::GET, ROUTE.into(), &path_string); let request = riot_api().request(reqwest::Method::GET, ROUTE.into(), &path_string);
let p = RIOT_API.execute_val::<riven::models::summoner_v4::Summoner>( let p = riot_api().execute_val::<riven::models::summoner_v4::Summoner>(
"summoner-v4.getBySummonerName", "summoner-v4.getBySummonerName",
ROUTE.into(), ROUTE.into(),
request, request,
@ -69,7 +69,7 @@ async fn get_nonoptional_invalid() -> Result<(), String> {
/// Check invalid code, make sure 403 is handled as expected. /// Check invalid code, make sure 403 is handled as expected.
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn tournament_forbidden() -> Result<(), String> { async fn tournament_forbidden() -> Result<(), String> {
let p = RIOT_API let p = riot_api()
.tournament_v5() .tournament_v5()
.get_tournament_code(ROUTE.to_regional(), "INVALID_CODE"); .get_tournament_code(ROUTE.to_regional(), "INVALID_CODE");
let r = p.await; let r = p.await;
@ -86,9 +86,9 @@ async fn tournament_forbidden() -> Result<(), String> {
// /// https://github.com/MingweiSamuel/Riven/issues/25 // /// https://github.com/MingweiSamuel/Riven/issues/25
// #[tokio_shared_rt::test] // #[tokio_shared_rt::test]
// async fn tft_league_getleagueentriesforsummoner() -> Result<(), String> { // async fn tft_league_getleagueentriesforsummoner() -> Result<(), String> {
// let sp = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "Caihonbbt"); // let sp = riot_api().summoner_v4().get_by_summoner_name(ROUTE, "Caihonbbt");
// let sr = sp.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get \"Caihonbbt\"".to_owned())?; // let sr = sp.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get \"Caihonbbt\"".to_owned())?;
// let lp = RIOT_API.tft_league_v1().get_league_entries_for_summoner(ROUTE, &sr.id); // let lp = riot_api().tft_league_v1().get_league_entries_for_summoner(ROUTE, &sr.id);
// let lr = lp.await.map_err(|e| e.to_string())?; // let lr = lp.await.map_err(|e| e.to_string())?;
// rassert!(!lr.is_empty()); // rassert!(!lr.is_empty());
// Ok(()) // Ok(())
@ -98,7 +98,7 @@ async fn tournament_forbidden() -> Result<(), String> {
/// https://github.com/MingweiSamuel/Riven/issues/24 /// https://github.com/MingweiSamuel/Riven/issues/24
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn tft_league_gettopratedladder() -> Result<(), String> { async fn tft_league_gettopratedladder() -> Result<(), String> {
let lp = RIOT_API let lp = riot_api()
.tft_league_v1() .tft_league_v1()
.get_top_rated_ladder(ROUTE, QueueType::RANKED_TFT_TURBO); .get_top_rated_ladder(ROUTE, QueueType::RANKED_TFT_TURBO);
let lr = lp.await.map_err(|e| e.to_string())?; let lr = lp.await.map_err(|e| e.to_string())?;

View File

@ -8,7 +8,7 @@ const ROUTE: PlatformRoute = PlatformRoute::EUW1;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn championmastery_getscore_ma5tery() -> Result<(), String> { async fn championmastery_getscore_ma5tery() -> Result<(), String> {
let sum = RIOT_API let sum = riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_name(ROUTE, "ma5tery"); .get_by_summoner_name(ROUTE, "ma5tery");
let sum = sum let sum = sum
@ -16,7 +16,7 @@ async fn championmastery_getscore_ma5tery() -> Result<(), String> {
.map_err(|e| format!("Error getting summoner: {}", e))? .map_err(|e| format!("Error getting summoner: {}", e))?
.ok_or_else(|| "Failed to find summoner".to_owned())?; .ok_or_else(|| "Failed to find summoner".to_owned())?;
let p = RIOT_API let p = riot_api()
.champion_mastery_v4() .champion_mastery_v4()
.get_champion_mastery_score_by_puuid(ROUTE, &sum.puuid); .get_champion_mastery_score_by_puuid(ROUTE, &sum.puuid);
let s = p let s = p
@ -32,7 +32,7 @@ async fn championmastery_getscore_ma5tery() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn championmastery_getall_ma5tery() -> Result<(), String> { async fn championmastery_getall_ma5tery() -> Result<(), String> {
let sum = RIOT_API let sum = riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_name(ROUTE, "ma5tery"); .get_by_summoner_name(ROUTE, "ma5tery");
let sum = sum let sum = sum
@ -40,7 +40,7 @@ async fn championmastery_getall_ma5tery() -> Result<(), String> {
.map_err(|e| format!("Error getting summoner: {}", e))? .map_err(|e| format!("Error getting summoner: {}", e))?
.ok_or_else(|| "Failed to find summoner".to_owned())?; .ok_or_else(|| "Failed to find summoner".to_owned())?;
let p = RIOT_API let p = riot_api()
.champion_mastery_v4() .champion_mastery_v4()
.get_all_champion_masteries_by_puuid(ROUTE, &sum.puuid); .get_all_champion_masteries_by_puuid(ROUTE, &sum.puuid);
let s = p let s = p
@ -53,7 +53,7 @@ async fn championmastery_getall_ma5tery() -> Result<(), String> {
/// https://github.com/RiotGames/developer-relations/issues/602 /// https://github.com/RiotGames/developer-relations/issues/602
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn spectator_combo() -> Result<(), String> { async fn spectator_combo() -> Result<(), String> {
let featured_p = RIOT_API.spectator_v4().get_featured_games(ROUTE); let featured_p = riot_api().spectator_v4().get_featured_games(ROUTE);
let featured = featured_p.await.map_err(|e| e.to_string())?; let featured = featured_p.await.map_err(|e| e.to_string())?;
if featured.game_list.is_empty() { if featured.game_list.is_empty() {
@ -62,7 +62,7 @@ async fn spectator_combo() -> Result<(), String> {
} }
// let summoner_name = &featured.game_list[0].participants[0].summoner_name; // let summoner_name = &featured.game_list[0].participants[0].summoner_name;
// let summoner_p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, summoner_name); // let summoner_p = riot_api().summoner_v4().get_by_summoner_name(ROUTE, summoner_name);
// let summoner = summoner_p.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to find summoner".to_owned())?; // let summoner = summoner_p.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to find summoner".to_owned())?;
let featured_game = &featured.game_list[0]; let featured_game = &featured.game_list[0];
@ -74,7 +74,7 @@ async fn spectator_combo() -> Result<(), String> {
) )
})?; })?;
let livegame_p = RIOT_API let livegame_p = riot_api()
.spectator_v4() .spectator_v4()
.get_current_game_info_by_summoner(ROUTE, &summoner_id); .get_current_game_info_by_summoner(ROUTE, &summoner_id);
let livegame_o = livegame_p.await.map_err(|e| e.to_string())?; let livegame_o = livegame_p.await.map_err(|e| e.to_string())?;

View File

@ -22,14 +22,14 @@ async fn tftmatchv1_get_list() -> Result<(), String> {
// /// Don't have acecess to tft-status-v1. // /// Don't have acecess to tft-status-v1.
// #[tokio_shared_rt::test] // #[tokio_shared_rt::test]
// async fn tftstatusv1_getplatformdata() -> Result<(), String> { // async fn tftstatusv1_getplatformdata() -> Result<(), String> {
// let p = RIOT_API.tft_status_v1().get_platform_data(ROUTE); // let p = riot_api().tft_status_v1().get_platform_data(ROUTE);
// let _s = p.await.map_err(|e| e.to_string())?; // let _s = p.await.map_err(|e| e.to_string())?;
// Ok(()) // Ok(())
// } // }
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn tftleaguev1_gettopratedladder() -> Result<(), String> { async fn tftleaguev1_gettopratedladder() -> Result<(), String> {
let p = RIOT_API let p = riot_api()
.tft_league_v1() .tft_league_v1()
.get_top_rated_ladder(ROUTE, QueueType::RANKED_TFT_TURBO); .get_top_rated_ladder(ROUTE, QueueType::RANKED_TFT_TURBO);
let l = p.await.map_err(|e| e.to_string())?; let l = p.await.map_err(|e| e.to_string())?;
@ -43,7 +43,7 @@ async fn tftleaguev1_gettopratedladder() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn tftmatchv1_getmatch() -> Result<(), String> { async fn tftmatchv1_getmatch() -> Result<(), String> {
let p = RIOT_API let p = riot_api()
.tft_match_v1() .tft_match_v1()
.get_match(ROUTE.to_regional(), "EUW1_6455483163"); .get_match(ROUTE.to_regional(), "EUW1_6455483163");
let _m = p let _m = p
@ -55,7 +55,7 @@ async fn tftmatchv1_getmatch() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn tftsummonerv1_getbyname() -> Result<(), String> { async fn tftsummonerv1_getbyname() -> Result<(), String> {
let p = RIOT_API let p = riot_api()
.tft_summoner_v1() .tft_summoner_v1()
.get_by_summoner_name(ROUTE, "相当猥琐"); .get_by_summoner_name(ROUTE, "相当猥琐");
let _s = p let _s = p
@ -67,7 +67,7 @@ async fn tftsummonerv1_getbyname() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn tftsummonerv1_getbyname_none() -> Result<(), String> { async fn tftsummonerv1_getbyname_none() -> Result<(), String> {
let p = RIOT_API let p = riot_api()
.tft_summoner_v1() .tft_summoner_v1()
.get_by_summoner_name(ROUTE, "this summoner does not exist"); .get_by_summoner_name(ROUTE, "this summoner does not exist");
rassert!(p.await.map_err(|e| e.to_string())?.is_none()); rassert!(p.await.map_err(|e| e.to_string())?.is_none());
@ -77,13 +77,13 @@ async fn tftsummonerv1_getbyname_none() -> Result<(), String> {
/// Get top rated player, get some of their matches. /// Get top rated player, get some of their matches.
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn tft_combo() -> Result<(), String> { async fn tft_combo() -> Result<(), String> {
let top_players = RIOT_API let top_players = riot_api()
.tft_league_v1() .tft_league_v1()
.get_top_rated_ladder(ROUTE, QueueType::RANKED_TFT_TURBO); .get_top_rated_ladder(ROUTE, QueueType::RANKED_TFT_TURBO);
let top_players = top_players.await.map_err(|e| e.to_string())?; let top_players = top_players.await.map_err(|e| e.to_string())?;
rassert!(!top_players.is_empty()); rassert!(!top_players.is_empty());
let top_player_entry = &top_players[0]; let top_player_entry = &top_players[0];
let top_player = RIOT_API let top_player = riot_api()
.tft_summoner_v1() .tft_summoner_v1()
.get_by_summoner_id(ROUTE, &top_player_entry.summoner_id); .get_by_summoner_id(ROUTE, &top_player_entry.summoner_id);
let top_player = top_player.await.map_err(|e| e.to_string())?; let top_player = top_player.await.map_err(|e| e.to_string())?;
@ -91,7 +91,7 @@ async fn tft_combo() -> Result<(), String> {
"Top player is {} with `puuid` {}.", "Top player is {} with `puuid` {}.",
top_player.name, top_player.puuid top_player.name, top_player.puuid
); );
let match_ids = RIOT_API.tft_match_v1().get_match_ids_by_puuid( let match_ids = riot_api().tft_match_v1().get_match_ids_by_puuid(
ROUTE.to_regional(), ROUTE.to_regional(),
&top_player.puuid, &top_player.puuid,
Some(10), Some(10),

View File

@ -21,7 +21,7 @@ async_tests!{
let leagues = (1..10) let leagues = (1..10)
.map(async move |i| { .map(async move |i| {
let leaguelist = RIOT_API.league_v4().get_league_entries(REGION, let leaguelist = riot_api().league_v4().get_league_entries(REGION,
QueueType::RANKED_SOLO_5x5, Tier::GOLD, Division::III, Some(i)); QueueType::RANKED_SOLO_5x5, Tier::GOLD, Division::III, Some(i));
let leaguelist = leaguelist.await let leaguelist = leaguelist.await
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
@ -32,7 +32,7 @@ async_tests!{
let summoners = leaguelist let summoners = leaguelist
.iter() .iter()
.map(async move |leagueentry| { .map(async move |leagueentry| {
let summonerfuture = RIOT_API.summoner_v4().get_by_summoner_id( let summonerfuture = riot_api().summoner_v4().get_by_summoner_id(
REGION, &leagueentry.summoner_id); REGION, &leagueentry.summoner_id);
summonerfuture.await summonerfuture.await
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?

View File

@ -1,6 +1,6 @@
mod testutils; mod testutils;
use riven::consts::*; use riven::consts::*;
use testutils::RIOT_API; use testutils::riot_api;
const ROUTE: PlatformRoute = PlatformRoute::LA1; const ROUTE: PlatformRoute = PlatformRoute::LA1;
@ -12,7 +12,7 @@ const CHALLENGE_ID_ARAM_1K_DPM: i64 = 101101;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn lol_challenges_v1_leaderboards_playerdata() -> Result<(), String> { async fn lol_challenges_v1_leaderboards_playerdata() -> Result<(), String> {
let challenge_id = CHALLENGE_ID_ARAM_1K_DPM; let challenge_id = CHALLENGE_ID_ARAM_1K_DPM;
let leaderboard = RIOT_API let leaderboard = riot_api()
.lol_challenges_v1() .lol_challenges_v1()
.get_challenge_leaderboards(ROUTE, challenge_id, Tier::GRANDMASTER, None) .get_challenge_leaderboards(ROUTE, challenge_id, Tier::GRANDMASTER, None)
.await .await
@ -38,7 +38,7 @@ async fn lol_challenges_v1_leaderboards_playerdata() -> Result<(), String> {
// Spot check 10% for `player-data`. // Spot check 10% for `player-data`.
for entry in leaderboard.iter().step_by(10) { for entry in leaderboard.iter().step_by(10) {
let _player_data = RIOT_API let _player_data = riot_api()
.lol_challenges_v1() .lol_challenges_v1()
.get_player_data(ROUTE, &entry.puuid) .get_player_data(ROUTE, &entry.puuid)
.await .await
@ -52,7 +52,7 @@ async fn lol_challenges_v1_leaderboards_playerdata() -> Result<(), String> {
/// /lol/challenges/v1/challenges/{challengeId}/config /// /lol/challenges/v1/challenges/{challengeId}/config
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn lol_challenges_v1_check_configs() -> Result<(), String> { async fn lol_challenges_v1_check_configs() -> Result<(), String> {
let challenges = RIOT_API let challenges = riot_api()
.lol_challenges_v1() .lol_challenges_v1()
.get_all_challenge_configs(ROUTE) .get_all_challenge_configs(ROUTE)
.await .await
@ -66,7 +66,7 @@ async fn lol_challenges_v1_check_configs() -> Result<(), String> {
// Spot-check 10% of the challenge IDs. // Spot-check 10% of the challenge IDs.
for challenge in challenges.iter().step_by(10) { for challenge in challenges.iter().step_by(10) {
RIOT_API riot_api()
.lol_challenges_v1() .lol_challenges_v1()
.get_challenge_configs(ROUTE, challenge.id) .get_challenge_configs(ROUTE, challenge.id)
.await .await
@ -87,7 +87,7 @@ async fn lol_challenges_v1_check_configs() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn lol_challenges_v1_check_percentiles() -> Result<(), String> { async fn lol_challenges_v1_check_percentiles() -> Result<(), String> {
// Check all percentiles. // Check all percentiles.
let percentiles = RIOT_API let percentiles = riot_api()
.lol_challenges_v1() .lol_challenges_v1()
.get_all_challenge_percentiles(ROUTE) .get_all_challenge_percentiles(ROUTE)
.await .await
@ -96,7 +96,7 @@ async fn lol_challenges_v1_check_percentiles() -> Result<(), String> {
// Spot-check 10% of the challenge IDs. // Spot-check 10% of the challenge IDs.
for &challenge_id in percentiles.keys().step_by(10) { for &challenge_id in percentiles.keys().step_by(10) {
RIOT_API riot_api()
.lol_challenges_v1() .lol_challenges_v1()
.get_challenge_percentiles(ROUTE, challenge_id) .get_challenge_percentiles(ROUTE, challenge_id)
.await .await

View File

@ -21,10 +21,10 @@ const ROUTE: PlatformRoute = PlatformRoute::NA1;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn summoner_double() -> Result<(), String> { async fn summoner_double() -> Result<(), String> {
let l1p = RIOT_API let l1p = riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_name(ROUTE, "lug nuts k"); .get_by_summoner_name(ROUTE, "lug nuts k");
let l2p = RIOT_API let l2p = riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_name(ROUTE, "lugnuts k"); .get_by_summoner_name(ROUTE, "lugnuts k");
let l1 = l1p let l1 = l1p
@ -41,7 +41,7 @@ async fn summoner_double() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn champion_getrotation() -> Result<(), String> { async fn champion_getrotation() -> Result<(), String> {
let p = RIOT_API.champion_v3().get_champion_info(ROUTE); let p = riot_api().champion_v3().get_champion_info(ROUTE);
let d = p.await.map_err(|e| e.to_string())?; let d = p.await.map_err(|e| e.to_string())?;
let new_len = d.free_champion_ids_for_new_players.len(); let new_len = d.free_champion_ids_for_new_players.len();
let free_len = d.free_champion_ids.len(); let free_len = d.free_champion_ids.len();
@ -54,7 +54,7 @@ async fn champion_getrotation() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn leagueexp_get() -> Result<(), String> { async fn leagueexp_get() -> Result<(), String> {
let p = RIOT_API.league_exp_v4().get_league_entries( let p = riot_api().league_exp_v4().get_league_entries(
ROUTE, ROUTE,
QueueType::RANKED_SOLO_5x5, QueueType::RANKED_SOLO_5x5,
Tier::CHALLENGER, Tier::CHALLENGER,
@ -70,14 +70,14 @@ async fn leagueexp_get() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn champion_mastery_v4() -> Result<(), String> { async fn champion_mastery_v4() -> Result<(), String> {
let summoner = RIOT_API let summoner = riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_name(ROUTE, "LugnutsK"); .get_by_summoner_name(ROUTE, "LugnutsK");
let summoner = summoner let summoner = summoner
.await .await
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
.ok_or_else(|| "'LugnutsK' not found!".to_owned())?; .ok_or_else(|| "'LugnutsK' not found!".to_owned())?;
let masteries = RIOT_API let masteries = riot_api()
.champion_mastery_v4() .champion_mastery_v4()
.get_all_champion_masteries_by_puuid(ROUTE, &summoner.puuid); .get_all_champion_masteries_by_puuid(ROUTE, &summoner.puuid);
let masteries = masteries.await.map_err(|e| e.to_string())?; let masteries = masteries.await.map_err(|e| e.to_string())?;
@ -89,7 +89,7 @@ async fn champion_mastery_v4() -> Result<(), String> {
// /// LOR // /// LOR
// #[tokio_shared_rt::test] // #[tokio_shared_rt::test]
// async fn async fn lor_ranked_get_leaderboards() -> Result<(), String> { // async fn async fn lor_ranked_get_leaderboards() -> Result<(), String> {
// let future = RIOT_API.lor_ranked_v1().get_leaderboards(Region::AMERICAS); // let future = riot_api().lor_ranked_v1().get_leaderboards(Region::AMERICAS);
// let _leaderboard = future.await.map_err(|e| e.to_string())?; // let _leaderboard = future.await.map_err(|e| e.to_string())?;
// Ok(()) // Ok(())
// } // }
@ -98,10 +98,10 @@ async fn champion_mastery_v4() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn clash_get_tournaments() -> Result<(), String> { async fn clash_get_tournaments() -> Result<(), String> {
let p = RIOT_API.clash_v1().get_tournaments(ROUTE); let p = riot_api().clash_v1().get_tournaments(ROUTE);
let tours = p.await.map_err(|e| e.to_string())?; let tours = p.await.map_err(|e| e.to_string())?;
if let Some(tour0) = tours.first() { if let Some(tour0) = tours.first() {
let p = RIOT_API.clash_v1().get_tournament_by_id(ROUTE, tour0.id); let p = riot_api().clash_v1().get_tournament_by_id(ROUTE, tour0.id);
let tour1 = p.await.map_err(|e| e.to_string())?; let tour1 = p.await.map_err(|e| e.to_string())?;
assert_eq!(Some(tour0.id), tour1.map(|t| t.id)); assert_eq!(Some(tour0.id), tour1.map(|t| t.id));
} }
@ -110,7 +110,7 @@ async fn clash_get_tournaments() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn clash_get_team_by_id_invalid() -> Result<(), String> { async fn clash_get_team_by_id_invalid() -> Result<(), String> {
let p = RIOT_API let p = riot_api()
.clash_v1() .clash_v1()
.get_team_by_id(ROUTE, "00000000-0000-0000-0000-000000000000"); .get_team_by_id(ROUTE, "00000000-0000-0000-0000-000000000000");
let team = p.await.map_err(|e| e.to_string())?; let team = p.await.map_err(|e| e.to_string())?;
@ -120,7 +120,7 @@ async fn clash_get_team_by_id_invalid() -> Result<(), String> {
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn status() -> Result<(), String> { async fn status() -> Result<(), String> {
let p = RIOT_API.lol_status_v4().get_platform_data(ROUTE); let p = riot_api().lol_status_v4().get_platform_data(ROUTE);
let status = p.await.map_err(|e| e.to_string())?; let status = p.await.map_err(|e| e.to_string())?;
println!("{:?}", status); println!("{:?}", status);
Ok(()) Ok(())

View File

@ -6,7 +6,7 @@ const ROUTE: PlatformRoute = PlatformRoute::PH2;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn status() -> Result<(), String> { async fn status() -> Result<(), String> {
let p = RIOT_API.lol_status_v4().get_platform_data(ROUTE); let p = riot_api().lol_status_v4().get_platform_data(ROUTE);
let status = p.await.map_err(|e| e.to_string())?; let status = p.await.map_err(|e| e.to_string())?;
println!("{:?}", status); println!("{:?}", status);
Ok(()) Ok(())

View File

@ -6,7 +6,7 @@ const ROUTE: PlatformRoute = PlatformRoute::SG2;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn status() -> Result<(), String> { async fn status() -> Result<(), String> {
let p = RIOT_API.lol_status_v4().get_platform_data(ROUTE); let p = riot_api().lol_status_v4().get_platform_data(ROUTE);
let status = p.await.map_err(|e| e.to_string())?; let status = p.await.map_err(|e| e.to_string())?;
println!("{:?}", status); println!("{:?}", status);
Ok(()) Ok(())

View File

@ -6,7 +6,7 @@ const ROUTE: PlatformRoute = PlatformRoute::TH2;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn status() -> Result<(), String> { async fn status() -> Result<(), String> {
let p = RIOT_API.lol_status_v4().get_platform_data(ROUTE); let p = riot_api().lol_status_v4().get_platform_data(ROUTE);
let status = p.await.map_err(|e| e.to_string())?; let status = p.await.map_err(|e| e.to_string())?;
println!("{:?}", status); println!("{:?}", status);
Ok(()) Ok(())

View File

@ -1,13 +1,13 @@
mod testutils; mod testutils;
use riven::consts::*; use riven::consts::*;
use riven::models::summoner_v4::Summoner; use riven::models::summoner_v4::Summoner;
use testutils::RIOT_API; use testutils::riot_api;
const ROUTE: PlatformRoute = PlatformRoute::TR1; const ROUTE: PlatformRoute = PlatformRoute::TR1;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn league_summoner_bulk_test() -> Result<(), String> { async fn league_summoner_bulk_test() -> Result<(), String> {
let p = RIOT_API let p = riot_api()
.league_v4() .league_v4()
.get_challenger_league(ROUTE, QueueType::RANKED_SOLO_5x5); .get_challenger_league(ROUTE, QueueType::RANKED_SOLO_5x5);
// let p = future_start(p); // let p = future_start(p);
@ -20,7 +20,7 @@ async fn league_summoner_bulk_test() -> Result<(), String> {
.iter() .iter()
.take(50) .take(50)
.map(|entry| { .map(|entry| {
RIOT_API riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_id(ROUTE, &entry.summoner_id) .get_by_summoner_id(ROUTE, &entry.summoner_id)
}) })

View File

@ -1,12 +1,14 @@
mod testutils; mod testutils;
use riven::consts::*; use riven::consts::*;
use testutils::RIOT_API; use testutils::riot_api;
const ROUTE: ValPlatformRoute = ValPlatformRoute::LATAM; const ROUTE: ValPlatformRoute = ValPlatformRoute::LATAM;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn val_content_ranked_test() -> Result<(), String> { async fn val_content_ranked_test() -> Result<(), String> {
let p = RIOT_API.val_content_v1().get_content(ROUTE, Some("zh-CN")); let p = riot_api()
.val_content_v1()
.get_content(ROUTE, Some("zh-CN"));
let contents = p let contents = p
.await .await
.map_err(|e| format!("Failed to get content: {}", e))?; .map_err(|e| format!("Failed to get content: {}", e))?;
@ -24,7 +26,7 @@ async fn val_content_ranked_test() -> Result<(), String> {
}) })
.ok_or(format!("No active acts of {} found.", contents.acts.len()))?; .ok_or(format!("No active acts of {} found.", contents.acts.len()))?;
let p = RIOT_API let p = riot_api()
.val_ranked_v1() .val_ranked_v1()
.get_leaderboard(ROUTE, &act.id, None, None); .get_leaderboard(ROUTE, &act.id, None, None);
let leaderboard = p.await.map_err(|e| e.to_string())?.ok_or(format!( let leaderboard = p.await.map_err(|e| e.to_string())?.ok_or(format!(

View File

@ -6,7 +6,7 @@ const ROUTE: PlatformRoute = PlatformRoute::VN2;
#[tokio_shared_rt::test] #[tokio_shared_rt::test]
async fn status() -> Result<(), String> { async fn status() -> Result<(), String> {
let p = RIOT_API.lol_status_v4().get_platform_data(ROUTE); let p = riot_api().lol_status_v4().get_platform_data(ROUTE);
let status = p.await.map_err(|e| e.to_string())?; let status = p.await.map_err(|e| e.to_string())?;
println!("{:?}", status); println!("{:?}", status);
Ok(()) Ok(())

View File

@ -1,8 +1,8 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::future::Future; use std::future::Future;
use std::sync::OnceLock;
use lazy_static::lazy_static;
use riven::consts::{PlatformRoute, QueueType, RegionalRoute}; use riven::consts::{PlatformRoute, QueueType, RegionalRoute};
use riven::{RiotApi, RiotApiConfig}; use riven::{RiotApi, RiotApiConfig};
@ -36,23 +36,30 @@ macro_rules! rassert_ne {
}; };
} }
lazy_static! { static RIOT_API: OnceLock<RiotApi> = OnceLock::new();
pub static ref RIOT_API: RiotApi = { pub fn riot_api() -> &'static RiotApi {
RIOT_API.get_or_init(|| {
// Initialize logger here, as a convenient trigger spot. // Initialize logger here, as a convenient trigger spot.
env_logger::init(); env_logger::init();
let api_key = std::env::var("RGAPI_KEY") let api_key = std::env::var("RGAPI_KEY")
.ok() .ok()
.or_else(|| std::fs::read_to_string("apikey.txt").ok()) .or_else(|| {
use std::iter::FromIterator;
let path =
std::path::PathBuf::from_iter([env!("CARGO_MANIFEST_DIR"), "../apikey.txt"]);
std::fs::read_to_string(path).ok()
})
.expect("Failed to find RGAPI_KEY env var or apikey.txt."); .expect("Failed to find RGAPI_KEY env var or apikey.txt.");
RiotApi::new(RiotApiConfig::with_key(api_key.trim()).preconfig_burst()) RiotApi::new(RiotApiConfig::with_key(api_key.trim()).preconfig_burst())
}; })
} }
pub async fn league_v4_match_v5_latest_combo(route: PlatformRoute) -> Result<(), String> { pub async fn league_v4_match_v5_latest_combo(route: PlatformRoute) -> Result<(), String> {
const NUM_MATCHES: usize = 10; const NUM_MATCHES: usize = 10;
let challenger_future = RIOT_API let challenger_future = riot_api()
.league_v4() .league_v4()
.get_challenger_league(route, QueueType::RANKED_SOLO_5x5); .get_challenger_league(route, QueueType::RANKED_SOLO_5x5);
let challenger_league = challenger_future let challenger_league = challenger_future
@ -77,14 +84,14 @@ pub async fn league_v4_match_v5_latest_combo(route: PlatformRoute) -> Result<(),
.iter() .iter()
.take(5) .take(5)
.map(|entry| async move { .map(|entry| async move {
let summoner_future = RIOT_API let summoner_future = riot_api()
.summoner_v4() .summoner_v4()
.get_by_summoner_id(route, &entry.summoner_id); .get_by_summoner_id(route, &entry.summoner_id);
let summoner_info = summoner_future let summoner_info = summoner_future
.await .await
.map_err(|e| format!("Failed to find summoner info: {}", e))?; .map_err(|e| format!("Failed to find summoner info: {}", e))?;
let match_ids_future = RIOT_API.match_v5().get_match_ids_by_puuid( let match_ids_future = riot_api().match_v5().get_match_ids_by_puuid(
route.to_regional(), route.to_regional(),
&summoner_info.puuid, &summoner_info.puuid,
Some(5), Some(5),
@ -119,7 +126,7 @@ pub async fn tft_match_v1_get(
) -> Result<(), String> { ) -> Result<(), String> {
let futures = matches.into_iter().map(|matche| async move { let futures = matches.into_iter().map(|matche| async move {
let matche = matche.as_ref(); let matche = matche.as_ref();
let p = RIOT_API.tft_match_v1().get_match(route, matche); let p = riot_api().tft_match_v1().get_match(route, matche);
let m = p let m = p
.await .await
.map_err(|e| format!("Failed to get match {}: {:?}", matche, e))? .map_err(|e| format!("Failed to get match {}: {:?}", matche, e))?
@ -158,7 +165,7 @@ pub async fn match_v5_get(
) -> Result<(), String> { ) -> Result<(), String> {
let futures = matches.into_iter().map(|matche| async move { let futures = matches.into_iter().map(|matche| async move {
let matche = matche.as_ref(); let matche = matche.as_ref();
let p = RIOT_API.match_v5().get_match(route, matche); let p = riot_api().match_v5().get_match(route, matche);
let m = p let m = p
.await .await
.map_err(|e| format!("Failed to get match {}: {:?}", matche, e))? .map_err(|e| format!("Failed to get match {}: {:?}", matche, e))?
@ -200,7 +207,7 @@ pub async fn match_v5_get_timeline(
) -> Result<(), String> { ) -> Result<(), String> {
let futures = matches.into_iter().map(|matche| async move { let futures = matches.into_iter().map(|matche| async move {
let matche = matche.as_ref(); let matche = matche.as_ref();
let p = RIOT_API.match_v5().get_timeline(route, matche); let p = riot_api().match_v5().get_timeline(route, matche);
let m = p let m = p
.await .await
.map_err(|e| format!("Failed to get match {}: {:?}", matche, e))? .map_err(|e| format!("Failed to get match {}: {:?}", matche, e))?