nt_time/serde_with/rfc_2822.rs
1// SPDX-FileCopyrightText: 2023 Shun Sakai
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! Use the well-known [RFC 2822 format] when serializing and deserializing a
6//! [`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::rfc_2822,
17//! };
18//!
19//! #[derive(Deserialize, Serialize)]
20//! struct Time {
21//! #[serde(with = "rfc_2822")]
22//! time: FileTime,
23//! }
24//!
25//! let ft = Time {
26//! time: FileTime::UNIX_EPOCH,
27//! };
28//! let json = serde_json::to_string(&ft).unwrap();
29//! assert_eq!(json, r#"{"time":"Thu, 01 Jan 1970 00:00:00 +0000"}"#);
30//!
31//! let ft: Time = serde_json::from_str(&json).unwrap();
32//! assert_eq!(ft.time, FileTime::UNIX_EPOCH);
33//! ```
34//!
35//! [RFC 2822 format]: https://datatracker.ietf.org/doc/html/rfc2822#section-3.3
36//! [`with`]: https://serde.rs/field-attrs.html#with
37
38pub mod option;
39
40use serde::{Deserializer, Serializer, de::Error as _, ser::Error as _};
41use time::serde::rfc2822;
42
43use crate::FileTime;
44
45#[allow(clippy::missing_errors_doc)]
46/// Serializes a [`FileTime`] into the given Serde serializer.
47///
48/// This serializes using the well-known [RFC 2822 format].
49///
50/// [RFC 2822 format]: https://datatracker.ietf.org/doc/html/rfc2822#section-3.3
51#[inline]
52pub fn serialize<S: Serializer>(ft: &FileTime, serializer: S) -> Result<S::Ok, S::Error> {
53 rfc2822::serialize(&(*ft).try_into().map_err(S::Error::custom)?, serializer)
54}
55
56#[allow(clippy::missing_errors_doc)]
57/// Deserializes a [`FileTime`] from the given Serde deserializer.
58///
59/// This deserializes from its [RFC 2822 representation].
60///
61/// [RFC 2822 representation]: https://datatracker.ietf.org/doc/html/rfc2822#section-3.3
62#[inline]
63pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<FileTime, D::Error> {
64 FileTime::try_from(rfc2822::deserialize(deserializer)?).map_err(D::Error::custom)
65}
66
67#[cfg(test)]
68mod tests {
69 use serde::{Deserialize, Serialize};
70 use serde_test::{Token, assert_ser_tokens_error, assert_tokens};
71
72 use super::*;
73
74 #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
75 struct Test {
76 #[serde(with = "crate::serde_with::rfc_2822")]
77 time: FileTime,
78 }
79
80 #[test]
81 fn serde() {
82 assert_tokens(
83 &Test {
84 time: FileTime::UNIX_EPOCH,
85 },
86 &[
87 Token::Struct {
88 name: "Test",
89 len: 1,
90 },
91 Token::Str("time"),
92 Token::BorrowedStr("Thu, 01 Jan 1970 00:00:00 +0000"),
93 Token::StructEnd,
94 ],
95 );
96 }
97
98 #[test]
99 fn serialize_error() {
100 assert_ser_tokens_error::<Test>(
101 &Test {
102 time: FileTime::NT_TIME_EPOCH,
103 },
104 &[
105 Token::Struct {
106 name: "Test",
107 len: 1,
108 },
109 Token::Str("time"),
110 ],
111 "The year component cannot be formatted into the requested format.",
112 );
113 }
114
115 #[test]
116 fn serialize_json() {
117 assert_eq!(
118 serde_json::to_string(&Test {
119 time: FileTime::UNIX_EPOCH
120 })
121 .unwrap(),
122 r#"{"time":"Thu, 01 Jan 1970 00:00:00 +0000"}"#
123 );
124 }
125
126 #[test]
127 fn deserialize_json() {
128 assert_eq!(
129 serde_json::from_str::<Test>(r#"{"time":"Thu, 01 Jan 1970 00:00:00 +0000"}"#).unwrap(),
130 Test {
131 time: FileTime::UNIX_EPOCH
132 }
133 );
134 }
135}