132 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
		
		
			
		
	
	
			132 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
|   | 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<T, D::Error>
 | ||
|  | 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<u16>,
 | ||
|  |             #[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<u16>,
 | ||
|  |             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()
 | ||
|  |         );
 | ||
|  |     }
 | ||
|  | }
 |