Compare commits

...

3 Commits

Author SHA1 Message Date
_ f6251c8a33 add current_maximum and current_minimum 2025-05-03 16:47:28 -05:00
_ 124a28c016 refactor 2025-05-03 15:43:31 -05:00
_ e8aba202af cargo fmt 2025-05-03 13:54:52 -05:00
9 changed files with 1361 additions and 603 deletions

900
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,18 +2,20 @@
name = "weather_dot_gov_rs"
description = "Parses DWML XML from forecast.weather.gov"
repository = "https://six-five-six-four.com/git/reactor/weather_dot_gov_rs"
license = "AGPL-3.0"
version = "0.1.0"
# https://git.pixie.town/thufie/npl-builder/src/commit/a3dbbdb0ee3894c15ea2adb23ee2af6fdc9a1c09/cnpl.md
license = "CNPL"
version = "0.2.0"
edition = "2021"
readme = "README.md"
keywords = ["weather", "forecast", "dwml"]
[dependencies]
quick-xml = { version = "0.37.2", features = ["overlapped-lists", "serialize"] }
serde = { version = "1.0.217", features = ["derive"] }
chrono = "0.4.41"
quick-xml = { version = "0.37.5", features = ["overlapped-lists", "serialize"] }
serde = { version = "1.0.219", features = ["derive"] }
[dev-dependencies]
anyhow = "1.0.95"
reqwest = "0.12.12"
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] }
reqwest = { version = "0.12.15", default-features = false, features = ["rustls-tls"] }
tokio = { version = "1.44.2", features = ["macros", "rt"] }

185
data/empire_city_2.xml Normal file
View File

@ -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&amp;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-03T16: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-03T16: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>79</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&amp;j=ntsra&amp;ip=80&amp;jp=30</icon-link>
<icon-link>https://forecast.weather.gov/DualImage.php?i=shra&amp;j=shra&amp;ip=30&amp;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&amp;j=nshra&amp;ip=50&amp;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&amp;j=shra&amp;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&amp;warncounty=NYC061&amp;firewxzone=NYZ212&amp;local_place1=New+York+NY&amp;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>A slight chance of showers and thunderstorms, then showers likely and possibly a thunderstorm after 5pm. Partly sunny, with a steady temperature around 79. South wind around 14 mph, with gusts as high as 25 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 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-03T15: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>56</value> </temperature> <humidity type="relative" time-layout="k-p1h-n1-1"> <value>38</value> </humidity> <weather time-layout="k-p1h-n1-1"> <name>Weather Type, Coverage, Intensity</name> <weather-conditions weather-summary="Partly Cloudy"/> <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.82</value> </pressure> </parameters></data></dwml>

185
data/empire_city_3.xml Normal file
View File

@ -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&amp;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&amp;j=ntsra&amp;ip=80&amp;jp=30</icon-link>
<icon-link>https://forecast.weather.gov/DualImage.php?i=shra&amp;j=shra&amp;ip=30&amp;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&amp;j=nshra&amp;ip=50&amp;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&amp;j=shra&amp;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&amp;warncounty=NYC061&amp;firewxzone=NYZ212&amp;local_place1=New+York+NY&amp;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>

171
data/sunset_city_2.xml Normal file
View File

