1use core::{ffi::c_void, marker::PhantomData};
4
5use serde::{Deserialize, Deserializer};
6
7use crate::error::WeatherKitError;
8use crate::ffi;
9use crate::private::parse_json_from_handle;
10use crate::service::WeatherMetadata;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum TemperatureUnit {}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum LengthUnit {}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22#[non_exhaustive]
23pub enum Deviation {
24 MuchHigher,
26 Higher,
28 Normal,
30 Lower,
32 MuchLower,
34 Unknown(String),
36}
37
38impl Deviation {
39 pub(crate) fn from_raw(value: String) -> Self {
40 match value.as_str() {
41 "muchHigher" => Self::MuchHigher,
42 "higher" => Self::Higher,
43 "normal" => Self::Normal,
44 "lower" => Self::Lower,
45 "muchLower" => Self::MuchLower,
46 other => Self::Unknown(other.to_owned()),
47 }
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
53#[non_exhaustive]
54pub enum TrendBaselineKind {
55 Mean,
57 Unknown(String),
59}
60
61impl TrendBaselineKind {
62 pub(crate) fn from_raw(value: String) -> Self {
63 match value.as_str() {
64 "mean" => Self::Mean,
65 other => Self::Unknown(other.to_owned()),
66 }
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Deserialize)]
72#[serde(rename_all = "camelCase", bound(deserialize = ""))]
73pub struct TrendBaseline<Unit> {
74 #[serde(deserialize_with = "deserialize_trend_baseline_kind")]
76 pub kind: TrendBaselineKind,
77 pub value: f64,
79 pub start_date: String,
81 #[serde(skip)]
82 marker: PhantomData<Unit>,
83}
84
85#[derive(Debug, Clone, PartialEq, Deserialize)]
87#[serde(rename_all = "camelCase", bound(deserialize = ""))]
88pub struct Trend<Unit> {
89 pub baseline: TrendBaseline<Unit>,
91 pub current_value: f64,
93 #[serde(deserialize_with = "deserialize_deviation")]
95 pub deviation: Deviation,
96 #[serde(skip)]
97 marker: PhantomData<Unit>,
98}
99
100#[derive(Debug, Clone, PartialEq, Deserialize)]
102#[serde(rename_all = "camelCase", bound(deserialize = ""))]
103pub struct Percentiles<Unit> {
104 pub p10: f64,
106 pub p50: f64,
108 pub p90: f64,
110 #[serde(skip)]
111 marker: PhantomData<Unit>,
112}
113
114#[derive(Debug, Clone, PartialEq, Deserialize)]
116#[serde(tag = "kind", content = "trend", rename_all = "camelCase")]
117pub enum HistoricalComparison {
118 HighTemperature(Trend<TemperatureUnit>),
120 LowTemperature(Trend<TemperatureUnit>),
122 PrecipitationAmount(Trend<LengthUnit>),
124 SnowfallAmount(Trend<LengthUnit>),
126}
127
128#[derive(Debug, Clone, PartialEq, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct HistoricalComparisons {
132 pub comparisons: Vec<HistoricalComparison>,
134 pub metadata: WeatherMetadata,
136}
137
138impl HistoricalComparisons {
139 pub(crate) fn option_from_owned_ptr(ptr: *mut c_void) -> Result<Option<Self>, WeatherKitError> {
140 parse_json_from_handle(
141 ptr,
142 ffi::json_handle::wk_json_handle_release,
143 ffi::json_handle::wk_json_handle_copy_json,
144 "historical comparisons",
145 )
146 }
147
148 pub fn len(&self) -> usize {
150 self.comparisons.len()
151 }
152
153 pub fn is_empty(&self) -> bool {
155 self.comparisons.is_empty()
156 }
157
158 pub fn iter(&self) -> std::slice::Iter<'_, HistoricalComparison> {
160 self.comparisons.iter()
161 }
162}
163
164impl<'a> IntoIterator for &'a HistoricalComparisons {
165 type Item = &'a HistoricalComparison;
166 type IntoIter = std::slice::Iter<'a, HistoricalComparison>;
167
168 fn into_iter(self) -> Self::IntoIter {
169 self.iter()
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
175#[non_exhaustive]
176pub enum WeatherChangeDirection {
177 Increase,
179 Decrease,
181 Steady,
183 Unknown(String),
185}
186
187impl WeatherChangeDirection {
188 pub(crate) fn from_raw(value: String) -> Self {
189 match value.as_str() {
190 "increase" => Self::Increase,
191 "decrease" => Self::Decrease,
192 "steady" => Self::Steady,
193 other => Self::Unknown(other.to_owned()),
194 }
195 }
196}
197
198#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
200#[serde(rename_all = "camelCase")]
201pub struct WeatherChange {
202 pub date: String,
204 #[serde(deserialize_with = "deserialize_weather_change_direction")]
206 pub day_precipitation_amount: WeatherChangeDirection,
207 #[serde(deserialize_with = "deserialize_weather_change_direction")]
209 pub high_temperature: WeatherChangeDirection,
210 #[serde(deserialize_with = "deserialize_weather_change_direction")]
212 pub low_temperature: WeatherChangeDirection,
213 #[serde(deserialize_with = "deserialize_weather_change_direction")]
215 pub night_precipitation_amount: WeatherChangeDirection,
216}
217
218#[derive(Debug, Clone, PartialEq, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct WeatherChanges {
222 pub changes: Vec<WeatherChange>,
224 pub metadata: WeatherMetadata,
226}
227
228impl WeatherChanges {
229 pub(crate) fn option_from_owned_ptr(ptr: *mut c_void) -> Result<Option<Self>, WeatherKitError> {
230 parse_json_from_handle(
231 ptr,
232 ffi::json_handle::wk_json_handle_release,
233 ffi::json_handle::wk_json_handle_copy_json,
234 "weather changes",
235 )
236 }
237
238 pub fn len(&self) -> usize {
240 self.changes.len()
241 }
242
243 pub fn is_empty(&self) -> bool {
245 self.changes.is_empty()
246 }
247
248 pub fn iter(&self) -> std::slice::Iter<'_, WeatherChange> {
250 self.changes.iter()
251 }
252}
253
254impl<'a> IntoIterator for &'a WeatherChanges {
255 type Item = &'a WeatherChange;
256 type IntoIter = std::slice::Iter<'a, WeatherChange>;
257
258 fn into_iter(self) -> Self::IntoIter {
259 self.iter()
260 }
261}
262
263pub(crate) fn deserialize_deviation<'de, D>(deserializer: D) -> Result<Deviation, D::Error>
264where
265 D: Deserializer<'de>,
266{
267 let raw = String::deserialize(deserializer)?;
268 Ok(Deviation::from_raw(raw))
269}
270
271pub(crate) fn deserialize_trend_baseline_kind<'de, D>(
272 deserializer: D,
273) -> Result<TrendBaselineKind, D::Error>
274where
275 D: Deserializer<'de>,
276{
277 let raw = String::deserialize(deserializer)?;
278 Ok(TrendBaselineKind::from_raw(raw))
279}
280
281pub(crate) fn deserialize_weather_change_direction<'de, D>(
282 deserializer: D,
283) -> Result<WeatherChangeDirection, D::Error>
284where
285 D: Deserializer<'de>,
286{
287 let raw = String::deserialize(deserializer)?;
288 Ok(WeatherChangeDirection::from_raw(raw))
289}