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