tmdb_api/client/
mod.rs

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