solana_sdk/
deserialize_utils.rs

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