add current_maximum and current_minimum
parent
124a28c016
commit
f6251c8a33
|
@ -0,0 +1,185 @@
|
||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<dwml version="1.0" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd">
|
||||||
|
<head>
|
||||||
|
<product concise-name="dwmlByDay" operational-mode="developmental" srsName="WGS 1984">
|
||||||
|
<creation-date refresh-frequency='PT1H'>2025-05-03T15:51:08-04:00</creation-date>
|
||||||
|
<category>current observations and forecast</category>
|
||||||
|
</product>
|
||||||
|
<source>
|
||||||
|
<production-center>New York, NY</production-center>
|
||||||
|
<credit>https://www.weather.gov/okx/</credit>
|
||||||
|
<more-information>https://www.nws.noaa.gov/forecasts/xml/</more-information>
|
||||||
|
</source>
|
||||||
|
</head>
|
||||||
|
<data type="forecast">
|
||||||
|
|
||||||
|
<location>
|
||||||
|
<location-key>point1</location-key>
|
||||||
|
<description>New York, NY</description>
|
||||||
|
<point latitude="40.71" longitude="-74.01"/>
|
||||||
|
<city state="NY">New York</city>
|
||||||
|
<height datum="mean sea level">7</height>
|
||||||
|
</location>
|
||||||
|
<moreWeatherInformation applicable-location="point1">https://forecast.weather.gov/MapClick.php?lat=40.71&lon=-74.01</moreWeatherInformation>
|
||||||
|
<time-layout time-coordinate="local" summarization="12hourly">
|
||||||
|
<layout-key>k-p12h-n15-1</layout-key>
|
||||||
|
<start-valid-time period-name="This Afternoon">2025-05-03T17:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Tonight">2025-05-03T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Sunday">2025-05-04T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Sunday Night">2025-05-04T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Monday">2025-05-05T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Monday Night">2025-05-05T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Tuesday">2025-05-06T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Tuesday Night">2025-05-06T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Wednesday">2025-05-07T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Wednesday Night">2025-05-07T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Thursday">2025-05-08T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Thursday Night">2025-05-08T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Friday">2025-05-09T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Friday Night">2025-05-09T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Saturday">2025-05-10T06:00:00-04:00</start-valid-time>
|
||||||
|
</time-layout>
|
||||||
|
|
||||||
|
<time-layout time-coordinate="local" summarization="12hourly">
|
||||||
|
<layout-key>k-p24h-n8-1</layout-key>
|
||||||
|
<start-valid-time period-name="This Afternoon">2025-05-03T17:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Sunday">2025-05-04T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Monday">2025-05-05T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Tuesday">2025-05-06T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Wednesday">2025-05-07T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Thursday">2025-05-08T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Friday">2025-05-09T06:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Saturday">2025-05-10T06:00:00-04:00</start-valid-time>
|
||||||
|
</time-layout>
|
||||||
|
|
||||||
|
<time-layout time-coordinate="local" summarization="12hourly">
|
||||||
|
<layout-key>k-p24h-n7-2</layout-key>
|
||||||
|
<start-valid-time period-name="Tonight">2025-05-03T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Sunday Night">2025-05-04T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Monday Night">2025-05-05T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Tuesday Night">2025-05-06T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Wednesday Night">2025-05-07T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Thursday Night">2025-05-08T18:00:00-04:00</start-valid-time>
|
||||||
|
<start-valid-time period-name="Friday Night">2025-05-09T18:00:00-04:00</start-valid-time>
|
||||||
|
</time-layout>
|
||||||
|
|
||||||
|
|
||||||
|
<parameters applicable-location="point1">
|
||||||
|
|
||||||
|
<temperature type="maximum" units="Fahrenheit" time-layout="k-p24h-n8-1">
|
||||||
|
<name>Daily Maximum Temperature</name>
|
||||||
|
<value>78</value>
|
||||||
|
<value>72</value>
|
||||||
|
<value>62</value>
|
||||||
|
<value>64</value>
|
||||||
|
<value>69</value>
|
||||||
|
<value>66</value>
|
||||||
|
<value>59</value>
|
||||||
|
<value>67</value>
|
||||||
|
</temperature>
|
||||||
|
|
||||||
|
<temperature type="minimum" units="Fahrenheit" time-layout="k-p24h-n7-2">
|
||||||
|
<name>Daily Minimum Temperature</name>
|
||||||
|
<value>61</value>
|
||||||
|
<value>57</value>
|
||||||
|
<value>60</value>
|
||||||
|
<value>59</value>
|
||||||
|
<value>59</value>
|
||||||
|
<value>52</value>
|
||||||
|
<value>52</value>
|
||||||
|
</temperature>
|
||||||
|
|
||||||
|
<probability-of-precipitation type="12 hour" units="percent" time-layout="k-p12h-n15-1">
|
||||||
|
<name>12 Hourly Probability of Precipitation</name>
|
||||||
|
<value>60</value>
|
||||||
|
<value>80</value>
|
||||||
|
<value>60</value>
|
||||||
|
<value>60</value>
|
||||||
|
<value>90</value>
|
||||||
|
<value>90</value>
|
||||||
|
<value>90</value>
|
||||||
|
<value>50</value>
|
||||||
|
<value xsi:nil="true"></value>
|
||||||
|
<value xsi:nil="true"></value>
|
||||||
|
<value>40</value>
|
||||||
|
<value>40</value>
|
||||||
|
<value>30</value>
|
||||||
|
<value>30</value>
|
||||||
|
<value xsi:nil="true"></value>
|
||||||
|
</probability-of-precipitation>
|
||||||
|
|
||||||
|
<weather time-layout="k-p12h-n15-1">
|
||||||
|
<name>Weather Type, Coverage, Intensity</name>
|
||||||
|
<weather-conditions weather-summary="Showers Likely"/>
|
||||||
|
<weather-conditions weather-summary="Showers then Chance T-storms"/>
|
||||||
|
<weather-conditions weather-summary="Chance Showers then Showers Likely"/>
|
||||||
|
<weather-conditions weather-summary="Showers Likely"/>
|
||||||
|
<weather-conditions weather-summary="Showers"/>
|
||||||
|
<weather-conditions weather-summary="Showers"/>
|
||||||
|
<weather-conditions weather-summary="Showers"/>
|
||||||
|
<weather-conditions weather-summary="Chance T-storms then Chance Showers"/>
|
||||||
|
<weather-conditions weather-summary="Mostly Sunny"/>
|
||||||
|
<weather-conditions weather-summary="Partly Cloudy"/>
|
||||||
|
<weather-conditions weather-summary="Partly Sunny then Chance Showers"/>
|
||||||
|
<weather-conditions weather-summary="Chance Showers"/>
|
||||||
|
<weather-conditions weather-summary="Chance Showers"/>
|
||||||
|
<weather-conditions weather-summary="Chance Showers"/>
|
||||||
|
<weather-conditions weather-summary="Mostly Sunny"/>
|
||||||
|
</weather>
|
||||||
|
|
||||||
|
<conditions-icon type="forecast-NWS" time-layout="k-p12h-n15-1">
|
||||||
|
<name>Conditions Icon</name> <icon-link>https://forecast.weather.gov/newimages/medium/shra60.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/DualImage.php?i=nshra&j=ntsra&ip=80&jp=30</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/DualImage.php?i=shra&j=shra&ip=30&jp=60</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/nshra60.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/shra90.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/nshra90.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/shra90.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/DualImage.php?i=nscttsra&j=nshra&ip=50&jp=50</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/sct.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/nsct.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/DualImage.php?i=bkn&j=shra&jp=40</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/nshra40.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/shra30.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/hi_nshwrs30.png</icon-link>
|
||||||
|
<icon-link>https://forecast.weather.gov/newimages/medium/sct.png</icon-link>
|
||||||
|
</conditions-icon>
|
||||||
|
|
||||||
|
<hazards time-layout="">
|
||||||
|
<name>Watches, Warnings, and Advisories</name>
|
||||||
|
<hazard-conditions>
|
||||||
|
<hazard headline="Hazardous Weather Outlook">
|
||||||
|
<hazardTextURL>https://forecast.weather.gov/showsigwx.php?warnzone=NYZ072&warncounty=NYC061&firewxzone=NYZ212&local_place1=New+York+NY&product1=Hazardous+Weather+Outlook</hazardTextURL>
|
||||||
|
</hazard>
|
||||||
|
</hazard-conditions>
|
||||||
|
</hazards>
|
||||||
|
|
||||||
|
<wordedForecast time-layout="k-p12h-n15-1" dataSource="okxNetcdf" wordGenerator="markMitchell">
|
||||||
|
<name>Text Forecast</name>
|
||||||
|
<text>Showers likely and possibly a thunderstorm. Partly sunny, with a steady temperature around 78. Southwest wind around 13 mph, with gusts as high as 25 mph. Chance of precipitation is 60%.</text>
|
||||||
|
<text>Showers and possibly a thunderstorm before 11pm, then a chance of showers and thunderstorms between 11pm and 2am, then a slight chance of showers after 2am. Low around 61. South wind around 10 mph. Chance of precipitation is 80%. New rainfall amounts between a tenth and quarter of an inch, except higher amounts possible in thunderstorms. </text>
|
||||||
|
<text>A chance of showers, then showers likely and possibly a thunderstorm after 2pm. Cloudy, with a high near 72. South wind 9 to 14 mph. Chance of precipitation is 60%. New rainfall amounts of less than a tenth of an inch, except higher amounts possible in thunderstorms. </text>
|
||||||
|
<text>Showers likely and possibly a thunderstorm. Cloudy, with a low around 57. East wind 9 to 11 mph. Chance of precipitation is 60%. New precipitation amounts between a quarter and half of an inch possible. </text>
|
||||||
|
<text>Showers, with thunderstorms also possible after 2pm. High near 62. East wind 11 to 14 mph. Chance of precipitation is 90%. New rainfall amounts between a half and three quarters of an inch possible. </text>
|
||||||
|
<text>Showers, with thunderstorms also possible after 2am. Low around 60. Chance of precipitation is 90%. New rainfall amounts between a half and three quarters of an inch possible. </text>
|
||||||
|
<text>Showers and possibly a thunderstorm before 8am, then showers between 8am and 2pm, then showers and possibly a thunderstorm after 2pm. High near 64. Chance of precipitation is 90%.</text>
|
||||||
|
<text>A chance of showers and thunderstorms before 8pm, then a chance of showers between 8pm and 2am. Mostly cloudy, with a low around 59. Chance of precipitation is 50%.</text>
|
||||||
|
<text>Mostly sunny, with a high near 69.</text>
|
||||||
|
<text>Partly cloudy, with a low around 59.</text>
|
||||||
|
<text>A 40 percent chance of showers. Partly sunny, with a high near 66.</text>
|
||||||
|
<text>A 40 percent chance of showers. Mostly cloudy, with a low around 52.</text>
|
||||||
|
<text>A 30 percent chance of showers. Partly sunny, with a high near 59.</text>
|
||||||
|
<text>A 30 percent chance of showers. Partly cloudy, with a low around 52.</text>
|
||||||
|
<text>Mostly sunny, with a high near 67.</text>
|
||||||
|
</wordedForecast>
|
||||||
|
|
||||||
|
</parameters>
|
||||||
|
</data>
|
||||||
|
<data type="current observations">
|
||||||
|
<location>
|
||||||
|
<location-key>point1</location-key>
|
||||||
|
<point latitude="40.78" longitude="-73.97"/>
|
||||||
|
<area-description>New York City, Central Park, NY</area-description>
|
||||||
|
<height datum="mean sea level" height-units="feet">154</height>
|
||||||
|
</location>
|
||||||
|
<moreWeatherInformation applicable-location="point1">https://www.nws.noaa.gov/data/obhistory/KNYC.html</moreWeatherInformation> <time-layout time-coordinate="local"> <layout-key>k-p1h-n1-1</layout-key> <start-valid-time period-name="current">2025-05-03T16:51:00-04:00</start-valid-time> </time-layout> <parameters applicable-location="point1"> <temperature type="apparent" units="Fahrenheit" time-layout="k-p1h-n1-1"> <value>84</value> </temperature> <temperature type="dew point" units="Fahrenheit" time-layout="k-p1h-n1-1"> <value>55</value> </temperature> <humidity type="relative" time-layout="k-p1h-n1-1"> <value>37</value> </humidity> <weather time-layout="k-p1h-n1-1"> <name>Weather Type, Coverage, Intensity</name> <weather-conditions weather-summary="Fair"/> <weather-conditions> <value> <visibility units="statute miles">10.00</visibility> </value> </weather-conditions> </weather> <conditions-icon type="forecast-NWS" time-layout="k-p1h-n1-1"> <name>Conditions Icon</name> <icon-link>https://forecast.weather.gov/newimages/medium/sct.png</icon-link> </conditions-icon> <direction type="wind" units="degrees true" time-layout="k-p1h-n1-1"> <value>NA</value> </direction> <wind-speed type="gust" units="knots" time-layout="k-p1h-n1-1"> <value>NA</value> </wind-speed> <wind-speed type="sustained" units="knots" time-layout="k-p1h-n1-1"> <value>NA</value> </wind-speed> <pressure type="barometer" units="inches of mercury" time-layout="k-p1h-n1-1"> <value>29.84</value> </pressure> </parameters></data></dwml>
|
|
@ -5,7 +5,9 @@ fn main() -> Result<()> {
|
||||||
let mut args = std::env::args();
|
let mut args = std::env::args();
|
||||||
args.next().unwrap();
|
args.next().unwrap();
|
||||||
|
|
||||||
let path = args.next().context("Usage: cargo run --example file -- untracked/data.xml")?;
|
let path = args
|
||||||
|
.next()
|
||||||
|
.context("Usage: cargo run --example file -- untracked/data.xml")?;
|
||||||
let s = std::fs::read_to_string(&path)?;
|
let s = std::fs::read_to_string(&path)?;
|
||||||
let dwml = weather_dot_gov_rs::Dwml::from_str(&s)?;
|
let dwml = weather_dot_gov_rs::Dwml::from_str(&s)?;
|
||||||
|
|
||||||
|
|
121
src/dwml.rs
121
src/dwml.rs
|
@ -10,6 +10,32 @@ pub struct Dwml {
|
||||||
pub data: (Forecast, CurrentObservations),
|
pub data: (Forecast, CurrentObservations),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dwml {
|
||||||
|
/// Returns the current "high" temperature.
|
||||||
|
///
|
||||||
|
/// Panics if the data doesn't show a current high
|
||||||
|
pub fn current_maximum(&self) -> TempWithUnit {
|
||||||
|
self.forecast()
|
||||||
|
.parameters
|
||||||
|
.temp_by_type(TempType::Maximum)
|
||||||
|
.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current "low" temperature.
|
||||||
|
///
|
||||||
|
/// Panics if the data doesn't show a current low
|
||||||
|
pub fn current_minimum(&self) -> TempWithUnit {
|
||||||
|
self.forecast()
|
||||||
|
.parameters
|
||||||
|
.temp_by_type(TempType::Minimum)
|
||||||
|
.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn forecast(&self) -> &Forecast {
|
||||||
|
&self.data.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for Dwml {
|
impl std::str::FromStr for Dwml {
|
||||||
type Err = quick_xml::DeError;
|
type Err = quick_xml::DeError;
|
||||||
|
|
||||||
|
@ -51,24 +77,6 @@ pub struct TimeLayout {
|
||||||
pub start_valid_time: Vec<StartValidTime>,
|
pub start_valid_time: Vec<StartValidTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeLayout {
|
|
||||||
/// For a given time, returns the index of the interval that the time falls in.
|
|
||||||
fn lookup(&self, t: &chrono::DateTime<FixedOffset>) -> Option<usize> {
|
|
||||||
for i in 0..self.start_valid_time.len() - 1 {
|
|
||||||
let Some(start) = self.start_valid_time.get(i) else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
let Some(stop) = self.start_valid_time.get(i + 1) else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
if start.value.0 <= *t && *t < stop.value.0 {
|
|
||||||
return Some(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
pub enum TimeCoordinate {
|
pub enum TimeCoordinate {
|
||||||
#[serde(rename = "local")]
|
#[serde(rename = "local")]
|
||||||
|
@ -86,7 +94,7 @@ pub struct StartValidTime {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
pub struct DateTime (pub chrono::DateTime<FixedOffset>);
|
pub struct DateTime(pub chrono::DateTime<FixedOffset>);
|
||||||
impl TryFrom<String> for DateTime {
|
impl TryFrom<String> for DateTime {
|
||||||
type Error = chrono::ParseError;
|
type Error = chrono::ParseError;
|
||||||
|
|
||||||
|
@ -101,6 +109,26 @@ impl TryFrom<String> for DateTime {
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
pub struct CurrentObservations {
|
pub struct CurrentObservations {
|
||||||
pub location: Location,
|
pub location: Location,
|
||||||
|
pub parameters: CurrentParameters,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
pub struct CurrentParameters {
|
||||||
|
pub temperature: (Temperature, Temperature),
|
||||||
|
pub humidity: Humidity,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
pub struct Humidity {
|
||||||
|
#[serde(rename = "@type")]
|
||||||
|
pub humidity_type: HumidityType,
|
||||||
|
pub value: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
pub enum HumidityType {
|
||||||
|
#[serde(rename = "relative")]
|
||||||
|
Relative,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
@ -126,7 +154,7 @@ pub struct Source {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
pub struct Parameters {
|
pub struct Parameters {
|
||||||
pub temperature: (Temperature, Temperature),
|
pub temperature: [Temperature; 2],
|
||||||
|
|
||||||
#[serde(rename = "probability-of-precipitation")]
|
#[serde(rename = "probability-of-precipitation")]
|
||||||
pub probability_of_precipitation: ProbabilityOfPrecipitation,
|
pub probability_of_precipitation: ProbabilityOfPrecipitation,
|
||||||
|
@ -134,6 +162,22 @@ pub struct Parameters {
|
||||||
pub weather: Weather,
|
pub weather: Weather,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parameters {
|
||||||
|
fn temp_by_type(&self, temp_type: TempType) -> &Temperature {
|
||||||
|
for temp in &self.temperature {
|
||||||
|
if temp.temp_type == temp_type {
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Couldn't find a Temperature with the given TempType");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TempWithUnit {
|
||||||
|
pub units: TempUnits,
|
||||||
|
pub value: i32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
pub struct Temperature {
|
pub struct Temperature {
|
||||||
#[serde(rename = "@type")]
|
#[serde(rename = "@type")]
|
||||||
|
@ -148,6 +192,19 @@ pub struct Temperature {
|
||||||
pub value: Vec<i32>,
|
pub value: Vec<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Temperature {
|
||||||
|
fn first(&self) -> TempWithUnit {
|
||||||
|
let value = *self
|
||||||
|
.value
|
||||||
|
.first()
|
||||||
|
.expect("temperatures should always have at least one value");
|
||||||
|
TempWithUnit {
|
||||||
|
units: self.units,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
pub struct ProbabilityOfPrecipitation {
|
pub struct ProbabilityOfPrecipitation {
|
||||||
#[serde(rename = "@time-layout")]
|
#[serde(rename = "@time-layout")]
|
||||||
|
@ -180,7 +237,7 @@ pub struct PrecipitationValue {
|
||||||
pub value: Option<u32>,
|
pub value: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||||
pub enum TempType {
|
pub enum TempType {
|
||||||
#[serde(rename = "apparent")]
|
#[serde(rename = "apparent")]
|
||||||
Apparent,
|
Apparent,
|
||||||
|
@ -192,7 +249,7 @@ pub enum TempType {
|
||||||
Maximum,
|
Maximum,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||||
pub enum TempUnits {
|
pub enum TempUnits {
|
||||||
Celsius,
|
Celsius,
|
||||||
Fahrenheit,
|
Fahrenheit,
|
||||||
|
@ -203,24 +260,16 @@ pub struct TimeLayoutKey(String);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anyhow::{Context as _, Result};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use anyhow::{Context as _, Result};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empire_city() {
|
fn empire_city() {
|
||||||
let s = include_str!("../data/empire_city.xml");
|
let s = include_str!("../data/empire_city.xml");
|
||||||
let dwml: Dwml = quick_xml::de::from_str(s).unwrap();
|
let dwml: Dwml = quick_xml::de::from_str(s).unwrap();
|
||||||
|
|
||||||
for (input, expected) in [
|
assert_eq!(dwml.current_maximum().value, 33);
|
||||||
("2025-01-14T18:59:59-05:00", None),
|
assert_eq!(dwml.current_minimum().value, 26);
|
||||||
("2025-01-14T19:00:00-05:00", Some(0)),
|
|
||||||
("2025-01-21T05:59:59-05:00", Some(12)),
|
|
||||||
("2025-01-21T06:00:00-05:00", None),
|
|
||||||
] {
|
|
||||||
let dt = chrono::DateTime::parse_from_rfc3339(input).unwrap();
|
|
||||||
let actual = dwml.data.0.time_layout.0.lookup(&dt);
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -237,10 +286,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn date() -> Result<()> {
|
fn date() -> Result<()> {
|
||||||
for s in [
|
for s in ["2025-01-01T23:00:00-00:00", "2025-01-01T23:00:00-00:00"] {
|
||||||
"2025-01-01T23:00:00-00:00"
|
let _ = chrono::DateTime::parse_from_rfc3339(s).context(s).unwrap();
|
||||||
] {
|
|
||||||
let _ = chrono::DateTime::parse_from_rfc3339(&s).context(s).unwrap();
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue