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}