use serde::{Deserialize, Deserializer}; /// This helper function enables successful deserialization of versioned structs; new structs may /// include additional fields if they impl Default and are added to the end of the struct. Right /// now, this function is targeted at `bincode` deserialization; the error match may need to be /// updated if another package needs to be used in the future. pub fn default_on_eof<'de, T, D>(d: D) -> Result where D: Deserializer<'de>, T: Deserialize<'de> + Default, { let result = T::deserialize(d); match result { Err(err) if err.to_string() == "io error: unexpected end of file" => Ok(T::default()), result => result, } } #[cfg(test)] pub mod tests { use super::*; use bincode::deserialize; #[test] fn test_default_on_eof() { #[derive(Serialize, Deserialize, Debug, PartialEq)] struct Foo { bar: u16, #[serde(deserialize_with = "default_on_eof")] baz: Option, #[serde(deserialize_with = "default_on_eof")] quz: String, } let data = vec![1, 0]; assert_eq!( Foo { bar: 1, baz: None, quz: "".to_string(), }, deserialize(&data).unwrap() ); let data = vec![1, 0, 0]; assert_eq!( Foo { bar: 1, baz: None, quz: "".to_string(), }, deserialize(&data).unwrap() ); let data = vec![1, 0, 1]; assert_eq!( Foo { bar: 1, baz: None, quz: "".to_string(), }, deserialize(&data).unwrap() ); let data = vec![1, 0, 1, 0]; assert_eq!( Foo { bar: 1, baz: None, quz: "".to_string(), }, deserialize(&data).unwrap() ); let data = vec![1, 0, 1, 0, 0, 1]; assert_eq!( Foo { bar: 1, baz: Some(0), quz: "".to_string(), }, deserialize(&data).unwrap() ); let data = vec![1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 116]; assert_eq!( Foo { bar: 1, baz: Some(0), quz: "t".to_string(), }, deserialize(&data).unwrap() ); } #[test] #[should_panic] fn test_default_on_eof_additional_untagged_fields() { // If later fields are not tagged `deserialize_with = "default_on_eof"`, deserialization // will panic on any missing fields/data #[derive(Serialize, Deserialize, Debug, PartialEq)] struct Foo { bar: u16, #[serde(deserialize_with = "default_on_eof")] baz: Option, quz: String, } // Fully populated struct will deserialize let data = vec![1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 116]; assert_eq!( Foo { bar: 1, baz: Some(0), quz: "t".to_string(), }, deserialize(&data).unwrap() ); // Will panic because `quz` is missing, even though `baz` is tagged let data = vec![1, 0, 1, 0]; assert_eq!( Foo { bar: 1, baz: None, quz: "".to_string(), }, deserialize(&data).unwrap() ); } }