covid_tracker/
lib.rs

1mod conversions;
2mod models;
3
4use std::{cmp::min, error::Error};
5use crate::models::{country_model::Country, arguments::Arguments};
6use clap::Parser;
7use scraper::{ElementRef, Html, Selector};
8
9use crate::conversions::conversion;
10
11const URL: &str = "https://www.worldometers.info/coronavirus/";
12
13pub async fn run() -> Result<(), Box<dyn Error>> {
14    let args = get_parsed_arguments();
15    let address_string = String::from("https://www.worldometers.info/coronavirus/");
16    let resp = get_raw_text().await?;
17
18    let fragment = Html::parse_fragment(&resp);
19    let main_table = Selector::parse("table#main_table_countries_today tbody").unwrap();
20    let table_fragment = fragment.select(&main_table).next().unwrap();
21    let world_row_selector = Selector::parse("tr.total_row_world:not([data-continent])")
22        .expect("Could not find the world row");
23    let country_row_selector =
24        Selector::parse(r#"tr[style=""]"#).expect("Could not find the country rows");
25
26    let world_rows: Vec<ElementRef> = table_fragment.select(&world_row_selector).collect();
27    let rows: Vec<ElementRef> = table_fragment.select(&country_row_selector).collect();
28
29    let mut items = parse_for_countries(rows);
30    items.sort_by(|a, b| b.total_cases.cmp(&a.total_cases));
31
32    let row_count: usize = min(args.count.into(), items.len());
33    if !args.world_stats {
34        let mut temp_items = parse_for_countries(world_rows);
35        temp_items.append(&mut items);
36        items = temp_items;
37    }
38
39    println!(
40        "\n\nSource: {} as of {}",
41        address_string,
42        chrono::Local::now().to_rfc2822()
43    );
44    let table = conversion::convert_row_items_for_table(&items[0..row_count], args.show_full);
45    println!("{}", table.display().unwrap());
46    Ok(())
47}
48
49async fn get_raw_text() -> Result<String, reqwest::Error> {
50    let address_string = String::from(URL);
51     reqwest::get(&address_string).await?.text().await
52}
53fn get_parsed_arguments() -> Arguments {
54    Arguments::parse()
55}
56
57fn parse_for_countries(raw_data: Vec<ElementRef>) -> Vec<Country> {
58    let mut parsed: Vec<Country> = Vec::new();
59    for row in raw_data {
60        let td_sel = Selector::parse("td").expect("error parsing row");
61        let tds: Vec<ElementRef> = row.select(&td_sel).collect();
62        parsed.push(Country::build(&tds))
63    }
64    parsed
65}