1use core::fmt;
10
11use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
12
13use super::FileTime;
14
15impl Serialize for FileTime {
16 #[inline]
56 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
57 serializer.serialize_newtype_struct("FileTime", &self.to_raw())
58 }
59}
60
61impl<'de> Deserialize<'de> for FileTime {
62 #[inline]
95 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
96 struct FileTimeVisitor;
97
98 impl<'de> Visitor<'de> for FileTimeVisitor {
99 type Value = FileTime;
100
101 #[inline]
102 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(formatter, "a newtype struct `FileTime`")
104 }
105
106 #[inline]
107 fn visit_newtype_struct<D: Deserializer<'de>>(
108 self,
109 deserializer: D,
110 ) -> Result<Self::Value, D::Error> {
111 <_>::deserialize(deserializer).map(FileTime::new)
112 }
113 }
114
115 deserializer.deserialize_newtype_struct("FileTime", FileTimeVisitor)
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
124 struct Test {
125 time: FileTime,
126 }
127
128 #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
129 struct TestOption {
130 time: Option<FileTime>,
131 }
132
133 #[test]
134 fn serde() {
135 use serde_test::{Token, assert_tokens};
136
137 assert_tokens(
138 &Test {
139 time: FileTime::NT_TIME_EPOCH,
140 },
141 &[
142 Token::Struct {
143 name: "Test",
144 len: 1,
145 },
146 Token::Str("time"),
147 Token::NewtypeStruct { name: "FileTime" },
148 Token::U64(u64::MIN),
149 Token::StructEnd,
150 ],
151 );
152 assert_tokens(
153 &Test {
154 time: FileTime::UNIX_EPOCH,
155 },
156 &[
157 Token::Struct {
158 name: "Test",
159 len: 1,
160 },
161 Token::Str("time"),
162 Token::NewtypeStruct { name: "FileTime" },
163 Token::U64(116_444_736_000_000_000),
164 Token::StructEnd,
165 ],
166 );
167 assert_tokens(
168 &Test {
169 time: FileTime::MAX,
170 },
171 &[
172 Token::Struct {
173 name: "Test",
174 len: 1,
175 },
176 Token::Str("time"),
177 Token::NewtypeStruct { name: "FileTime" },
178 Token::U64(u64::MAX),
179 Token::StructEnd,
180 ],
181 );
182 }
183
184 #[test]
185 fn deserialize_error() {
186 use serde_test::{Token, assert_de_tokens_error};
187
188 assert_de_tokens_error::<Test>(
189 &[
190 Token::Struct {
191 name: "Test",
192 len: 1,
193 },
194 Token::Str("time"),
195 Token::BorrowedStr("FileTime"),
196 ],
197 r#"invalid type: string "FileTime", expected a newtype struct `FileTime`"#,
198 );
199 assert_de_tokens_error::<Test>(
200 &[
201 Token::Struct {
202 name: "Test",
203 len: 1,
204 },
205 Token::Str("time"),
206 Token::NewtypeStruct { name: "FileTime" },
207 Token::Bool(bool::default()),
208 ],
209 "invalid type: boolean `false`, expected u64",
210 );
211 assert_de_tokens_error::<Test>(
212 &[
213 Token::Struct {
214 name: "Test",
215 len: 1,
216 },
217 Token::Str("time"),
218 Token::NewtypeStruct { name: "FileTime" },
219 Token::I64(i64::MIN),
220 ],
221 "invalid value: integer `-9223372036854775808`, expected u64",
222 );
223 }
224
225 #[test]
226 fn serde_optional() {
227 use serde_test::{Token, assert_tokens};
228
229 assert_tokens(
230 &TestOption {
231 time: Some(FileTime::NT_TIME_EPOCH),
232 },
233 &[
234 Token::Struct {
235 name: "TestOption",
236 len: 1,
237 },
238 Token::Str("time"),
239 Token::Some,
240 Token::NewtypeStruct { name: "FileTime" },
241 Token::U64(u64::MIN),
242 Token::StructEnd,
243 ],
244 );
245 assert_tokens(
246 &TestOption {
247 time: Some(FileTime::UNIX_EPOCH),
248 },
249 &[
250 Token::Struct {
251 name: "TestOption",
252 len: 1,
253 },
254 Token::Str("time"),
255 Token::Some,
256 Token::NewtypeStruct { name: "FileTime" },
257 Token::U64(116_444_736_000_000_000),
258 Token::StructEnd,
259 ],
260 );
261 assert_tokens(
262 &TestOption {
263 time: Some(FileTime::MAX),
264 },
265 &[
266 Token::Struct {
267 name: "TestOption",
268 len: 1,
269 },
270 Token::Str("time"),
271 Token::Some,
272 Token::NewtypeStruct { name: "FileTime" },
273 Token::U64(u64::MAX),
274 Token::StructEnd,
275 ],
276 );
277 assert_tokens(
278 &TestOption { time: None },
279 &[
280 Token::Struct {
281 name: "TestOption",
282 len: 1,
283 },
284 Token::Str("time"),
285 Token::None,
286 Token::StructEnd,
287 ],
288 );
289 }
290
291 #[test]
292 fn deserialize_optional_error() {
293 use serde_test::{Token, assert_de_tokens_error};
294
295 assert_de_tokens_error::<TestOption>(
296 &[
297 Token::Struct {
298 name: "TestOption",
299 len: 1,
300 },
301 Token::Str("time"),
302 Token::BorrowedStr("FileTime"),
303 ],
304 r#"invalid type: string "FileTime", expected option"#,
305 );
306 assert_de_tokens_error::<TestOption>(
307 &[
308 Token::Struct {
309 name: "TestOption",
310 len: 1,
311 },
312 Token::Str("time"),
313 Token::Some,
314 Token::BorrowedStr("FileTime"),
315 ],
316 r#"invalid type: string "FileTime", expected a newtype struct `FileTime`"#,
317 );
318 assert_de_tokens_error::<TestOption>(
319 &[
320 Token::Struct {
321 name: "TestOption",
322 len: 1,
323 },
324 Token::Str("time"),
325 Token::Some,
326 Token::NewtypeStruct { name: "FileTime" },
327 Token::Bool(bool::default()),
328 ],
329 "invalid type: boolean `false`, expected u64",
330 );
331 assert_de_tokens_error::<TestOption>(
332 &[
333 Token::Struct {
334 name: "TestOption",
335 len: 1,
336 },
337 Token::Str("time"),
338 Token::Some,
339 Token::NewtypeStruct { name: "FileTime" },
340 Token::I64(i64::MIN),
341 ],
342 "invalid value: integer `-9223372036854775808`, expected u64",
343 );
344 }
345
346 #[test]
347 fn serialize_json() {
348 assert_eq!(
349 serde_json::to_string(&Test {
350 time: FileTime::NT_TIME_EPOCH
351 })
352 .unwrap(),
353 r#"{"time":0}"#
354 );
355 assert_eq!(
356 serde_json::to_string(&Test {
357 time: FileTime::UNIX_EPOCH
358 })
359 .unwrap(),
360 r#"{"time":116444736000000000}"#
361 );
362 assert_eq!(
363 serde_json::to_string(&Test {
364 time: FileTime::MAX
365 })
366 .unwrap(),
367 r#"{"time":18446744073709551615}"#
368 );
369 }
370
371 #[cfg(feature = "std")]
372 #[test_strategy::proptest]
373 fn serialize_json_roundtrip(raw: u64) {
374 use proptest::prop_assert_eq;
375
376 let ft = Test {
377 time: FileTime::new(raw),
378 };
379 let json = serde_json::to_string(&ft).unwrap();
380 prop_assert_eq!(json, format!(r#"{{"time":{raw}}}"#));
381 }
382
383 #[test]
384 fn serialize_optional_json() {
385 assert_eq!(
386 serde_json::to_string(&TestOption {
387 time: Some(FileTime::NT_TIME_EPOCH)
388 })
389 .unwrap(),
390 r#"{"time":0}"#
391 );
392 assert_eq!(
393 serde_json::to_string(&TestOption {
394 time: Some(FileTime::UNIX_EPOCH)
395 })
396 .unwrap(),
397 r#"{"time":116444736000000000}"#
398 );
399 assert_eq!(
400 serde_json::to_string(&TestOption {
401 time: Some(FileTime::MAX)
402 })
403 .unwrap(),
404 r#"{"time":18446744073709551615}"#
405 );
406 assert_eq!(
407 serde_json::to_string(&TestOption { time: None }).unwrap(),
408 r#"{"time":null}"#
409 );
410 }
411
412 #[cfg(feature = "std")]
413 #[test_strategy::proptest]
414 fn serialize_optional_json_roundtrip(raw: Option<u64>) {
415 use proptest::prop_assert_eq;
416
417 let ft = TestOption {
418 time: raw.map(FileTime::new),
419 };
420 let json = serde_json::to_string(&ft).unwrap();
421 if let Some(r) = raw {
422 prop_assert_eq!(json, format!(r#"{{"time":{r}}}"#));
423 } else {
424 prop_assert_eq!(json, r#"{"time":null}"#);
425 }
426 }
427
428 #[test]
429 fn deserialize_json() {
430 assert_eq!(
431 serde_json::from_str::<Test>(r#"{"time":0}"#).unwrap(),
432 Test {
433 time: FileTime::NT_TIME_EPOCH
434 }
435 );
436 assert_eq!(
437 serde_json::from_str::<Test>(r#"{"time":116444736000000000}"#).unwrap(),
438 Test {
439 time: FileTime::UNIX_EPOCH
440 }
441 );
442 assert_eq!(
443 serde_json::from_str::<Test>(r#"{"time":18446744073709551615}"#).unwrap(),
444 Test {
445 time: FileTime::MAX
446 }
447 );
448 }
449
450 #[cfg(feature = "std")]
451 #[test_strategy::proptest]
452 fn deserialize_json_roundtrip(raw: u64) {
453 use proptest::prop_assert_eq;
454
455 let json = format!(r#"{{"time":{raw}}}"#);
456 let ft = serde_json::from_str::<Test>(&json).unwrap();
457 prop_assert_eq!(ft.time, FileTime::new(raw));
458 }
459
460 #[test]
461 fn deserialize_optional_json() {
462 assert_eq!(
463 serde_json::from_str::<TestOption>(r#"{"time":0}"#).unwrap(),
464 TestOption {
465 time: Some(FileTime::NT_TIME_EPOCH)
466 }
467 );
468 assert_eq!(
469 serde_json::from_str::<TestOption>(r#"{"time":116444736000000000}"#).unwrap(),
470 TestOption {
471 time: Some(FileTime::UNIX_EPOCH)
472 }
473 );
474 assert_eq!(
475 serde_json::from_str::<TestOption>(r#"{"time":18446744073709551615}"#).unwrap(),
476 TestOption {
477 time: Some(FileTime::MAX)
478 }
479 );
480 assert_eq!(
481 serde_json::from_str::<TestOption>(r#"{"time":null}"#).unwrap(),
482 TestOption { time: None }
483 );
484 }
485
486 #[cfg(feature = "std")]
487 #[test_strategy::proptest]
488 fn deserialize_optional_json_roundtrip(raw: Option<u64>) {
489 use std::string::String;
490
491 use proptest::prop_assert_eq;
492
493 let json = if let Some(r) = raw {
494 format!(r#"{{"time":{r}}}"#)
495 } else {
496 String::from(r#"{"time":null}"#)
497 };
498 let ft = serde_json::from_str::<TestOption>(&json).unwrap();
499 prop_assert_eq!(ft.time, raw.map(FileTime::new));
500 }
501}