atlas_serde/
lib.rs

1//! Serde helpers.
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4use serde::{Deserialize, Deserializer};
5
6/// This helper function enables successful deserialization of versioned structs; new structs may
7/// include additional fields if they impl Default and are added to the end of the struct. Right
8/// now, this function is targeted at `bincode` deserialization; the error match may need to be
9/// updated if another package needs to be used in the future.
10pub fn default_on_eof<'de, T, D>(d: D) -> Result<T, D::Error>
11where
12    D: Deserializer<'de>,
13    T: Deserialize<'de> + Default,
14{
15    let result = T::deserialize(d);
16    ignore_eof_error::<'de, T, D::Error>(result)
17}
18
19pub fn ignore_eof_error<'de, T, D>(result: Result<T, D>) -> Result<T, D>
20where
21    T: Deserialize<'de> + Default,
22    D: std::fmt::Display,
23{
24    match result {
25        Err(err) if err.to_string() == "io error: unexpected end of file" => Ok(T::default()),
26        Err(err) if err.to_string() == "io error: failed to fill whole buffer" => Ok(T::default()),
27        result => result,
28    }
29}
30
31#[cfg(test)]
32pub mod tests {
33    use {super::*, bincode::deserialize};
34
35    #[test]
36    fn test_default_on_eof() {
37        #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, PartialEq, Eq)]
38        struct Foo {
39            bar: u16,
40            #[serde(deserialize_with = "default_on_eof")]
41            baz: Option<u16>,
42            #[serde(deserialize_with = "default_on_eof")]
43            quz: String,
44        }
45
46        let data = vec![1, 0];
47        assert_eq!(
48            Foo {
49                bar: 1,
50                baz: None,
51                quz: "".to_string(),
52            },
53            deserialize(&data).unwrap()
54        );
55
56        let data = vec![1, 0, 0];
57        assert_eq!(
58            Foo {
59                bar: 1,
60                baz: None,
61                quz: "".to_string(),
62            },
63            deserialize(&data).unwrap()
64        );
65
66        let data = vec![1, 0, 1];
67        assert_eq!(
68            Foo {
69                bar: 1,
70                baz: None,
71                quz: "".to_string(),
72            },
73            deserialize(&data).unwrap()
74        );
75
76        let data = vec![1, 0, 1, 0];
77        assert_eq!(
78            Foo {
79                bar: 1,
80                baz: None,
81                quz: "".to_string(),
82            },
83            deserialize(&data).unwrap()
84        );
85
86        let data = vec![1, 0, 1, 0, 0, 1];
87        assert_eq!(
88            Foo {
89                bar: 1,
90                baz: Some(0),
91                quz: "".to_string(),
92            },
93            deserialize(&data).unwrap()
94        );
95
96        let data = vec![1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 116];
97        assert_eq!(
98            Foo {
99                bar: 1,
100                baz: Some(0),
101                quz: "t".to_string(),
102            },
103            deserialize(&data).unwrap()
104        );
105    }
106
107    #[test]
108    #[should_panic]
109    fn test_default_on_eof_additional_untagged_fields() {
110        // If later fields are not tagged `deserialize_with = "default_on_eof"`, deserialization
111        // will panic on any missing fields/data
112        #[derive(serde_derive::Serialize, serde_derive::Deserialize, Debug, PartialEq, Eq)]
113        struct Foo {
114            bar: u16,
115            #[serde(deserialize_with = "default_on_eof")]
116            baz: Option<u16>,
117            quz: String,
118        }
119
120        // Fully populated struct will deserialize
121        let data = vec![1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 116];
122        assert_eq!(
123            Foo {
124                bar: 1,
125                baz: Some(0),
126                quz: "t".to_string(),
127            },
128            deserialize(&data).unwrap()
129        );
130
131        // Will panic because `quz` is missing, even though `baz` is tagged
132        let data = vec![1, 0, 1, 0];
133        assert_eq!(
134            Foo {
135                bar: 1,
136                baz: None,
137                quz: "".to_string(),
138            },
139            deserialize(&data).unwrap()
140        );
141    }
142}