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