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