1 // Copyright (C) 2018 François Laignel <fengalin@free.fr>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 use std::convert::{TryFrom, TryInto};
10 
11 use glib::translate::{FromGlib, ToGlib};
12 use glib::value::{SetValue, SetValueOptional};
13 use glib::StaticType;
14 
15 use serde::de::{Deserialize, Deserializer, Error};
16 use serde::ser;
17 use serde::ser::{Serialize, Serializer};
18 use DateTime;
19 
20 #[derive(Serialize, Deserialize)]
21 enum DateTimeVariants {
22     Y(i32),
23     YM(i32, i32),
24     YMD(i32, i32, i32),
25     YMDhmTz(i32, i32, i32, i32, i32, f32),
26     YMDhmsTz(i32, i32, i32, i32, i32, f64, f32),
27 }
28 
29 // Note: ser / de for `glib::Date` should be implemented in the `glib` crate
30 // However, there is no `ser_de` feature in `glib` right now. The limitation is that
31 // `Date` fields can only be ser / de when they are used in `Value`s (which implies
32 // `Array`s, `List`s, `Structure` fields and `Tag`s)
33 pub(crate) struct Date(glib::Date);
34 
35 impl From<glib::Date> for Date {
from(glib_date: glib::Date) -> Self36     fn from(glib_date: glib::Date) -> Self {
37         Date(glib_date)
38     }
39 }
40 
41 impl SetValue for Date {
set_value(value: &mut glib::Value, this: &Self)42     unsafe fn set_value(value: &mut glib::Value, this: &Self) {
43         glib::value::SetValue::set_value(value, &this.0);
44     }
45 }
46 
47 impl SetValueOptional for Date {
set_value_optional(value: &mut glib::Value, this: Option<&Self>)48     unsafe fn set_value_optional(value: &mut glib::Value, this: Option<&Self>) {
49         glib::value::SetValueOptional::set_value_optional(value, this.map(|this| &this.0));
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.get_year() as i32,
63             self.0.get_month().to_glib() as i32,
64             self.0.get_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.get_year(),
75                 self.get_month(),
76                 self.get_day(),
77                 self.get_hour(),
78                 self.get_minute(),
79                 f64::from(self.get_second()) + f64::from(self.get_microsecond()) / 1_000_000f64,
80                 self.get_time_zone_offset(),
81             )
82         } else if self.has_time() {
83             DateTimeVariants::YMDhmTz(
84                 self.get_year(),
85                 self.get_month(),
86                 self.get_day(),
87                 self.get_hour(),
88                 self.get_minute(),
89                 self.get_time_zone_offset(),
90             )
91         } else if self.has_day() {
92             DateTimeVariants::YMD(self.get_year(), self.get_month(), self.get_day())
93         } else if self.has_month() {
94             DateTimeVariants::YM(self.get_year(), self.get_month())
95         } else if self.has_year() {
96             DateTimeVariants::Y(self.get_year())
97         } else {
98             return Err(ser::Error::custom(format!(
99                 "no parts could be found in `DateTime` {}",
100                 self,
101             )));
102         };
103 
104         variant.serialize(serializer)
105     }
106 }
107 
108 impl TryFrom<DateTimeVariants> for Date {
109     type Error = &'static str;
110 
try_from(dt_variant: DateTimeVariants) -> Result<Self, Self::Error>111     fn try_from(dt_variant: DateTimeVariants) -> Result<Self, Self::Error> {
112         match dt_variant {
113             DateTimeVariants::YMD(y, m, d) => {
114                 let month = glib::DateMonth::from_glib(m);
115                 if let glib::DateMonth::__Unknown(_) = month {
116                     return Err("Out of range `month` for `Date`");
117                 }
118 
119                 Ok(Date(glib::Date::new_dmy(
120                     d.try_into().map_err(|_| "Out of range `day` for `Date`")?,
121                     month,
122                     y.try_into().map_err(|_| "Out of range `year` for `Date`")?,
123                 )))
124             }
125             _ => Err("Incompatible variant for `Date` (expecting \"YMD\")"),
126         }
127     }
128 }
129 
130 impl<'de> Deserialize<'de> for Date {
deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>131     fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
132         DateTimeVariants::deserialize(deserializer)
133             .and_then(|dt_variant| dt_variant.try_into().map_err(D::Error::custom))
134     }
135 }
136 
137 #[allow(clippy::many_single_char_names)]
138 impl From<DateTimeVariants> for DateTime {
from(dt_variant: DateTimeVariants) -> Self139     fn from(dt_variant: DateTimeVariants) -> Self {
140         match dt_variant {
141             DateTimeVariants::Y(y) => DateTime::new_y(y),
142             DateTimeVariants::YM(y, m) => DateTime::new_ym(y, m),
143             DateTimeVariants::YMD(y, m, d) => DateTime::new_ymd(y, m, d),
144             DateTimeVariants::YMDhmTz(y, m, d, h, mn, tz) => {
145                 DateTime::new(tz, y, m, d, h, mn, -1f64)
146             }
147             DateTimeVariants::YMDhmsTz(y, m, d, h, mn, s, tz) => {
148                 DateTime::new(tz, y, m, d, h, mn, s)
149             }
150         }
151     }
152 }
153 
154 impl<'de> Deserialize<'de> for DateTime {
deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>155     fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
156         DateTimeVariants::deserialize(deserializer).map(|dt_variant| dt_variant.into())
157     }
158 }
159 
160 #[cfg(test)]
161 mod tests {
162     extern crate ron;
163     extern crate serde_json;
164 
165     use DateTime;
166 
167     #[test]
test_serialize()168     fn test_serialize() {
169         ::init().unwrap();
170 
171         let mut pretty_config = ron::ser::PrettyConfig::default();
172         pretty_config.new_line = "".to_string();
173 
174         let datetime = DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64);
175         let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
176         assert_eq!(
177             Ok("YMDhmsTz(2018, 5, 28, 16, 6, 42.123456, 2)".to_owned()),
178             res,
179         );
180 
181         let res = serde_json::to_string(&datetime).unwrap();
182         assert_eq!(
183             r#"{"YMDhmsTz":[2018,5,28,16,6,42.123456,2.0]}"#.to_owned(),
184             res
185         );
186 
187         let datetime = DateTime::new(2f32, 2018, 5, 28, 16, 6, -1f64);
188         let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
189         assert_eq!(Ok("YMDhmTz(2018, 5, 28, 16, 6, 2)".to_owned()), res,);
190 
191         let datetime = DateTime::new_ymd(2018, 5, 28);
192         let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
193         assert_eq!(Ok("YMD(2018, 5, 28)".to_owned()), res);
194 
195         let datetime = DateTime::new_ym(2018, 5);
196         let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
197         assert_eq!(Ok("YM(2018, 5)".to_owned()), res);
198 
199         let datetime = DateTime::new_y(2018);
200         let res = ron::ser::to_string_pretty(&datetime, pretty_config);
201         assert_eq!(Ok("Y(2018)".to_owned()), res);
202     }
203 
204     #[test]
test_deserialize()205     fn test_deserialize() {
206         ::init().unwrap();
207 
208         let datetime_ron = "YMDhmsTz(2018, 5, 28, 16, 6, 42.123456, 2)";
209         let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
210         assert_eq!(
211             datetime_de,
212             DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64)
213         );
214 
215         let datetime_json = r#"{"YMDhmsTz":[2018,5,28,16,6,42.123456,2.0]}"#;
216         let datetime_de: DateTime = serde_json::from_str(datetime_json).unwrap();
217         assert_eq!(
218             datetime_de,
219             DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64)
220         );
221 
222         let datetime_ron = "YMDhmTz(2018, 5, 28, 16, 6, 2)";
223         let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
224         assert_eq!(datetime_de, DateTime::new(2f32, 2018, 5, 28, 16, 6, -1f64));
225 
226         let datetime_ron = "YMD(2018, 5, 28)";
227         let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
228         assert_eq!(datetime_de, DateTime::new_ymd(2018, 5, 28));
229 
230         let datetime_ron = "YM(2018, 5)";
231         let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
232         assert_eq!(datetime_de, DateTime::new_ym(2018, 5));
233 
234         let datetime_ron = "Y(2018)";
235         let datetime_de: DateTime = ron::de::from_str(datetime_ron).unwrap();
236         assert_eq!(datetime_de, DateTime::new_y(2018));
237     }
238 
239     #[test]
test_serde_roundtrip()240     fn test_serde_roundtrip() {
241         ::init().unwrap();
242 
243         let datetime = DateTime::new(2f32, 2018, 5, 28, 16, 6, 42.123_456f64);
244         let datetime_ser = ron::ser::to_string(&datetime).unwrap();
245         let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
246         assert_eq!(datetime_de, datetime);
247 
248         let datetime = DateTime::new(2f32, 2018, 5, 28, 16, 6, -1f64);
249         let datetime_ser = ron::ser::to_string(&datetime).unwrap();
250         let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
251         assert_eq!(datetime_de, datetime);
252 
253         let datetime = DateTime::new_ymd(2018, 5, 28);
254         let datetime_ser = ron::ser::to_string(&datetime).unwrap();
255         let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
256         assert_eq!(datetime_de, datetime);
257 
258         let datetime = DateTime::new_ym(2018, 5);
259         let datetime_ser = ron::ser::to_string(&datetime).unwrap();
260         let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
261         assert_eq!(datetime_de, datetime);
262 
263         let datetime = DateTime::new_y(2018);
264         let datetime_ser = ron::ser::to_string(&datetime).unwrap();
265         let datetime_de: DateTime = ron::de::from_str(datetime_ser.as_str()).unwrap();
266         assert_eq!(datetime_de, datetime);
267     }
268 }
269