tmdb_api/client/
mod.rs

1pub mod prelude;
2
3pub use reqwest;
4#[cfg(feature = "reqwest-middleware")]
5pub use reqwest_middleware;
6
7mod reqwest_impl;
8
9use std::borrow::Cow;
10
11pub use self::prelude::Executor;
12pub type ReqwestClient = Client<::reqwest::Client>;
13
14const BASE_URL: &str = "https://api.themoviedb.org/3";
15
16#[derive(Debug, thiserror::Error)]
17pub enum ClientBuilderError {
18    #[error("missing api key")]
19    MissingApiKey,
20}
21
22pub struct ClientBuilder<E: prelude::Executor> {
23    base_url: Cow<'static, str>,
24    executor: Option<E>,
25    api_key: Option<String>,
26}
27
28impl<E: prelude::Executor> Default for ClientBuilder<E> {
29    fn default() -> Self {
30        Self {
31            base_url: Cow::Borrowed(BASE_URL),
32            executor: None,
33            api_key: None,
34        }
35    }
36}
37
38impl<E: prelude::Executor> ClientBuilder<E> {
39    pub fn with_base_url<U: Into<Cow<'static, str>>>(mut self, value: U) -> Self {
40        self.base_url = value.into();
41        self
42    }
43
44    pub fn set_base_url<U: Into<Cow<'static, str>>>(&mut self, value: U) {
45        self.base_url = value.into();
46    }
47
48    pub fn with_executor(mut self, executor: E) -> Self {
49        self.executor = Some(executor);
50        self
51    }
52
53    pub fn set_executor(mut self, executor: E) {
54        self.executor = Some(executor);
55    }
56
57    pub fn with_api_key(mut self, value: String) -> Self {
58        self.api_key = Some(value);
59        self
60    }
61
62    pub fn set_api_key(mut self, value: String) {
63        self.api_key = Some(value);
64    }
65
66    pub fn build(self) -> Result<Client<E>, ClientBuilderError> {
67        let base_url = self.base_url;
68        let executor = self.executor.unwrap_or_default();
69        let api_key = self.api_key.ok_or(ClientBuilderError::MissingApiKey)?;
70
71        Ok(Client {
72            executor,
73            base_url,
74            api_key,
75        })
76    }
77}
78
79#[derive(Serialize)]
80struct WithApiKey<'a, V> {
81    api_key: &'a str,
82    #[serde(flatten)]
83    inner: V,
84}
85
86/// HTTP client for TMDB
87///
88/// ```rust
89/// use tmdb_api::client::Client;
90/// use tmdb_api::client::reqwest::Client as ReqwestClient;
91///
92/// let client = Client::<ReqwestClient>::new("this-is-my-secret-token".into());
93/// ```
94pub struct Client<E> {
95    executor: E,
96    base_url: Cow<'static, str>,
97    api_key: String,
98}
99
100impl<E: std::fmt::Debug> std::fmt::Debug for Client<E> {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        f.debug_struct(stringify!(Client))
103            .field("executor", &self.executor)
104            .field("base_url", &self.base_url)
105            .field("api_key", &"REDACTED")
106            .finish()
107    }
108}
109
110impl<E: Executor> Client<E> {
111    pub fn builder() -> ClientBuilder<E> {
112        ClientBuilder::default()
113    }
114
115    pub fn new(api_key: String) -> Self {
116        Self {
117            executor: E::default(),
118            base_url: Cow::Borrowed(BASE_URL),
119            api_key,
120        }
121    }
122
123    pub fn base_url(&self) -> &str {
124        &self.base_url
125    }
126
127    // pub async fn execute<T: serde::de::DeserializeOwned>(
128    //     &self,
129    //     path: &str,
130    //     mut params: Vec<(&str, Cow<'_, str>)>,
131    // ) -> Result<T, crate::error::Error> {
132    //     params.push(("api_key", Cow::Borrowed(self.api_key.as_str())));
133
134    //     let url = format!("{}{}", self.base_url, path);
135    //     self.executor.execute(&url, params).await
136    // }
137
138    pub async fn execute<T: serde::de::DeserializeOwned, P: serde::Serialize>(
139        &self,
140        path: &str,
141        params: &P,
142    ) -> Result<T, crate::error::Error> {
143        let url = format!("{}{}", self.base_url, path);
144        self.executor
145            .execute(
146                &url,
147                WithApiKey {
148                    api_key: &self.api_key,
149                    inner: params,
150                },
151            )
152            .await
153    }
154}