1use crate::ids::{Aid, Cid, EpisodeId, SeasonId};
2use crate::models::{Fnval, VideoQuality};
3use crate::{BpiError, BpiResult};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum CheeseInfoId {
8 Season(SeasonId),
9 Episode(EpisodeId),
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct CheeseInfoParams {
15 id: CheeseInfoId,
16}
17
18impl CheeseInfoParams {
19 pub fn from_season_id(season_id: SeasonId) -> Self {
20 Self {
21 id: CheeseInfoId::Season(season_id),
22 }
23 }
24
25 pub fn from_episode_id(episode_id: EpisodeId) -> Self {
26 Self {
27 id: CheeseInfoId::Episode(episode_id),
28 }
29 }
30
31 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
32 match self.id {
33 CheeseInfoId::Season(season_id) => vec![("season_id", season_id.to_string())],
34 CheeseInfoId::Episode(episode_id) => vec![("ep_id", episode_id.to_string())],
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub struct CheeseEpListParams {
42 season_id: SeasonId,
43 page_size: Option<u32>,
44 page: Option<u32>,
45}
46
47impl CheeseEpListParams {
48 pub fn new(season_id: SeasonId) -> Self {
49 Self {
50 season_id,
51 page_size: None,
52 page: None,
53 }
54 }
55
56 pub fn with_page_size(mut self, page_size: u32) -> BpiResult<Self> {
57 self.page_size = Some(validate_positive("ps", page_size)?);
58 Ok(self)
59 }
60
61 pub fn with_page(mut self, page: u32) -> BpiResult<Self> {
62 self.page = Some(validate_positive("pn", page)?);
63 Ok(self)
64 }
65
66 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
67 let mut params = vec![("season_id", self.season_id.to_string())];
68
69 if let Some(page_size) = self.page_size {
70 params.push(("ps", page_size.to_string()));
71 }
72
73 if let Some(page) = self.page {
74 params.push(("pn", page.to_string()));
75 }
76
77 params
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub struct CheeseVideoStreamParams {
84 aid: Aid,
85 episode_id: EpisodeId,
86 cid: Cid,
87 quality: Option<VideoQuality>,
88 fnval: Option<Fnval>,
89}
90
91impl CheeseVideoStreamParams {
92 pub fn new(aid: Aid, episode_id: EpisodeId, cid: Cid) -> Self {
93 Self {
94 aid,
95 episode_id,
96 cid,
97 quality: None,
98 fnval: None,
99 }
100 }
101
102 pub fn with_quality(mut self, quality: VideoQuality) -> Self {
103 self.quality = Some(quality);
104 self
105 }
106
107 pub fn with_fnval(mut self, fnval: Fnval) -> Self {
108 self.fnval = Some(fnval);
109 self
110 }
111
112 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
113 let mut params = vec![
114 ("avid", self.aid.to_string()),
115 ("ep_id", self.episode_id.to_string()),
116 ("cid", self.cid.to_string()),
117 ("fnver", "0".to_string()),
118 ];
119
120 if self.fnval.is_some_and(|fnval| fnval.is_fourk()) {
121 params.push(("fourk", "1".to_string()));
122 }
123
124 if let Some(quality) = self.quality {
125 params.push(("qn", quality.as_u32().to_string()));
126 }
127
128 if let Some(fnval) = self.fnval {
129 params.push(("fnval", fnval.bits().to_string()));
130 }
131
132 params
133 }
134}
135
136fn validate_positive(field: &'static str, value: u32) -> BpiResult<u32> {
137 if value == 0 {
138 return Err(BpiError::invalid_parameter(
139 field,
140 "value must be greater than zero",
141 ));
142 }
143
144 Ok(value)
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn cheese_info_params_serializes_episode_id() -> BpiResult<()> {
153 let params = CheeseInfoParams::from_episode_id(EpisodeId::new(20_767)?);
154
155 assert_eq!(params.query_pairs(), vec![("ep_id", "20767".to_string())]);
156 Ok(())
157 }
158
159 #[test]
160 fn cheese_ep_list_params_serializes_optional_pagination() -> BpiResult<()> {
161 let params = CheeseEpListParams::new(SeasonId::new(556)?)
162 .with_page_size(50)?
163 .with_page(1)?;
164
165 assert_eq!(
166 params.query_pairs(),
167 vec![
168 ("season_id", "556".to_string()),
169 ("ps", "50".to_string()),
170 ("pn", "1".to_string()),
171 ]
172 );
173 Ok(())
174 }
175
176 #[test]
177 fn cheese_video_stream_params_serializes_required_query() -> BpiResult<()> {
178 let params = CheeseVideoStreamParams::new(
179 Aid::new(997_984_154)?,
180 EpisodeId::new(163_956)?,
181 Cid::new(1_183_682_680)?,
182 );
183
184 assert_eq!(
185 params.query_pairs(),
186 vec![
187 ("avid", "997984154".to_string()),
188 ("ep_id", "163956".to_string()),
189 ("cid", "1183682680".to_string()),
190 ("fnver", "0".to_string()),
191 ]
192 );
193 Ok(())
194 }
195}