mangadex_api/v5/manga/id/feed/
get.rs1use derive_builder::Builder;
30use serde::Serialize;
31use uuid::Uuid;
32
33use crate::HttpClientRef;
34use mangadex_api_schema::v5::ChapterListResponse;
35use mangadex_api_types::{
36 ContentRating, IncludeExternalUrl, IncludeFuturePages, IncludeFuturePublishAt,
37 IncludeFutureUpdates, Language, MangaDexDateTime, MangaFeedSortOrder,
38 ReferenceExpansionResource,
39};
40
41#[cfg_attr(
42 feature = "deserializable-endpoint",
43 derive(serde::Deserialize, getset::Getters, getset::Setters)
44)]
45#[derive(Debug, Serialize, Clone, Builder)]
46#[serde(rename_all = "camelCase")]
47#[builder(
48 setter(into, strip_option),
49 build_fn(error = "mangadex_api_types::error::BuilderError")
50)]
51#[cfg_attr(feature = "non_exhaustive", non_exhaustive)]
52pub struct GetMangaFeed {
53 #[doc(hidden)]
55 #[serde(skip)]
56 #[builder(pattern = "immutable")]
57 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
58 pub http_client: HttpClientRef,
59
60 #[serde(skip_serializing)]
61 pub manga_id: Uuid,
62
63 #[builder(default)]
65 pub limit: Option<u32>,
66 #[builder(default)]
67 pub offset: Option<u32>,
68 #[builder(setter(each = "add_translated_language"), default)]
69 pub translated_language: Vec<Language>,
70 #[builder(setter(each = "add_original_language"), default)]
71 pub original_language: Vec<Language>,
72 #[builder(setter(each = "exclude_original_language"), default)]
73 pub excluded_original_language: Vec<Language>,
74 #[builder(setter(each = "add_content_rating"), default)]
75 pub content_rating: Vec<ContentRating>,
76 #[builder(setter(each = "excluded_group"), default)]
78 pub excluded_groups: Vec<Uuid>,
79 #[builder(setter(each = "excluded_uploader"), default)]
81 pub excluded_uploaders: Vec<Uuid>,
82 #[builder(default)]
86 pub include_future_updates: Option<IncludeFutureUpdates>,
87 #[builder(default)]
89 pub created_at_since: Option<MangaDexDateTime>,
90 #[builder(default)]
92 pub updated_at_since: Option<MangaDexDateTime>,
93 #[builder(default)]
95 pub publish_at_since: Option<MangaDexDateTime>,
96 #[builder(default)]
97 pub order: Option<MangaFeedSortOrder>,
98 #[builder(setter(each = "include"), default)]
99 pub includes: Vec<ReferenceExpansionResource>,
100 #[builder(default)]
101 pub include_empty_pages: Option<IncludeFuturePages>,
102 #[builder(default)]
103 pub include_future_publish_at: Option<IncludeFuturePublishAt>,
104 #[builder(default)]
105 pub include_external_url: Option<IncludeExternalUrl>,
106}
107
108endpoint! {
109 GET ("/manga/{}/feed", manga_id),
110 #[query] GetMangaFeed,
111 #[flatten_result] ChapterListResponse,
112 GetMangaFeedBuilder
113}
114
115#[cfg(test)]
116mod tests {
117 use serde_json::json;
118 use time::OffsetDateTime;
119 use url::Url;
120 use uuid::Uuid;
121 use wiremock::matchers::{method, path_regex};
122 use wiremock::{Mock, MockServer, ResponseTemplate};
123
124 use crate::{HttpClient, MangaDexClient};
125 use mangadex_api_types::MangaDexDateTime;
126
127 #[tokio::test]
128 async fn get_manga_feed_fires_a_request_to_base_url() -> anyhow::Result<()> {
129 let mock_server = MockServer::start().await;
130 let http_client: HttpClient = HttpClient::builder()
131 .base_url(Url::parse(&mock_server.uri())?)
132 .build()?;
133 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
134
135 let manga_id = Uuid::new_v4();
136 let chapter_id = Uuid::new_v4();
137 let uploader_id = Uuid::new_v4();
138
139 let datetime = MangaDexDateTime::new(&OffsetDateTime::now_utc());
140
141 let response_body = json!({
142 "result": "ok",
143 "response": "collection",
144 "data": [
145 {
146 "id": chapter_id,
147 "type": "chapter",
148 "attributes": {
149 "title": "Chapter title",
150 "volume": null,
151 "chapter": "1",
152 "pages": 4,
153 "translatedLanguage": "en",
154 "hash": "123456abcdef",
155 "data": [
156 "1.jpg"
157 ],
158 "dataSaver": [
159 "1.jpg"
160 ],
161 "uploader": uploader_id,
162 "version": 1,
163 "createdAt": datetime.to_string(),
164 "updatedAt": datetime.to_string(),
165 "publishAt": datetime.to_string(),
166 "readableAt": datetime.to_string(),
167 },
168 "relationships": []
169 }
170 ],
171 "limit": 1,
172 "offset": 0,
173 "total": 1
174 });
175
176 Mock::given(method("GET"))
177 .and(path_regex(r"/manga/[0-9a-fA-F-]+/feed"))
178 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
179 .expect(1)
180 .mount(&mock_server)
181 .await;
182
183 let _ = mangadex_client
184 .manga()
185 .id(manga_id)
186 .feed()
187 .get()
188 .limit(1u32)
189 .send()
190 .await?;
191
192 Ok(())
193 }
194}