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
use crate::constants::LanguageCode;
use crate::dto::ddragon::{AllChampions, ChampionExtended, ChampionFullData};
use crate::error::{ClientError, HyperError};
use crate::types::{Cache, Client};
use crate::utils::{construct_hyper_client, get_latest_ddragon_version, CachedClient};
use log::debug;

use futures::prelude::*;
use hyper::{Body, Request, Uri};

use std::collections::HashMap;

use async_trait::async_trait;

use hyper::header::HeaderValue;
use parking_lot::Mutex;
use serde::de::DeserializeOwned;
use snafu::ResultExt;
use std::fmt::Debug;
use std::sync::Arc;

#[derive(Debug)]
pub struct DDragonClient {
    client: Client,
    cache: Cache,
    version: String,
    base_url: String,
}

impl DDragonClient {
    pub async fn new(language: LanguageCode) -> Result<DDragonClient, ClientError> {
        let client = construct_hyper_client();
        let cache: Cache = Arc::new(Mutex::new(HashMap::new()));
        let version = get_latest_ddragon_version(client.clone()).await?;
        let base_url = format!(
            "https://ddragon.leagueoflegends.com/cdn/{}/data/{}",
            version, &language
        );
        Ok(DDragonClient {
            version,
            base_url,
            client,
            cache,
        })
    }

    pub(crate) async fn new_for_lapi(
        client: Client,
        cache: Cache,
        lang: LanguageCode,
    ) -> Result<DDragonClient, ClientError> {
        let version = get_latest_ddragon_version(client.clone()).await?;
        let base_url = format!(
            "https://ddragon.leagueoflegends.com/cdn/{}/data/{}",
            version, &lang
        );
        Ok(DDragonClient {
            version,
            client,
            cache,
            base_url,
        })
    }

    pub async fn get_champions(&mut self) -> Result<AllChampions, ClientError> {
        let url: Uri = format!("{}/champion.json", &self.base_url).parse().unwrap();
        self.cached_resp(url).await
    }

    pub async fn get_champion(&mut self, name: &str) -> Result<ChampionFullData, ClientError> {
        let name = name.to_owned();
        let url: Uri = format!("{}/champion/{}.json", &self.base_url, &name)
            .parse()
            .unwrap();
        let mut resp = self.cached_resp::<ChampionExtended>(url).await?;
        Ok(resp.data.remove(&name).unwrap())
    }
}

#[async_trait]
impl CachedClient for DDragonClient {
    async fn cached_resp<T: Debug + DeserializeOwned + Send>(
        &self,
        url: Uri,
    ) -> Result<T, ClientError> {
        let maybe_resp: Option<T> = self
            .cache
            .lock()
            .get(&url)
            .map(|res| serde_json::from_str(res).unwrap());

        if let Some(resp) = maybe_resp {
            debug!("Found cached: {:?}", resp);
            Ok(resp)
        } else {
            debug!("Nothing in cache. Fetching...");
            // We got nothing in cache, try fetching from utl
            let req = Request::builder()
                .uri(url.clone())
                .body(Default::default())
                .unwrap();
            let resp = self.client.request(req).await.context(HyperError)?;
            let body = resp.into_body();
            let bytes = hyper::body::to_bytes(body).await.unwrap();
            let string_response = String::from_utf8(bytes.to_vec()).unwrap();
            //.context(FromUTF8Error);
            debug!("Deserializing...");
            let deserialized: T = serde_json::from_str(&string_response).unwrap();
            self.cache.lock().insert(url, string_response);
            Ok(deserialized)
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::constants::LanguageCode;
    use crate::ddragon::DDragonClient;
    use crate::dto::ddragon::{AllChampions, ChampionFullData};
    use std::time::Instant;

    #[test]
    fn creates_proper_instance() {
        smol::run(async {
            let cli = DDragonClient::new(LanguageCode::RUSSIA).await.unwrap();
            println!("{:?}", &cli)
        })
    }

    #[test]
    fn gets_full_champion_data() {
        smol::run(async {
            let mut client = DDragonClient::new(LanguageCode::UNITED_STATES)
                .await
                .unwrap();
            let xayah = client.get_champion("Xayah").await.unwrap();
            assert_eq!(xayah.name, "Xayah");
        })
    }
}