rodalies_cli/rodalies/
client.rs

1use clap::crate_version;
2use scraper::Html;
3use std::{error::Error, fs, time::Duration};
4use surf::{Client, Config, Response, StatusCode, Url};
5
6/// Configures and returns the HTTP client that will interact with the `rodalies.gencat.cat` site.
7pub fn init_client() -> Client {
8    let rodalies_url = "https://rodalies.gencat.cat";
9
10    Config::new()
11        .set_base_url(Url::parse(rodalies_url).unwrap())
12        .set_timeout(Some(Duration::from_secs(5)))
13        .try_into()
14        .unwrap()
15}
16
17/// Returns the HTML body parsed of the main search page.
18pub async fn get_search_page(client: &Client) -> Result<Html, Box<dyn Error>> {
19    let mut response = client
20        .get("/en/horaris")
21        .header(
22            "User-Agent",
23            format!(
24                "rodalies-cli/{} (github.com/gerardcl/rodalies-cli)",
25                crate_version!()
26            ),
27        )
28        .await?;
29
30    let body_response = get_page_body(&mut response).await?;
31
32    Ok(Html::parse_document(&body_response))
33}
34
35/// Returns the HTML body parsed of the timetable searched result page.
36pub async fn get_timetable_page(
37    client: &Client,
38    from: String,
39    to: String,
40    date: String,
41) -> Result<Html, Box<dyn Error>> {
42    let mut response = client
43        .post("/en/horaris")
44        .header(
45            "User-Agent",
46            format!(
47                "rodalies-cli/{} (github.com/gerardcl/rodalies-cli)",
48                crate_version!()
49            ),
50        )
51        .content_type("application/x-www-form-urlencoded")
52        .body_string(format!(
53            "origen={}&desti={}&dataViatge={}&horaIni=00&lang=en&cercaRodalies=true&tornada=false",
54            from, to, date
55        ))
56        .await?;
57
58    let body_response = get_page_body(&mut response).await?;
59
60    Ok(Html::parse_document(&body_response))
61}
62
63/// Returns the raw body of the provided HTTP response.
64async fn get_page_body(response: &mut Response) -> Result<String, Box<dyn Error>> {
65    let error = match response.status() {
66        StatusCode::Ok => false,
67        _ => {
68            println!(
69                "⛔ Rodalies server failed with HTTP Status: {}",
70                response.status()
71            );
72            true
73        }
74    };
75
76    if error {
77        return Err(
78            ("🚨 Please, try again later or open an issue if the error persists...").into(),
79        );
80    }
81
82    Ok(response.body_string().await?)
83}
84
85pub fn get_html_from_file(file_path: &str) -> Result<Html, Box<dyn Error>> {
86    let html_file = fs::read_to_string(file_path).expect("Should have been able to read the file");
87    Ok(Html::parse_document(&html_file))
88}
89
90#[cfg(test)]
91mod tests {
92    use super::init_client;
93    use surf::Url;
94
95    #[test]
96    fn test_init_client_with_rodalies_web() {
97        let client = init_client();
98        let expected_url = "https://rodalies.gencat.cat";
99        assert!(client
100            .config()
101            .base_url
102            .eq(&Some(Url::parse(expected_url).unwrap())));
103    }
104}