Skip to main content

bpi_rs/login/
params.rs

1use crate::ids::Mid;
2use crate::{BpiError, BpiResult};
3
4/// Parameters for `/x/safecenter/login_notice`.
5#[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/// Parameters for `/x/member/web/login/log`.
32#[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/// Parameters for `/x/passport-login/web/qrcode/poll`.
71#[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}