reddit_tube/types/
main_page.rs1use once_cell::sync::Lazy;
2use scraper::{
3 Html,
4 Selector,
5};
6
7#[derive(Debug, thiserror::Error)]
9pub enum FromHtmlError {
10 #[error("missing download form")]
12 MissingDownloadForm,
13
14 #[error("missing csrf data")]
16 MissingCsrf,
17}
18
19#[derive(Debug)]
21pub struct MainPage {
22 pub csrf_key: String,
24
25 pub csrf_value: String,
27}
28
29impl MainPage {
30 pub(crate) fn from_html(html: &Html) -> Result<Self, FromHtmlError> {
32 static DOWNLOAD_FORM_SELECTOR: Lazy<Selector> = Lazy::new(|| {
33 Selector::parse("#download-form").expect("invalid download form selector")
34 });
35 static CSRF_SELECTOR: Lazy<Selector> =
36 Lazy::new(|| Selector::parse("[name][value]").expect("invalid csrf selector"));
37
38 let download_form = html
39 .select(&DOWNLOAD_FORM_SELECTOR)
40 .next()
41 .ok_or(FromHtmlError::MissingDownloadForm)?;
42
43 let (csrf_key, csrf_value) = download_form
44 .select(&CSRF_SELECTOR)
45 .filter_map(|element| {
46 let value = element.value();
47 Some((value.attr("name")?, value.attr("value")?))
48 })
49 .find(|(name, _)| name != &"url")
50 .ok_or(FromHtmlError::MissingCsrf)?;
51
52 Ok(MainPage {
53 csrf_key: csrf_key.to_string(),
54 csrf_value: csrf_value.to_string(),
55 })
56 }
57}
58
59#[cfg(test)]
60mod test {
61 use super::*;
62
63 const SAMPLE_1: &str = include_str!("../../test_data/main_page.html");
64
65 #[test]
66 fn parse() {
67 let html = Html::parse_document(SAMPLE_1);
68 let page = MainPage::from_html(&html).expect("failed to parse main page sample 1");
69 dbg!(page);
70 }
71}