opgg 0.1.0

Rust asynchronous client for accessing the hero statistics API provided by [OP.GG](https://op.gg).
Documentation
use reqwest::Client as HttpClient;
use reqwest::ClientBuilder;
use reqwest::Url;
use serde::de::DeserializeOwned;

use super::ChampionPosition;
use super::GameMode;
use super::error::Error;
use crate::champion::Info;
use crate::champion::MetaEntry;
use crate::champion::Response;
use crate::champion::Summary;

const BASE_URL: &str = "https://lol-api-champion.op.gg";

#[derive(Debug, Clone)]
pub struct Client {
    base_url: Url,
    http: HttpClient,
}

impl Default for Client {
    fn default() -> Self { Self { base_url: Url::parse(BASE_URL).unwrap(), http: Default::default() } }
}

impl Client {
    pub fn new() -> Self { Self::default() }

    pub fn builder() -> ClientBuilder { HttpClient::builder() }

    pub async fn champion_meta(&self) -> Result<Response<Vec<MetaEntry>>, Error> {
        self.get("api/meta/champions").await
    }

    pub async fn champion_list(&self, mode: GameMode) -> Result<Response<Vec<Summary>>, Error> {
        let path = format!("/api/global/champions/{}", mode);
        self.get(&path).await
    }

    pub async fn champion_info(
        &self, mode: GameMode, champion_id: u32, position: ChampionPosition,
    ) -> Result<Response<Info>, Error> {
        if !mode.allows_position(position) {
            return Err(Error::InvalidModePosition { mode, position });
        }
        let path = format!("api/global/champions/{}/{}/{}", mode, champion_id, position);
        self.get(&path).await
    }

    async fn get<T>(&self, path: &str) -> Result<T, Error>
    where
        T: DeserializeOwned,
    {
        let mut url = self.base_url.join(path).map_err(|err| Error::UrlConstruction(err.to_string()))?;
        url.query_pairs_mut().append_pair("hl", "zh_CN");
        let response = self.http.get(url).send().await?.error_for_status()?;
        Ok(response.json::<T>().await?)
    }
}