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}