Skip to main content

mangadex_api/
v5.rs

1pub mod api_client;
2pub mod at_home;
3pub mod auth;
4pub mod author;
5pub mod captcha;
6pub mod chapter;
7pub mod cover;
8pub mod custom_list;
9pub mod feed;
10pub mod forums;
11pub mod legacy;
12pub mod manga;
13cfg_oauth! {
14    pub mod oauth;
15}
16pub mod ping;
17pub mod rating;
18pub mod report;
19pub mod scanlation_group;
20pub mod search;
21pub mod settings;
22pub mod statistics;
23pub mod upload;
24pub mod user;
25
26use crate::Result;
27pub use mangadex_api_schema::v5 as schema;
28use mangadex_api_schema::v5::oauth::ClientInfo;
29pub(crate) use mangadex_api_schema::v5::AuthTokens;
30
31use reqwest::Client;
32
33use std::sync::Arc;
34use tokio::sync::RwLock;
35
36#[cfg(feature = "utils")]
37use crate::utils::download::DownloadBuilder;
38use crate::v5::api_client::ApiClientEndpoint;
39use crate::v5::at_home::AtHomeBuilder;
40use crate::v5::auth::AuthBuilder;
41use crate::v5::author::AuthorBuilder;
42use crate::v5::captcha::CaptchaBuilder;
43use crate::v5::chapter::ChapterBuilder;
44use crate::v5::cover::CoverBuilder;
45use crate::v5::custom_list::CustomListBuilder;
46use crate::v5::feed::FeedBuilder;
47use crate::v5::forums::ForumsEndpoint;
48use crate::v5::legacy::LegacyBuilder;
49use crate::v5::manga::MangaBuilder;
50#[cfg(feature = "oauth")]
51use crate::v5::oauth::OAuthBuider;
52use crate::v5::ping::PingEndpointBuilder;
53use crate::v5::rating::RatingBuilder;
54use crate::v5::report::ReportBuilder;
55use crate::v5::scanlation_group::ScanlationGroupBuilder;
56use crate::v5::search::SearchBuilder;
57use crate::v5::settings::SettingsBuilder;
58use crate::v5::statistics::StatisticsBuilder;
59use crate::v5::upload::UploadBuilder;
60use crate::v5::user::UserBuilder;
61use crate::HttpClient;
62use crate::HttpClientRef;
63
64/// API client to make requests to the MangaDex v5 API.
65#[derive(Clone, Debug)]
66#[non_exhaustive]
67pub struct MangaDexClient {
68    pub http_client: HttpClientRef,
69}
70
71impl Default for MangaDexClient {
72    /// Create a new `MangaDexClient` with the default [`reqwest::Client`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html) settings.
73    ///
74    /// # Examples
75    ///
76    /// ```rust
77    /// use reqwest::Client;
78    ///
79    /// use mangadex_api::v5::MangaDexClient;
80    ///
81    /// # async fn run() -> Result<(), reqwest::Error> {
82    /// let client = MangaDexClient::default();
83    /// # Ok(())
84    /// # }
85    /// ```
86    fn default() -> Self {
87        Self::new_with_http_client(HttpClient::default())
88    }
89}
90
91impl MangaDexClient {
92    /// Create a new `MangaDexClient` with a custom [`reqwest::Client`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html).
93    ///
94    /// # Examples
95    ///
96    /// ```rust
97    /// use reqwest::Client;
98    ///
99    /// use mangadex_api::v5::MangaDexClient;
100    ///
101    /// # async fn run() -> Result<(), reqwest::Error> {
102    /// let reqwest_client = Client::builder()
103    ///     .timeout(std::time::Duration::from_secs(10))
104    ///     .build()?;
105    ///
106    /// let client = MangaDexClient::new(reqwest_client);
107    /// # Ok(())
108    /// # }
109    /// ```
110    pub fn new(client: Client) -> Self {
111        Self::new_with_http_client_ref(create_ref_counted_http_client(HttpClient::new(client)))
112    }
113
114    /// Create a new `MangaDexClient` with a custom client reference
115    pub fn new_with_http_client_ref(http_client: HttpClientRef) -> Self {
116        Self { http_client }
117    }
118    /// Create a new `MangaDexClient` with a custom [`HttpClient`].
119    ///
120    /// In most cases, providing a custom [`HttpClient`] isn't necessary.
121    /// This function is primarily useful for mock testing but is available for anyone that needs to
122    /// change the base URL if it changes due to an unforeseen event.
123    ///
124    /// # Examples
125    ///
126    /// ```rust
127    /// use reqwest::Client;
128    /// use url::Url;
129    ///
130    /// use mangadex_api::v5::MangaDexClient;
131    /// use mangadex_api::HttpClient;
132    ///
133    /// # async fn run() -> anyhow::Result<()> {
134    /// let reqwest_client = Client::builder()
135    ///     .timeout(std::time::Duration::from_secs(10))
136    ///     .build()?;
137    ///
138    /// let http_client = HttpClient::builder()
139    ///     .client(reqwest_client)
140    ///     .base_url(Url::parse("127.0.0.1:8080")?)
141    ///     .build()?;
142    ///
143    /// let client = MangaDexClient::new_with_http_client(http_client);
144    /// # Ok(())
145    /// # }
146    /// ```
147    pub fn new_with_http_client(http_client: HttpClient) -> Self {
148        Self::new_with_http_client_ref(create_ref_counted_http_client(http_client))
149    }
150
151    /// Return the Reqwest `Client`.
152    ///
153    /// This can be used to create manual HTTP requests.
154    ///
155    /// Using this is generally not advised as it can provide mutable access to the [`HttpClient`].
156    pub fn get_http_client(&self) -> HttpClientRef {
157        self.http_client.clone()
158    }
159
160    pub async fn set_auth_tokens(&self, auth_tokens: &AuthTokens) -> Result<()> {
161        let mut client = self.http_client.write().await;
162
163        client.set_auth_tokens(auth_tokens);
164        Ok(())
165    }
166
167    pub async fn clear_auth_tokens(&self) -> Result<()> {
168        let mut client = self.http_client.write().await;
169
170        client.clear_auth_tokens();
171        Ok(())
172    }
173    pub async fn get_auth_tokens(&self) -> Result<AuthTokens> {
174        let client = &self.http_client.read().await;
175        client
176            .get_tokens()
177            .cloned()
178            .ok_or(crate::error::Error::MissingTokens)
179    }
180
181    pub async fn set_captcha<A: Into<String>>(&self, captcha: A) -> Result<()> {
182        let mut client = self.http_client.write().await;
183        client.set_captcha(captcha);
184        Ok(())
185    }
186    pub async fn get_captcha(&self) -> Result<String> {
187        let client = &self.http_client.read().await;
188        client
189            .get_captcha()
190            .cloned()
191            .ok_or(crate::error::Error::MissingCaptcha)
192    }
193    pub async fn clear_captcha(&self) -> Result<()> {
194        let mut client = self.http_client.write().await;
195        client.clear_captcha();
196        Ok(())
197    }
198
199    cfg_oauth! {
200        pub async fn get_client_info(&self) -> Result<ClientInfo> {
201            let client = &self.http_client.read().await;
202            client
203                .get_client_info()
204                .cloned()
205                .ok_or(crate::error::Error::MissingClientInfo)
206        }
207    }
208    /// Get a builder for handling the At-Home endpoints.
209    ///
210    /// <https://api.mangadex.org/swagger.html#/AtHome>
211    pub fn at_home(&self) -> AtHomeBuilder {
212        AtHomeBuilder::new(self.http_client.clone())
213    }
214
215    /// Get a builder for handling the authentication endpoints.
216    ///
217    /// This builder is deprecated
218    ///
219    /// <https://api.mangadex.org/docs/redoc.html#tag/Authentication>
220    pub fn auth(&self) -> AuthBuilder {
221        AuthBuilder::new(self.http_client.clone())
222    }
223
224    /// Get a builder for handling the author endpoints.
225    ///
226    /// <https://api.mangadex.org/swagger.html#/Author>
227    pub fn author(&self) -> AuthorBuilder {
228        AuthorBuilder::new(self.http_client.clone())
229    }
230
231    /// Get a builder for handling the captcha endpoints.
232    ///
233    /// <https://api.mangadex.org/swagger.html#/Captcha>
234    pub fn captcha(&self) -> CaptchaBuilder {
235        CaptchaBuilder::new(self.http_client.clone())
236    }
237
238    /// Get a builder for handling the chapter endpoints.
239    ///
240    /// <https://api.mangadex.org/swagger.html#/Chapter>
241    pub fn chapter(&self) -> ChapterBuilder {
242        ChapterBuilder::new(self.http_client.clone())
243    }
244
245    pub fn client(&self) -> ApiClientEndpoint {
246        ApiClientEndpoint::new(self.http_client.clone())
247    }
248
249    /// Get a builder for handling manga volume cover art endpoints.
250    ///
251    /// <https://api.mangadex.org/swagger.html#/Cover>
252    pub fn cover(&self) -> CoverBuilder {
253        CoverBuilder::new(self.http_client.clone())
254    }
255
256    /// Get a builder for handling the custom list endpoints.
257    ///
258    /// <https://api.mangadex.org/swagger.html#/CustomList>
259    pub fn custom_list(&self) -> CustomListBuilder {
260        CustomListBuilder::new(self.http_client.clone())
261    }
262
263    /// Get a builder for handling the feed endpoints.
264    ///
265    /// <https://api.mangadex.org/swagger.html#/Feed>
266    pub fn feed(&self) -> FeedBuilder {
267        FeedBuilder::new(self.http_client.clone())
268    }
269
270    /// Get a builder for handling the infrastructure endpoints.
271    ///
272    /// <https://api.mangadex.org/swagger.html#/Infrastructure>
273    pub fn ping(&self) -> PingEndpointBuilder {
274        PingEndpointBuilder::new(self.http_client.clone())
275    }
276
277    /// Get a builder for handling the legacy endpoints.
278    ///
279    /// <https://api.mangadex.org/swagger.html#/Legacy>
280    pub fn legacy(&self) -> LegacyBuilder {
281        LegacyBuilder::new(self.http_client.clone())
282    }
283
284    /// Get a builder for handling the manga endpoints.
285    ///
286    /// <https://api.mangadex.org/swagger.html#/Manga>
287    pub fn manga(&self) -> MangaBuilder {
288        MangaBuilder::new(self.http_client.clone())
289    }
290
291    /// Get a builder for handling the rating endpoints.
292    ///
293    /// <https://api.mangadex.org/swagger.html#/Rating>
294    pub fn rating(&self) -> RatingBuilder {
295        RatingBuilder::new(self.http_client.clone())
296    }
297
298    /// Get a builder for handling the report endpoints.
299    ///
300    /// <https://api.mangadex.org/swagger.html#/Report>
301    pub fn report(&self) -> ReportBuilder {
302        ReportBuilder::new(self.http_client.clone())
303    }
304
305    /// Get a builder for handling the scanlation group endpoints.
306    ///
307    /// <https://api.mangadex.org/swagger.html#/ScanlationGroup>
308    pub fn scanlation_group(&self) -> ScanlationGroupBuilder {
309        ScanlationGroupBuilder::new(self.http_client.clone())
310    }
311
312    /// Get a builder for handling the search endpoints.
313    ///
314    /// This is a convenience builder that aggregates search endpoints from various categories.
315    pub fn search(&self) -> SearchBuilder {
316        SearchBuilder::new(self.http_client.clone())
317    }
318
319    /// Get a builder for handling the settings endpoints.
320    ///
321    /// <https://api.mangadex.org/swagger.html#/Settings>
322    // Not public yet as the settings endpoints are not stable as of MangaDex API v5.4.9.
323    pub fn settings(&self) -> SettingsBuilder {
324        SettingsBuilder::new(self.http_client.clone())
325    }
326
327    /// Get a builder for handling the statistics endpoints.
328    ///
329    /// <https://api.mangadex.org/swagger.html#/Statistics>
330    pub fn statistics(&self) -> StatisticsBuilder {
331        StatisticsBuilder::new(self.http_client.clone())
332    }
333
334    /// Get a builder for handling uploads.
335    ///
336    /// <https://api.mangadex.org/swagger.html#/Upload>
337    pub fn upload(&self) -> UploadBuilder {
338        UploadBuilder::new(self.http_client.clone())
339    }
340
341    /// Get a builder for handling the user endpoints.
342    ///
343    /// <https://api.mangadex.org/swagger.html#/User>
344    pub fn user(&self) -> UserBuilder {
345        UserBuilder::new(self.http_client.clone())
346    }
347
348    /// This is an api client for
349    /// `api.mangadex.dev`
350    pub fn api_dev_client() -> Self {
351        Self::new_with_http_client(HttpClient::api_dev_client())
352    }
353    cfg_utils! {
354        pub fn download(&self) -> DownloadBuilder {
355            DownloadBuilder::new(self.http_client.clone())
356        }
357    }
358
359    pub fn forums(&self) -> ForumsEndpoint {
360        ForumsEndpoint::new(self.http_client.clone())
361    }
362
363    cfg_oauth! {
364        pub fn oauth(&self) -> OAuthBuider {
365            OAuthBuider::new(self.http_client.clone())
366        }
367    }
368    cfg_oauth! {
369        pub async fn set_client_info(&self, client_info: &ClientInfo) -> Result<()> {
370            let mut client = self.http_client.write().await;
371            client.set_client_info(client_info);
372            Ok(())
373        }
374    }
375    cfg_oauth! {
376        pub async fn clear_client_info(&self) -> Result<()> {
377            let mut client = self.http_client.write().await;
378            client.clear_client_info();
379            Ok(())
380        }
381    }
382    pub async fn get_reqwest_client(&self) -> reqwest::Client {
383        self.get_http_client().read().await.client.clone()
384    }
385}
386
387/// Create a new reference counted `HttpClient`.
388fn create_ref_counted_http_client(http_client: HttpClient) -> HttpClientRef {
389    Arc::new(RwLock::new(http_client))
390}