use chumsky::prelude::*;
use crate::{
parsers::{any_whitespace, some_whitespace},
traits::Parsable,
CloudLayer, VerticalVisibility, Visibility, Weather, Wind,
};
#[derive(PartialEq, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[allow(missing_docs)]
pub enum Trend {
NoSignificantChanges,
NoSignificantWeather,
Becoming(TrendNewCondition),
Temporarily(TrendNewCondition),
}
impl Parsable for Trend {
fn parser<'src>() -> impl Parser<'src, &'src str, Self, extra::Err<crate::MetarError<'src>>> {
choice((
just("NOSIG").map(|_| Trend::NoSignificantChanges),
just("NSW").map(|_| Trend::NoSignificantWeather),
just("BECMG ")
.then(TrendNewCondition::parser())
.map(|(_, cond)| Trend::Becoming(cond)),
just("TEMPO ")
.then(TrendNewCondition::parser())
.map(|(_, cond)| Trend::Temporarily(cond)),
))
}
}
#[derive(PartialEq, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TrendNewCondition {
pub time: Vec<TrendTime>,
pub wind: Option<Wind>,
pub visibility: Option<Visibility>,
pub weather: Vec<Weather>,
pub cloud: Vec<CloudLayer>,
pub vertical_visibility: Option<VerticalVisibility>,
}
impl Parsable for TrendNewCondition {
fn parser<'src>() -> impl Parser<'src, &'src str, Self, extra::Err<crate::MetarError<'src>>> {
group((
TrendTime::parser()
.separated_by(some_whitespace())
.allow_trailing()
.collect::<Vec<_>>()
.or(empty().map(|()| vec![])),
Wind::parser().map(Some).or(empty().map(|()| None)),
Visibility::parser()
.map(Some)
.then_ignore(any_whitespace())
.or(empty().map(|()| None)),
choice((
just("NSW").map(|_| vec![]).then_ignore(any_whitespace()),
Weather::parser()
.separated_by(some_whitespace())
.allow_trailing()
.collect::<Vec<_>>(),
)),
CloudLayer::parser()
.separated_by(some_whitespace())
.allow_trailing()
.collect::<Vec<_>>(),
VerticalVisibility::parser()
.then_ignore(any_whitespace())
.map(Some)
.or(empty().map(|()| None)),
))
.map(
|(time, wind, visibility, weather, cloud, vertical_visibility)| TrendNewCondition {
time,
wind,
visibility,
weather,
cloud,
vertical_visibility,
},
)
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TrendTime {
From(u16),
Until(u16),
At(u16),
}
impl Parsable for TrendTime {
fn parser<'src>() -> impl Parser<'src, &'src str, Self, extra::Err<crate::MetarError<'src>>> {
let time = text::digits(10)
.exactly(4)
.to_slice()
.map(|d: &str| d.parse().unwrap());
choice((
just("FM").then(time).map(|(_, time)| TrendTime::From(time)),
just("TL")
.then(time)
.map(|(_, time)| TrendTime::Until(time)),
just("AT").then(time).map(|(_, time)| TrendTime::At(time)),
))
}
}