pokemon_tcg_sdk/
client.rs

1use crate::errors::{ClientError, ErrorEnvelope};
2use serde::{Deserialize, Serialize};
3
4pub struct Client {
5    pub(super) base_url: String,
6    pub(super) http_client: reqwest::Client,
7}
8
9impl Client {
10    /// Constructs a new client
11    /// 
12    /// # Errors
13    /// This method fails if the API key is invalid ("\n" etc.) 
14    /// or if a TLS backend cannot be initialized, or the resolver
15    /// cannot load the system configuration.
16    pub fn new(api_key: Option<&str>) -> Result<Self, ClientError> {
17        Ok(Client {
18            base_url: String::from(r#"https://api.pokemontcg.io/v2"#),
19            http_client: Client::get_http_client(api_key)?,
20        })
21    }
22
23    /// Constructs a client with a different base url than the default for the API.
24    ///
25    /// # Errors
26    /// This method fails if the API key is invalid ("\n" etc.) 
27    /// or if a TLS backend cannot be initialized, or the resolver
28    /// cannot load the system configuration.
29    pub fn with_base_url(base_url: &str, api_key: Option<&str>) -> Result<Self, ClientError> {
30        Ok(Client {
31            base_url: String::from(base_url),
32            http_client: Client::get_http_client(api_key)?,
33        })
34    }
35
36    /// Constructs a client with an API key that will be passed on every request.
37    /// 
38    /// # Errors
39    /// This method fails if the API key is invalid ("\n" etc.) 
40    /// or if a TLS backend cannot be initialized, or the resolver
41    /// cannot load the system configuration.
42    pub fn with_api_key(api_key: &str) -> Result<Self, ClientError> {
43        Client::new(Some(api_key))
44    }
45
46    /// Builds the reqwest http client that will be used for all API requests
47    pub(super) fn get_http_client(api_key: Option<&str>) -> Result<reqwest::Client, ClientError> {
48        let mut http_client = reqwest::Client::builder();
49        if let Some(key) = api_key {
50            let mut headers = reqwest::header::HeaderMap::new();
51            headers.insert("X-Api-Key", reqwest::header::HeaderValue::from_str(key)?);
52            http_client = http_client.default_headers(headers);
53        }
54
55        http_client.build().map_err(|e| e.into())
56    }
57
58    /// Parses the response from the API into either a generic data envelope ({data: T}) or 
59    /// an error envelope ({error: ...}) and returns T if successful
60    pub(super) fn parse_response<T>(result: ApiResult<T>) -> Result<T, ClientError> {
61        match result {
62            ApiResult::Ok(v) => Ok(v.data),
63            ApiResult::Err(e) => Err(e.into()),
64        }
65    }
66}
67
68#[derive(Debug, Serialize, Deserialize)]
69pub struct DataEnvelope<T> {
70    pub data: T,
71    #[serde(alias = "totalCount")]
72    pub total_count: Option<usize>,
73}
74
75#[derive(Debug, Serialize, Deserialize)]
76#[serde(untagged)]
77pub enum ApiResult<T> {
78    Ok(DataEnvelope<T>),
79    Err(ErrorEnvelope),
80}
81
82impl Default for Client {
83    /// Constructs a basic client with no API Key using the default URL.
84    /// 
85    /// # Panics
86    /// This method will panic if the construction of the reqwest http client fails,
87    /// if a TLS backend cannot be initialized, or the resolver
88    /// cannot load the system configuration.
89    fn default() -> Self {
90        Self::new(None).unwrap()
91    }
92}