Skip to main content

crossref_xml/
serializers.rs

1use serde::Serializer;
2
3pub fn serialize_version<S: Serializer>(_: &(), s: S) -> Result<S::Ok, S::Error> {
4    s.serialize_str("5.4.0")
5}
6
7pub fn serialize_xmlns<S: Serializer>(_: &(), s: S) -> Result<S::Ok, S::Error> {
8    s.serialize_str("http://www.crossref.org/schema/5.4.0")
9}
10
11pub fn serialize_xmlns_xsi<S: Serializer>(_: &(), s: S) -> Result<S::Ok, S::Error> {
12    s.serialize_str("http://www.w3.org/2001/XMLSchema-instance")
13}
14
15pub fn serialize_xsi_schemalocation<S: Serializer>(_: &(), s: S) -> Result<S::Ok, S::Error> {
16    s.serialize_str(
17        "http://www.crossref.org/schema/5.4.0 http://data.crossref.org/schemas/crossref5.4.0.xsd",
18    )
19}
20
21pub fn serialize_xmlns_jats<S: Serializer>(_: &(), s: S) -> Result<S::Ok, S::Error> {
22    s.serialize_str("http://www.ncbi.nlm.nih.gov/JATS1")
23}
24
25pub fn serialize_timestamp<S: Serializer>(_: &(), s: S) -> Result<S::Ok, S::Error> {
26    let now = std::time::SystemTime::now();
27    let epoch = now.duration_since(std::time::UNIX_EPOCH).unwrap();
28    s.serialize_str(&epoch.as_millis().to_string())
29}
30
31pub fn serialize_two_digits<S>(value: &Option<u32>, serializer: S) -> Result<S::Ok, S::Error>
32where
33    S: serde::Serializer,
34{
35    match value {
36        Some(v) => serializer.serialize_str(&format!("{:02}", v)),
37        None => serializer.serialize_none(),
38    }
39}
40
41#[cfg(test)]
42mod unit {
43    use super::*;
44    use serde::Serialize;
45
46    #[derive(Serialize)]
47    struct TestVersion {
48        #[serde(serialize_with = "serialize_version")]
49        version: (),
50    }
51
52    #[test]
53    fn test_serialize_version() {
54        let test = TestVersion { version: () };
55        let xml = quick_xml::se::to_string(&test).unwrap();
56        assert!(xml.contains("5.4.0"), "Version should be 5.4.0");
57    }
58
59    #[derive(Serialize)]
60    struct TestXmlns {
61        #[serde(serialize_with = "serialize_xmlns")]
62        xmlns: (),
63    }
64
65    #[test]
66    fn test_serialize_xmlns() {
67        let test = TestXmlns { xmlns: () };
68        let xml = quick_xml::se::to_string(&test).unwrap();
69        assert!(
70            xml.contains("http://www.crossref.org/schema/5.4.0"),
71            "xmlns should contain correct URL"
72        );
73    }
74
75    #[derive(Serialize)]
76    struct TestTimestamp {
77        #[serde(serialize_with = "serialize_timestamp")]
78        timestamp: (),
79    }
80
81    #[test]
82    fn test_serialize_timestamp() {
83        let before = std::time::SystemTime::now()
84            .duration_since(std::time::UNIX_EPOCH)
85            .unwrap()
86            .as_millis();
87
88        let test = TestTimestamp { timestamp: () };
89        let xml = quick_xml::se::to_string(&test).unwrap();
90
91        let after = std::time::SystemTime::now()
92            .duration_since(std::time::UNIX_EPOCH)
93            .unwrap()
94            .as_millis();
95
96        // Extract the timestamp value from the XML
97        assert!(
98            xml.contains("<timestamp>"),
99            "XML should contain timestamp tag"
100        );
101
102        // The timestamp should be between before and after
103        let timestamp_str = xml
104            .trim_start_matches("<TestTimestamp><timestamp>")
105            .trim_end_matches("</timestamp></TestTimestamp>");
106        let timestamp: u128 = timestamp_str.parse().expect("Should parse as u128");
107
108        assert!(
109            timestamp >= before && timestamp <= after,
110            "Timestamp should be between {} and {}, got {}",
111            before,
112            after,
113            timestamp
114        );
115    }
116
117    #[derive(Serialize)]
118    struct TestTwoDigits {
119        #[serde(serialize_with = "serialize_two_digits")]
120        value: Option<u32>,
121    }
122
123    #[test]
124    fn test_serialize_two_digits_some() {
125        let test = TestTwoDigits { value: Some(5) };
126        let xml = quick_xml::se::to_string(&test).unwrap();
127        assert!(
128            xml.contains("05"),
129            "Should format single digit with leading zero"
130        );
131    }
132
133    #[test]
134    fn test_serialize_two_digits_double_digit() {
135        let test = TestTwoDigits { value: Some(42) };
136        let xml = quick_xml::se::to_string(&test).unwrap();
137        assert!(xml.contains("42"), "Should format double digit as-is");
138    }
139
140    #[test]
141    fn test_serialize_two_digits_none() {
142        let test = TestTwoDigits { value: None };
143        let xml = quick_xml::se::to_string(&test).unwrap();
144        // When None, quick-xml typically omits the field or serializes it as empty
145        assert!(
146            !xml.contains("<value>") || xml.contains("<value/>") || xml.contains("<value></value>"),
147            "None should serialize as empty or omitted"
148        );
149    }
150}