use uom::si::f64::{Angle, Length, Pressure, ThermodynamicTemperature, Velocity};
macro_rules! enum_with_str_repr {
(
$(#[$enum_attr:meta])*
$ident: ident {
$(
$(#[$variant_attr:meta])*
$variant: ident => $val: literal $(| $alt: literal)*,
)*
}) => {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
$(#[$enum_attr])*
pub enum $ident {
$(
$(#[$variant_attr])*
$variant,
)*
}
impl From<$ident> for &'static str {
fn from(slf: $ident) -> Self {
use $ident::*;
match slf {
$(
$variant => $val,
)*
}
}
}
impl<'input> std::convert::TryFrom<&'input str> for $ident {
type Error = ();
fn try_from(val: &'input str) -> Result<Self, Self::Error> {
use $ident::*;
match val {
$(
$val $(| $alt)* => Ok($variant),
)*
_ => Err(())
}
}
}
};
}
enum_with_str_repr! {
ObservationFlag {
Auto => "AUTO",
Nil => "NIL",
Correction => "COR",
CorrectionA => "CCA",
CorrectionB => "CCB",
Delayed => "RTD",
}
}
enum_with_str_repr! {
CloudCoverage {
NoCloud => "SKC",
NilCloud => "NCD",
Clear => "CLR",
NoSignificantCloud => "NSC",
Few => "FEW" | "FW",
Scattered => "SCT" | "SC",
Broken => "BKN",
Overcast => "OVC",
VerticalVisibility => "VV",
}
}
enum_with_str_repr! {
CloudType {
Cumulonimbus => "CB",
ToweringCumulus => "TCU",
Cumulus => "CU",
Cirrus => "CI",
Altocumulus => "AC",
Stratus => "ST",
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ZuluDateTime {
pub day_of_month: u8,
pub time: ZuluTime,
pub is_zulu: bool,
}
impl ZuluDateTime {
#[cfg(feature = "chrono_helpers")]
pub fn as_datetime(&self, year: i32, month: u32) -> chrono::DateTime<chrono_tz::Tz> {
self.time.as_datetime(chrono::TimeZone::ymd(
&chrono_tz::Greenwich,
year,
month,
self.day_of_month as u32,
))
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ZuluTime {
pub hour: u8,
pub minute: u8,
}
impl ZuluTime {
#[cfg(feature = "chrono_helpers")]
pub fn as_datetime(
&self,
date: chrono::Date<chrono_tz::Tz>,
) -> chrono::DateTime<chrono_tz::Tz> {
date.and_hms(self.hour as u32, self.minute as u32, 0)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ZuluTimeRange {
pub begin: ZuluTime,
pub end: ZuluTime,
}
impl ZuluTimeRange {
#[cfg(feature = "chrono_helpers")]
pub fn as_start_and_duration(
&self,
date: chrono::Date<chrono_tz::Tz>,
) -> (chrono::DateTime<chrono_tz::Tz>, chrono::Duration) {
let begin = self.begin.as_datetime(date);
let end = self.end.as_datetime(date);
(begin, (end - begin))
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Wind {
pub direction: Option<Angle>,
pub speed: Option<Velocity>,
pub peak_gust: Option<Velocity>,
pub variance: Option<(Angle, Angle)>,
}
impl Wind {
pub fn is_calm(&self) -> Option<bool> {
if let (Some(Velocity { value: speed, .. }), Some(Angle { value: angle, .. })) =
(self.speed, self.direction)
{
Some(
speed < f64::EPSILON
&& angle < f64::EPSILON
&& self.peak_gust.is_none()
&& self.variance.is_none(),
)
} else {
None
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct RunwayVisibility<'input> {
pub designator: &'input str,
pub visibility: VisibilityType,
pub trend: Option<VisibilityTrend>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum VisibilityType {
Varying {
lower: RawVisibility,
upper: RawVisibility,
},
Fixed(RawVisibility),
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct RawVisibility {
pub out_of_range: Option<OutOfRange>,
pub distance: Length,
}
enum_with_str_repr! {
VisibilityTrend {
Up => "U",
Down => "D",
NoChange => "N",
}
}
enum_with_str_repr! {
OutOfRange {
Above => "P",
Below => "M",
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct RunwayReport<'input> {
pub designator: &'input str,
pub report_info: RunwayReportInfo,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum RunwayReportInfo {
Cleared { friction: Option<f64> },
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Weather {
pub intensity: Intensity,
pub vicinity: bool,
pub descriptor: Option<Descriptor>,
pub condition: Option<Condition>,
}
enum_with_str_repr! {
Intensity {
Light => "-",
Moderate => "",
Heavy => "+",
}
}
enum_with_str_repr! {
Descriptor {
Shallow => "MI",
Partial => "PR",
Patches => "BC",
LowDrifting => "DR",
Blowing => "BL",
Showers => "SH",
Thunderstorm => "TS",
Freezing => "FZ",
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Condition {
Precipitation(Vec<Precipitation>),
Obscuration(Obscuration),
Other(Other),
}
enum_with_str_repr! {
Precipitation {
Rain => "RA",
Drizzle => "DZ",
Snow => "SN",
SnowGrains => "SG",
IceCrystals => "IC",
IcePellets => "PL",
Hail => "GR",
Graupel => "GS",
Unknown => "UP",
}
}
enum_with_str_repr! {
Obscuration {
Fog => "FG",
Mist => "BR",
Haze => "HZ",
VolcanicAsh => "VA",
WidespreadDust => "DU",
Smoke => "FU",
Sand => "SA",
Spray => "PY",
}
}
enum_with_str_repr! {
Other {
Squall => "SQ",
SandWhirls => "PO",
Duststorm => "DS",
Sandstorm => "SS",
FunnelCloud => "FC",
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct CloudCover {
pub coverage: CloudCoverage,
pub base: Option<Length>,
pub cloud_type: Option<CloudType>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Temperatures {
pub air: ThermodynamicTemperature,
pub dewpoint: Option<ThermodynamicTemperature>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct AccumulatedRainfall {
pub recent: Length,
pub past: Length,
}
enum_with_str_repr! {
ColorState {
BluePlus => "BLU+",
Blue => "BLU",
White => "WHT",
Green => "GRN",
YellowOne => "YLO1" | "YLO",
YellowTwo => "YLO2",
Amber => "AMB",
Red => "RED",
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Color {
pub is_black: bool,
pub current_color: ColorState,
pub next_color: Option<ColorState>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Visibility {
pub prevailing: Option<RawVisibility>,
pub minimum_directional: Option<DirectionalVisibility>,
pub maximum_directional: Option<DirectionalVisibility>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct DirectionalVisibility {
pub direction: CompassDirection,
pub distance: RawVisibility,
}
enum_with_str_repr! {
CompassDirection {
NorthEast => "NE",
NorthWest => "NW",
North => "N",
SouthEast => "SE",
SouthWest => "SW",
South => "S",
East => "E",
West => "W",
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct WaterConditions {
pub temperature: Option<ThermodynamicTemperature>,
pub surface_state: Option<WaterSurfaceState>,
pub significant_wave_height: Option<Length>,
}
enum_with_str_repr! {
WaterSurfaceState {
GlassyCalm => "0",
RippledCalm => "1",
Smooth => "2",
Slight => "3",
Moderate => "4",
Rough => "5",
VeryRough => "6",
High => "7",
VeryHigh => "8",
Phenomenal => "9",
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum Trend {
NoSignificantChange,
Becoming(TrendReport),
Temporarily(TrendReport),
}
#[derive(Clone, PartialEq, Debug)]
pub struct TrendReport {
pub time: Option<TrendTime>,
pub wind: Option<Wind>,
pub visibility: Option<Visibility>,
pub weather: Vec<Weather>,
pub cloud_cover: Vec<CloudCover>,
pub color_state: Option<ColorState>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct TrendTime {
pub time_type: TrendTimeType,
pub time: ZuluTime,
}
enum_with_str_repr! {
TrendTimeType {
At => "AT",
From => "FM",
Until => "TL",
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct MetarReport<'input> {
pub identifier: &'input str,
pub observation_time: Option<ZuluDateTime>,
pub observation_validity_range: Option<ZuluTimeRange>,
pub observation_flags: Vec<ObservationFlag>,
pub wind: Option<Wind>,
pub visibility: Option<Visibility>,
pub runway_visibilities: Vec<RunwayVisibility<'input>>,
pub runway_reports: Vec<RunwayReport<'input>>,
pub weather: Vec<Weather>,
pub cloud_cover: Vec<CloudCover>,
pub cavok: bool,
pub temperatures: Option<Temperatures>,
pub pressure: Option<Pressure>,
pub accumulated_rainfall: Option<AccumulatedRainfall>,
pub color: Option<Color>,
pub recent_weather: Vec<Weather>,
pub water_conditions: Option<WaterConditions>,
pub trends: Vec<Trend>,
pub remark: Option<&'input str>,
pub maintenance_needed: bool,
}