gstreamer/
date_time_serde.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#![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
27// Note: ser / de for `glib::Date` should be implemented in the `glib` crate
28// However, there is no `serde` feature in `glib` right now. The limitation is that
29// `Date` fields can only be ser / de when they are used in `Value`s (which implies
30// `Array`s, `List`s, `Structure` fields and `Tag`s)
31pub(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}