openmeteo-rs 1.0.0

Rust client for the Open-Meteo weather API.
Documentation
use std::borrow::Cow;

use super::TowerLevel;
use crate::query::AsApiStr;
use crate::{Error, Result};

/// 15-minute variables for the general forecast endpoint.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Minutely15Var {
    /// Air temperature at 2 m.
    Temperature2m,
    /// Relative humidity at 2 m.
    RelativeHumidity2m,
    /// Dew point at 2 m.
    DewPoint2m,
    /// Apparent temperature.
    ApparentTemperature,
    /// Precipitation amount.
    Precipitation,
    /// Rain amount.
    Rain,
    /// Showers amount.
    Showers,
    /// Snowfall amount.
    Snowfall,
    /// Snowfall height.
    SnowfallHeight,
    /// Freezing level height.
    FreezingLevelHeight,
    /// Sunshine duration.
    SunshineDuration,
    /// WMO weather code.
    WeatherCode,
    /// Wind speed at a supported tower or near-surface level.
    ///
    /// The 15-minute forecast group documents wind speed at 10 m and 80 m.
    /// `TowerLevel::M120` and `TowerLevel::M180` are rejected by the forecast
    /// builder.
    WindSpeedAtTower(TowerLevel),
    /// Wind direction at a supported tower or near-surface level.
    ///
    /// The 15-minute forecast group documents wind direction at 10 m and 80 m.
    /// `TowerLevel::M120` and `TowerLevel::M180` are rejected by the forecast
    /// builder.
    WindDirectionAtTower(TowerLevel),
    /// Wind gusts at 10 m.
    WindGusts10m,
    /// Visibility distance.
    Visibility,
    /// Convective available potential energy.
    Cape,
    /// Lightning potential index.
    LightningPotential,
    /// Day/night flag.
    IsDay,
    /// Shortwave solar radiation, averaged over the preceding 15 minutes.
    ShortwaveRadiation,
    /// Direct solar radiation, averaged over the preceding 15 minutes.
    DirectRadiation,
    /// Direct normal irradiance, averaged over the preceding 15 minutes.
    DirectNormalIrradiance,
    /// Diffuse solar radiation, averaged over the preceding 15 minutes.
    DiffuseRadiation,
    /// Global tilted irradiance, averaged over the preceding 15 minutes.
    GlobalTiltedIrradiance,
    /// Terrestrial solar radiation, averaged over the preceding 15 minutes.
    TerrestrialRadiation,
    /// Instantaneous shortwave solar radiation.
    ShortwaveRadiationInstant,
    /// Instantaneous direct solar radiation.
    DirectRadiationInstant,
    /// Instantaneous direct normal irradiance.
    DirectNormalIrradianceInstant,
    /// Instantaneous diffuse solar radiation.
    DiffuseRadiationInstant,
    /// Instantaneous global tilted irradiance.
    GlobalTiltedIrradianceInstant,
    /// Instantaneous terrestrial solar radiation.
    TerrestrialRadiationInstant,
}

impl AsApiStr for Minutely15Var {
    fn as_api_str(&self) -> Cow<'static, str> {
        match self {
            Self::Temperature2m => Cow::Borrowed("temperature_2m"),
            Self::RelativeHumidity2m => Cow::Borrowed("relative_humidity_2m"),
            Self::DewPoint2m => Cow::Borrowed("dew_point_2m"),
            Self::ApparentTemperature => Cow::Borrowed("apparent_temperature"),
            Self::Precipitation => Cow::Borrowed("precipitation"),
            Self::Rain => Cow::Borrowed("rain"),
            Self::Showers => Cow::Borrowed("showers"),
            Self::Snowfall => Cow::Borrowed("snowfall"),
            Self::SnowfallHeight => Cow::Borrowed("snowfall_height"),
            Self::FreezingLevelHeight => Cow::Borrowed("freezing_level_height"),
            Self::SunshineDuration => Cow::Borrowed("sunshine_duration"),
            Self::WeatherCode => Cow::Borrowed("weather_code"),
            Self::WindSpeedAtTower(level) => Cow::Owned(format!("wind_speed_{}m", level.meters())),
            Self::WindDirectionAtTower(level) => {
                Cow::Owned(format!("wind_direction_{}m", level.meters()))
            }
            Self::WindGusts10m => Cow::Borrowed("wind_gusts_10m"),
            Self::Visibility => Cow::Borrowed("visibility"),
            Self::Cape => Cow::Borrowed("cape"),
            Self::LightningPotential => Cow::Borrowed("lightning_potential"),
            Self::IsDay => Cow::Borrowed("is_day"),
            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"),
        }
    }
}

impl Minutely15Var {
    pub(crate) fn validate_weather_request(self) -> Result<()> {
        let level = match self {
            Self::WindSpeedAtTower(level) | Self::WindDirectionAtTower(level) => level,
            _ => return Ok(()),
        };

        if !matches!(level, TowerLevel::M10 | TowerLevel::M80) {
            return Err(Error::InvalidParam {
                field: "minutely_15.tower_level",
                reason: "15-minute wind variables are available at 10 and 80 m".into(),
            });
        }

        Ok(())
    }
}