1use crate::ids::{Mid, SeasonId};
2use crate::{BpiError, BpiResult};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum CollectionArchiveSort {
7 Asc,
9 Desc,
11}
12
13impl CollectionArchiveSort {
14 fn as_str(self) -> &'static str {
15 match self {
16 Self::Asc => "asc",
17 Self::Desc => "desc",
18 }
19 }
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct VideoCollectionSeasonsArchivesParams {
25 mid: Mid,
26 season_id: SeasonId,
27 sort_reverse: Option<bool>,
28 page_num: u64,
29 page_size: u64,
30}
31
32impl VideoCollectionSeasonsArchivesParams {
33 pub fn new(mid: Mid, season_id: SeasonId) -> Self {
35 Self {
36 mid,
37 season_id,
38 sort_reverse: None,
39 page_num: 1,
40 page_size: 20,
41 }
42 }
43
44 pub fn with_sort_reverse(mut self, sort_reverse: bool) -> Self {
46 self.sort_reverse = Some(sort_reverse);
47 self
48 }
49
50 pub fn with_page_num(mut self, page_num: u64) -> BpiResult<Self> {
52 self.page_num = validate_positive_u64("page_num", page_num)?;
53 Ok(self)
54 }
55
56 pub fn with_page_size(mut self, page_size: u64) -> BpiResult<Self> {
58 self.page_size = validate_positive_u64("page_size", page_size)?;
59 Ok(self)
60 }
61
62 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
63 let mut pairs = vec![
64 ("mid", self.mid.to_string()),
65 ("season_id", self.season_id.to_string()),
66 ("page_num", self.page_num.to_string()),
67 ("page_size", self.page_size.to_string()),
68 ];
69
70 if let Some(sort_reverse) = self.sort_reverse {
71 pairs.push(("sort_reverse", sort_reverse.to_string()));
72 }
73
74 pairs
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct VideoCollectionHomeSeasonsSeriesParams {
81 mid: Mid,
82 page_num: u64,
83 page_size: u64,
84}
85
86impl VideoCollectionHomeSeasonsSeriesParams {
87 pub fn new(mid: Mid) -> Self {
89 Self {
90 mid,
91 page_num: 1,
92 page_size: 10,
93 }
94 }
95
96 pub fn with_page_num(mut self, page_num: u64) -> BpiResult<Self> {
98 self.page_num = validate_positive_u64("page_num", page_num)?;
99 Ok(self)
100 }
101
102 pub fn with_page_size(mut self, page_size: u64) -> BpiResult<Self> {
104 self.page_size = validate_positive_u64("page_size", page_size)?;
105 Ok(self)
106 }
107
108 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
109 vec![
110 ("mid", self.mid.to_string()),
111 ("page_num", self.page_num.to_string()),
112 ("page_size", self.page_size.to_string()),
113 ]
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct VideoCollectionSeasonsSeriesParams {
120 mid: Mid,
121 page_num: Option<u64>,
122 page_size: Option<u64>,
123}
124
125impl VideoCollectionSeasonsSeriesParams {
126 pub fn new(mid: Mid) -> Self {
128 Self {
129 mid,
130 page_num: None,
131 page_size: None,
132 }
133 }
134
135 pub fn with_page_num(mut self, page_num: u64) -> BpiResult<Self> {
137 self.page_num = Some(validate_positive_u64("page_num", page_num)?);
138 Ok(self)
139 }
140
141 pub fn with_page_size(mut self, page_size: u64) -> BpiResult<Self> {
143 self.page_size = Some(validate_positive_u64("page_size", page_size)?);
144 Ok(self)
145 }
146
147 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
148 let mut pairs = vec![("mid", self.mid.to_string())];
149
150 if let Some(page_num) = self.page_num {
151 pairs.push(("page_num", page_num.to_string()));
152 }
153 if let Some(page_size) = self.page_size {
154 pairs.push(("page_size", page_size.to_string()));
155 }
156
157 pairs
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct VideoCollectionSeriesInfoParams {
164 series_id: u64,
165}
166
167impl VideoCollectionSeriesInfoParams {
168 pub fn new(series_id: u64) -> BpiResult<Self> {
170 Ok(Self {
171 series_id: validate_positive_u64("series_id", series_id)?,
172 })
173 }
174
175 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
176 vec![("series_id", self.series_id.to_string())]
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
182pub struct VideoCollectionSeriesArchivesParams {
183 mid: Mid,
184 series_id: u64,
185 only_normal: Option<bool>,
186 sort: Option<CollectionArchiveSort>,
187 page_num: Option<u64>,
188 page_size: Option<u64>,
189}
190
191impl VideoCollectionSeriesArchivesParams {
192 pub fn new(mid: Mid, series_id: u64) -> BpiResult<Self> {
194 Ok(Self {
195 mid,
196 series_id: validate_positive_u64("series_id", series_id)?,
197 only_normal: None,
198 sort: None,
199 page_num: None,
200 page_size: None,
201 })
202 }
203
204 pub fn with_only_normal(mut self, only_normal: bool) -> Self {
206 self.only_normal = Some(only_normal);
207 self
208 }
209
210 pub fn with_sort(mut self, sort: CollectionArchiveSort) -> Self {
212 self.sort = Some(sort);
213 self
214 }
215
216 pub fn with_page_num(mut self, page_num: u64) -> BpiResult<Self> {
218 self.page_num = Some(validate_positive_u64("pn", page_num)?);
219 Ok(self)
220 }
221
222 pub fn with_page_size(mut self, page_size: u64) -> BpiResult<Self> {
224 self.page_size = Some(validate_positive_u64("ps", page_size)?);
225 Ok(self)
226 }
227
228 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
229 let mut pairs = vec![
230 ("mid", self.mid.to_string()),
231 ("series_id", self.series_id.to_string()),
232 ];
233
234 if let Some(only_normal) = self.only_normal {
235 pairs.push(("only_normal", only_normal.to_string()));
236 }
237 if let Some(sort) = self.sort {
238 pairs.push(("sort", sort.as_str().to_string()));
239 }
240 if let Some(page_num) = self.page_num {
241 pairs.push(("pn", page_num.to_string()));
242 }
243 if let Some(page_size) = self.page_size {
244 pairs.push(("ps", page_size.to_string()));
245 }
246
247 pairs
248 }
249}
250
251fn validate_positive_u64(field: &'static str, value: u64) -> BpiResult<u64> {
252 if value == 0 {
253 return Err(BpiError::invalid_parameter(field, "value must be non-zero"));
254 }
255
256 Ok(value)
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn seasons_archives_params_serializes_defaults() -> BpiResult<()> {
265 let params =
266 VideoCollectionSeasonsArchivesParams::new(Mid::new(1000001)?, SeasonId::new(4294056)?);
267
268 assert_eq!(
269 params.query_pairs(),
270 vec![
271 ("mid", "1000001".to_string()),
272 ("season_id", "4294056".to_string()),
273 ("page_num", "1".to_string()),
274 ("page_size", "20".to_string())
275 ]
276 );
277 Ok(())
278 }
279
280 #[test]
281 fn seasons_archives_params_serializes_sort_and_pagination() -> BpiResult<()> {
282 let params =
283 VideoCollectionSeasonsArchivesParams::new(Mid::new(1000001)?, SeasonId::new(4294056)?)
284 .with_sort_reverse(false)
285 .with_page_num(2)?
286 .with_page_size(30)?;
287
288 assert_eq!(
289 params.query_pairs(),
290 vec![
291 ("mid", "1000001".to_string()),
292 ("season_id", "4294056".to_string()),
293 ("page_num", "2".to_string()),
294 ("page_size", "30".to_string()),
295 ("sort_reverse", "false".to_string())
296 ]
297 );
298 Ok(())
299 }
300
301 #[test]
302 fn home_seasons_series_params_serializes_defaults() -> BpiResult<()> {
303 let params = VideoCollectionHomeSeasonsSeriesParams::new(Mid::new(1000001)?);
304
305 assert_eq!(
306 params.query_pairs(),
307 vec![
308 ("mid", "1000001".to_string()),
309 ("page_num", "1".to_string()),
310 ("page_size", "10".to_string())
311 ]
312 );
313 Ok(())
314 }
315
316 #[test]
317 fn seasons_series_params_serializes_optional_pagination() -> BpiResult<()> {
318 let params = VideoCollectionSeasonsSeriesParams::new(Mid::new(1000001)?)
319 .with_page_num(1)?
320 .with_page_size(5)?;
321
322 assert_eq!(
323 params.query_pairs(),
324 vec![
325 ("mid", "1000001".to_string()),
326 ("page_num", "1".to_string()),
327 ("page_size", "5".to_string())
328 ]
329 );
330 Ok(())
331 }
332
333 #[test]
334 fn series_info_params_rejects_zero_series_id() {
335 let err = VideoCollectionSeriesInfoParams::new(0).unwrap_err();
336
337 assert!(matches!(
338 err,
339 BpiError::InvalidParameter {
340 field: "series_id",
341 ..
342 }
343 ));
344 }
345
346 #[test]
347 fn series_archives_params_serializes_optional_filters() -> BpiResult<()> {
348 let params = VideoCollectionSeriesArchivesParams::new(Mid::new(1000001)?, 250285)?
349 .with_sort(CollectionArchiveSort::Asc)
350 .with_page_num(1)?
351 .with_page_size(10)?;
352
353 assert_eq!(
354 params.query_pairs(),
355 vec![
356 ("mid", "1000001".to_string()),
357 ("series_id", "250285".to_string()),
358 ("sort", "asc".to_string()),
359 ("pn", "1".to_string()),
360 ("ps", "10".to_string())
361 ]
362 );
363 Ok(())
364 }
365}