1use ureq::{Header, Response};
2use crate::error::ApiError;
3use serde::de::DeserializeOwned;
4
5pub const BASE_URL: &str = "https://api.guildwars2.com";
7pub const TIMEOUT: u64 = 10_000;
9
10#[derive(Debug, PartialEq)]
12pub enum Localisation {
13 English,
14 Spanish,
15 German,
16 French,
17 Chinese,
18}
19
20impl ToString for Localisation {
21 fn to_string(&self) -> String {
23 match self {
24 Localisation::English => "en".to_string(),
25 Localisation::Spanish => "es".to_string(),
26 Localisation::German => "de".to_string(),
27 Localisation::French => "fr".to_string(),
28 Localisation::Chinese => "zh".to_string(),
29 }
30 }
31}
32
33pub struct Client {
35 api_key: Option<String>,
37 lang: Option<Localisation>,
40}
41
42impl Client {
43 pub fn new() -> Client {
45 Client {
46 api_key: None,
47 lang: None,
48 }
49 }
50
51 pub fn set_api_key(mut self, api_key: String) -> Client {
53 self.api_key = Some(api_key);
54 self
55 }
56
57 pub fn set_lang(mut self, lang: Localisation) -> Client {
59 self.lang = Some(lang);
60 self
61 }
62
63 fn create_lang_header(&self) -> Header {
66 let lang = self.lang().unwrap_or(&Localisation::English).to_string();
67 let header = Header::new("Accept-Language", &lang);
68 header
69 }
70
71 fn create_auth_header(&self) -> Header {
74 let api_key = self.api_key().expect("Guild Wars 2 API key is not set").to_owned();
75 let header = Header::new("Authorization", &format!("Bearer {}", api_key));
76 header
77 }
78
79 pub fn request<T>(&self, url: &str) -> Result<T, ApiError>
82 where T: DeserializeOwned {
83 let full_url = format!("{base_url}/{url}", base_url=BASE_URL, url=url);
84 let lang_header = self.create_lang_header();
85 let response = ureq::get(&full_url)
86 .set(lang_header.name(), lang_header.value())
87 .timeout_connect(TIMEOUT)
88 .timeout_read(TIMEOUT)
89 .call();
90 Client::handle_response(response)
91 }
92
93 pub fn authenticated_request<T>(&self, url: &str) -> Result<T, ApiError>
101 where T: DeserializeOwned {
102 let full_url = format!("{base_url}/{url}", base_url=BASE_URL, url=url);
103 let lang_header = self.create_lang_header();
104 let auth_header = self.create_auth_header();
105
106 let response = ureq::get(&full_url)
107 .set(lang_header.name(), lang_header.value())
108 .set(auth_header.name(), auth_header.value())
109 .timeout_connect(TIMEOUT)
110 .timeout_read(TIMEOUT)
111 .call();
112 Client::handle_response(response)
113 }
114
115 fn handle_response<T>(response: Response) -> Result<T, ApiError>
119 where T: DeserializeOwned {
120 if response.ok() {
121 return Ok(response.into_json_deserialize::<T>().unwrap());
122 } else {
123 match response.status() {
124 403 => Err(ApiError::new(response.into_json().unwrap())),
126 404 => Err(ApiError::new(response.into_json().unwrap())),
128 408 =>
130 Err(ApiError::new("Client timed out. Probably due to the official API being down.".to_string())),
131 _ => Err(ApiError::new(response.into_json().unwrap())),
132 }
133 }
134 }
135
136 pub fn api_key(&self) -> Option<&str> {
139 match &self.api_key {
140 Some(key) => Some(&key),
141 None => None,
142 }
143 }
144
145 pub fn lang(&self) -> Option<&Localisation> {
148 match &self.lang {
149 Some(lang) => Some(&lang),
150 None => None,
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use crate::client::*;
158 #[test]
164 fn create_client() {
165 let api_key = "ABCDEFGH-1324-5678-9012-IJKLMNOPQRSTUVXYZABC-1234-5678-9012-ABCDEFGHIJKL"
166 .to_string();
167 let client = Client::new().set_api_key(api_key.clone()).set_lang(Localisation::French);
168 assert_eq!(&api_key, client.api_key().unwrap());
169 assert_eq!(&Localisation::French, client.lang().unwrap());
170 }
171}