@ -0,0 +1,171 @@
<?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-03T13:27:14-07:00</creation-date>
<category>current observations and forecast</category>
</product>
<source>
<production-center>San Francisco Bay Area/Monterey, CA</production-center>
<credit>https://www.weather.gov/mtr</credit>
<more-information>https://www.nws.noaa.gov/forecasts/xml/</more-information>
</source>
</head>
<data type="forecast">
<location>
<location-key>point1</location-key>
<description>San Francisco, CA</description>
<point latitude="37.77" longitude="-122.41"/>
<city state="CA">San Francisco</city>
<height datum="mean sea level">131</height>
</location>
<moreWeatherInformation applicable-location="point1">https://forecast.weather.gov/MapClick.php?lat=37.77&amp;lon=-122.41</moreWeatherInformation>
<time-layout time-coordinate="local" summarization="12hourly">
<layout-key>k-p12h-n13-1</layout-key>
<start-valid-time period-name="This Afternoon">2025-05-03T13:00:00-07:00</start-valid-time>
<start-valid-time period-name="Tonight">2025-05-03T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Sunday">2025-05-04T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Sunday Night">2025-05-04T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Monday">2025-05-05T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Monday Night">2025-05-05T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Tuesday">2025-05-06T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Tuesday Night">2025-05-06T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Wednesday">2025-05-07T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Wednesday Night">2025-05-07T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Thursday">2025-05-08T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Thursday Night">2025-05-08T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Friday">2025-05-09T06:00:00-07:00</start-valid-time>
</time-layout>
<time-layout time-coordinate="local" summarization="12hourly">
<layout-key>k-p24h-n7-1</layout-key>
<start-valid-time period-name="This Afternoon">2025-05-03T13:00:00-07:00</start-valid-time>
<start-valid-time period-name="Sunday">2025-05-04T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Monday">2025-05-05T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Tuesday">2025-05-06T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Wednesday">2025-05-07T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Thursday">2025-05-08T06:00:00-07:00</start-valid-time>
<start-valid-time period-name="Friday">2025-05-09T06:00:00-07:00</start-valid-time>
</time-layout>
<time-layout time-coordinate="local" summarization="12hourly">
<layout-key>k-p24h-n6-2</layout-key>
<start-valid-time period-name="Tonight">2025-05-03T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Sunday Night">2025-05-04T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Monday Night">2025-05-05T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Tuesday Night">2025-05-06T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Wednesday Night">2025-05-07T18:00:00-07:00</start-valid-time>
<start-valid-time period-name="Thursday Night">2025-05-08T18:00:00-07:00</start-valid-time>
</time-layout>
<parameters applicable-location="point1">
<temperature type="maximum" units="Fahrenheit" time-layout="k-p24h-n7-1">
<name>Daily Maximum Temperature</name>
<value>62</value>
<value>67</value>
<value>76</value>
<value>69</value>
<value>65</value>
<value>67</value>
<value>67</value>
</temperature>
<temperature type="minimum" units="Fahrenheit" time-layout="k-p24h-n6-2">
<name>Daily Minimum Temperature</name>
<value>50</value>
<value>55</value>
<value>53</value>
<value>52</value>
<value>51</value>
<value>50</value>
</temperature>
<probability-of-precipitation type="12 hour" units="percent" time-layout="k-p12h-n13-1">
<name>12 Hourly Probability of Precipitation</name>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
<value xsi:nil="true"></value>
</probability-of-precipitation>
<weather time-layout="k-p12h-n13-1">
<name>Weather Type, Coverage, Intensity</name>
<weather-conditions weather-summary="Mostly Sunny and Breezy"/>
<weather-conditions weather-summary="Mostly Clear"/>
<weather-conditions weather-summary="Sunny"/>
<weather-conditions weather-summary="Clear"/>
<weather-conditions weather-summary="Sunny"/>
<weather-conditions weather-summary="Clear"/>
<weather-conditions weather-summary="Sunny"/>
<weather-conditions weather-summary="Partly Cloudy"/>
<weather-conditions weather-summary="Mostly Sunny"/>
<weather-conditions weather-summary="Partly Cloudy"/>
<weather-conditions weather-summary="Mostly Sunny"/>
<weather-conditions weather-summary="Partly Cloudy"/>
<weather-conditions weather-summary="Sunny"/>
</weather>
<conditions-icon type="forecast-NWS" time-layout="k-p12h-n13-1">
<name>Conditions Icon</name> <icon-link>https://forecast.weather.gov/newimages/medium/wind_sct.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/nfew.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/skc.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/nskc.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/skc.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/nskc.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/few.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/nsct.png</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/newimages/medium/sct.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/nsct.png</icon-link>
<icon-link>https://forecast.weather.gov/newimages/medium/few.png</icon-link>
</conditions-icon>
<hazards time-layout="">
<name>Watches, Warnings, and Advisories</name>
<hazard-conditions>
<hazard headline="Wind Advisory">
<hazardTextURL>https://forecast.weather.gov/showsigwx.php?warnzone=CAZ006&amp;warncounty=CAC075&amp;firewxzone=CAZ006&amp;local_place1=San+Francisco+CA&amp;product1=Wind+Advisory</hazardTextURL>
</hazard>
</hazard-conditions>
</hazards>
<wordedForecast time-layout="k-p12h-n13-1" dataSource="mtrNetcdf" wordGenerator="markMitchell">
<name>Text Forecast</name>
<text>Mostly sunny, with a high near 62. Breezy, with a west wind 21 to 23 mph, with gusts as high as 30 mph. </text>
<text>Mostly clear, with a low around 50. West northwest wind 15 to 20 mph decreasing to 9 to 14 mph after midnight. Winds could gust as high as 29 mph. </text>
<text>Sunny, with a high near 67. West wind 7 to 17 mph, with gusts as high as 21 mph. </text>
<text>Clear, with a low around 55. West wind 8 to 15 mph, with gusts as high as 28 mph. </text>
<text>Sunny, with a high near 76. North wind 7 to 13 mph, with gusts as high as 25 mph. </text>
<text>Clear, with a low around 53.</text>
<text>Sunny, with a high near 69.</text>
<text>Partly cloudy, with a low around 52.</text>
<text>Mostly sunny, with a high near 65.</text>
<text>Partly cloudy, with a low around 51.</text>
<text>Mostly sunny, with a high near 67.</text>
<text>Partly cloudy, with a low around 50.</text>
<text>Sunny, with a high near 67.</text>
</wordedForecast>
</parameters>
</data>
<data type="current observations">
<location>
<location-key>point1</location-key>
<point latitude="37.77056" longitude="-122.42694"/>
<area-description>SAN FRANCISCO DOWNTOWN, CA</area-description>
<height datum="mean sea level" height-units="feet">150.0</height>
</location>
<moreWeatherInformation applicable-location="point1">https://www.weather.gov/wrh/LocalWeather?wfo=mtr&amp;zone=CAZ006</moreWeatherInformation> <time-layout time-coordinate="local"> <layout-key>k-p1h-n1-1</layout-key> <start-valid-time period-name="current">2025-05-03T13:30:00</start-valid-time> </time-layout> <parameters applicable-location="point1"> <temperature type="apparent" units="Fahrenheit" time-layout="k-p1h-n1-1"> <value>57</value> </temperature> <temperature type="dew point" units="Fahrenheit" time-layout="k-p1h-n1-1"> <value>46</value> </temperature> <humidity type="relative" time-layout="k-p1h-n1-1"> <value>67</value> </humidity> <weather time-layout="k-p1h-n1-1"> <name>Weather Type, Coverage, Intensity</name> <weather-conditions weather-summary="NA"/> <weather-conditions> <value> <visibility units="statute miles">NA</visibility> </value> </weather-conditions> </weather> <conditions-icon type="forecast-NWS" time-layout="k-p1h-n1-1"> <name>Conditions Icon</name> <icon-link>NULL</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>NA</value> </pressure> </parameters></data></dwml>

