use crate::RubbleResult;
use clap::Args;
use reqwest;
use serde_json::{json, Value};
#[derive(Args)]
pub struct WeatherArgs {
#[arg(default_value = "portsmouth", short, long)]
pub city: String,
}
impl WeatherArgs {
pub fn empty() -> Self {
Self {
city: String::from(""),
}
}
}
#[derive(Debug)]
struct WeatherCondition {
feelslike_c: String,
temp_c: String,
temp_f: String,
humidity: String,
icon: String,
weather_desc: String,
}
impl WeatherCondition {
fn to_json(&self) -> Value {
json!({
"feelslike_c": self.feelslike_c,
"temp_c": self.temp_c,
"temp_f": self.temp_f,
"humidity": self.humidity,
"icon": self.icon,
"weather_desc": self.weather_desc
})
}
fn new(
feelslike_c: String,
temp_c: String,
temp_f: String,
humidity: String,
weather_desc: String,
) -> Self {
Self {
feelslike_c,
temp_c,
temp_f,
humidity,
icon: WeatherCondition::weather_icon(weather_desc.as_str()),
weather_desc,
}
}
fn weather_icon(weather_desc: &str) -> String {
let icon = match weather_desc {
"Unknown" => "✨",
"Cloudy" => "☁️",
"Fog" => "🌫",
"Heavy rain" => "🌧",
"Heavy showers" => "🌧",
"Heavy snow" => "❄️",
"Heavy snow showers" => "❄️",
"Light rain" => "🌦",
"Light showers" => "🌦",
"Light sleet" => "🌧",
"Light sleet showers" => "🌧",
"Light snow" => "🌨",
"Light snow showers" => "🌨",
"Partly cloudy" => "⛅️",
"Sunny" => "☀️",
"Thundery heavy rain" => "🌩",
"Thundery showers" => "⛈",
"Thundery snow showers" => "⛈",
"Very cloudy" => "☁️",
_ => "✨",
};
icon.to_string()
}
}
async fn check_wttr(city: &String) -> Value {
let url = format!("https://wttr.in/{}?format=j1", city);
let body = reqwest::get(url).await.unwrap().text().await.unwrap();
let wttr_json: Value = serde_json::from_str(body.as_str()).unwrap();
wttr_json["current_condition"][0].clone()
}
pub async fn weather_json(args: &WeatherArgs) -> RubbleResult {
let current_condition = check_wttr(&args.city).await;
let weather = WeatherCondition::new(
current_condition["FeelsLikeC"]
.as_str()
.unwrap()
.to_string(),
current_condition["temp_C"].as_str().unwrap().to_string(),
current_condition["temp_F"].as_str().unwrap().to_string(),
current_condition["humidity"].as_str().unwrap().to_string(),
current_condition["weatherDesc"][0]["value"]
.as_str()
.unwrap()
.to_string(),
);
let r = Ok(weather.to_json());
RubbleResult::new(r, default_format())
}
crate::avaliable_formattings!(
"{{feelslike_c}} {{icon}}",
"feelslike_c temp_c temp_f humidity icon weather_desc"
);
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_weather_icon() {
assert_eq!(WeatherCondition::weather_icon("Unknown"), "✨");
assert_eq!(WeatherCondition::weather_icon("Cloudy"), "☁️");
assert_eq!(WeatherCondition::weather_icon("Fog"), "🌫");
assert_eq!(WeatherCondition::weather_icon("Heavy rain"), "🌧");
assert_eq!(WeatherCondition::weather_icon("Heavy showers"), "🌧");
assert_eq!(WeatherCondition::weather_icon("Heavy snow"), "❄️");
assert_eq!(WeatherCondition::weather_icon("Heavy snow showers"), "❄️");
assert_eq!(WeatherCondition::weather_icon("Light rain"), "🌦");
assert_eq!(WeatherCondition::weather_icon("Light showers"), "🌦");
assert_eq!(WeatherCondition::weather_icon("Light sleet"), "🌧");
assert_eq!(WeatherCondition::weather_icon("Light sleet showers"), "🌧");
assert_eq!(WeatherCondition::weather_icon("Light snow"), "🌨");
assert_eq!(WeatherCondition::weather_icon("Light snow showers"), "🌨");
assert_eq!(WeatherCondition::weather_icon("Partly cloudy"), "⛅️");
assert_eq!(WeatherCondition::weather_icon("Sunny"), "☀️");
assert_eq!(WeatherCondition::weather_icon("Thundery heavy rain"), "🌩");
assert_eq!(WeatherCondition::weather_icon("Thundery showers"), "⛈");
}
}