1#![allow(clippy::upper_case_acronyms)]
4
5use glib::{
6 translate::{FromGlib, IntoGlib},
7 types::StaticType,
8 value::{ToValue, ToValueOptional},
9};
10use serde::{
11 de::{Deserialize, Deserializer, Error},
12 ser,
13 ser::{Serialize, Serializer},
14};
15
16use crate::DateTime;
17
18#[derive(serde::Serialize, serde::Deserialize)]
19enum DateTimeVariants {
20 Y(i32),
21 YM(i32, i32),
22 YMD(i32, i32, i32),
23 YMDhmTz(i32, i32, i32, i32, i32, f32),
24 YMDhmsTz(i32, i32, i32, i32, i32, f64, f32),
25}
26
27pub(crate) struct Date(glib::Date);
32
33impl From<glib::Date> for Date {
34 #[inline]
35 fn from(glib_date: glib::Date) -> Self {
36 skip_assert_initialized!();
37 Date(glib_date)
38 }
39}
40
41impl ToValue for Date {
42 fn to_value(&self) -> glib::Value {
43 self.0.to_value()
44 }
45
46 fn value_type(&self) -> glib::Type {
47 glib::Date::static_type()
48 }
49}
50
51impl ToValueOptional for Date {
52 fn to_value_optional(s: Option<&Self>) -> glib::Value {
53 skip_assert_initialized!();
54 s.map(|s| &s.0).to_value()
55 }
56}
57
58impl StaticType for Date {
59 #[inline]
60 fn static_type() -> glib::Type {
61 glib::Date::static_type()
62 }
63}
64
65impl From<Date> for glib::Value {
66 #[inline]
67 fn from(v: Date) -> glib::Value {
68 skip_assert_initialized!();
69 v.0.into()
70 }
71}
72
73impl Serialize for Date {
74 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
75 DateTimeVariants::YMD(
76 self.0.year() as i32,
77 self.0.month().into_glib(),
78 self.0.day() as i32,
79 )
80 .serialize(serializer)
81 }
82}
83
84impl Serialize for DateTime {
85 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
86 let variant = if self.has_second() {
87 DateTimeVariants::YMDhmsTz(
88 self.year(),
89 self.month().unwrap(),
90 self.day().unwrap(),
91 self.hour().unwrap(),
92 self.minute().unwrap(),
93 f64::from(self.second().unwrap())
94 + f64::from(self.microsecond().unwrap()) / 1_000_000f64,
95 self.time_zone_offset().unwrap(),
96 )
97 } else if self.has_time() {
98 DateTimeVariants::YMDhmTz(
99 self.year(),
100 self.month().unwrap(),
101 self.day().unwrap(),
102 self.hour().unwrap(),
103 self.minute().unwrap(),
104 self.time_zone_offset().unwrap(),
105 )
106 } else if self.has_day() {
107 DateTimeVariants::YMD(self.year(), self.month().unwrap(), self.day().unwrap())
108 } else if self.has_month() {
109 DateTimeVariants::YM(self.year(), self.month().unwrap())
110 } else if self.has_year() {
111 DateTimeVariants::Y(self.year())
112 } else {
113 return Err(ser::Error::custom(format!(
114 "no parts could be found in `DateTime` {self}",
115 )));
116 };
117
118 variant.serialize(serializer)
119 }
120}
121
122impl TryFrom<DateTimeVariants> for Date {
123 type Error = glib::BoolError;
124
125 fn try_from(dt_variant: DateTimeVariants) -> Result<Self, Self::Error> {
126 skip_assert_initialized!();
127 match dt_variant {
128 DateTimeVariants::YMD(y, m, d) => {
129 let month = unsafe { glib::DateMonth::from_glib(m) };
130 if let glib::DateMonth::__Unknown(_) = month {
131 return Err(glib::bool_error!("Out of range `month` for `Date`"));
132 }
133
134 Ok(Date(glib::Date::from_dmy(
135 d.try_into()
136 .map_err(|_| glib::bool_error!("Out of range `day` for `Date`"))?,
137 month,
138 y.try_into()
139 .map_err(|_| glib::bool_error!("Out of range `year` for `Date`"))?,
140 )?))
141 }
142 _ => Err(glib::bool_error!(
143 "Incompatible variant for `Date` (expecting \"YMD\")"
144 )),
145 }
146 }
147}
148
149impl<'de> Deserialize<'de> for Date {
150 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
151 skip_assert_initialized!();
152 DateTimeVariants::deserialize(deserializer)
153 .and_then(|dt_variant| dt_variant.try_into().map_err(D::Error::custom))
154 }
155}
156
157#[allow(clippy::many_single_char_names)]
158impl TryFrom<DateTimeVariants> for DateTime {
159 type Error = glib::BoolError;
160
161 fn try_from(dt_variant: DateTimeVariants) -> Result<Self, Self::Error> {
162 skip_assert_initialized!();
163 match dt_variant {
164 DateTimeVariants::Y(y) => DateTime::from_y(y),
165 DateTimeVariants::YM(y, m) => DateTime::from_ym(y, m),
166 DateTimeVariants::YMD(y, m, d) => DateTime::from_ymd(y, m, d),
167 DateTimeVariants::YMDhmTz(y, m, d, h, mn, tz) => {
168 DateTime::new(tz, y, m, d, h, mn, None)
169 }
170 DateTimeVariants::YMDhmsTz(y, m, d, h, mn, s, tz) => {
171 DateTime::new(tz, y, m, d, h, mn, s)
172 }
173 }
174 }
175}
176
177impl<'de> Deserialize<'de> for DateTime {
178 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
179 skip_assert_initialized!();
180 DateTimeVariants::deserialize(deserializer)
181 .and_then(|dt_variant| dt_variant.try_into().map_err(D::Error::custom))
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use crate::DateTime;
188
189 #[test]
190 fn test_serialize() {
191 crate::init().unwrap();
192
193 let pretty_config = ron::ser::PrettyConfig::new().new_line("".to_string());
194
195 let datetime = DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap();
196 let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
197 assert_eq!(
198 Ok("YMDhmsTz(2018, 5, 28, 16, 6, 42.123456, 2.0)".to_owned()),
199 res,
200 );
201
202 let res = serde_json::to_string(&datetime).unwrap();
203 assert_eq!(
204 r#"{"YMDhmsTz":[2018,5,28,16,6,42.123456,2.0]}"#.to_owned(),
205 res
206 );
207
208 let datetime = DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap();
209 let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
210 assert_eq!(Ok("YMDhmTz(2018, 5, 28, 16, 6, 2.0)".to_owned()), res,);
211
212 let datetime = DateTime::from_ymd(2018, 5, 28).unwrap();
213 let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
214 assert_eq!(Ok("YMD(2018, 5, 28)".to_owned()), res);
215
216 let datetime = DateTime::from_ym(2018, 5).unwrap();
217 let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
218 assert_eq!(Ok("YM(2018, 5)".to_owned()), res);
219
220 let datetime = DateTime::from_y(2018).unwrap();
221 let res = ron::ser::to_string_pretty(&datetime, pretty_config);
222 assert_eq!(Ok("Y(2018)".to_owned()), res);
223 }
224
225 #[test]
226 fn test_deserialize() {
227 crate::init().unwrap();
228
229 let datetime_ron = "YMDhmsTz(2018, 5, 28, 16, 6, 42.123456, 2)";
230 let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
231 assert_eq!(
232 datetime_de,
233 DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap()
234 );
235
236 let datetime_json = r#"{"YMDhmsTz":[2018,5,28,16,6,42.123456,2.0]}"#;
237 let datetime_de: DateTime = serde_json::from_str(datetime_json).unwrap();
238 assert_eq!(
239 datetime_de,
240 DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap()
241 );
242
243 let datetime_ron = "YMDhmTz(2018, 5, 28, 16, 6, 2)";
244 let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
245 assert_eq!(
246 datetime_de,
247 DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap()
248 );
249
250 let datetime_ron = "YMD(2018, 5, 28)";
251 let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
252 assert_eq!(datetime_de, DateTime::from_ymd(2018, 5, 28).unwrap());
253
254 let datetime_ron = "YM(2018, 5)";
255 let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
256 assert_eq!(datetime_de, DateTime::from_ym(2018, 5).unwrap());
257
258 let datetime_ron = "Y(2018)";
259 let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
260 assert_eq!(datetime_de, DateTime::from_y(2018).unwrap());
261 }
262
263 #[test]
264 fn test_serde_roundtrip() {
265 crate::init().unwrap();
266
267 let datetime = DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64).unwrap();
268 let datetime_ser = ron::ser::to_string(&datetime).unwrap();
269 let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
270 assert_eq!(datetime_de, datetime);
271
272 let datetime = DateTime::new(2f32, 2018, 5, 28, 16, 6, None).unwrap();
273 let datetime_ser = ron::ser::to_string(&datetime).unwrap();
274 let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
275 assert_eq!(datetime_de, datetime);
276
277 let datetime = DateTime::from_ymd(2018, 5, 28).unwrap();
278 let datetime_ser = ron::ser::to_string(&datetime).unwrap();
279 let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
280 assert_eq!(datetime_de, datetime);
281
282 let datetime = DateTime::from_ym(2018, 5).unwrap();
283 let datetime_ser = ron::ser::to_string(&datetime).unwrap();
284 let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
285 assert_eq!(datetime_de, datetime);
286
287 let datetime = DateTime::from_y(2018).unwrap();
288 let datetime_ser = ron::ser::to_string(&datetime).unwrap();
289 let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
290 assert_eq!(datetime_de, datetime);
291 }
292}