Skip to main content

weatherkit/
changes.rs

1use core::{ffi::c_void, marker::PhantomData};
2
3use serde::{Deserialize, Deserializer};
4
5use crate::error::WeatherKitError;
6use crate::ffi;
7use crate::private::parse_json_from_handle;
8use crate::service::WeatherMetadata;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TemperatureUnit {}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum LengthUnit {}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum Deviation {
19    MuchHigher,
20    Higher,
21    Normal,
22    Lower,
23    MuchLower,
24    Unknown(String),
25}
26
27impl Deviation {
28    pub(crate) fn from_raw(value: String) -> Self {
29        match value.as_str() {
30            "muchHigher" => Self::MuchHigher,
31            "higher" => Self::Higher,
32            "normal" => Self::Normal,
33            "lower" => Self::Lower,
34            "muchLower" => Self::MuchLower,
35            other => Self::Unknown(other.to_owned()),
36        }
37    }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41#[non_exhaustive]
42pub enum TrendBaselineKind {
43    Mean,
44    Unknown(String),
45}
46
47impl TrendBaselineKind {
48    pub(crate) fn from_raw(value: String) -> Self {
49        match value.as_str() {
50            "mean" => Self::Mean,
51            other => Self::Unknown(other.to_owned()),
52        }
53    }
54}
55
56#[derive(Debug, Clone, PartialEq, Deserialize)]
57#[serde(rename_all = "camelCase", bound(deserialize = ""))]
58pub struct TrendBaseline<Unit> {
59    #[serde(deserialize_with = "deserialize_trend_baseline_kind")]
60    pub kind: TrendBaselineKind,
61    pub value: f64,
62    pub start_date: String,
63    #[serde(skip)]
64    marker: PhantomData<Unit>,
65}
66
67#[derive(Debug, Clone, PartialEq, Deserialize)]
68#[serde(rename_all = "camelCase", bound(deserialize = ""))]
69pub struct Trend<Unit> {
70    pub baseline: TrendBaseline<Unit>,
71    pub current_value: f64,
72    #[serde(deserialize_with = "deserialize_deviation")]
73    pub deviation: Deviation,
74    #[serde(skip)]
75    marker: PhantomData<Unit>,
76}
77
78#[derive(Debug, Clone, PartialEq, Deserialize)]
79#[serde(rename_all = "camelCase", bound(deserialize = ""))]
80pub struct Percentiles<Unit> {
81    pub p10: f64,
82    pub p50: f64,
83    pub p90: f64,
84    #[serde(skip)]
85    marker: PhantomData<Unit>,
86}
87
88#[derive(Debug, Clone, PartialEq, Deserialize)]
89#[serde(tag = "kind", content = "trend", rename_all = "camelCase")]
90pub enum HistoricalComparison {
91    HighTemperature(Trend<TemperatureUnit>),
92    LowTemperature(Trend<TemperatureUnit>),
93    PrecipitationAmount(Trend<LengthUnit>),
94    SnowfallAmount(Trend<LengthUnit>),
95}
96
97#[derive(Debug, Clone, PartialEq, Deserialize)]
98#[serde(rename_all = "camelCase")]
99pub struct HistoricalComparisons {
100    pub comparisons: Vec<HistoricalComparison>,
101    pub metadata: WeatherMetadata,
102}
103
104impl HistoricalComparisons {
105    pub(crate) fn option_from_owned_ptr(ptr: *mut c_void) -> Result<Option<Self>, WeatherKitError> {
106        parse_json_from_handle(
107            ptr,
108            ffi::json_handle::wk_json_handle_release,
109            ffi::json_handle::wk_json_handle_copy_json,
110            "historical comparisons",
111        )
112    }
113
114    pub fn len(&self) -> usize {
115        self.comparisons.len()
116    }
117
118    pub fn is_empty(&self) -> bool {
119        self.comparisons.is_empty()
120    }
121
122    pub fn iter(&self) -> std::slice::Iter<'_, HistoricalComparison> {
123        self.comparisons.iter()
124    }
125}
126
127impl<'a> IntoIterator for &'a HistoricalComparisons {
128    type Item = &'a HistoricalComparison;
129    type IntoIter = std::slice::Iter<'a, HistoricalComparison>;
130
131    fn into_iter(self) -> Self::IntoIter {
132        self.iter()
133    }
134}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
137#[non_exhaustive]
138pub enum WeatherChangeDirection {
139    Increase,
140    Decrease,
141    Steady,
142    Unknown(String),
143}
144
145impl WeatherChangeDirection {
146    pub(crate) fn from_raw(value: String) -> Self {
147        match value.as_str() {
148            "increase" => Self::Increase,
149            "decrease" => Self::Decrease,
150            "steady" => Self::Steady,
151            other => Self::Unknown(other.to_owned()),
152        }
153    }
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
157#[serde(rename_all = "camelCase")]
158pub struct WeatherChange {
159    pub date: String,
160    #[serde(deserialize_with = "deserialize_weather_change_direction")]
161    pub day_precipitation_amount: WeatherChangeDirection,
162    #[serde(deserialize_with = "deserialize_weather_change_direction")]
163    pub high_temperature: WeatherChangeDirection,
164    #[serde(deserialize_with = "deserialize_weather_change_direction")]
165    pub low_temperature: WeatherChangeDirection,
166    #[serde(deserialize_with = "deserialize_weather_change_direction")]
167    pub night_precipitation_amount: WeatherChangeDirection,
168}
169
170#[derive(Debug, Clone, PartialEq, Deserialize)]
171#[serde(rename_all = "camelCase")]
172pub struct WeatherChanges {
173    pub changes: Vec<WeatherChange>,
174    pub metadata: WeatherMetadata,
175}
176
177impl WeatherChanges {
178    pub(crate) fn option_from_owned_ptr(ptr: *mut c_void) -> Result<Option<Self>, WeatherKitError> {
179        parse_json_from_handle(
180            ptr,
181            ffi::json_handle::wk_json_handle_release,
182            ffi::json_handle::wk_json_handle_copy_json,
183            "weather changes",
184        )
185    }
186
187    pub fn len(&self) -> usize {
188        self.changes.len()
189    }
190
191    pub fn is_empty(&self) -> bool {
192        self.changes.is_empty()
193    }
194
195    pub fn iter(&self) -> std::slice::Iter<'_, WeatherChange> {
196        self.changes.iter()
197    }
198}
199
200impl<'a> IntoIterator for &'a WeatherChanges {
201    type Item = &'a WeatherChange;
202    type IntoIter = std::slice::Iter<'a, WeatherChange>;
203
204    fn into_iter(self) -> Self::IntoIter {
205        self.iter()
206    }
207}
208
209pub(crate) fn deserialize_deviation<'de, D>(deserializer: D) -> Result<Deviation, D::Error>
210where
211    D: Deserializer<'de>,
212{
213    let raw = String::deserialize(deserializer)?;
214    Ok(Deviation::from_raw(raw))
215}
216
217pub(crate) fn deserialize_trend_baseline_kind<'de, D>(
218    deserializer: D,
219) -> Result<TrendBaselineKind, D::Error>
220where
221    D: Deserializer<'de>,
222{
223    let raw = String::deserialize(deserializer)?;
224    Ok(TrendBaselineKind::from_raw(raw))
225}
226
227pub(crate) fn deserialize_weather_change_direction<'de, D>(
228    deserializer: D,
229) -> Result<WeatherChangeDirection, D::Error>
230where
231    D: Deserializer<'de>,
232{
233    let raw = String::deserialize(deserializer)?;
234    Ok(WeatherChangeDirection::from_raw(raw))
235}