ugc_scraper/parser/
mod.rs1use crate::{ParseError, Result};
2use scraper::{ElementRef, Selector};
3use steamid_ng::SteamID;
4use time::format_description::FormatItem;
5use time::macros::format_description;
6
7mod map_history;
8mod match_page;
9mod player;
10mod player_details;
11mod seasons;
12mod team;
13mod team_lookup;
14mod team_matches;
15mod team_roster_history;
16mod transactions;
17
18pub use map_history::*;
19pub use match_page::*;
20pub use player::*;
21pub use player_details::*;
22pub use seasons::*;
23pub use team::*;
24pub use team_lookup::*;
25pub use team_matches::*;
26pub use team_roster_history::*;
27pub use transactions::*;
28
29pub trait Parser {
30 type Output;
31 fn parse(&self, document: &str) -> Result<Self::Output>;
32}
33
34trait ElementExt<'a> {
35 fn first_text(&self) -> Option<&'a str>;
36}
37
38impl<'a> ElementExt<'a> for ElementRef<'a> {
39 fn first_text(&self) -> Option<&'a str> {
40 self.text().map(str::trim).find(|s| !s.is_empty())
41 }
42}
43
44fn select_text<'a>(el: ElementRef<'a>, selector: &Selector) -> Option<&'a str> {
45 el.select(selector)
46 .next()
47 .and_then(|item| item.text().find(|s| !s.trim().is_empty()))
48 .map(str::trim)
49}
50
51fn select_last_text<'a>(el: ElementRef<'a>, selector: &Selector) -> Option<&'a str> {
52 el.select(selector)
53 .next()
54 .and_then(|item| item.text().last())
55 .map(str::trim)
56}
57
58const DATE_FORMAT: &[FormatItem<'static>] =
59 format_description!("[month padding:none]/[day padding:none]/[year]");
60const MEMBER_DATE_FORMAT: &[FormatItem<'static>] = format_description!(
61 "[month repr:short] [day padding:none], [year]\n/\n[hour padding:none]:[minute] [period]\n(ET)"
62);
63const MEMBER_DATE_ALT_FORMAT: &[FormatItem<'static>] =
64 format_description!("[month repr:short] [day padding:none], [year]");
65const ROSTER_HISTORY_DATE_FORMAT: &[FormatItem<'static>] =
66 format_description!("[month repr:short] [day padding:none], [year]");
67
68fn team_id_from_link(link: &str) -> Result<u32, ParseError> {
69 link.rsplit_once('=')
70 .and_then(|part| part.1.parse().ok())
71 .ok_or_else(|| ParseError::InvalidLink {
72 link: link.to_string(),
73 role: "team id",
74 })
75}
76
77fn match_id_from_link(link: &str) -> Result<u32, ParseError> {
78 link.rsplit_once('=')
79 .and_then(|part| part.1.parse().ok())
80 .ok_or_else(|| ParseError::InvalidLink {
81 link: link.to_string(),
82 role: "match id",
83 })
84}
85
86fn steam_id_from_link(link: &str) -> Result<SteamID, ParseError> {
87 link.rsplit_once('=')
88 .and_then(|part| part.1.parse::<u64>().ok())
89 .ok_or_else(|| ParseError::InvalidLink {
90 link: link.to_string(),
91 role: "user id",
92 })
93 .map(SteamID::from)
94}