use std::borrow::Cow;
use super::{PressureLevel, SoilMoistureDepth, SoilTemperatureDepth, TowerLevel};
use crate::query::AsApiStr;
use crate::{Error, Result};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum HourlyVar {
Temperature2m,
TemperatureAtTower(TowerLevel),
RelativeHumidity2m,
DewPoint2m,
ApparentTemperature,
WetBulbTemperature2m,
PressureMsl,
SurfacePressure,
CloudCover,
CloudCoverLow,
CloudCoverMid,
CloudCoverHigh,
PrecipitationProbability,
Precipitation,
Rain,
Showers,
Snowfall,
SnowDepth,
WeatherCode,
Visibility,
Evapotranspiration,
Et0FaoEvapotranspiration,
VapourPressureDeficit,
IsDay,
SunshineDuration,
UvIndex,
UvIndexClearSky,
TotalColumnIntegratedWaterVapour,
ShortwaveRadiation,
DirectRadiation,
DirectNormalIrradiance,
DiffuseRadiation,
GlobalTiltedIrradiance,
TerrestrialRadiation,
ShortwaveRadiationInstant,
DirectRadiationInstant,
DirectNormalIrradianceInstant,
DiffuseRadiationInstant,
GlobalTiltedIrradianceInstant,
TerrestrialRadiationInstant,
Cape,
LiftedIndex,
ConvectiveInhibition,
FreezingLevelHeight,
BoundaryLayerHeight,
WindSpeed10m,
WindSpeedAtTower(TowerLevel),
WindDirection10m,
WindDirectionAtTower(TowerLevel),
WindGusts10m,
SoilTemperature(SoilTemperatureDepth),
SoilMoisture {
from: SoilMoistureDepth,
to: SoilMoistureDepth,
},
TemperatureAtPressure(PressureLevel),
RelativeHumidityAtPressure(PressureLevel),
DewPointAtPressure(PressureLevel),
CloudCoverAtPressure(PressureLevel),
WindSpeedAtPressure(PressureLevel),
WindDirectionAtPressure(PressureLevel),
GeopotentialHeightAtPressure(PressureLevel),
Other(Cow<'static, str>),
}
impl AsApiStr for HourlyVar {
fn as_api_str(&self) -> Cow<'static, str> {
match self {
Self::Temperature2m => Cow::Borrowed("temperature_2m"),
Self::TemperatureAtTower(level) => {
Cow::Owned(format!("temperature_{}m", level.meters()))
}
Self::RelativeHumidity2m => Cow::Borrowed("relative_humidity_2m"),
Self::DewPoint2m => Cow::Borrowed("dew_point_2m"),
Self::ApparentTemperature => Cow::Borrowed("apparent_temperature"),
Self::WetBulbTemperature2m => Cow::Borrowed("wet_bulb_temperature_2m"),
Self::PressureMsl => Cow::Borrowed("pressure_msl"),
Self::SurfacePressure => Cow::Borrowed("surface_pressure"),
Self::CloudCover => Cow::Borrowed("cloud_cover"),
Self::CloudCoverLow => Cow::Borrowed("cloud_cover_low"),
Self::CloudCoverMid => Cow::Borrowed("cloud_cover_mid"),
Self::CloudCoverHigh => Cow::Borrowed("cloud_cover_high"),
Self::PrecipitationProbability => Cow::Borrowed("precipitation_probability"),
Self::Precipitation => Cow::Borrowed("precipitation"),
Self::Rain => Cow::Borrowed("rain"),
Self::Showers => Cow::Borrowed("showers"),
Self::Snowfall => Cow::Borrowed("snowfall"),
Self::SnowDepth => Cow::Borrowed("snow_depth"),
Self::WeatherCode => Cow::Borrowed("weather_code"),
Self::Visibility => Cow::Borrowed("visibility"),
Self::Evapotranspiration => Cow::Borrowed("evapotranspiration"),
Self::Et0FaoEvapotranspiration => Cow::Borrowed("et0_fao_evapotranspiration"),
Self::VapourPressureDeficit => Cow::Borrowed("vapour_pressure_deficit"),
Self::IsDay => Cow::Borrowed("is_day"),
Self::SunshineDuration => Cow::Borrowed("sunshine_duration"),
Self::UvIndex => Cow::Borrowed("uv_index"),
Self::UvIndexClearSky => Cow::Borrowed("uv_index_clear_sky"),
Self::TotalColumnIntegratedWaterVapour => {
Cow::Borrowed("total_column_integrated_water_vapour")
}
Self::ShortwaveRadiation => Cow::Borrowed("shortwave_radiation"),
Self::DirectRadiation => Cow::Borrowed("direct_radiation"),
Self::DirectNormalIrradiance => Cow::Borrowed("direct_normal_irradiance"),
Self::DiffuseRadiation => Cow::Borrowed("diffuse_radiation"),
Self::GlobalTiltedIrradiance => Cow::Borrowed("global_tilted_irradiance"),
Self::TerrestrialRadiation => Cow::Borrowed("terrestrial_radiation"),
Self::ShortwaveRadiationInstant => Cow::Borrowed("shortwave_radiation_instant"),
Self::DirectRadiationInstant => Cow::Borrowed("direct_radiation_instant"),
Self::DirectNormalIrradianceInstant => {
Cow::Borrowed("direct_normal_irradiance_instant")
}
Self::DiffuseRadiationInstant => Cow::Borrowed("diffuse_radiation_instant"),
Self::GlobalTiltedIrradianceInstant => {
Cow::Borrowed("global_tilted_irradiance_instant")
}
Self::TerrestrialRadiationInstant => Cow::Borrowed("terrestrial_radiation_instant"),
Self::Cape => Cow::Borrowed("cape"),
Self::LiftedIndex => Cow::Borrowed("lifted_index"),
Self::ConvectiveInhibition => Cow::Borrowed("convective_inhibition"),
Self::FreezingLevelHeight => Cow::Borrowed("freezing_level_height"),
Self::BoundaryLayerHeight => Cow::Borrowed("boundary_layer_height"),
Self::WindSpeed10m => Cow::Borrowed("wind_speed_10m"),
Self::WindSpeedAtTower(level) => Cow::Owned(format!("wind_speed_{}m", level.meters())),
Self::WindDirection10m => Cow::Borrowed("wind_direction_10m"),
Self::WindDirectionAtTower(level) => {
Cow::Owned(format!("wind_direction_{}m", level.meters()))
}
Self::WindGusts10m => Cow::Borrowed("wind_gusts_10m"),
Self::SoilTemperature(depth) => {
Cow::Owned(format!("soil_temperature_{}cm", depth.cm()))
}
Self::SoilMoisture { from, to } => {
Cow::Owned(format!("soil_moisture_{}_to_{}cm", from.cm(), to.cm()))
}
Self::TemperatureAtPressure(level) => {
Cow::Owned(format!("temperature_{}hPa", level.hpa()))
}
Self::RelativeHumidityAtPressure(level) => {
Cow::Owned(format!("relative_humidity_{}hPa", level.hpa()))
}
Self::DewPointAtPressure(level) => Cow::Owned(format!("dew_point_{}hPa", level.hpa())),
Self::CloudCoverAtPressure(level) => {
Cow::Owned(format!("cloud_cover_{}hPa", level.hpa()))
}
Self::WindSpeedAtPressure(level) => {
Cow::Owned(format!("wind_speed_{}hPa", level.hpa()))
}
Self::WindDirectionAtPressure(level) => {
Cow::Owned(format!("wind_direction_{}hPa", level.hpa()))
}
Self::GeopotentialHeightAtPressure(level) => {
Cow::Owned(format!("geopotential_height_{}hPa", level.hpa()))
}
Self::Other(token) => token.clone(),
}
}
}
impl HourlyVar {
pub fn other(token: impl Into<Cow<'static, str>>) -> Self {
Self::Other(token.into())
}
pub(crate) fn validate_weather_request(&self) -> Result<()> {
if let Self::SoilMoisture { from, to } = self
&& from.cm() >= to.cm()
{
return Err(Error::InvalidParam {
field: "hourly.soil_moisture",
reason: "soil moisture depth ranges must be ordered from shallower to deeper depth"
.into(),
});
}
if let Self::TemperatureAtTower(TowerLevel::M10) = self {
return Err(Error::InvalidParam {
field: "hourly.temperature_at_tower",
reason: "temperature tower variables are available at 80, 120, and 180 m".into(),
});
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::query::AsApiStr;
#[test]
fn pressure_level_hourly_tokens_are_formatted() {
assert_eq!(
HourlyVar::TemperatureAtPressure(PressureLevel::Hpa850).as_api_str(),
"temperature_850hPa"
);
assert_eq!(
HourlyVar::DewPointAtPressure(PressureLevel::Hpa975).as_api_str(),
"dew_point_975hPa"
);
assert_eq!(
HourlyVar::CloudCoverAtPressure(PressureLevel::Hpa30).as_api_str(),
"cloud_cover_30hPa"
);
}
#[test]
fn tower_altitude_tokens_are_formatted() {
assert_eq!(
HourlyVar::TemperatureAtTower(TowerLevel::M80).as_api_str(),
"temperature_80m"
);
assert_eq!(
HourlyVar::WindSpeedAtTower(TowerLevel::M120).as_api_str(),
"wind_speed_120m"
);
assert_eq!(
HourlyVar::WindDirectionAtTower(TowerLevel::M180).as_api_str(),
"wind_direction_180m"
);
}
}