Add more readings, remove description
This commit is contained in:
parent
10b376e06e
commit
bc2f19efd8
1 changed files with 96 additions and 33 deletions
129
src/main.rs
129
src/main.rs
|
@ -8,10 +8,18 @@ use colored::Colorize;
|
|||
struct Arguments {
|
||||
#[clap(help = "Plume Labs location ID. For example, https://air.plumelabs.com/air-quality-in-XXX")]
|
||||
location: String,
|
||||
#[clap(short, long, help = "Display raw Air Quality Index")]
|
||||
#[clap(short, long, help = "Display raw value without descriptor")]
|
||||
raw: bool,
|
||||
#[clap(short, long, help = "Display AccuWeather Air Quality Index description")]
|
||||
description: bool,
|
||||
#[clap(short, long, help = "Display raw concentration in µg/m³")]
|
||||
concentration: bool,
|
||||
#[clap(short = 'p', long = "pm25", help = "Display fine PM2.5 inhalable particulate matter")]
|
||||
fine_particulate: bool,
|
||||
#[clap(short = 'P', long = "pm10", help = "Display coarse PM10 inhalable particulate matter")]
|
||||
coarse_particulate: bool,
|
||||
#[clap(short, long = "no2", help = "Display nitrogen dioxide NO2")]
|
||||
nitrogen_dioxide: bool,
|
||||
#[clap(short, long = "o3", help = "Display ozone O3")]
|
||||
ozone: bool,
|
||||
}
|
||||
|
||||
#[derive(From, Debug)]
|
||||
|
@ -21,26 +29,91 @@ enum Error {
|
|||
InvalidResponse,
|
||||
}
|
||||
|
||||
struct Reading {
|
||||
concentration: u32,
|
||||
aqi: u32,
|
||||
}
|
||||
|
||||
struct Data {
|
||||
aqi: u32,
|
||||
fine_particulate: Reading,
|
||||
coarse_particulate: Reading,
|
||||
nitrogen_dioxide: Reading,
|
||||
ozone: Reading,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
fn fetch(location: &str) -> Result<Self, Error> {
|
||||
let response = reqwest::blocking::get(format!("https://air.plumelabs.com/air-quality-in-{location}"))?;
|
||||
if !response.status().is_success() {
|
||||
return Err(Error::HttpError);
|
||||
}
|
||||
let content = response.text_with_charset("utf-8")?;
|
||||
let document = Html::parse_document(&content);
|
||||
let get_value = |selector: &str| {
|
||||
let selector = selector.to_owned();
|
||||
let selector = Selector::parse(&selector).unwrap();
|
||||
Ok(match match document.select(&selector).next() {
|
||||
Some(element) => element,
|
||||
None => return Err(Error::InvalidResponse),
|
||||
}.inner_html().trim().parse::<f32>() {
|
||||
Ok(aqi) => aqi,
|
||||
Err(_) => return Err(Error::InvalidResponse),
|
||||
} as u32)
|
||||
};
|
||||
let get_reading = |name| -> Result<Reading, Error> {
|
||||
Ok(Reading {
|
||||
concentration: get_value(&format!("li[data-id=\"{name}\"][data-format=\"value_upm\"] div[data-role=\"pollutant-level\"]"))?,
|
||||
aqi: get_value(&format!("li[data-id=\"{name}\"][data-format=\"pi\"] div[data-role=\"pollutant-level\"]"))?,
|
||||
})
|
||||
};
|
||||
Ok(Self {
|
||||
aqi: get_value("span[data-role=\"current-pi\"")?,
|
||||
fine_particulate: get_reading("PM25")?,
|
||||
coarse_particulate: get_reading("PM10")?,
|
||||
nitrogen_dioxide: get_reading("NO2")?,
|
||||
ozone: get_reading("O3")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let arguments = Arguments::parse();
|
||||
let response = reqwest::blocking::get(format!("https://air.plumelabs.com/air-quality-in-{}", arguments.location))?;
|
||||
if !response.status().is_success() {
|
||||
return Err(Error::HttpError);
|
||||
}
|
||||
let content = response.text_with_charset("utf-8")?;
|
||||
let document = Html::parse_document(&content);
|
||||
let selector = Selector::parse("span[data-role=\"current-pi\"]").unwrap();
|
||||
let aqi = match match document.select(&selector).next() {
|
||||
Some(element) => element,
|
||||
None => return Err(Error::InvalidResponse),
|
||||
}.inner_html().trim().parse::<f32>() {
|
||||
Ok(aqi) => aqi,
|
||||
Err(_) => return Err(Error::InvalidResponse),
|
||||
} as u32;
|
||||
let data = Data::fetch(&arguments.location)?;
|
||||
let descriptor;
|
||||
let Reading { aqi, concentration } = if arguments.fine_particulate {
|
||||
descriptor = "PM2.5 ";
|
||||
data.fine_particulate
|
||||
} else if arguments.coarse_particulate {
|
||||
descriptor = "PM10 ";
|
||||
data.coarse_particulate
|
||||
} else if arguments.nitrogen_dioxide {
|
||||
descriptor = "NO2 ";
|
||||
data.nitrogen_dioxide
|
||||
} else if arguments.ozone {
|
||||
descriptor = "O3 ";
|
||||
data.ozone
|
||||
} else {
|
||||
descriptor = "";
|
||||
Reading {
|
||||
aqi: data.aqi,
|
||||
concentration: data.aqi,
|
||||
}
|
||||
};
|
||||
if arguments.raw {
|
||||
println!("{}", aqi);
|
||||
println!("{}", if arguments.concentration {
|
||||
concentration
|
||||
} else {
|
||||
aqi
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
let display = if arguments.concentration {
|
||||
format!("{concentration} µg/m³")
|
||||
} else {
|
||||
format!("{aqi} AQI")
|
||||
};
|
||||
print!("{}", descriptor);
|
||||
println!("{} {}",
|
||||
match aqi {
|
||||
250.. => "Dangerous".purple(),
|
||||
|
@ -52,22 +125,12 @@ fn main() -> Result<(), Error> {
|
|||
}.bold(),
|
||||
match aqi {
|
||||
// 250.. => (no appropriate terminal color) // Dangerous 250+
|
||||
150.. => aqi.to_string().purple(), // Very Unhealthy 150-249
|
||||
100.. => aqi.to_string().magenta(), // Unhealthy 100-149
|
||||
50.. => aqi.to_string().red(), // Poor 50-99
|
||||
20.. => aqi.to_string().yellow(), // Fair 20-49
|
||||
0.. => aqi.to_string().green(), // Excellent 0-19
|
||||
150.. => display.purple(), // Very Unhealthy 150-249
|
||||
100.. => display.magenta(), // Unhealthy 100-149
|
||||
50.. => display.red(), // Poor 50-99
|
||||
20.. => display.yellow(), // Fair 20-49
|
||||
0.. => display.green(), // Excellent 0-19
|
||||
}.bold()
|
||||
);
|
||||
if arguments.description {
|
||||
println!("{}", match aqi {
|
||||
250.. => "Any exposure to the air, even for a few minutes, can lead to serious health effects on everybody. Avoid outdoor activities.",
|
||||
150.. => "Health effects will be immediately felt by sensitive groups and should avoid outdoor activity. Healthy individuals are likely to experience difficulty breathing and throat irritation; consider staying indoors and rescheduling outdoor activities.",
|
||||
100.. => "Health effects can be immediately felt by sensitive groups. Healthy individuals may experience difficulty breathing and throat irritation with prolonged exposure. Limit outdoor activity.",
|
||||
50.. => "The air has reached a high level of pollution and is unhealthy for sensitive groups. Reduce time spent outside if you are feeling symptoms such as difficulty breathing or throat irritation.",
|
||||
20.. => "The air quality is generally acceptable for most individuals. However, sensitive groups may experience minor to moderate symptoms from long-term exposure.",
|
||||
0.. => "The air quality is ideal for most individuals; enjoy your normal outdoor activities.",
|
||||
});
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
|
Reference in a new issue