use ureq::{Header, Response};
use crate::error::ApiError;
use serde::de::DeserializeOwned;
pub const BASE_URL: &str = "https://api.guildwars2.com";
pub const TIMEOUT: u64 = 10_000;
#[derive(Debug, PartialEq)]
pub enum Localisation {
English,
Spanish,
German,
French,
Chinese,
}
impl ToString for Localisation {
fn to_string(&self) -> String {
match self {
Localisation::English => "en".to_string(),
Localisation::Spanish => "es".to_string(),
Localisation::German => "de".to_string(),
Localisation::French => "fr".to_string(),
Localisation::Chinese => "zh".to_string(),
}
}
}
pub struct Client {
api_key: Option<String>,
lang: Option<Localisation>,
}
impl Client {
pub fn new() -> Client {
Client {
api_key: None,
lang: None,
}
}
pub fn set_api_key(mut self, api_key: String) -> Client {
self.api_key = Some(api_key);
self
}
pub fn set_lang(mut self, lang: Localisation) -> Client {
self.lang = Some(lang);
self
}
fn create_lang_header(&self) -> Header {
let lang = self.lang().unwrap_or(&Localisation::English).to_string();
let header = Header::new("Accept-Language", &lang);
header
}
fn create_auth_header(&self) -> Header {
let api_key = self.api_key().expect("Guild Wars 2 API key is not set").to_owned();
let header = Header::new("Authorization", &format!("Bearer {}", api_key));
header
}
pub fn request<T>(&self, url: &str) -> Result<T, ApiError>
where T: DeserializeOwned {
let full_url = format!("{base_url}/{url}", base_url=BASE_URL, url=url);
let lang_header = self.create_lang_header();
let response = ureq::get(&full_url)
.set(lang_header.name(), lang_header.value())
.timeout_connect(TIMEOUT)
.timeout_read(TIMEOUT)
.call();
Client::handle_response(response)
}
pub fn authenticated_request<T>(&self, url: &str) -> Result<T, ApiError>
where T: DeserializeOwned {
let full_url = format!("{base_url}/{url}", base_url=BASE_URL, url=url);
let lang_header = self.create_lang_header();
let auth_header = self.create_auth_header();
let response = ureq::get(&full_url)
.set(lang_header.name(), lang_header.value())
.set(auth_header.name(), auth_header.value())
.timeout_connect(TIMEOUT)
.timeout_read(TIMEOUT)
.call();
Client::handle_response(response)
}
fn handle_response<T>(response: Response) -> Result<T, ApiError>
where T: DeserializeOwned {
if response.ok() {
return Ok(response.into_json_deserialize::<T>().unwrap());
} else {
match response.status() {
403 => Err(ApiError::new(response.into_json().unwrap())),
404 => Err(ApiError::new(response.into_json().unwrap())),
408 =>
Err(ApiError::new("Client timed out. Probably due to the official API being down.".to_string())),
_ => Err(ApiError::new(response.into_json().unwrap())),
}
}
}
pub fn api_key(&self) -> Option<&str> {
match &self.api_key {
Some(key) => Some(&key),
None => None,
}
}
pub fn lang(&self) -> Option<&Localisation> {
match &self.lang {
Some(lang) => Some(&lang),
None => None,
}
}
}
#[cfg(test)]
mod tests {
use crate::client::*;
#[test]
fn create_client() {
let api_key = "ABCDEFGH-1324-5678-9012-IJKLMNOPQRSTUVXYZABC-1234-5678-9012-ABCDEFGHIJKL"
.to_string();
let client = Client::new().set_api_key(api_key.clone()).set_lang(Localisation::French);
assert_eq!(&api_key, client.api_key().unwrap());
assert_eq!(&Localisation::French, client.lang().unwrap());
}
}