nt_time/serde_with/unix_time/nanoseconds/
option.rs

1// SPDX-FileCopyrightText: 2024 Shun Sakai
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! Use [Unix time] in nanoseconds when serializing and deserializing an
6//! [`Option<FileTime>`].
7//!
8//! Use this module in combination with Serde's [`with`] attribute.
9//!
10//! # Examples
11//!
12//! ```
13//! use nt_time::{
14//!     FileTime,
15//!     serde::{Deserialize, Serialize},
16//!     serde_with::unix_time,
17//! };
18//!
19//! #[derive(Deserialize, Serialize)]
20//! struct Time {
21//!     #[serde(with = "unix_time::nanoseconds::option")]
22//!     time: Option<FileTime>,
23//! }
24//!
25//! let ft = Time {
26//!     time: Some(FileTime::NT_TIME_EPOCH),
27//! };
28//! let json = serde_json::to_string(&ft).unwrap();
29//! assert_eq!(json, r#"{"time":-11644473600000000000}"#);
30//!
31//! let ft: Time = serde_json::from_str(&json).unwrap();
32//! assert_eq!(ft.time, Some(FileTime::NT_TIME_EPOCH));
33//!
34//! let ft = Time { time: None };
35//! let json = serde_json::to_string(&ft).unwrap();
36//! assert_eq!(json, r#"{"time":null}"#);
37//!
38//! let ft: Time = serde_json::from_str(&json).unwrap();
39//! assert_eq!(ft.time, None);
40//! ```
41//!
42//! [Unix time]: https://en.wikipedia.org/wiki/Unix_time
43//! [`with`]: https://serde.rs/field-attrs.html#with
44
45use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
46
47use crate::FileTime;
48
49#[allow(clippy::missing_errors_doc)]
50/// Serializes an [`Option<FileTime>`] into the given Serde serializer.
51///
52/// This serializes using [Unix time] in nanoseconds.
53///
54/// [Unix time]: https://en.wikipedia.org/wiki/Unix_time
55#[inline]
56pub fn serialize<S: Serializer>(ft: &Option<FileTime>, serializer: S) -> Result<S::Ok, S::Error> {
57    ft.map(FileTime::to_unix_time_nanos).serialize(serializer)
58}
59
60#[allow(clippy::missing_errors_doc)]
61/// Deserializes an [`Option<FileTime>`] from the given Serde deserializer.
62///
63/// This deserializes from its [Unix time] in nanoseconds.
64///
65/// [Unix time]: https://en.wikipedia.org/wiki/Unix_time
66#[inline]
67pub fn deserialize<'de, D: Deserializer<'de>>(
68    deserializer: D,
69) -> Result<Option<FileTime>, D::Error> {
70    Option::deserialize(deserializer)?
71        .map(FileTime::from_unix_time_nanos)
72        .transpose()
73        .map_err(D::Error::custom)
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
81    struct Test {
82        #[serde(with = "crate::serde_with::unix_time::nanoseconds::option")]
83        time: Option<FileTime>,
84    }
85
86    #[test]
87    fn serialize_json() {
88        assert_eq!(
89            serde_json::to_string(&Test {
90                time: Some(FileTime::NT_TIME_EPOCH)
91            })
92            .unwrap(),
93            r#"{"time":-11644473600000000000}"#
94        );
95        assert_eq!(
96            serde_json::to_string(&Test {
97                time: Some(FileTime::UNIX_EPOCH)
98            })
99            .unwrap(),
100            r#"{"time":0}"#
101        );
102        assert_eq!(
103            serde_json::to_string(&Test {
104                time: Some(FileTime::MAX)
105            })
106            .unwrap(),
107            r#"{"time":1833029933770955161500}"#
108        );
109        assert_eq!(
110            serde_json::to_string(&Test { time: None }).unwrap(),
111            r#"{"time":null}"#
112        );
113    }
114
115    #[test]
116    fn deserialize_json() {
117        assert_eq!(
118            serde_json::from_str::<Test>(r#"{"time":-11644473600000000000}"#).unwrap(),
119            Test {
120                time: Some(FileTime::NT_TIME_EPOCH)
121            }
122        );
123        assert_eq!(
124            serde_json::from_str::<Test>(r#"{"time":0}"#).unwrap(),
125            Test {
126                time: Some(FileTime::UNIX_EPOCH)
127            }
128        );
129        assert_eq!(
130            serde_json::from_str::<Test>(r#"{"time":1833029933770955161500}"#).unwrap(),
131            Test {
132                time: Some(FileTime::MAX)
133            }
134        );
135        assert_eq!(
136            serde_json::from_str::<Test>(r#"{"time":null}"#).unwrap(),
137            Test { time: None }
138        );
139    }
140}