use chumsky::prelude::*;
use crate::{traits::Parsable, Data, MetarError};
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum WindSpeed {
Knots {
speed: Data<u32>,
gusting: Option<Data<u32>>,
},
MetresPerSecond {
speed: Data<u32>,
gusting: Option<Data<u32>>,
},
KilometresPerHour {
speed: Data<u32>,
gusting: Option<Data<u32>>,
},
Greater,
}
impl Parsable for WindSpeed {
fn parser<'src>() -> impl chumsky::Parser<'src, &'src str, Self, extra::Err<MetarError<'src>>> {
choice((
just("P99KT").map(|_| WindSpeed::Greater),
just("P99MPS").map(|_| WindSpeed::Greater),
just("P199KPH").map(|_| WindSpeed::Greater),
just("//KT").map(|_| WindSpeed::Knots {
speed: Data::Unknown,
gusting: None,
}),
group((
text::digits(10).exactly(2).to_slice(),
choice((
just("G//").map(|_| Some(Data::Unknown)),
just("G")
.then(text::digits(10).exactly(2).to_slice())
.map(|(_, gust): (&str, &str)| Some(Data::Known(gust.parse().unwrap()))),
empty().map(|()| None),
)),
just("KT"),
))
.map(|(spd, gusting, _): (&str, Option<Data<u32>>, &str)| {
WindSpeed::Knots {
speed: Data::Known(spd.parse().unwrap()),
gusting,
}
}),
just("//MPS").map(|_| WindSpeed::MetresPerSecond {
speed: Data::Unknown,
gusting: None,
}),
group((
text::digits(10).exactly(2).to_slice(),
choice((
just("G//").map(|_| Some(Data::Unknown)),
just("G")
.then(text::digits(10).exactly(2).to_slice())
.map(|(_, gust): (&str, &str)| Some(Data::Known(gust.parse().unwrap()))),
empty().map(|()| None),
)),
just("MPS"),
))
.map(|(spd, gusting, _): (&str, Option<Data<u32>>, &str)| {
WindSpeed::MetresPerSecond {
speed: Data::Known(spd.parse().unwrap()),
gusting,
}
}),
just("//KPH").map(|_| WindSpeed::KilometresPerHour {
speed: Data::Unknown,
gusting: None,
}),
group((
text::digits(10).exactly(3).to_slice(),
choice((
just("G//").map(|_| Some(Data::Unknown)),
just("G")
.then(text::digits(10).exactly(3).to_slice())
.map(|(_, gust): (&str, &str)| Some(Data::Known(gust.parse().unwrap()))),
empty().map(|()| None),
)),
just("KPH"),
))
.map(|(spd, gusting, _): (&str, Option<Data<u32>>, &str)| {
WindSpeed::KilometresPerHour {
speed: Data::Known(spd.parse().unwrap()),
gusting,
}
}),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_kts() {
assert_eq!(
WindSpeed::parse("//KT").unwrap(),
WindSpeed::Knots {
speed: Data::Unknown,
gusting: None
}
);
assert_eq!(
WindSpeed::parse("00KT").unwrap(),
WindSpeed::Knots {
speed: Data::Known(0),
gusting: None
}
);
assert_eq!(
WindSpeed::parse("40G60KT").unwrap(),
WindSpeed::Knots {
speed: Data::Known(40),
gusting: Some(Data::Known(60))
}
);
assert_eq!(
WindSpeed::parse("40G//KT").unwrap(),
WindSpeed::Knots {
speed: Data::Known(40),
gusting: Some(Data::Unknown)
}
);
assert_eq!(
WindSpeed::parse("99KT").unwrap(),
WindSpeed::Knots {
speed: Data::Known(99),
gusting: None
}
);
assert_eq!(WindSpeed::parse("P99KT").unwrap(), WindSpeed::Greater);
}
#[test]
fn valid_mps() {
assert_eq!(
WindSpeed::parse("//MPS").unwrap(),
WindSpeed::MetresPerSecond {
speed: Data::Unknown,
gusting: None
}
);
assert_eq!(
WindSpeed::parse("00MPS").unwrap(),
WindSpeed::MetresPerSecond {
speed: Data::Known(0),
gusting: None
}
);
assert_eq!(
WindSpeed::parse("40G60MPS").unwrap(),
WindSpeed::MetresPerSecond {
speed: Data::Known(40),
gusting: Some(Data::Known(60))
}
);
assert_eq!(
WindSpeed::parse("40G//MPS").unwrap(),
WindSpeed::MetresPerSecond {
speed: Data::Known(40),
gusting: Some(Data::Unknown)
}
);
assert_eq!(
WindSpeed::parse("99MPS").unwrap(),
WindSpeed::MetresPerSecond {
speed: Data::Known(99),
gusting: None
}
);
assert_eq!(WindSpeed::parse("P99MPS").unwrap(), WindSpeed::Greater);
}
#[test]
fn valid_kph() {
assert_eq!(
WindSpeed::parse("//KPH").unwrap(),
WindSpeed::KilometresPerHour {
speed: Data::Unknown,
gusting: None
}
);
assert_eq!(
WindSpeed::parse("000KPH").unwrap(),
WindSpeed::KilometresPerHour {
speed: Data::Known(0),
gusting: None
}
);
assert_eq!(
WindSpeed::parse("040G060KPH").unwrap(),
WindSpeed::KilometresPerHour {
speed: Data::Known(40),
gusting: Some(Data::Known(60))
}
);
assert_eq!(
WindSpeed::parse("040G//KPH").unwrap(),
WindSpeed::KilometresPerHour {
speed: Data::Known(40),
gusting: Some(Data::Unknown)
}
);
assert_eq!(
WindSpeed::parse("199KPH").unwrap(),
WindSpeed::KilometresPerHour {
speed: Data::Known(199),
gusting: None
}
);
assert_eq!(WindSpeed::parse("P199KPH").unwrap(), WindSpeed::Greater);
}
}