use crate::errors::FejError; use chrono::DateTime; use chrono_tz::Tz; use reqwest::blocking as reqwest; use std::collections::HashMap; use std::convert::{From, TryFrom}; mod basic_date; mod pickup_time; mod street; pub use basic_date::BasicDate; pub use pickup_time::PickupTime; pub use street::Street; /// Endpoint for the search feature const SEARCH_URL: &str = "https://www.ivago.be/nl/particulier/autocomplete/garbage/streets"; /// Endpoint for populating the initial cookies const BASE_URL: &str = "https://www.ivago.be/nl/particulier/afval/ophaling"; /// Endpoint for the actual calendar output const CAL_URL: &str = "https://www.ivago.be/nl/particulier/garbage/pick-up/pickups"; /// Searches the Ivago API for streets in the given city. /// /// # Arguments /// /// * `search_term` - Search term to use to look for streets pub fn search_streets(search_term: &str) -> Result, FejError> { let client = reqwest::Client::new(); let response = client.get(SEARCH_URL).query(&[("q", search_term)]).send()?; let data: Vec> = response.json()?; // This is pretty cool, filter_map first does get() on all the maps, and // then filters out any None values // Then, we do the same thing for streets Ok(data .iter() .filter_map(|m| m.get("value")) .filter_map(|v| Street::try_from(v.as_str()).ok()) .collect()) } /// Returns the pickup times for the various trash types. /// /// # Arguments /// /// * `street` - Street to look up /// * `number` - House number in given street /// * `start_date` - Earliest date for the results /// * `end_date` - Latest date for the results pub fn get_pickup_times( street: &Street, number: &u32, start_date: &DateTime, end_date: &DateTime, ) -> Result, FejError> { let client = reqwest::Client::builder().cookie_store(true).build()?; // This populates the cookies with the necessary values client .post(BASE_URL) .form(&[ ("garbage_type", ""), ("ivago_street", &String::from(street)), ("number", &number.to_string()), ("form_id", "garbage_address_form"), ]) .send()?; let response = client .get(CAL_URL) .query(&[ ("_format", "json"), ("type", ""), ("start", &start_date.timestamp().to_string()), ("end", &end_date.timestamp().to_string()), ]) .send()?; let data: Vec> = response.json()?; let mut output: Vec = Vec::new(); for map in data .iter() .filter(|m| m.contains_key("date") && m.contains_key("label")) { // Because we filtered the maps in the loop, we can safely use unwrap // here if let Ok(date) = BasicDate::try_from(map.get("date").unwrap().as_str()) { output.push(PickupTime::new(date, map.get("label").unwrap().to_string())) } } Ok(output) }