132 lines
3.6 KiB
Rust
132 lines
3.6 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()),
|
|
Err(err) if err.to_string() == "io error: failed to fill whole buffer" => Ok(T::default()),
|
|
result => result,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub mod tests {
|
|
use {super::*, 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()
|
|
);
|
|
}
|
|
}
|