nt_time/serde_with/iso_8601/
option.rs1use serde::{Deserializer, Serializer, de::Error as _, ser::Error as _};
53use time::{OffsetDateTime, serde::iso8601};
54
55use crate::FileTime;
56
57#[allow(clippy::missing_errors_doc)]
58#[inline]
64pub fn serialize<S: Serializer>(ft: &Option<FileTime>, serializer: S) -> Result<S::Ok, S::Error> {
65 iso8601::option::serialize(
66 &(*ft)
67 .map(OffsetDateTime::try_from)
68 .transpose()
69 .map_err(S::Error::custom)?,
70 serializer,
71 )
72}
73
74#[allow(clippy::missing_errors_doc)]
75#[inline]
81pub fn deserialize<'de, D: Deserializer<'de>>(
82 deserializer: D,
83) -> Result<Option<FileTime>, D::Error> {
84 iso8601::option::deserialize(deserializer)?
85 .map(FileTime::try_from)
86 .transpose()
87 .map_err(D::Error::custom)
88}
89
90#[cfg(test)]
91mod tests {
92 use serde::{Deserialize, Serialize};
93 use serde_test::{Token, assert_de_tokens_error, assert_tokens};
94
95 use super::*;
96
97 #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
98 struct Test {
99 #[serde(with = "crate::serde_with::iso_8601::option")]
100 time: Option<FileTime>,
101 }
102
103 #[test]
104 fn serde() {
105 assert_tokens(
106 &Test {
107 time: Some(FileTime::NT_TIME_EPOCH),
108 },
109 &[
110 Token::Struct {
111 name: "Test",
112 len: 1,
113 },
114 Token::Str("time"),
115 Token::Some,
116 Token::BorrowedStr("+001601-01-01T00:00:00.000000000Z"),
117 Token::StructEnd,
118 ],
119 );
120 assert_tokens(
121 &Test {
122 time: Some(FileTime::UNIX_EPOCH),
123 },
124 &[
125 Token::Struct {
126 name: "Test",
127 len: 1,
128 },
129 Token::Str("time"),
130 Token::Some,
131 Token::BorrowedStr("+001970-01-01T00:00:00.000000000Z"),
132 Token::StructEnd,
133 ],
134 );
135 assert_tokens(
136 &Test { time: None },
137 &[
138 Token::Struct {
139 name: "Test",
140 len: 1,
141 },
142 Token::Str("time"),
143 Token::None,
144 Token::StructEnd,
145 ],
146 );
147 }
148
149 #[cfg(feature = "large-dates")]
150 #[test]
151 fn serde_with_large_dates() {
152 assert_tokens(
153 &Test {
154 time: Some(FileTime::MAX),
155 },
156 &[
157 Token::Struct {
158 name: "Test",
159 len: 1,
160 },
161 Token::Str("time"),
162 Token::Some,
163 Token::BorrowedStr("+060056-05-28T05:36:10.955161500Z"),
164 Token::StructEnd,
165 ],
166 );
167 }
168
169 #[test]
170 fn deserialize_error() {
171 assert_de_tokens_error::<Test>(
172 &[
173 Token::Struct {
174 name: "Test",
175 len: 1,
176 },
177 Token::Str("time"),
178 Token::Some,
179 Token::BorrowedStr("+001600-12-31T23:59:59.999999900Z"),
180 Token::StructEnd,
181 ],
182 "date and time is before `1601-01-01 00:00:00 UTC`",
183 );
184 }
185
186 #[cfg(not(feature = "large-dates"))]
187 #[test]
188 fn deserialize_error_without_large_dates() {
189 assert_de_tokens_error::<Test>(
190 &[
191 Token::Struct {
192 name: "Test",
193 len: 1,
194 },
195 Token::Str("time"),
196 Token::Some,
197 Token::BorrowedStr("+010000-01-01T00:00:00.000000000Z"),
198 Token::StructEnd,
199 ],
200 "unexpected trailing characters; the end of input was expected",
201 );
202 }
203
204 #[cfg(feature = "large-dates")]
205 #[test]
206 fn deserialize_error_with_large_dates() {
207 assert_de_tokens_error::<Test>(
208 &[
209 Token::Struct {
210 name: "Test",
211 len: 1,
212 },
213 Token::Str("time"),
214 Token::Some,
215 Token::BorrowedStr("+060056-05-28T05:36:10.955161600Z"),
216 Token::StructEnd,
217 ],
218 "date and time is after `+60056-05-28 05:36:10.955161500 UTC`",
219 );
220 }
221
222 #[test]
223 fn serialize_json() {
224 assert_eq!(
225 serde_json::to_string(&Test {
226 time: Some(FileTime::NT_TIME_EPOCH)
227 })
228 .unwrap(),
229 r#"{"time":"+001601-01-01T00:00:00.000000000Z"}"#
230 );
231 assert_eq!(
232 serde_json::to_string(&Test {
233 time: Some(FileTime::UNIX_EPOCH)
234 })
235 .unwrap(),
236 r#"{"time":"+001970-01-01T00:00:00.000000000Z"}"#
237 );
238 assert_eq!(
239 serde_json::to_string(&Test { time: None }).unwrap(),
240 r#"{"time":null}"#
241 );
242 }
243
244 #[cfg(feature = "large-dates")]
245 #[test]
246 fn serialize_json_with_large_dates() {
247 assert_eq!(
248 serde_json::to_string(&Test {
249 time: Some(FileTime::MAX)
250 })
251 .unwrap(),
252 r#"{"time":"+060056-05-28T05:36:10.955161500Z"}"#
253 );
254 }
255
256 #[test]
257 fn deserialize_json() {
258 assert_eq!(
259 serde_json::from_str::<Test>(r#"{"time":"1601-01-01T00:00:00Z"}"#).unwrap(),
260 Test {
261 time: Some(FileTime::NT_TIME_EPOCH)
262 }
263 );
264 assert_eq!(
265 serde_json::from_str::<Test>(r#"{"time":"1970-01-01T00:00:00Z"}"#).unwrap(),
266 Test {
267 time: Some(FileTime::UNIX_EPOCH)
268 }
269 );
270 assert_eq!(
271 serde_json::from_str::<Test>(r#"{"time":null}"#).unwrap(),
272 Test { time: None }
273 );
274 }
275
276 #[cfg(feature = "large-dates")]
277 #[test]
278 fn deserialize_json_with_large_dates() {
279 assert_eq!(
280 serde_json::from_str::<Test>(r#"{"time":"+060056-05-28T05:36:10.955161500Z"}"#)
281 .unwrap(),
282 Test {
283 time: Some(FileTime::MAX)
284 }
285 );
286 }
287}