nt_time/serde_with/rfc_3339/
option.rs1use serde::{Deserializer, Serializer, de::Error as _, ser::Error as _};
46use time::{OffsetDateTime, serde::rfc3339};
47
48use crate::FileTime;
49
50#[allow(clippy::missing_errors_doc)]
51#[inline]
57pub fn serialize<S: Serializer>(ft: &Option<FileTime>, serializer: S) -> Result<S::Ok, S::Error> {
58 rfc3339::option::serialize(
59 &(*ft)
60 .map(OffsetDateTime::try_from)
61 .transpose()
62 .map_err(S::Error::custom)?,
63 serializer,
64 )
65}
66
67#[allow(clippy::missing_errors_doc)]
68#[inline]
74pub fn deserialize<'de, D: Deserializer<'de>>(
75 deserializer: D,
76) -> Result<Option<FileTime>, D::Error> {
77 rfc3339::option::deserialize(deserializer)?
78 .map(FileTime::try_from)
79 .transpose()
80 .map_err(D::Error::custom)
81}
82
83#[cfg(test)]
84mod tests {
85 use serde::{Deserialize, Serialize};
86 use serde_test::{Token, assert_de_tokens_error, assert_ser_tokens_error, assert_tokens};
87
88 use super::*;
89
90 #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
91 struct Test {
92 #[serde(with = "crate::serde_with::rfc_3339::option")]
93 time: Option<FileTime>,
94 }
95
96 #[test]
97 fn serde() {
98 assert_tokens(
99 &Test {
100 time: Some(FileTime::NT_TIME_EPOCH),
101 },
102 &[
103 Token::Struct {
104 name: "Test",
105 len: 1,
106 },
107 Token::Str("time"),
108 Token::Some,
109 Token::BorrowedStr("1601-01-01T00:00:00Z"),
110 Token::StructEnd,
111 ],
112 );
113 assert_tokens(
114 &Test {
115 time: Some(FileTime::UNIX_EPOCH),
116 },
117 &[
118 Token::Struct {
119 name: "Test",
120 len: 1,
121 },
122 Token::Str("time"),
123 Token::Some,
124 Token::BorrowedStr("1970-01-01T00:00:00Z"),
125 Token::StructEnd,
126 ],
127 );
128 assert_tokens(
129 &Test { time: None },
130 &[
131 Token::Struct {
132 name: "Test",
133 len: 1,
134 },
135 Token::Str("time"),
136 Token::None,
137 Token::StructEnd,
138 ],
139 );
140 }
141
142 #[cfg(not(feature = "large-dates"))]
143 #[test]
144 fn serialize_error_without_large_dates() {
145 assert_ser_tokens_error::<Test>(
146 &Test {
147 time: Some(FileTime::MAX),
148 },
149 &[
150 Token::Struct {
151 name: "Test",
152 len: 1,
153 },
154 Token::Str("time"),
155 ],
156 "timestamp must be in the range -377705116800..=253402300799",
157 );
158 }
159
160 #[cfg(feature = "large-dates")]
161 #[test]
162 fn serialize_error_with_large_dates() {
163 assert_ser_tokens_error::<Test>(
164 &Test {
165 time: Some(FileTime::MAX),
166 },
167 &[
168 Token::Struct {
169 name: "Test",
170 len: 1,
171 },
172 Token::Str("time"),
173 ],
174 "The year component cannot be formatted into the requested format.",
175 );
176 }
177
178 #[test]
179 fn deserialize_error() {
180 assert_de_tokens_error::<Test>(
181 &[
182 Token::Struct {
183 name: "Test",
184 len: 1,
185 },
186 Token::Str("time"),
187 Token::Some,
188 Token::BorrowedStr("1600-12-31T23:59:59.999999900Z"),
189 Token::StructEnd,
190 ],
191 "date and time is before `1601-01-01 00:00:00 UTC`",
192 );
193 }
194
195 #[test]
196 fn serialize_json() {
197 assert_eq!(
198 serde_json::to_string(&Test {
199 time: Some(FileTime::NT_TIME_EPOCH)
200 })
201 .unwrap(),
202 r#"{"time":"1601-01-01T00:00:00Z"}"#
203 );
204 assert_eq!(
205 serde_json::to_string(&Test {
206 time: Some(FileTime::UNIX_EPOCH)
207 })
208 .unwrap(),
209 r#"{"time":"1970-01-01T00:00:00Z"}"#
210 );
211 assert_eq!(
212 serde_json::to_string(&Test { time: None }).unwrap(),
213 r#"{"time":null}"#
214 );
215 }
216
217 #[test]
218 fn deserialize_json() {
219 assert_eq!(
220 serde_json::from_str::<Test>(r#"{"time":"1601-01-01T00:00:00Z"}"#).unwrap(),
221 Test {
222 time: Some(FileTime::NT_TIME_EPOCH)
223 }
224 );
225 assert_eq!(
226 serde_json::from_str::<Test>(r#"{"time":"1970-01-01T00:00:00Z"}"#).unwrap(),
227 Test {
228 time: Some(FileTime::UNIX_EPOCH)
229 }
230 );
231 assert_eq!(
232 serde_json::from_str::<Test>(r#"{"time":null}"#).unwrap(),
233 Test { time: None }
234 );
235 }
236}