diff --git a/src/cgg_data_source.rs b/src/cgg_data_source.rs index 2fe8787..7f43d39 100644 --- a/src/cgg_data_source.rs +++ b/src/cgg_data_source.rs @@ -95,6 +95,8 @@ fn extract_json(pattern: &str, page: &str) -> String { } impl DataSource for CGGDataSource { + fn init(&self, _client: &ureq::Agent) {} + fn get_alias(&self) -> &str { "CGG" } @@ -103,10 +105,10 @@ impl DataSource for CGGDataSource { 0 } - fn get_champs_with_positions_and_patch( + fn get_champs_with_positions( &self, client: &ureq::Agent, - ) -> (IndexMap>, String) { + ) -> IndexMap> { let req = client.get("https://champion.gg").call().unwrap(); let page = &req.into_string().unwrap(); @@ -122,11 +124,10 @@ impl DataSource for CGGDataSource { .unwrap() .insert(entry.0.to_owned(), entry.1.to_owned()); } - let patch = datas.lol.champions_report[0].patch.to_owned(); - let mut champions: IndexMap> = IndexMap::new(); + let mut champions: IndexMap> = IndexMap::new(); for champ in &datas.lol.champions_report { - let id = champ.champion_id.to_string(); + let id = champ.champion_id; if champions.contains_key(&id) { let mut roles = champions.get(&id).unwrap().to_owned(); roles.push(champ.role.to_owned()); @@ -140,7 +141,7 @@ impl DataSource for CGGDataSource { CHAMPIONS_REPORT.lock().unwrap().push(report); } - (champions, patch) + champions } fn get_champ_data_with_win_pourcentage( @@ -206,6 +207,7 @@ impl DataSource for CGGDataSource { win_rate: stat.win_rate.unwrap(), games: stat.games.unwrap(), kda: stat.kda.unwrap(), + patch: champ.patch.to_owned() }, )); } diff --git a/src/data_source.rs b/src/data_source.rs index 93963a5..48a8615 100644 --- a/src/data_source.rs +++ b/src/data_source.rs @@ -35,17 +35,20 @@ pub struct Stat { pub win_rate: f64, pub games: u32, pub kda: f64, + pub patch: String } pub trait DataSource { + fn init(&self, client: &ureq::Agent); + fn get_alias(&self) -> &str; fn get_timeout(&self) -> u64; - fn get_champs_with_positions_and_patch( + fn get_champs_with_positions( &self, client: &ureq::Agent, - ) -> (IndexMap>, String); + ) -> IndexMap>; fn get_champ_data_with_win_pourcentage( &self, @@ -58,7 +61,6 @@ pub trait DataSource { &self, champ: &ChampInfo, positions: &[String], - ver: &str, path: &PathBuf, client: &ureq::Agent, ) { @@ -98,7 +100,7 @@ pub trait DataSource { "{} {} {} - {:.2}% wins - {} games - {:.2} kda", self.get_alias(), build.0, - ver, + build.2.patch, build.2.win_rate, build.2.games, build.2.kda diff --git a/src/kb_data_source.rs b/src/kb_data_source.rs index 98ec863..4cfe5bc 100644 --- a/src/kb_data_source.rs +++ b/src/kb_data_source.rs @@ -3,13 +3,12 @@ use crate::data_source::{Build, DataSource, Item, Stat}; use crate::time::Duration; use crate::ChampInfo; use indexmap::IndexMap; +use lazy_static::lazy_static; use serde_derive::Deserialize; use serde_json::{json, Value}; +use std::sync::Mutex; -pub struct KBDataSource { - token: Option, - internal_classname_mapping: IndexMap, -} +pub struct KBDataSource; #[derive(Deserialize, Debug)] struct ChampionResponse { @@ -74,8 +73,9 @@ struct KBBuild { #[serde(rename = "skillOrder")] skill_order: String, wins: f64, - games: f64, + games: u32, summoner: Summoner, + patch: Patch, } #[derive(Deserialize, Debug)] @@ -90,17 +90,11 @@ struct Summoner { name: String, } -impl KBDataSource { - pub fn new(client: &ureq::Agent) -> KBDataSource { - let mut datasource = KBDataSource { - token: None, - internal_classname_mapping: IndexMap::new(), - }; - datasource.token = datasource.get_auth_token(client); - datasource.internal_classname_mapping = datasource.get_classname_mapping(client); - datasource - } +lazy_static! { + static ref TOKEN: Mutex> = Mutex::new(None); +} +impl KBDataSource { // It will be better to use Result... fn get_auth_token(&self, client: &ureq::Agent) -> Option { let mut bundle = match client.get("https://koreanbuilds.net/bundle.js").call() { @@ -126,33 +120,22 @@ impl KBDataSource { } } - fn get_classname_mapping(&self, client: &ureq::Agent) -> IndexMap { - let mut mapping = IndexMap::new(); - if let Some(data) = self.get_champion_response(client) { - for champ in data.champions { - mapping.insert(champ.classname, champ.name); - } - }; - mapping - } - fn get_champion_response(&self, client: &ureq::Agent) -> Option { - let token = match self.token.clone() { - Some(t) => t, - None => String::new(), - }; - match client - .get("https://api.koreanbuilds.net/champions?patchid=-1") - .set("Accept", "application/json") - .set("Authorization", token.as_str()) - .call() - { - Ok(resp) => match resp.into_json() { - Ok(val) => val, + if let Some(token) = TOKEN.lock().unwrap().as_ref() { + return match client + .get("https://api.koreanbuilds.net/champions?patchid=-1") + .set("Accept", "application/json") + .set("Authorization", token.as_str()) + .call() + { + Ok(resp) => match resp.into_json() { + Ok(val) => val, + Err(_) => None, + }, Err(_) => None, - }, - Err(_) => None, + }; } + None } fn get_positions(position: Option) -> Vec { @@ -285,15 +268,22 @@ impl KBDataSource { build.position.to_owned().to_uppercase(), blocks, Stat { - win_rate: (build.wins / build.games) * 100., - games: build.games as u32, + win_rate: (build.wins / build.games as f64) * 100., + games: build.games, kda: 0.0, + patch: build.patch.patch_version.to_owned(), }, ) } } impl DataSource for KBDataSource { + fn init(&self, client: &ureq::Agent) { + if let Some(t) = self.get_auth_token(client) { + TOKEN.lock().unwrap().replace(t); + } + } + fn get_alias(&self) -> &str { "KB" } @@ -302,25 +292,18 @@ impl DataSource for KBDataSource { 300 } - fn get_champs_with_positions_and_patch( - &self, - client: &ureq::Agent, - ) -> (IndexMap>, String) { + fn get_champs_with_positions(&self, client: &ureq::Agent) -> IndexMap> { let mut champions = IndexMap::new(); let data: ChampionResponse = match self.get_champion_response(client) { Some(val) => val, None => { - return (champions, String::new()); + return champions; } }; - let patch = match data.patches.get(0) { - Some(p) => p.patch_version.clone(), - None => return (champions, String::new()), - }; for champ in data.champions { - champions.insert(champ.classname, KBDataSource::get_positions(champ.builds)); + champions.insert(champ.id, KBDataSource::get_positions(champ.builds)); } - (champions, patch) + champions } fn get_champ_data_with_win_pourcentage( @@ -330,45 +313,42 @@ impl DataSource for KBDataSource { client: &ureq::Agent, ) -> Vec<(String, Vec, Stat)> { let mut champ_data = vec![]; - if let Some(token) = self.token.clone() { - if let Some(map_id) = self.internal_classname_mapping.get(&champ.id) { - let data: BuildResponse = match client - .get(&format!( - "https://api.koreanbuilds.net/builds?chmpname={}&patchid=-2&position=COMPOSITE", - map_id - )) - .set("Accept", "application/json") - .set("Authorization", token.as_str()) - .call() - { - Ok(resp) => match resp.into_json() { - Ok(val) => val, - Err(_) => { - return vec![]; - } - }, + if let Some(token) = TOKEN.lock().unwrap().as_ref() { + let data: BuildResponse = match client + .get(&format!( + "https://api.koreanbuilds.net/builds?chmpname={}&patchid=-2&position=COMPOSITE", + champ.id + )) + .set("Accept", "application/json") + .set("Authorization", token.as_str()) + .call() + { + Ok(resp) => match resp.into_json() { + Ok(val) => val, Err(_) => { return vec![]; } - }; + }, + Err(_) => { + return vec![]; + } + }; - for pos in position { - let mut build: Option<&KBBuild> = None; + for pos in position { + let mut build: Option<&KBBuild> = None; - for b in &data.builds2 { - if b.position.to_uppercase() == *pos { - build = Some(b); - break; - } - } - - - if let Some(b) = build { - champ_data.push(self.get_build(&b)); + for b in &data.builds2 { + if b.position.to_uppercase() == *pos { + build = Some(b); + break; } } + + if let Some(b) = build { + champ_data.push(self.get_build(&b)); + } } - }; + } champ_data } } @@ -379,10 +359,7 @@ mod tests { #[test] fn test_get_auth_token() { - let datasource = KBDataSource { - token: None, - internal_classname_mapping: IndexMap::new(), - }; + let datasource = KBDataSource; let client = ureq::AgentBuilder::new() .timeout(Duration::from_secs(10)) .build(); @@ -397,10 +374,11 @@ mod tests { let client = ureq::AgentBuilder::new() .timeout(Duration::from_secs(10)) .build(); - let datasource = KBDataSource::new(&client); - let champs_with_positions_and_patch = - datasource.get_champs_with_positions_and_patch(&client); - assert!(champs_with_positions_and_patch.0.len() > 0); + let datasource = KBDataSource; + datasource.init(&client); + let champs_with_positions = + datasource.get_champs_with_positions(&client); + assert!(champs_with_positions.len() > 0); } #[test] @@ -408,15 +386,16 @@ mod tests { let client = ureq::AgentBuilder::new() .timeout(Duration::from_secs(10)) .build(); - let datasource = KBDataSource::new(&client); + let datasource = KBDataSource; + datasource.init(&client); let champ = ChampInfo { - id: String::from("Aatrox"), - name: String::from("Aatrox"), + id: String::from("Annie"), + name: String::from("Annie"), key: String::from("1"), }; let result = datasource.get_champ_data_with_win_pourcentage( &champ, - &vec!["TOP".to_string()], + &vec!["MID".to_string()], &client, ); assert!(!result.is_empty()); diff --git a/src/main.rs b/src/main.rs index 05bbf2c..e5f82a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use indexmap::IndexMap; -use lazy_static::lazy_static; #[cfg(target_os = "windows")] use log::debug; use log::{error, info, LevelFilter}; @@ -75,62 +74,53 @@ fn main() -> Result<(), Box> { .init()?; info!("CGG Item Sets"); - lazy_static! { - static ref LOL_CHAMPS_DIR: PathBuf = match lol_champ_dir() { - Ok(x) => x, - Err(_e) => PathBuf::from(DEFAULT_LOL_CHAMPS_DIR), - }; - static ref CLIENT: ureq::Agent = ureq::AgentBuilder::new() - .user_agent(USER_AGENT_VALUE) - .timeout(Duration::from_secs(10)) - .build(); - static ref REALM: Realm = CLIENT - .get("https://ddragon.leagueoflegends.com/realms/euw.json") - .call() - .unwrap() - .into_json() - .unwrap(); - static ref CHAMPION: Champion = CLIENT - .get(&format!( - "https://ddragon.leagueoflegends.com/cdn/{}/data/en_US/champion.json", - REALM.v - )) - .call() - .unwrap() - .into_json() - .unwrap(); - static ref DATA_SOURCES: Vec> = vec![ - Box::new(PBDataSource), - Box::new(CGGDataSource), - Box::new(KBDataSource::new(&CLIENT)), - ]; - } + let lol_champs_dir: PathBuf = match lol_champ_dir() { + Ok(x) => x, + Err(_e) => PathBuf::from(DEFAULT_LOL_CHAMPS_DIR), + }; + info!("LoL Champs Folder: {}", lol_champs_dir.to_str().unwrap()); - info!("LoL Champs Folder: {}", LOL_CHAMPS_DIR.to_str().unwrap()); - info!("LoL version: {}", REALM.v); - info!("LoL numbers of champs: {}", CHAMPION.data.len()); + let client: ureq::Agent = ureq::AgentBuilder::new() + .user_agent(USER_AGENT_VALUE) + .timeout(Duration::from_secs(10)) + .build(); - let mut threads = vec![]; - for data_source in DATA_SOURCES.iter() { - threads.push(thread::spawn(move || { - let init = Instant::now(); - execute_data_source(&data_source, &CLIENT, &CHAMPION, &LOL_CHAMPS_DIR); - info!( - "{}: done in {} ms", - data_source.get_alias(), - init.elapsed().as_millis() - ); - })); - } - for child in threads { - let _ = child.join(); - } + let realm: Realm = client + .get("https://ddragon.leagueoflegends.com/realms/euw.json") + .call()? + .into_json()?; + info!("LoL version: {}", realm.v); + + let champion: Champion = client + .get(&format!( + "https://ddragon.leagueoflegends.com/cdn/{}/data/en_US/champion.json", + realm.v + )) + .call()? + .into_json()?; + info!("LoL numbers of champs: {}", champion.data.len()); + + static DATA_SOURCES: [&'static (dyn DataSource + Sync + Send); 3] = [ + &PBDataSource, + &CGGDataSource, + &KBDataSource, + ]; + DATA_SOURCES.par_iter().for_each(|data_source| { + + let init = Instant::now(); + execute_data_source(*data_source, &client, &champion, &lol_champs_dir); + info!( + "{}: done in {} ms", + data_source.get_alias(), + init.elapsed().as_millis() + ); + }); Ok(()) } -fn get_champ_from_key(champs: &Champion, key: &str) -> Option { +fn get_champ_from_key(champs: &Champion, key: u32) -> Option { for champ in champs.data.values() { - if key == champ.key { + if key.to_string() == champ.key { return Some(champ.id.to_owned()); } } @@ -138,14 +128,15 @@ fn get_champ_from_key(champs: &Champion, key: &str) -> Option { } fn execute_data_source( - data_source: &Box, + data_source: &(dyn DataSource + Sync + Send), client: &ureq::Agent, champion: &Champion, lol_champs_dir: &PathBuf, ) { - let (champs, patch) = data_source.get_champs_with_positions_and_patch(&client); + data_source.init(client); + + let champs = data_source.get_champs_with_positions(&client); - info!("{} version: {}", data_source.get_alias(), patch); info!( "{} numbers of champs: {}", data_source.get_alias(), @@ -159,8 +150,7 @@ fn execute_data_source( client, champion, lol_champs_dir, - &patch, - id, + *id, positions, ); }); @@ -171,8 +161,7 @@ fn execute_data_source( client, champion, lol_champs_dir, - &patch, - id, + *id, positions, ); thread::sleep(Duration::from_millis(data_source.get_timeout())); @@ -181,32 +170,25 @@ fn execute_data_source( } fn get_and_write_item_set( - data_source: &Box, + data_source: &(dyn DataSource + Sync + Send), client: &ureq::Agent, champion: &Champion, lol_champs_dir: &PathBuf, - patch: &str, - id: &str, + id: u32, positions: &[String], ) { - let mut champ_id: String = id.to_owned(); - - if id.parse::().is_ok() { - if let Some(c_id) = get_champ_from_key(&champion, &champ_id) { - champ_id = c_id; - } - } - - if let Some(champ) = champion.data.get(&champ_id) { - if positions.is_empty() { - error!("{}: {} empty positions", data_source.get_alias(), &champ_id); + if let Some(champ_id) = get_champ_from_key(&champion, id) { + if let Some(champ) = champion.data.get(&champ_id) { + if positions.is_empty() { + error!("{}: {} empty positions", data_source.get_alias(), &champ_id); + } else { + let path = lol_champs_dir.join(&champ_id).join("Recommended"); + fs::create_dir_all(&path).unwrap(); + data_source.write_item_set(&champ, &positions, &path, &client); + } } else { - let path = lol_champs_dir.join(&champ_id).join("Recommended"); - fs::create_dir_all(&path).unwrap(); - data_source.write_item_set(&champ, &positions, &patch, &path, &client); + error!("{} not found in LoL champs", &champ_id); } - } else { - error!("{} not found in LoL champs", &champ_id); } } diff --git a/src/pb_data_source.rs b/src/pb_data_source.rs index 615189b..4dfa50b 100644 --- a/src/pb_data_source.rs +++ b/src/pb_data_source.rs @@ -6,6 +6,8 @@ use crate::ChampInfo; pub struct PBDataSource; impl DataSource for PBDataSource { + fn init(&self, _client: &ureq::Agent) {} + fn get_alias(&self) -> &str { "PB" } @@ -14,11 +16,11 @@ impl DataSource for PBDataSource { 0 } - fn get_champs_with_positions_and_patch( + fn get_champs_with_positions( &self, _client: &ureq::Agent, - ) -> (IndexMap>, String) { - (IndexMap::new(), String::new()) + ) -> IndexMap> { + IndexMap::new() } fn get_champ_data_with_win_pourcentage(