etwin_scraper_tools/
lib.rs1use scraper::ElementRef;
2use thiserror::Error;
3
4pub trait ElementRefExt<'a> {
5 fn get_opt_text(&self) -> Result<Option<&'a str>, TextNodeExcess>;
6 fn get_one_text(&self) -> Result<&'a str, &'static str>;
7}
8
9impl<'a> ElementRefExt<'a> for ElementRef<'a> {
10 fn get_opt_text(&self) -> Result<Option<&'a str>, TextNodeExcess> {
11 get_opt_text(*self)
12 }
13
14 fn get_one_text(&self) -> Result<&'a str, &'static str> {
15 get_one_text(*self)
16 }
17}
18
19#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Error)]
20#[error("expected at most {} text nodes, but found strictly more", .max_expected)]
21pub struct TextNodeExcess {
22 pub max_expected: usize,
23}
24
25pub fn get_opt_text(node: ElementRef) -> Result<Option<&str>, TextNodeExcess> {
26 let mut it = node.text();
27 match (it.next(), it.next()) {
28 (t, None) => Ok(t),
29 (_, Some(_)) => Err(TextNodeExcess { max_expected: 1 }),
30 }
31}
32
33pub fn get_one_text(node: ElementRef) -> Result<&str, &'static str> {
34 let mut it = node.text();
35 match (it.next(), it.next()) {
36 (None, None) => Err("TooFewTextNodes: expected 1, got 0"),
37 (Some(t), None) => Ok(t),
38 (_, Some(_)) => Err("TooManyTextNodes: expected 1, got 2 or more"),
39 }
40}
41
42#[derive(Debug, Copy, Clone, Eq, PartialEq)]
43pub struct FlashVars<'a>(&'a str);
44
45#[derive(Debug, Clone)]
46pub struct FlashVarsIter<'a>(std::iter::Map<std::str::Split<'a, char>, for<'r> fn(&'r str) -> (&'r str, &'r str)>);
47
48impl<'a> FlashVars<'a> {
49 pub fn new(r: &'a str) -> Self {
50 Self(r)
51 }
52}
53
54impl<'a> IntoIterator for FlashVars<'a> {
55 type Item = (&'a str, &'a str);
56 type IntoIter = FlashVarsIter<'a>;
57
58 fn into_iter(self) -> Self::IntoIter {
59 FlashVarsIter(self.0.split('&').map(|item| item.split_once('=').unwrap_or((item, ""))))
60 }
61}
62
63impl<'a> Iterator for FlashVarsIter<'a> {
64 type Item = (&'a str, &'a str);
65
66 fn next(&mut self) -> Option<Self::Item> {
67 self.0.next()
68 }
69}
70
71#[macro_export]
72macro_rules! selector {
73 ($selector:literal $(,)?) => {{
74 static SELECTOR: ::once_cell::race::OnceBox<::scraper::Selector> = ::once_cell::race::OnceBox::new();
75 SELECTOR.get_or_init(|| match Selector::parse($selector) {
76 Ok(selector) => Box::new(selector),
77 Err(e) => {
78 panic!("invalid selector {:?}: {:?}", $selector, e)
79 }
80 })
81 }};
82}