shift_client/types/rewards/
rewards_page.rs1use crate::util::extract_csrf_token;
2use once_cell::sync::Lazy;
3use scraper::{ElementRef, Html, Selector};
4
5static ALERT_NOTICE_SELECTOR: Lazy<Selector> =
6 Lazy::new(|| Selector::parse(".alert.notice p").expect("invalid ALERT_NOTICE_SELECTOR"));
7
8#[derive(Debug, thiserror::Error)]
10pub enum FromHtmlError {
11 #[error("missing csrf token")]
13 MissingCsrfToken,
14
15 #[error("invalid alert notice")]
17 InvalidAlertNotice(#[from] FromElementError),
18}
19
20#[derive(Debug)]
22pub struct RewardsPage {
23 pub csrf_token: String,
25
26 pub alert_notice: Option<AlertNotice>,
28}
29
30impl RewardsPage {
31 pub(crate) fn from_html(html: &Html) -> Result<Self, FromHtmlError> {
33 let csrf_token = extract_csrf_token(html)
34 .ok_or(FromHtmlError::MissingCsrfToken)?
35 .to_string();
36
37 let alert_notice = html
38 .select(&ALERT_NOTICE_SELECTOR)
39 .next()
40 .map(AlertNotice::from_element)
41 .transpose()?;
42
43 Ok(Self {
44 csrf_token,
45 alert_notice,
46 })
47 }
48}
49
50#[derive(Debug, thiserror::Error)]
52pub enum FromElementError {
53 #[error("missing text")]
55 MissingText,
56
57 #[error("unknown text '{0}'")]
58 UnknownText(String),
59}
60
61#[derive(Debug)]
62pub enum AlertNotice {
63 ShiftCodeAlreadyRedeemed,
65
66 LaunchShiftGame,
68
69 ShiftCodeRedeemed,
71
72 ShiftCodeRedeemFail,
74}
75
76impl AlertNotice {
77 fn from_element(el: ElementRef) -> Result<Self, FromElementError> {
79 let text = el.text().next().ok_or(FromElementError::MissingText)?;
80
81 match text {
82 "This SHiFT code has already been redeemed" => Ok(Self::ShiftCodeAlreadyRedeemed),
83 "To continue to redeem SHiFT codes, please launch a SHiFT-enabled title first!" => {
84 Ok(Self::LaunchShiftGame)
85 }
86 "Your code was successfully redeemed" => Ok(Self::ShiftCodeRedeemed),
87 "Failed to redeem your SHiFT code" => Ok(Self::ShiftCodeRedeemFail),
88 _ => Err(FromElementError::UnknownText(text.to_string())),
89 }
90 }
91}
92
93#[cfg(test)]
94mod test {
95 use super::*;
96
97 const SAMPLE_1: &str = include_str!("../../../test_data/rewards_page_1.html");
98
99 #[test]
100 fn sample_1() {
101 let html = Html::parse_document(SAMPLE_1);
102 let _page = RewardsPage::from_html(&html).unwrap();
103 }
104}