16
examples/file.rs Normal file
View File

@ -0,0 +1,16 @@
use anyhow::{Context as _, Result};
use std::str::FromStr as _;
fn main() -> Result<()> {
let mut args = std::env::args();
args.next().unwrap();
let path = args
.next()
.context("Usage: cargo run --example file -- untracked/data.xml")?;
let s = std::fs::read_to_string(&path)?;
let dwml = weather_dot_gov_rs::Dwml::from_str(&s)?;
println!("{dwml:#?}");
Ok(())
}

View File

@ -1,7 +1,7 @@
use anyhow::{Context as _, Result};
use std::str::FromStr as _;
#[tokio::main]
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let mut args = std::env::args();
args.next().context("Expected app name as arg0")?;

294
src/dwml.rs Normal file
View File

@ -0,0 +1,294 @@
//! Encapsulates the DWML implementation details
use chrono::FixedOffset;
use serde::Deserialize;
/// Top-level structure
#[derive(Debug, Deserialize, PartialEq)]
pub struct Dwml {
pub head: Head,
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 {
type Err = quick_xml::DeError;
fn from_str(s: &str) -> Result<Self, quick_xml::DeError> {
quick_xml::de::from_str(s)
}
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Head {
pub product: Product,
pub source: Source,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Forecast {
pub location: Location,
#[serde(rename = "time-layout")]
pub time_layout: (TimeLayout, TimeLayout, TimeLayout),
pub parameters: Parameters,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Location {
#[serde(rename = "area-description")]
pub area_description: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct TimeLayout {
#[serde(rename = "@time-coordinate")]
pub time_coordinate: TimeCoordinate,
#[serde(rename = "layout-key")]
pub layout_key: TimeLayoutKey,
#[serde(rename = "start-valid-time")]
pub start_valid_time: Vec<StartValidTime>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub enum TimeCoordinate {
#[serde(rename = "local")]
Local,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct StartValidTime {
#[serde(rename = "@period-name")]
pub period_name: String,
#[serde(rename = "$value")]
pub value: DateTime,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(try_from = "String")]
pub struct DateTime(pub chrono::DateTime<FixedOffset>);
impl TryFrom<String> for DateTime {
type Error = chrono::ParseError;
fn try_from(s: String) -> Result<Self, Self::Error> {
chrono::DateTime::parse_from_rfc3339(&s).map(Self)
}
}
/// Observations at time of request
///
/// Note that `time-layout` isn't parsed because in San Franscisco it returns a time with no timezone, which makes the schema too complicated. It's in local California time, so we could graft on a timezone from another part of the XML, but why bother.
#[derive(Debug, Deserialize, PartialEq)]
pub struct CurrentObservations {
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)]
pub struct Product {
#[serde(rename = "creation-date")]
pub creation_date: CreationDate,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct CreationDate {
#[serde(rename = "@refresh-frequency")]
pub refresh_frequency: String,
#[serde(rename = "$value")]
pub value: String,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Source {
#[serde(rename = "production-center")]
pub production_center: String,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Parameters {
pub temperature: [Temperature; 2],
#[serde(rename = "probability-of-precipitation")]
pub probability_of_precipitation: ProbabilityOfPrecipitation,
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)]
pub struct Temperature {
#[serde(rename = "@type")]
pub temp_type: TempType,
#[serde(rename = "@units")]
pub units: TempUnits,
#[serde(rename = "@time-layout")]
pub time_layout: TimeLayoutKey,
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)]
pub struct ProbabilityOfPrecipitation {
#[serde(rename = "@time-layout")]
pub time_layout: TimeLayoutKey,
pub value: Vec<PrecipitationValue>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Weather {
#[serde(rename = "@time-layout")]
pub time_layout: TimeLayoutKey,
#[serde(rename = "weather-conditions")]
pub weather_conditions: Vec<WeatherCondition>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct WeatherCondition {
/// e.g. "Showers Likely" or "Partly Sunny"
#[serde(rename = "@weather-summary")]
pub weather_summary: String,
}
// If only there was a percentage that could convey the concept of
// no precipitation.
#[derive(Debug, Deserialize, PartialEq)]
pub struct PrecipitationValue {
#[serde(rename = "$text")]
pub value: Option<u32>,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
pub enum TempType {
#[serde(rename = "apparent")]
Apparent,
#[serde(rename = "dew point")]
DewPoint,
#[serde(rename = "minimum")]
Minimum,
#[serde(rename = "maximum")]
Maximum,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
pub enum TempUnits {
Celsius,
Fahrenheit,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct TimeLayoutKey(String);
#[cfg(test)]
mod tests {
use super::*;
use anyhow::{Context as _, Result};
#[test]
fn empire_city() {
let s = include_str!("../data/empire_city.xml");
let dwml: Dwml = quick_xml::de::from_str(s).unwrap();
assert_eq!(dwml.current_maximum().value, 33);
assert_eq!(dwml.current_minimum().value, 26);
}
#[test]
fn sunset_city() {
let s = include_str!("../data/sunset_city.xml");
let _: Dwml = quick_xml::de::from_str(s).unwrap();
}
#[test]
fn windy_city() {
let s = include_str!("../data/windy_city.xml");
let _: Dwml = quick_xml::de::from_str(s).unwrap();
}
#[test]
fn date() -> Result<()> {
for s in ["2025-01-01T23:00:00-00:00", "2025-01-01T23:00:00-00:00"] {
let _ = chrono::DateTime::parse_from_rfc3339(s).context(s).unwrap();
}
Ok(())
}
}

View File

@ -1,193 +1,8 @@
use serde::Deserialize;
//! Parses the DWML machine-readable format produced by forecast.weather.gov.
//!
//! Try `cargo run --example reqwest 40.71 -74.01`
#[derive(Debug, Deserialize, PartialEq)]
pub struct Dwml {
pub head: Head,
pub data: (Forecast, CurrentObservations),
}
pub mod dwml;
impl std::str::FromStr for Dwml {
type Err = quick_xml::DeError;
fn from_str(s: &str) -> Result<Self, quick_xml::DeError> {
quick_xml::de::from_str(s)
}
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Head {
pub product: Product,
pub source: Source,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Forecast {
pub location: Location,
#[serde(rename = "time-layout")]
pub time_layout: (TimeLayout, TimeLayout, TimeLayout),
pub parameters: Parameters,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Location {
#[serde(rename = "area-description")]
pub area_description: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct TimeLayout {
#[serde(rename = "@time-coordinate")]
pub time_coordinate: TimeCoordinate,
#[serde(rename = "layout-key")]
pub layout_key: LayoutKey,
#[serde(rename = "start-valid-time")]
pub start_valid_time: Vec<StartValidTime>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub enum TimeCoordinate {
#[serde(rename = "local")]
Local,
}
#[derive(Debug, Deserialize, PartialEq)]
pub enum LayoutKey {
#[serde(rename = "k-p12h-n14-1")]
K12Hourly,
#[serde(rename = "k-p24h-n7-1")]
K24Hourly1,
#[serde(rename = "k-p24h-n7-2")]
K24Hourly2,
#[serde(rename = "k-p1h-n1-1")]
K1Hour,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct StartValidTime {
#[serde(rename = "@period-name")]
pub period_name: String,
#[serde(rename = "$value")]
pub value: String,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct CurrentObservations {
pub location: Location,
#[serde(rename = "time-layout")]
pub time_layout: TimeLayout,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Product {
#[serde(rename = "creation-date")]
pub creation_date: CreationDate,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct CreationDate {
#[serde(rename = "@refresh-frequency")]
pub refresh_frequency: String,
#[serde(rename = "$value")]
pub value: String,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Source {
#[serde(rename = "production-center")]
pub production_center: String,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Parameters {
pub temperature: (Temperature, Temperature),
#[serde(rename = "probability-of-precipitation")]
pub probability_of_precipitation: ProbabilityOfPrecipitation,
pub weather: Weather,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Temperature {
#[serde(rename = "@type")]
pub temp_type: TempType,
#[serde(rename = "@units")]
pub units: TempUnits,
#[serde(rename = "@time-layout")]
pub time_layout: LayoutKey,
pub value: Vec<i32>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct ProbabilityOfPrecipitation {
#[serde(rename = "@time-layout")]
pub time_layout: LayoutKey,
pub value: Vec<PrecipitationValue>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Weather {
#[serde(rename = "@time-layout")]
pub time_layout: LayoutKey,
#[serde(rename = "weather-conditions")]
pub weather_conditions: Vec<WeatherCondition>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct WeatherCondition {
#[serde(rename = "@weather-summary")]
pub weather_summary: String,
}
// If only there was a percentage that could convey the concept of
// no precipitation.
#[derive(Debug, Deserialize, PartialEq)]
pub struct PrecipitationValue {
#[serde(rename = "$text")]
pub value: Option<u32>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub enum TempType {
#[serde(rename = "apparent")]
Apparent,
#[serde(rename = "dew point")]
DewPoint,
#[serde(rename = "minimum")]
Minimum,
#[serde(rename = "maximum")]
Maximum,
}
#[derive(Debug, Deserialize, PartialEq)]
pub enum TempUnits {
Celsius,
Fahrenheit,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse() {
let s = include_str!("../data/empire_city.xml");
let _: Dwml = quick_xml::de::from_str(s).unwrap();
let s = include_str!("../data/sunset_city.xml");
let _: Dwml = quick_xml::de::from_str(s).unwrap();
let s = include_str!("../data/windy_city.xml");
let _: Dwml = quick_xml::de::from_str(s).unwrap();
}
}
/// Top-level structure for parsed data
pub use dwml::Dwml;