1use crate::ids::Mid;
2use crate::{BpiError, BpiResult};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct LoginNoticeParams {
7 mid: Mid,
8 buvid: Option<String>,
9}
10
11impl LoginNoticeParams {
12 pub fn new(mid: Mid) -> Self {
13 Self { mid, buvid: None }
14 }
15
16 pub fn with_buvid(mut self, buvid: impl Into<String>) -> BpiResult<Self> {
17 self.buvid = Some(normalize_non_blank("buvid", buvid.into())?);
18 Ok(self)
19 }
20
21 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
22 let mut query = vec![("mid", self.mid.to_string())];
23 if let Some(buvid) = &self.buvid {
24 query.push(("buvid", buvid.clone()));
25 }
26
27 query
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct LoginLogParams {
34 jsonp: String,
35 web_location: String,
36}
37
38impl Default for LoginLogParams {
39 fn default() -> Self {
40 Self {
41 jsonp: "jsonp".to_string(),
42 web_location: "333.33".to_string(),
43 }
44 }
45}
46
47impl LoginLogParams {
48 pub fn new() -> Self {
49 Self::default()
50 }
51
52 pub fn with_jsonp(mut self, jsonp: impl Into<String>) -> BpiResult<Self> {
53 self.jsonp = normalize_non_blank("jsonp", jsonp.into())?;
54 Ok(self)
55 }
56
57 pub fn with_web_location(mut self, web_location: impl Into<String>) -> BpiResult<Self> {
58 self.web_location = normalize_non_blank("web_location", web_location.into())?;
59 Ok(self)
60 }
61
62 pub(crate) fn query_pairs(&self) -> Vec<(&'static str, String)> {
63 vec![
64 ("jsonp", self.jsonp.clone()),
65 ("web_location", self.web_location.clone()),
66 ]
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct LoginQrPollParams {
73 qrcode_key: String,
74}
75
76impl LoginQrPollParams {
77 pub fn new(qrcode_key: impl Into<String>) -> BpiResult<Self> {
78 Ok(Self {
79 qrcode_key: normalize_non_blank("qrcode_key", qrcode_key.into())?,
80 })
81 }
82
83 pub(crate) fn query_pairs(&self) -> [(&'static str, String); 1] {
84 [("qrcode_key", self.qrcode_key.clone())]
85 }
86}
87
88fn normalize_non_blank(field: &'static str, value: String) -> BpiResult<String> {
89 let value = value.trim();
90 if value.is_empty() {
91 return Err(BpiError::invalid_parameter(field, "value cannot be blank"));
92 }
93
94 Ok(value.to_string())
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn login_notice_params_serializes_mid() -> BpiResult<()> {
103 let params = LoginNoticeParams::new(Mid::new(4279370)?);
104
105 assert_eq!(params.query_pairs(), vec![("mid", "4279370".to_string())]);
106 Ok(())
107 }
108
109 #[test]
110 fn login_notice_params_serializes_buvid() -> BpiResult<()> {
111 let params = LoginNoticeParams::new(Mid::new(4279370)?).with_buvid("BUVID3")?;
112
113 assert_eq!(
114 params.query_pairs(),
115 vec![
116 ("mid", "4279370".to_string()),
117 ("buvid", "BUVID3".to_string()),
118 ]
119 );
120 Ok(())
121 }
122
123 #[test]
124 fn login_notice_params_rejects_blank_buvid() {
125 let err = LoginNoticeParams::new(Mid::new(4279370).unwrap())
126 .with_buvid(" ")
127 .unwrap_err();
128
129 assert!(matches!(
130 err,
131 BpiError::InvalidParameter { field: "buvid", .. }
132 ));
133 }
134
135 #[test]
136 fn login_log_params_serializes_defaults() {
137 let params = LoginLogParams::new();
138
139 assert_eq!(
140 params.query_pairs(),
141 vec![
142 ("jsonp", "jsonp".to_string()),
143 ("web_location", "333.33".to_string()),
144 ]
145 );
146 }
147
148 #[test]
149 fn login_log_params_serializes_custom_location() -> BpiResult<()> {
150 let params = LoginLogParams::new().with_web_location("1550101")?;
151
152 assert_eq!(
153 params.query_pairs(),
154 vec![
155 ("jsonp", "jsonp".to_string()),
156 ("web_location", "1550101".to_string()),
157 ]
158 );
159 Ok(())
160 }
161
162 #[test]
163 fn login_qr_poll_params_serializes_qrcode_key() -> BpiResult<()> {
164 let params = LoginQrPollParams::new("qr-key-123")?;
165
166 assert_eq!(
167 params.query_pairs(),
168 [("qrcode_key", "qr-key-123".to_string())]
169 );
170 Ok(())
171 }
172
173 #[test]
174 fn login_qr_poll_params_rejects_blank_key() {
175 let err = LoginQrPollParams::new(" ").unwrap_err();
176
177 assert!(matches!(
178 err,
179 BpiError::InvalidParameter {
180 field: "qrcode_key",
181 ..
182 }
183 ));
184 }
185}