//! @generated by satay. Do not edit by hand.

use std::fmt;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: PsiData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Pm25Response {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: Pm25Data,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AirTemperatureResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: AirTemperatureData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RelativeHumidityResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: RelativeHumidityData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WindSpeedResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: WindSpeedData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WindDirectionResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: WindDirectionData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RainfallResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: RainfallData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwoHrForecastResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: TwoHrForecastData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwentyFourHrForecastResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: TwentyFourHrForecastData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FourDayOutlookResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    /// Chronologically ordered forecasts for the next 4 days
    pub data: FourDayOutlookData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UvResponse {
    /// Response status code (0 for success)
    pub code: NeaSuccessCode,
    /// Error message (empty string for success)
    #[cfg_attr(feature = "serde", serde(rename = "errorMsg"))]
    pub error_msg: String,
    pub data: UvData,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WeatherSubApiResponse {
    /// Response status code (0 for success)
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub code: Option<NeaSuccessCode>,
    /// Error message (empty string for success)
    #[cfg_attr(
        feature = "serde",
        serde(rename = "errorMsg", default, skip_serializing_if = "Option::is_none")
    )]
    pub error_msg: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub data: Option<WeatherSubApiData>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InvalidParamsError {
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub code: Option<f64>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub name: Option<String>,
    /// Bad-request error message for invalid date or pagination token
    #[cfg_attr(
        feature = "serde",
        serde(rename = "errorMsg", default, skip_serializing_if = "Option::is_none")
    )]
    pub error_msg: Option<NeaInvalidParamsErrorMsg>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InvalidParamsError22 {
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub code: Option<f64>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub name: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(rename = "errorMsg", default, skip_serializing_if = "Option::is_none")
    )]
    pub error_msg: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DataNotFoundError {
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub code: Option<f64>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub name: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(rename = "errorMsg", default, skip_serializing_if = "Option::is_none")
    )]
    pub error_msg: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RateLimitError {
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub code: Option<f64>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub name: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(rename = "errorMsg", default, skip_serializing_if = "Option::is_none")
    )]
    pub error_msg: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MissingAuthResponse {
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub message: Option<String>,
}
/// Response status code (0 for success)
#[nutype::nutype(
    validate(less_or_equal = 0),
    derive(
        Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaSuccessCode(u8);
/// ISO 8601 date or date-time in Singapore Time (SGT)
pub type NeaOffsetDateTime = satay_runtime::OffsetDateTime;
/// SGT calendar date (YYYY-MM-DD)
pub type NeaDate = satay_runtime::Date;
/// WGS84 latitude for Singapore
#[nutype::nutype(
    validate(finite, greater_or_equal = 1.0, less_or_equal = 1.5),
    derive(
        Debug, Clone, Copy, PartialEq, PartialOrd, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaLatitude(f64);
/// WGS84 longitude for Singapore
#[nutype::nutype(
    validate(finite, greater_or_equal = 103.5, less_or_equal = 104.2),
    derive(
        Debug, Clone, Copy, PartialEq, PartialOrd, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaLongitude(f64);
/// Regional air-quality or index reading
#[nutype::nutype(
    validate(less_or_equal = 500),
    derive(
        Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaRegionalReading(u16);
/// Regional PM2.5 one-hour reading (µg/m³)
#[nutype::nutype(
    validate(less_or_equal = 500),
    derive(
        Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaPm25HourlyReading(u16);
/// Wind direction in degrees
#[nutype::nutype(
    validate(less_or_equal = 360),
    derive(
        Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaWindDirectionDegrees(u16);
/// UV index for the hour
#[nutype::nutype(
    validate(less_or_equal = 16),
    derive(
        Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaUvIndex(u8);
/// Relative humidity percentage
#[nutype::nutype(
    validate(finite, greater_or_equal = 0.0, less_or_equal = 100.0),
    derive(
        Debug, Clone, Copy, PartialEq, PartialOrd, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaHumidityPercent(f64);
/// Air temperature in degrees Celsius
#[nutype::nutype(
    validate(finite, greater_or_equal = 15.0, less_or_equal = 45.0),
    derive(
        Debug, Clone, Copy, PartialEq, PartialOrd, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaTemperatureCelsius(f64);
/// Wind speed in km/h
#[nutype::nutype(
    validate(finite, greater_or_equal = 0.0, less_or_equal = 120.0),
    derive(
        Debug, Clone, Copy, PartialEq, PartialOrd, AsRef, Deref, TryFrom, Into, Display
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaWindSpeedKmh(f64);
/// NEA MSS short weather forecast code
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaForecastCode {
    #[cfg_attr(feature = "serde", serde(rename = "FA"))]
    FairDay,
    #[cfg_attr(feature = "serde", serde(rename = "FN"))]
    FairNight,
    #[cfg_attr(feature = "serde", serde(rename = "FW"))]
    FairAndWarm,
    #[cfg_attr(feature = "serde", serde(rename = "CL"))]
    Cloudy,
    #[cfg_attr(feature = "serde", serde(rename = "PC"))]
    PartlyCloudyDay,
    #[cfg_attr(feature = "serde", serde(rename = "PN"))]
    PartlyCloudyNight,
    #[cfg_attr(feature = "serde", serde(rename = "HZ"))]
    Hazy,
    #[cfg_attr(feature = "serde", serde(rename = "SH"))]
    SlightlyHazy,
    #[cfg_attr(feature = "serde", serde(rename = "WI"))]
    Windy,
    #[cfg_attr(feature = "serde", serde(rename = "MS"))]
    Mist,
    #[cfg_attr(feature = "serde", serde(rename = "FG"))]
    Fog,
    #[cfg_attr(feature = "serde", serde(rename = "LR"))]
    LightRain,
    #[cfg_attr(feature = "serde", serde(rename = "MR"))]
    ModerateRain,
    #[cfg_attr(feature = "serde", serde(rename = "HR"))]
    HeavyRain,
    #[cfg_attr(feature = "serde", serde(rename = "PS"))]
    PassingShowers,
    #[cfg_attr(feature = "serde", serde(rename = "LS"))]
    LightShowers,
    #[cfg_attr(feature = "serde", serde(rename = "HS"))]
    HeavyShowers,
    #[cfg_attr(feature = "serde", serde(rename = "TS"))]
    ThunderyShowers,
    #[cfg_attr(feature = "serde", serde(rename = "HT"))]
    HeavyThunderyShowers,
    #[cfg_attr(feature = "serde", serde(rename = "HG"))]
    HeavyThunderyShowersWithGustyWinds,
    #[cfg_attr(feature = "serde", serde(rename = "TL"))]
    ThunderyShowersAlt,
}
impl NeaForecastCode {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::FairDay => "FA",
            Self::FairNight => "FN",
            Self::FairAndWarm => "FW",
            Self::Cloudy => "CL",
            Self::PartlyCloudyDay => "PC",
            Self::PartlyCloudyNight => "PN",
            Self::Hazy => "HZ",
            Self::SlightlyHazy => "SH",
            Self::Windy => "WI",
            Self::Mist => "MS",
            Self::Fog => "FG",
            Self::LightRain => "LR",
            Self::ModerateRain => "MR",
            Self::HeavyRain => "HR",
            Self::PassingShowers => "PS",
            Self::LightShowers => "LS",
            Self::HeavyShowers => "HS",
            Self::ThunderyShowers => "TS",
            Self::HeavyThunderyShowers => "HT",
            Self::HeavyThunderyShowersWithGustyWinds => "HG",
            Self::ThunderyShowersAlt => "TL",
        }
    }
}
impl AsRef<str> for NeaForecastCode {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaForecastCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// Lightning event type (C = cloud-to-cloud, G = cloud-to-ground)
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaLightningType {
    #[cfg_attr(feature = "serde", serde(rename = "C"))]
    CloudToCloud,
    #[cfg_attr(feature = "serde", serde(rename = "G"))]
    CloudToGround,
}
impl NeaLightningType {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::CloudToCloud => "C",
            Self::CloudToGround => "G",
        }
    }
}
impl AsRef<str> for NeaLightningType {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaLightningType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// NEA MSS human-readable weather forecast text
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaForecastText {
    Fair,
    #[cfg_attr(feature = "serde", serde(rename = "Fair (Day)"))]
    FairDay,
    #[cfg_attr(feature = "serde", serde(rename = "Fair (Night)"))]
    FairNight,
    #[cfg_attr(feature = "serde", serde(rename = "Fair and Warm"))]
    FairAndWarm,
    #[cfg_attr(feature = "serde", serde(rename = "Partly Cloudy"))]
    PartlyCloudy,
    #[cfg_attr(feature = "serde", serde(rename = "Partly Cloudy (Day)"))]
    PartlyCloudyDay,
    #[cfg_attr(feature = "serde", serde(rename = "Partly Cloudy (Night)"))]
    PartlyCloudyNight,
    Cloudy,
    Hazy,
    #[cfg_attr(feature = "serde", serde(rename = "Slightly Hazy"))]
    SlightlyHazy,
    Windy,
    Mist,
    Fog,
    #[cfg_attr(feature = "serde", serde(rename = "Light Rain"))]
    LightRain,
    #[cfg_attr(feature = "serde", serde(rename = "Moderate Rain"))]
    ModerateRain,
    #[cfg_attr(feature = "serde", serde(rename = "Heavy Rain"))]
    HeavyRain,
    #[cfg_attr(feature = "serde", serde(rename = "Passing Showers"))]
    PassingShowers,
    #[cfg_attr(feature = "serde", serde(rename = "Light Showers"))]
    LightShowers,
    Showers,
    #[cfg_attr(feature = "serde", serde(rename = "Heavy Showers"))]
    HeavyShowers,
    #[cfg_attr(feature = "serde", serde(rename = "Thundery Showers"))]
    ThunderyShowers,
    #[cfg_attr(feature = "serde", serde(rename = "Heavy Thundery Showers"))]
    HeavyThunderyShowers,
    #[cfg_attr(
        feature = "serde",
        serde(rename = "Heavy Thundery Showers with Gusty Winds")
    )]
    HeavyThunderyShowersWithGustyWinds,
}
impl NeaForecastText {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::Fair => "Fair",
            Self::FairDay => "Fair (Day)",
            Self::FairNight => "Fair (Night)",
            Self::FairAndWarm => "Fair and Warm",
            Self::PartlyCloudy => "Partly Cloudy",
            Self::PartlyCloudyDay => "Partly Cloudy (Day)",
            Self::PartlyCloudyNight => "Partly Cloudy (Night)",
            Self::Cloudy => "Cloudy",
            Self::Hazy => "Hazy",
            Self::SlightlyHazy => "Slightly Hazy",
            Self::Windy => "Windy",
            Self::Mist => "Mist",
            Self::Fog => "Fog",
            Self::LightRain => "Light Rain",
            Self::ModerateRain => "Moderate Rain",
            Self::HeavyRain => "Heavy Rain",
            Self::PassingShowers => "Passing Showers",
            Self::LightShowers => "Light Showers",
            Self::Showers => "Showers",
            Self::HeavyShowers => "Heavy Showers",
            Self::ThunderyShowers => "Thundery Showers",
            Self::HeavyThunderyShowers => "Heavy Thundery Showers",
            Self::HeavyThunderyShowersWithGustyWinds => "Heavy Thundery Showers with Gusty Winds",
        }
    }
}
impl AsRef<str> for NeaForecastText {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaForecastText {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// Day of week for multi-day outlook forecasts
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaDayOfWeek {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday,
}
impl NeaDayOfWeek {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::Monday => "Monday",
            Self::Tuesday => "Tuesday",
            Self::Wednesday => "Wednesday",
            Self::Thursday => "Thursday",
            Self::Friday => "Friday",
            Self::Saturday => "Saturday",
            Self::Sunday => "Sunday",
        }
    }
}
impl AsRef<str> for NeaDayOfWeek {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaDayOfWeek {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// Bad-request error message for invalid date or pagination token
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaInvalidParamsErrorMsg {
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "Invalid date format. Date format must be YYYY-MM-DD (2024-06-01) or YYYY-MM-DDTHH:mm:ss (2024-06-01T08:30:00)."
        )
    )]
    InvalidDateFormat,
    #[cfg_attr(feature = "serde", serde(rename = "Invalid pagination token."))]
    InvalidPaginationToken,
}
impl NeaInvalidParamsErrorMsg {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::InvalidDateFormat => {
                "Invalid date format. Date format must be YYYY-MM-DD (2024-06-01) or YYYY-MM-DDTHH:mm:ss (2024-06-01T08:30:00)."
            }
            Self::InvalidPaginationToken => "Invalid pagination token.",
        }
    }
}
impl AsRef<str> for NeaInvalidParamsErrorMsg {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaInvalidParamsErrorMsg {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// Weather sub-API selector (lightning or wbgt)
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaWeatherSubApi {
    #[cfg_attr(feature = "serde", serde(rename = "lightning"))]
    Lightning,
    #[cfg_attr(feature = "serde", serde(rename = "wbgt"))]
    WetBulbGlobeTemperature,
}
impl NeaWeatherSubApi {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::Lightning => "lightning",
            Self::WetBulbGlobeTemperature => "wbgt",
        }
    }
}
impl AsRef<str> for NeaWeatherSubApi {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaWeatherSubApi {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// NEA weather station identifier (S + 2–3 digits)
#[nutype::nutype(
    validate(regex = "^S[0-9]{2,3}$", len_char_min = 3, len_char_max = 4),
    derive(
        Debug, Clone, PartialEq, Eq, PartialOrd, Ord, AsRef, Deref, TryFrom, Into, Display, Hash
    ),
    cfg_attr(feature = "serde", derive(Serialize, Deserialize))
)]
pub struct NeaStationId(String);
/// WGS84 coordinates for a weather station
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaStationLocation {
    /// WGS84 latitude for Singapore
    pub latitude: NeaLatitude,
    /// WGS84 longitude for Singapore
    pub longitude: NeaLongitude,
}
/// 16-point compass wind direction for outlook forecasts
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaWindDirection16 {
    N,
    #[cfg_attr(feature = "serde", serde(rename = "NNE"))]
    Nne,
    #[cfg_attr(feature = "serde", serde(rename = "NE"))]
    Ne,
    #[cfg_attr(feature = "serde", serde(rename = "ENE"))]
    Ene,
    E,
    #[cfg_attr(feature = "serde", serde(rename = "ESE"))]
    Ese,
    #[cfg_attr(feature = "serde", serde(rename = "SE"))]
    Se,
    #[cfg_attr(feature = "serde", serde(rename = "SSE"))]
    Sse,
    S,
    #[cfg_attr(feature = "serde", serde(rename = "SSW"))]
    Ssw,
    #[cfg_attr(feature = "serde", serde(rename = "SW"))]
    Sw,
    #[cfg_attr(feature = "serde", serde(rename = "WSW"))]
    Wsw,
    W,
    #[cfg_attr(feature = "serde", serde(rename = "WNW"))]
    Wnw,
    #[cfg_attr(feature = "serde", serde(rename = "NW"))]
    Nw,
    #[cfg_attr(feature = "serde", serde(rename = "NNW"))]
    Nnw,
}
impl NeaWindDirection16 {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::N => "N",
            Self::Nne => "NNE",
            Self::Ne => "NE",
            Self::Ene => "ENE",
            Self::E => "E",
            Self::Ese => "ESE",
            Self::Se => "SE",
            Self::Sse => "SSE",
            Self::S => "S",
            Self::Ssw => "SSW",
            Self::Sw => "SW",
            Self::Wsw => "WSW",
            Self::W => "W",
            Self::Wnw => "WNW",
            Self::Nw => "NW",
            Self::Nnw => "NNW",
        }
    }
}
impl AsRef<str> for NeaWindDirection16 {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaWindDirection16 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// WGS84 latitude as a decimal string
pub type NeaStringLatitude = f64;
/// WGS84 longitude as a decimal string
pub type NeaStringLongitude = f64;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaWeatherStation {
    /// NEA weather station identifier (S + 2–3 digits)
    pub id: NeaStationId,
    /// NEA weather station identifier (S + 2–3 digits)
    #[cfg_attr(feature = "serde", serde(rename = "deviceId"))]
    pub device_id: NeaStationId,
    /// Station's name
    pub name: String,
    /// WGS84 coordinates for a weather station
    pub location: NeaStationLocation,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaReadingSnapshot {
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub timestamp: satay_runtime::OffsetDateTime,
    pub data: Vec<NeaStationReading>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaStationReading {
    /// NEA weather station identifier (S + 2–3 digits)
    #[cfg_attr(feature = "serde", serde(rename = "stationId"))]
    pub station_id: NeaStationId,
    pub value: f64,
}
/// Unit of measure - Percentage
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaHumidityRange {
    /// Relative humidity percentage
    pub low: NeaHumidityPercent,
    /// Relative humidity percentage
    pub high: NeaHumidityPercent,
    /// Unit of measure for NEA station readings and outlook ranges
    pub unit: NeaMeasurementUnit,
}
/// Unit of measure - Degrees Celsius
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaTemperatureRange {
    /// Air temperature in degrees Celsius
    pub low: NeaTemperatureCelsius,
    /// Air temperature in degrees Celsius
    pub high: NeaTemperatureCelsius,
    /// Unit of measure for NEA station readings and outlook ranges
    pub unit: NeaMeasurementUnit,
}
/// Unit of measure - Kilometeres per hour
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaWindSpeedRange {
    /// Wind speed in km/h
    pub low: NeaWindSpeedKmh,
    /// Wind speed in km/h
    pub high: NeaWindSpeedKmh,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaOutlookWind {
    /// Unit of measure - Kilometeres per hour
    pub speed: NeaWindSpeedRange,
    /// 16-point compass wind direction for outlook forecasts
    pub direction: NeaWindDirection16,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaMssForecast {
    /// NEA MSS short weather forecast code
    pub code: NeaForecastCode,
    /// NEA MSS human-readable weather forecast text
    pub text: NeaForecastText,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaGeoPoint {
    /// WGS84 latitude for Singapore
    pub latitude: NeaLatitude,
    /// WGS84 longitude for Singapore
    pub longitude: NeaLongitude,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AirTemperatureData {
    pub stations: Vec<NeaWeatherStation>,
    pub readings: Vec<NeaReadingSnapshot>,
    /// Information about the reading
    #[cfg_attr(feature = "serde", serde(rename = "readingType"))]
    pub reading_type: String,
    /// Unit of measure for NEA station readings and outlook ranges
    #[cfg_attr(feature = "serde", serde(rename = "readingUnit"))]
    pub reading_unit: NeaMeasurementUnit,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RelativeHumidityData {
    pub stations: Vec<NeaWeatherStation>,
    pub readings: Vec<NeaReadingSnapshot>,
    /// Information about the reading
    #[cfg_attr(feature = "serde", serde(rename = "readingType"))]
    pub reading_type: String,
    /// Unit of measure for NEA station readings and outlook ranges
    #[cfg_attr(feature = "serde", serde(rename = "readingUnit"))]
    pub reading_unit: NeaMeasurementUnit,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WindSpeedData {
    pub stations: Vec<NeaWeatherStation>,
    pub readings: Vec<NeaReadingSnapshot>,
    /// Information about the reading
    #[cfg_attr(feature = "serde", serde(rename = "readingType"))]
    pub reading_type: String,
    /// Unit of measure for NEA station readings and outlook ranges
    #[cfg_attr(feature = "serde", serde(rename = "readingUnit"))]
    pub reading_unit: NeaMeasurementUnit,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WindDirectionData {
    pub stations: Vec<NeaWeatherStation>,
    pub readings: Vec<NeaReadingSnapshot>,
    /// Information about the reading
    #[cfg_attr(feature = "serde", serde(rename = "readingType"))]
    pub reading_type: String,
    /// Unit of measure for NEA station readings and outlook ranges
    #[cfg_attr(feature = "serde", serde(rename = "readingUnit"))]
    pub reading_unit: NeaMeasurementUnit,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RainfallData {
    pub stations: Vec<NeaWeatherStation>,
    pub readings: Vec<NeaReadingSnapshot>,
    /// Information about the reading
    #[cfg_attr(feature = "serde", serde(rename = "readingType"))]
    pub reading_type: String,
    /// Unit of measure for NEA station readings and outlook ranges
    #[cfg_attr(feature = "serde", serde(rename = "readingUnit"))]
    pub reading_unit: NeaMeasurementUnit,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
/// Chronologically ordered forecasts for the next 4 days
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FourDayOutlookData {
    pub records: Vec<FourDayOutlookDay>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FourDayOutlookDay {
    /// SGT calendar date (YYYY-MM-DD)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_date")
    )]
    pub date: satay_runtime::Date,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "updatedTimestamp",
            with = "satay_runtime::serde_string::as_offset_datetime"
        )
    )]
    pub updated_timestamp: satay_runtime::OffsetDateTime,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub timestamp: satay_runtime::OffsetDateTime,
    /// Forecast summary for the day
    pub forecasts: Vec<FourDayOutlookPeriod>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FourDayOutlookPeriod {
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub timestamp: satay_runtime::OffsetDateTime,
    /// Unit of measure - Degrees Celsius
    pub temperature: NeaTemperatureRange,
    /// Unit of measure - Percentage
    #[cfg_attr(feature = "serde", serde(rename = "relativeHumidity"))]
    pub relative_humidity: NeaHumidityRange,
    pub forecast: NeaOutlookForecastDetail,
    /// Day of week for multi-day outlook forecasts
    pub day: NeaDayOfWeek,
    pub wind: NeaOutlookWind,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaOutlookForecastDetail {
    pub summary: String,
    /// NEA MSS short weather forecast code
    pub code: NeaForecastCode,
    /// NEA MSS human-readable weather forecast text
    pub text: NeaForecastText,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwentyFourHrForecastData {
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub area_metadata: Option<Vec<NeaForecastArea>>,
    pub records: Vec<TwentyFourHrForecastDay>,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwentyFourHrForecastDay {
    /// SGT calendar date (YYYY-MM-DD)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_date")
    )]
    pub date: satay_runtime::Date,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "updatedTimestamp",
            with = "satay_runtime::serde_string::as_offset_datetime"
        )
    )]
    pub updated_timestamp: satay_runtime::OffsetDateTime,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub timestamp: satay_runtime::OffsetDateTime,
    /// A general weather forecast for the 24 hour period
    pub general: TwentyFourHrForecastGeneral,
    /// Forecasts for various areas in Singapore
    pub periods: Vec<TwentyFourHrForecastPeriod>,
}
/// A general weather forecast for the 24 hour period
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwentyFourHrForecastGeneral {
    /// Period of time the forecast is valid for
    #[cfg_attr(feature = "serde", serde(rename = "validPeriod"))]
    pub valid_period: NeaValidPeriod,
    /// Unit of measure - Degrees Celsius
    pub temperature: NeaTemperatureRange,
    /// Unit of measure - Percentage
    #[cfg_attr(feature = "serde", serde(rename = "relativeHumidity"))]
    pub relative_humidity: NeaHumidityRange,
    pub forecast: NeaMssForecast,
    pub wind: NeaOutlookWind,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwentyFourHrForecastPeriod {
    /// Period of time the forecast is valid for
    #[cfg_attr(feature = "serde", serde(rename = "timePeriod"))]
    pub time_period: NeaValidPeriod,
    pub regions: TwentyFourHrRegionalForecast,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwentyFourHrRegionalForecast {
    pub west: NeaMssForecast,
    pub east: NeaMssForecast,
    pub central: NeaMssForecast,
    pub north: NeaMssForecast,
    pub south: NeaMssForecast,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwoHrForecastData {
    pub area_metadata: Vec<NeaForecastArea>,
    pub items: Vec<TwoHrForecastSnapshot>,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaForecastArea {
    /// Name of the area
    pub name: String,
    pub label_location: NeaGeoPoint,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwoHrForecastSnapshot {
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub update_timestamp: satay_runtime::OffsetDateTime,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub timestamp: satay_runtime::OffsetDateTime,
    /// Period of time the forecast is valid for
    pub valid_period: NeaValidPeriod,
    /// Forecasts for various areas in Singapore
    pub forecasts: Vec<TwoHrAreaForecast>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TwoHrAreaForecast {
    pub area: String,
    /// NEA MSS human-readable weather forecast text
    pub forecast: NeaForecastText,
}
/// Period of time the forecast is valid for
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaValidPeriod {
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub start: satay_runtime::OffsetDateTime,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub end: satay_runtime::OffsetDateTime,
    pub text: String,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiData {
    #[cfg_attr(feature = "serde", serde(rename = "regionMetadata"))]
    pub region_metadata: Vec<NeaRegionMetadata>,
    pub items: Vec<PsiSnapshot>,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiSnapshot {
    /// SGT calendar date (YYYY-MM-DD)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_date")
    )]
    pub date: satay_runtime::Date,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "updatedTimestamp",
            with = "satay_runtime::serde_string::as_offset_datetime"
        )
    )]
    pub updated_timestamp: satay_runtime::OffsetDateTime,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub timestamp: satay_runtime::OffsetDateTime,
    /// Overall and regional PSI data including pollutant concentrations and sub-indices
    pub readings: PsiReadings,
}
/// Overall and regional PSI data including pollutant concentrations and sub-indices
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiReadings {
    /// Concentration is measured in micrograms per cubic metre
    pub co_sub_index: PsiCoSubIndexRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub so2_twenty_four_hourly: PsiSo2TwentyFourHourRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub so2_sub_index: PsiSo2SubIndexRegional,
    /// Concentration is measured in micrograms per cubic metre
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub psi_three_hourly: Option<PsiThreeHourRegional>,
    /// Concentration is measured in micrograms per cubic metre
    pub co_eight_hour_max: PsiCoEightHourMaxRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub no2_one_hour_max: PsiNo2OneHourMaxRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub pm10_sub_index: PsiPm10SubIndexRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub pm25_sub_index: PsiPm25SubIndexRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub o3_eight_hour_max: PsiO3EightHourMaxRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub psi_twenty_four_hourly: PsiTwentyFourHourRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub o3_sub_index: PsiO3SubIndexRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub pm25_twenty_four_hourly: PsiPm25TwentyFourHourRegional,
    /// Concentration is measured in micrograms per cubic metre
    pub pm10_twenty_four_hourly: PsiPm10TwentyFourHourRegional,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaRegionMetadata {
    /// Region name
    pub name: NeaRegionMetadataName,
    #[cfg_attr(feature = "serde", serde(rename = "labelLocation"))]
    pub label_location: NeaGeoPoint,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiCoEightHourMaxRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiCoSubIndexRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiNo2OneHourMaxRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiO3EightHourMaxRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiO3SubIndexRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiPm10SubIndexRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiPm10TwentyFourHourRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiPm25SubIndexRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiPm25TwentyFourHourRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiThreeHourRegional {
    /// Regional air-quality or index reading
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub east: Option<NeaRegionalReading>,
    /// Regional air-quality or index reading
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub west: Option<NeaRegionalReading>,
    /// Regional air-quality or index reading
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub north: Option<NeaRegionalReading>,
    /// Regional air-quality or index reading
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub south: Option<NeaRegionalReading>,
    /// Regional air-quality or index reading
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub central: Option<NeaRegionalReading>,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiTwentyFourHourRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiSo2SubIndexRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
/// Concentration is measured in micrograms per cubic metre
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PsiSo2TwentyFourHourRegional {
    /// Regional air-quality or index reading
    pub east: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub west: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub north: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub south: NeaRegionalReading,
    /// Regional air-quality or index reading
    pub central: NeaRegionalReading,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Pm25Data {
    #[cfg_attr(feature = "serde", serde(rename = "regionMetadata"))]
    pub region_metadata: Vec<NeaRegionMetadata>,
    pub items: Vec<Pm25Snapshot>,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Pm25Snapshot {
    /// SGT calendar date (YYYY-MM-DD)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_date")
    )]
    pub date: satay_runtime::Date,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "updatedTimestamp",
            with = "satay_runtime::serde_string::as_offset_datetime"
        )
    )]
    pub updated_timestamp: satay_runtime::OffsetDateTime,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub timestamp: satay_runtime::OffsetDateTime,
    /// Overall and regional PSI data including pollutant concentrations and sub-indices
    pub readings: Pm25Readings,
}
/// Overall and regional PSI data including pollutant concentrations and sub-indices
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Pm25Readings {
    pub pm25_one_hourly: Pm25OneHourRegional,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Pm25OneHourRegional {
    /// Regional PM2.5 one-hour reading (µg/m³)
    pub east: NeaPm25HourlyReading,
    /// Regional PM2.5 one-hour reading (µg/m³)
    pub west: NeaPm25HourlyReading,
    /// Regional PM2.5 one-hour reading (µg/m³)
    pub north: NeaPm25HourlyReading,
    /// Regional PM2.5 one-hour reading (µg/m³)
    pub south: NeaPm25HourlyReading,
    /// Regional PM2.5 one-hour reading (µg/m³)
    pub central: NeaPm25HourlyReading,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UvData {
    pub records: Vec<UvDayRecord>,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UvDayRecord {
    pub index: Vec<UvHourlyIndex>,
    /// SGT calendar date (YYYY-MM-DD)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_date")
    )]
    pub date: satay_runtime::Date,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "updatedTimestamp",
            with = "satay_runtime::serde_string::as_offset_datetime"
        )
    )]
    pub updated_timestamp: satay_runtime::OffsetDateTime,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub timestamp: satay_runtime::OffsetDateTime,
}
/// Reverse-chronologically ordered indexes
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UvHourlyIndex {
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(with = "satay_runtime::serde_string::as_offset_datetime")
    )]
    pub hour: satay_runtime::OffsetDateTime,
    /// UV index for the hour
    pub value: NeaUvIndex,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WeatherSubApiData {
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub records: Option<Vec<WeatherSubApiDayRecord>>,
    /// Token to retrieve next page if exists
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "paginationToken",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub pagination_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WeatherSubApiDayRecord {
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(
            with = "satay_runtime::serde_string::as_offset_datetime::option",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub datetime: Option<satay_runtime::OffsetDateTime>,
    /// Weather sub-API observation (lightning strikes or WBGT station readings)
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub item: Option<WeatherSubApiObservation>,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "updatedTimestamp",
            with = "satay_runtime::serde_string::as_offset_datetime::option",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub updated_timestamp: Option<satay_runtime::OffsetDateTime>,
}
/// Weather sub-API observation (lightning strikes or WBGT station readings)
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WeatherSubApiObservation {
    /// Type of the weather information (`observation` or `forecast`). Lightning is always `observation.
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub r#type: Option<String>,
    /// Indicates whether the weather information includes station information.
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "isStationData",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub is_station_data: Option<bool>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub readings: Option<Vec<WeatherSubApiLightningReading>>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WeatherSubApiLightningReading {
    /// WGS84 coordinates (API returns lat/long as decimal strings)
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub location: Option<NeaLightningGeoPoint>,
    /// ISO 8601 date or date-time in Singapore Time (SGT)
    #[cfg_attr(
        feature = "serde",
        serde(
            with = "satay_runtime::serde_string::as_offset_datetime::option",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub datetime: Option<satay_runtime::OffsetDateTime>,
    /// A textual description of the weather event, which can include types such as "Cloud to Cloud" or "Cloud to Ground."
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub text: Option<String>,
    /// Lightning event type (C = cloud-to-cloud, G = cloud-to-ground)
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub r#type: Option<NeaLightningType>,
    /// WBGT monitoring station metadata
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub station: Option<NeaWbgtStation>,
    /// 15-minute average WBGT (°C) as a decimal string
    #[cfg_attr(
        feature = "serde",
        serde(
            with = "satay_runtime::serde_string::as_f64::option",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub wbgt: Option<f64>,
    /// Heat stress advisory level from WBGT
    #[cfg_attr(
        feature = "serde",
        serde(
            rename = "heatStress",
            default,
            skip_serializing_if = "Option::is_none"
        )
    )]
    pub heat_stress: Option<NeaHeatStressLevel>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WeatherSubApiInvalidParamsError {
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub code: Option<f64>,
    #[cfg_attr(
        feature = "serde",
        serde(default, skip_serializing_if = "Option::is_none")
    )]
    pub name: Option<String>,
    #[cfg_attr(
        feature = "serde",
        serde(rename = "errorMsg", default, skip_serializing_if = "Option::is_none")
    )]
    pub error_msg: Option<String>,
}
/// Unit of measure for NEA station readings and outlook ranges
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaMeasurementUnit {
    #[cfg_attr(feature = "serde", serde(rename = "deg C"))]
    DegC,
    #[cfg_attr(feature = "serde", serde(rename = "Degrees Celsius"))]
    DegreesCelsius,
    Percentage,
    #[cfg_attr(feature = "serde", serde(rename = "percentage"))]
    PercentLower,
    #[cfg_attr(feature = "serde", serde(rename = "knots"))]
    Knots,
    #[cfg_attr(feature = "serde", serde(rename = "degrees"))]
    Degrees,
    #[cfg_attr(feature = "serde", serde(rename = "mm"))]
    Millimeters,
}
impl NeaMeasurementUnit {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::DegC => "deg C",
            Self::DegreesCelsius => "Degrees Celsius",
            Self::Percentage => "Percentage",
            Self::PercentLower => "percentage",
            Self::Knots => "knots",
            Self::Degrees => "degrees",
            Self::Millimeters => "mm",
        }
    }
}
impl AsRef<str> for NeaMeasurementUnit {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaMeasurementUnit {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// WGS84 coordinates (API returns lat/long as decimal strings)
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaLightningGeoPoint {
    /// WGS84 latitude as a decimal string
    #[cfg_attr(feature = "serde", serde(with = "satay_runtime::serde_string::as_f64"))]
    pub latitude: f64,
    /// WGS84 longitude as a decimal string
    #[cfg_attr(feature = "serde", serde(with = "satay_runtime::serde_string::as_f64"))]
    pub longitude: f64,
}
/// WBGT monitoring station metadata
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NeaWbgtStation {
    /// NEA weather station identifier (S + 2–3 digits)
    pub id: NeaStationId,
    /// Station name
    pub name: String,
    /// Town centre or site label for the station
    #[cfg_attr(feature = "serde", serde(rename = "townCenter"))]
    pub town_center: String,
}
/// 15-minute average WBGT (°C) as a decimal string
pub type NeaWbgt = f64;
/// Heat stress advisory level from WBGT
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaHeatStressLevel {
    Low,
    Moderate,
    High,
}
impl NeaHeatStressLevel {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::Low => "Low",
            Self::Moderate => "Moderate",
            Self::High => "High",
        }
    }
}
impl AsRef<str> for NeaHeatStressLevel {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaHeatStressLevel {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
/// Region name
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NeaRegionMetadataName {
    #[cfg_attr(feature = "serde", serde(rename = "west"))]
    West,
    #[cfg_attr(feature = "serde", serde(rename = "east"))]
    East,
    #[cfg_attr(feature = "serde", serde(rename = "north"))]
    North,
    #[cfg_attr(feature = "serde", serde(rename = "south"))]
    South,
    #[cfg_attr(feature = "serde", serde(rename = "central"))]
    Central,
}
impl NeaRegionMetadataName {
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::West => "west",
            Self::East => "east",
            Self::North => "north",
            Self::South => "south",
            Self::Central => "central",
        }
    }
}
impl AsRef<str> for NeaRegionMetadataName {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}
impl fmt::Display for NeaRegionMetadataName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}
