mangadex_api_schema_rust/v5/
manga_links.rs

1use serde::{Deserialize, Serialize};
2use url::Url;
3
4/// Related links for a manga.
5#[derive(Clone, Debug, Default, Deserialize, Hash, PartialEq, Serialize)]
6#[serde(rename_all = "snake_case")]
7#[cfg_attr(feature = "non_exhaustive", non_exhaustive)]
8#[cfg_attr(feature = "specta", derive(specta::Type))]
9pub struct MangaLinks {
10    /// Amazon Product URL.
11    ///
12    /// Stored as full URL.
13    ///
14    /// # Examples
15    ///
16    /// - `https://www.amazon.co.jp/gp/product/B074CHFLT2`
17    #[serde(rename = "amz")]
18    #[cfg_attr(feature = "specta", specta(type = Option<String>))]
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub amazon: Option<Url>,
21
22    /// AniList ID.
23    ///
24    /// `https://anilist.co/manga/{id}`
25    ///
26    /// # Examples
27    ///
28    /// - `112847`
29    #[serde(rename = "al")]
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub anilist: Option<String>,
32
33    /// Anime-Planet slug.
34    ///
35    /// `https://www.anime-planet.com/manga/{slug}`
36    ///
37    /// # Examples
38    ///
39    /// - `age`
40    /// - `under-grand-hotel`
41    #[serde(rename = "ap")]
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub anime_planet: Option<String>,
44
45    /// BookWalker URI.
46    ///
47    /// Stored has "series/{id}".
48    ///
49    /// `https://bookwalker.jp/{slug}`
50    ///
51    /// # Examples
52    ///
53    /// - `series/289459`
54    #[serde(rename = "bw")]
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub book_walker: Option<BookWalker>,
57    /// CDJapan URL.
58    ///
59    /// Stored as full URL.
60    ///
61    /// # Examples
62    ///
63    /// - `http://www.cdjapan.co.jp/product/NEOBK-1963980`
64    /// - `http://www.cdjapan.co.jp/searches?term.cat_id=UD-14-06-03&page=&agg_use=cat_ids_hierarchal_treeish_foldable&term.media_format=&f=major&q=%E7%B5%B6%E5%AF%BE%E3%81%AB%E3%81%A8%E3%81%8D%E3%82%81%E3%81%84%E3%81%A6%E3%81%AF%E3%81%84%E3%81%91%E3%81%AA%E3%81%84%EF%BC%81&f=major&q=&f=major&q=&order=scoreboost_cdj&range.rel=&range.sale_price=&term.caption=&term.audio_language=`
65    #[serde(rename = "cdj")]
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub cd_japan: Option<String>,
68
69    /// EbookJapan URL.
70    ///
71    /// Stored as full URL.
72    ///
73    /// # Examples
74    ///
75    /// - `https://ebookjapan.yahoo.co.jp/books/444727/A001841690/`
76    /// - `https://www.ebookjapan.jp/ebj/371654/`
77    #[serde(rename = "ebj")]
78    #[serde(skip_serializing_if = "Option::is_none")]
79    #[cfg_attr(feature = "specta", specta(type = Option<String>))]
80    pub ebook_japan: Option<Url>,
81
82    /// Official English URL.
83    ///
84    /// Stored as full URL, official English-licenced URL.
85    ///
86    /// # Examples
87    ///
88    /// - `https://kodanshacomics.com/series/we-must-never-fall-in-love/`
89    #[serde(rename = "engtl")]
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub english_translation: Option<String>,
92
93    /// Kitsu ID.
94    ///
95    /// One of:
96    ///
97    /// - `https://kitsu.io/api/edge/manga/{id}`
98    /// - `https://kitsu.io/api/edge/manga?filter[slug]={slug}`
99    ///
100    /// If integer, use id version of the URL, otherwise use slug one
101    ///
102    /// # Examples
103    ///
104    /// - `23040`
105    #[serde(rename = "kt")]
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub kitsu: Option<String>,
108
109    /// MangaUpdates ID.
110    ///
111    /// `https://www.mangaupdates.com/series.html?id={id}`
112    ///
113    /// # Examples
114    ///
115    /// - `157722`
116    #[serde(rename = "mu")]
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub manga_updates: Option<MangaUpdates>,
119
120    /// MyAnimeList ID.
121    ///
122    /// `https://myanimelist.net/manga/{id}`
123    ///
124    /// # Examples
125    ///
126    /// - `12648`
127    #[serde(rename = "mal")]
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub my_anime_list: Option<MyAnimeList>,
130
131    /// NovelUpdates slug.
132    ///
133    /// `https://www.novelupdates.com/series/{slug}`
134    ///
135    /// # Examples
136    ///
137    /// - `an-active-hunter-in-hokkaido-has-been-thrown-into-a-different-world`
138    #[serde(rename = "nu")]
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub novel_updates: Option<NovelUpdates>,
141
142    /// Raw URL.
143    ///
144    /// Stored as full URL, untranslated stuff URL (original language).
145    ///
146    /// # Examples
147    ///
148    /// - `https://manga.bilibili.com/m/detail/mc29443?from=manga_index`
149    /// - `https://www.sunday-webry.com/detail-yoru.php?title_id=1282c`
150    #[cfg_attr(feature = "specta", specta(type = Option<String>))]
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub raw: Option<Url>,
153}
154
155impl MangaLinks {
156    /// Check if there are no available links.
157    ///
158    /// This returns `false` if at least one of the fields is `Some`.
159    pub(crate) fn has_no_links(&self) -> bool {
160        self.amazon.is_none()
161            && self.anilist.is_none()
162            && self.anime_planet.is_none()
163            && self.book_walker.is_none()
164            && self.cd_japan.is_none()
165            && self.ebook_japan.is_none()
166            && self.english_translation.is_none()
167            && self.kitsu.is_none()
168            && self.manga_updates.is_none()
169            && self.my_anime_list.is_none()
170            && self.novel_updates.is_none()
171            && self.raw.is_none()
172    }
173}
174
175/// BookWalker URI.
176///
177/// Example: "`series/91701`".
178#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
179#[cfg_attr(feature = "specta", derive(specta::Type))]
180pub struct BookWalker(pub String);
181
182impl std::fmt::Display for BookWalker {
183    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
184        fmt.write_str(&format!("https://bookwalker.jp/{}", self.0))
185    }
186}
187
188/// MangaUpdates ID.
189///
190/// Example: "`132515`".
191#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
192#[cfg_attr(feature = "specta", derive(specta::Type))]
193pub struct MangaUpdates(pub String);
194
195impl std::fmt::Display for MangaUpdates {
196    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
197        fmt.write_str(&format!(
198            "https://www.mangaupdates.com/series.html?id={}",
199            self.0
200        ))
201    }
202}
203
204/// MyAnimeList ID.
205///
206/// Example: "`98436`".
207#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
208#[cfg_attr(feature = "specta", derive(specta::Type))]
209pub struct MyAnimeList(pub String);
210
211impl std::fmt::Display for MyAnimeList {
212    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
213        fmt.write_str(&format!("https://myanimelist.net/manga/{}", self.0))
214    }
215}
216
217/// NovelUpdates slug.
218///
219/// Example: "`novel-updates`".
220#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
221#[cfg_attr(feature = "specta", derive(specta::Type))]
222pub struct NovelUpdates(pub String);
223
224impl std::fmt::Display for NovelUpdates {
225    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
226        fmt.write_str(&format!("https://www.novelupdates.com/series/{}/", self.0))
227    }
228}