1use crate::ids::Aid;
2use crate::{BpiError, BpiResult};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct UpArchivesListParams {
7 page: u32,
8 page_size: Option<u32>,
9}
10
11impl UpArchivesListParams {
12 pub fn new(page: u32) -> BpiResult<Self> {
13 Ok(Self {
14 page: validate_non_zero_u32("pn", page)?,
15 page_size: None,
16 })
17 }
18
19 pub fn with_page_size(mut self, page_size: u32) -> BpiResult<Self> {
20 self.page_size = Some(validate_non_zero_u32("ps", page_size)?);
21 Ok(self)
22 }
23
24 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
25 let mut query = vec![("pn", self.page.to_string())];
26
27 if let Some(page_size) = self.page_size {
28 query.push(("ps", page_size.to_string()));
29 }
30
31 query
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub struct UpArchiveVideosParams {
38 aid: Aid,
39}
40
41impl UpArchiveVideosParams {
42 pub fn new(aid: Aid) -> Self {
43 Self { aid }
44 }
45
46 pub(crate) fn query_pairs(&self) -> [(&'static str, String); 1] {
47 [("aid", self.aid.to_string())]
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
53pub struct UpArchiveCompareParams {
54 timestamp: Option<u64>,
55 size: Option<u32>,
56}
57
58impl UpArchiveCompareParams {
59 pub fn new() -> Self {
60 Self::default()
61 }
62
63 pub fn with_timestamp(mut self, timestamp: u64) -> BpiResult<Self> {
64 self.timestamp = Some(validate_non_zero_u64("t", timestamp)?);
65 Ok(self)
66 }
67
68 pub fn with_size(mut self, size: u32) -> BpiResult<Self> {
69 self.size = Some(validate_non_zero_u32("size", size)?);
70 Ok(self)
71 }
72
73 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
74 let mut query = Vec::new();
75
76 if let Some(timestamp) = self.timestamp {
77 query.push(("t", timestamp.to_string()));
78 }
79
80 if let Some(size) = self.size {
81 query.push(("size", size.to_string()));
82 }
83
84 query
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum UpVideoTrendMetric {
91 Play,
92 Danmaku,
93 Reply,
94 Share,
95 Coin,
96 Favorite,
97 Charge,
98 Like,
99}
100
101impl UpVideoTrendMetric {
102 const fn code(self) -> u8 {
103 match self {
104 Self::Play => 1,
105 Self::Danmaku => 2,
106 Self::Reply => 3,
107 Self::Share => 4,
108 Self::Coin => 5,
109 Self::Favorite => 6,
110 Self::Charge => 7,
111 Self::Like => 8,
112 }
113 }
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub struct UpVideoTrendParams {
119 metric: UpVideoTrendMetric,
120}
121
122impl UpVideoTrendParams {
123 pub fn new(metric: UpVideoTrendMetric) -> Self {
124 Self { metric }
125 }
126
127 pub(crate) fn query_pairs(&self) -> [(&'static str, String); 1] {
128 [("type", self.metric.code().to_string())]
129 }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum UpArticleTrendMetric {
135 Read,
136 Reply,
137 Share,
138 Coin,
139 Favorite,
140 Like,
141}
142
143impl UpArticleTrendMetric {
144 const fn code(self) -> u8 {
145 match self {
146 Self::Read => 1,
147 Self::Reply => 2,
148 Self::Share => 3,
149 Self::Coin => 4,
150 Self::Favorite => 5,
151 Self::Like => 6,
152 }
153 }
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158pub struct UpArticleTrendParams {
159 metric: UpArticleTrendMetric,
160}
161
162impl UpArticleTrendParams {
163 pub fn new(metric: UpArticleTrendMetric) -> Self {
164 Self { metric }
165 }
166
167 pub(crate) fn query_pairs(&self) -> [(&'static str, String); 1] {
168 [("type", self.metric.code().to_string())]
169 }
170}
171
172fn validate_non_zero_u32(field: &'static str, value: u32) -> BpiResult<u32> {
173 if value == 0 {
174 return Err(BpiError::invalid_parameter(field, "value must be non-zero"));
175 }
176
177 Ok(value)
178}
179
180fn validate_non_zero_u64(field: &'static str, value: u64) -> BpiResult<u64> {
181 if value == 0 {
182 return Err(BpiError::invalid_parameter(field, "value must be non-zero"));
183 }
184
185 Ok(value)
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn up_archives_list_params_serializes_required_page() -> BpiResult<()> {
194 let params = UpArchivesListParams::new(1)?;
195
196 assert_eq!(params.query_pairs(), vec![("pn", "1".to_string())]);
197 Ok(())
198 }
199
200 #[test]
201 fn up_archives_list_params_serializes_optional_page_size() -> BpiResult<()> {
202 let params = UpArchivesListParams::new(2)?.with_page_size(20)?;
203
204 assert_eq!(
205 params.query_pairs(),
206 vec![("pn", "2".to_string()), ("ps", "20".to_string())]
207 );
208 Ok(())
209 }
210
211 #[test]
212 fn up_archives_list_params_rejects_zero_page() {
213 let err = UpArchivesListParams::new(0).unwrap_err();
214
215 assert!(matches!(
216 err,
217 BpiError::InvalidParameter { field: "pn", .. }
218 ));
219 }
220
221 #[test]
222 fn up_archives_list_params_rejects_zero_page_size() -> BpiResult<()> {
223 let err = UpArchivesListParams::new(1)?.with_page_size(0).unwrap_err();
224
225 assert!(matches!(
226 err,
227 BpiError::InvalidParameter { field: "ps", .. }
228 ));
229 Ok(())
230 }
231
232 #[test]
233 fn up_archive_videos_params_serializes_aid_query() -> BpiResult<()> {
234 let params = UpArchiveVideosParams::new(Aid::new(113602455409683)?);
235
236 assert_eq!(
237 params.query_pairs(),
238 [("aid", "113602455409683".to_string())]
239 );
240 Ok(())
241 }
242
243 #[test]
244 fn up_archive_compare_params_serializes_empty_defaults() {
245 let params = UpArchiveCompareParams::new();
246
247 assert!(params.query_pairs().is_empty());
248 }
249
250 #[test]
251 fn up_archive_compare_params_serializes_optional_filters() -> BpiResult<()> {
252 let params = UpArchiveCompareParams::new()
253 .with_timestamp(1_720_000_000)?
254 .with_size(3)?;
255
256 assert_eq!(
257 params.query_pairs(),
258 vec![("t", "1720000000".to_string()), ("size", "3".to_string())]
259 );
260 Ok(())
261 }
262
263 #[test]
264 fn up_archive_compare_params_rejects_zero_size() {
265 let err = UpArchiveCompareParams::new().with_size(0).unwrap_err();
266
267 assert!(matches!(
268 err,
269 BpiError::InvalidParameter { field: "size", .. }
270 ));
271 }
272
273 #[test]
274 fn up_video_trend_params_serializes_metric_code() {
275 let params = UpVideoTrendParams::new(UpVideoTrendMetric::Play);
276
277 assert_eq!(params.query_pairs(), [("type", "1".to_string())]);
278 }
279
280 #[test]
281 fn up_video_trend_metric_maps_like_code() {
282 let params = UpVideoTrendParams::new(UpVideoTrendMetric::Like);
283
284 assert_eq!(params.query_pairs(), [("type", "8".to_string())]);
285 }
286
287 #[test]
288 fn up_article_trend_params_serializes_metric_code() {
289 let params = UpArticleTrendParams::new(UpArticleTrendMetric::Read);
290
291 assert_eq!(params.query_pairs(), [("type", "1".to_string())]);
292 }
293
294 #[test]
295 fn up_article_trend_metric_maps_like_code() {
296 let params = UpArticleTrendParams::new(UpArticleTrendMetric::Like);
297
298 assert_eq!(params.query_pairs(), [("type", "6".to_string())]);
299 }
300}