Skip to main content

bpi_rs/video/collection/
params.rs

1use crate::ids::{Mid, SeasonId};
2use crate::{BpiError, BpiResult};
3
4/// 视频合集稿件排序方式。
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum CollectionArchiveSort {
7    /// 最早的在前。
8    Asc,
9    /// 最新的在前。
10    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/// `/x/polymer/web-space/seasons_archives_list` 的参数。
23#[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    /// 为指定用户的视频合集创建参数。
34    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    /// 设置稿件列表是否按倒序返回。
45    pub fn with_sort_reverse(mut self, sort_reverse: bool) -> Self {
46        self.sort_reverse = Some(sort_reverse);
47        self
48    }
49
50    /// 设置页码。
51    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    /// 设置每页数量。
57    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/// `/x/polymer/web-space/home/seasons_series` 的参数。
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct VideoCollectionHomeSeasonsSeriesParams {
81    mid: Mid,
82    page_num: u64,
83    page_size: u64,
84}
85
86impl VideoCollectionHomeSeasonsSeriesParams {
87    /// 为主页合集/系列接口创建参数。
88    pub fn new(mid: Mid) -> Self {
89        Self {
90            mid,
91            page_num: 1,
92            page_size: 10,
93        }
94    }
95
96    /// 设置页码。
97    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    /// 设置每页数量。
103    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/// `/x/polymer/web-space/seasons_series_list` 的参数。
118#[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    /// 为合集和系列列表接口创建参数。
127    pub fn new(mid: Mid) -> Self {
128        Self {
129            mid,
130            page_num: None,
131            page_size: None,
132        }
133    }
134
135    /// 设置页码。
136    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    /// 设置每页数量。
142    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/// `/x/series/series` 的参数。
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct VideoCollectionSeriesInfoParams {
164    series_id: u64,
165}
166
167impl VideoCollectionSeriesInfoParams {
168    /// 为指定视频系列创建参数。
169    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/// `/x/series/archives` 的参数。
181#[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    /// 为指定系列内的稿件创建参数。
193    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    /// 控制是否只返回普通稿件。
205    pub fn with_only_normal(mut self, only_normal: bool) -> Self {
206        self.only_normal = Some(only_normal);
207        self
208    }
209
210    /// 设置稿件排序方式。
211    pub fn with_sort(mut self, sort: CollectionArchiveSort) -> Self {
212        self.sort = Some(sort);
213        self
214    }
215
216    /// 设置页码。
217    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    /// 设置每页数量。
223    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}