use error_chain::bail;
use scraper::html::Html;
use scraper::selector::Selector;
use std::collections::HashMap;
use url::Url;
use crate::error::*;
use crate::model::*;
pub trait ParkParser {
fn new() -> Self;
fn get_ride_times(&self, html: &str) -> Result<Vec<RideTime>>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct GenericParkParser {
selector: Selector,
}
impl ParkParser for GenericParkParser {
fn new() -> Self {
let selector = Selector::parse("nav.panel > a > span:not(.has-text-grey)").unwrap();
GenericParkParser { selector }
}
fn get_ride_times(&self, html: &str) -> Result<Vec<RideTime>> {
let html = Html::parse_document(&html);
let all_rides = html.select(&self.selector);
let mut ride_times = Vec::new();
let mut name_being_processed = true;
let mut temp_ride_time = RideTime::default();
let mut all_rides = all_rides.peekable();
while let Some(span) = all_rides.next() {
if name_being_processed {
let next = all_rides.peek();
if let Some(next) = next {
let next_str = next.text().collect::<String>();
let should_break = match next_str.as_str().trim() {
"Closed" => false,
"Open" => false,
time if time.ends_with("mins") => false,
_ => true,
};
if should_break {
continue;
}
}
}
if name_being_processed {
temp_ride_time.name = span.text().next().unwrap().trim().to_owned();
name_being_processed = false;
} else {
let status_str: String = span.text().collect();
let status = match status_str.trim() {
"Closed" => RideStatus::Closed,
"Open" => RideStatus::Open,
time if time.ends_with("mins") => {
let split_str_min = time.split_ascii_whitespace().next();
if split_str_min.is_none() {
bail!(ErrorKind::WaitTimeParse(time.to_string()))
}
let time_int_res = split_str_min.unwrap().parse::<i16>();
match time_int_res {
Ok(time) => RideStatus::Wait(time.unsigned_abs()),
Err(_) => {
bail!(ErrorKind::WaitTimeParse(time.to_string()))
}
}
}
_ => {
bail!(ErrorKind::WaitTimeParse("".to_string()))
}
};
temp_ride_time.status = status;
ride_times.push(std::mem::take(&mut temp_ride_time));
name_being_processed = true
}
}
Ok(ride_times)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FrontPageParser {
selector: Selector,
}
impl FrontPageParser {
const BASE_URL: &'static str = "https://queue-times.com";
pub fn new() -> Self {
let selector = Selector::parse(".panel-block").unwrap();
FrontPageParser { selector }
}
pub fn get_park_urls(&self, html: &str) -> Result<HashMap<String, Url>> {
let mut park_to_url = HashMap::new();
let html = Html::parse_document(&html);
let parks = html.select(&self.selector);
let mut temp_park = (String::default(), Url::parse(Self::BASE_URL).unwrap());
for park in parks {
let link = park.value().attr("href");
match link {
None => {
bail!(ErrorKind::HrefMissing)
}
Some(link) => {
temp_park.1 = Url::parse(Self::BASE_URL)?
.join(&(link.to_string() + "/"))?
.join("queue_times")?; }
}
let park_name: String = park.text().next().unwrap().to_string();
temp_park.0 = park_name.trim().to_owned();
park_to_url.insert(temp_park.0, temp_park.1);
}
Ok(park_to_url)
}
}
impl Default for FrontPageParser {
fn default() -> Self {
let selector = Selector::parse(".panel-block").unwrap();
FrontPageParser { selector }
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn cedar_point_parses() {
let html_str = reqwest::blocking::get("https://queue-times.com/en-US/parks/50/queue_times")
.unwrap()
.text()
.unwrap();
let parser = GenericParkParser::new();
let rides = parser.get_ride_times(&html_str).unwrap();
assert!(!rides.is_empty(), "No rides were parsed.");
println!("{:?}", rides)
}
#[test]
fn blackpool_parses() {
let html = reqwest::blocking::get("https://queue-times.com/en-US/parks/273/queue_times")
.unwrap()
.text()
.unwrap();
let parser = GenericParkParser::new();
let rides = parser.get_ride_times(&html).unwrap();
assert!(!rides.is_empty(), "No rides were parsed.");
println!("{:?}", rides)
}
#[test]
fn laronde_parses() {
let html = reqwest::blocking::get("https://queue-times.com/en-US/parks/48/queue_times")
.unwrap()
.text()
.unwrap();
let parser = GenericParkParser::new();
let rides = parser.get_ride_times(&html).unwrap();
assert!(!rides.is_empty(), "No rides were parsed.");
println!("{:?}", rides)
}
#[test]
fn frontier_city_parses() {
let html = reqwest::blocking::get("https://queue-times.com/en-US/parks/282/queue_times")
.unwrap()
.text()
.unwrap();
let parser = GenericParkParser::new();
let rides = parser.get_ride_times(&html).unwrap();
assert!(!rides.is_empty(), "No rides were parsed.");
println!("{:?}", rides)
}
#[test]
fn front_page_parses() {
let html = reqwest::blocking::get("https://queue-times.com/en-US/parks/")
.unwrap()
.text()
.unwrap();
assert!(!html.is_empty(), "Front page was not fetched!");
let parser = FrontPageParser::new();
let parks = parser.get_park_urls(&html).unwrap();
reqwest::blocking::get(parks.get("Cedar Point").unwrap().to_owned()).unwrap();
println!("{:?}", parks)
}
}