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}