use borsh::{to_vec, BorshSerialize};
use borsh_incremental::BorshDeserializeIncremental;
#[derive(BorshSerialize, BorshDeserializeIncremental, Debug, PartialEq)]
#[incremental(error = std::io::Error)]
struct Simple {
#[incremental(default = 42)]
a: u32,
#[incremental(default = 7)]
b: u32,
}
#[test]
fn test_incremental_deser() {
let data = Simple { a: 1, b: 2 };
let bytes = to_vec(&data).unwrap();
let decoded = Simple::try_from(&bytes[..]).unwrap();
assert_eq!(decoded, data);
}
#[test]
fn test_truncated_input_defaults_used() {
#[derive(BorshSerialize, BorshDeserializeIncremental, Debug, PartialEq)]
#[incremental(error = std::io::Error)]
struct T {
#[incremental(default = 10)]
a: u32,
#[incremental(default = 20)]
b: u32,
#[incremental(default = 30)]
c: u32,
}
let data = T { a: 1, b: 2, c: 3 };
let bytes = borsh::to_vec(&data).unwrap();
let truncated = &bytes[..4]; let decoded = T::try_from(truncated).unwrap();
assert_eq!(decoded, T { a: 1, b: 20, c: 30 });
}
#[test]
fn test_custom_deser_with() {
#[derive(BorshDeserializeIncremental, Debug, PartialEq)]
#[incremental(error = std::io::Error)]
struct U {
#[incremental(deser_with = read_bool)]
x: bool,
#[incremental(default = 99)]
y: u8,
}
let buf = [1u8]; let decoded = U::try_from(&buf[..]).unwrap();
assert_eq!(decoded, U { x: true, y: 99 });
}
#[test]
fn test_empty_input_all_defaults() {
#[derive(BorshDeserializeIncremental, Debug, PartialEq)]
#[incremental(error = std::io::Error)]
struct V {
#[incremental(default = "hi".to_string())]
s: String,
#[incremental(default = 123)]
n: u32,
}
let decoded = V::try_from(&[][..]).unwrap();
assert_eq!(
decoded,
V {
s: "hi".into(),
n: 123
}
);
}
#[test]
fn test_plain_fields_no_attrs() {
#[derive(BorshSerialize, BorshDeserializeIncremental, Debug, PartialEq)]
#[incremental(error = std::io::Error)]
struct W {
a: u8,
b: u8,
}
let data = W { a: 5, b: 6 };
let bytes = borsh::to_vec(&data).unwrap();
let decoded = W::try_from(&bytes[..1]).unwrap(); assert_eq!(decoded, W { a: 5, b: 0 }); }
fn read_bool(data: &mut &[u8]) -> Result<bool, std::io::Error> {
if data.is_empty() {
return Ok(false);
}
let b = data[0] != 0;
*data = &data[1..]; Ok(b)
}
#[test]
fn test_backward_compat_v1_to_v2() {
use borsh::BorshSerialize;
#[derive(BorshSerialize)]
struct ExampleV1 {
name: String,
count: u32,
}
#[derive(BorshDeserializeIncremental, Debug, PartialEq)]
struct ExampleV2 {
#[incremental(default = "Unnamed".to_string())]
name: String,
count: u32,
#[incremental(default = "active".to_string())]
status: String, }
let old = ExampleV1 {
name: "Alice".into(),
count: 5,
};
let old_bytes = borsh::to_vec(&old).unwrap();
let v2 = ExampleV2::try_from(&old_bytes[..]).unwrap();
assert_eq!(
v2,
ExampleV2 {
name: "Alice".into(),
count: 5,
status: "active".into()
}
);
}
#[test]
fn test_truncated_input_v2_defaults() {
use borsh::BorshSerialize;
#[derive(BorshSerialize)]
struct ExampleV1 {
name: String,
count: u32,
}
#[derive(BorshDeserializeIncremental, Debug, PartialEq)]
struct ExampleV2 {
#[incremental(default = "Unnamed".to_string())]
name: String,
count: u32,
#[incremental(default = "active".to_string())]
status: String,
}
let old = ExampleV1 {
name: "Alice".into(),
count: 5,
};
let old_bytes = borsh::to_vec(&old).unwrap();
let truncated_after_name = &old_bytes[..old_bytes.len() - 4];
let v2 = ExampleV2::try_from(truncated_after_name).unwrap();
assert_eq!(
v2,
ExampleV2 {
name: "Alice".into(),
count: 0,
status: "active".into()
}
);
}
#[test]
fn test_truncated_input_v2_partial_field_errors() {
use borsh::BorshSerialize;
#[derive(BorshSerialize)]
struct ExampleV1 {
name: String,
count: u32,
}
#[derive(BorshDeserializeIncremental, Debug, PartialEq)]
struct ExampleV2 {
#[incremental(default = "Unnamed".to_string())]
name: String,
count: u32,
#[incremental(default = "active".to_string())]
status: String,
}
let old = ExampleV1 {
name: "Alice".into(),
count: 5,
};
let old_bytes = borsh::to_vec(&old).unwrap();
let truncated = &old_bytes[..(4 + 2)];
let err = ExampleV2::try_from(truncated).unwrap_err();
let k = err.kind();
assert!(
k == std::io::ErrorKind::UnexpectedEof || k == std::io::ErrorKind::InvalidData,
"got {:?}",
k
);
}
#[test]
fn test_default_error_type_is_io_error() {
#[derive(BorshDeserializeIncremental, Debug, PartialEq)]
struct E {
#[incremental(default = 1)]
x: u8,
}
let e = E::try_from(&[][..]).unwrap();
assert_eq!(e.x, 1);
}
#[test]
fn test_custom_deser_with_consumes_then_defaults() {
fn read_one(data: &mut &[u8]) -> Result<u8, std::io::Error> {
let v = data.first().copied().unwrap_or(0);
if !data.is_empty() {
*data = &data[1..];
}
Ok(v)
}
#[derive(BorshDeserializeIncremental, Debug, PartialEq)]
struct C {
#[incremental(deser_with = read_one)]
head: u8,
#[incremental(default = 77)]
tail: u8,
}
let buf = [9u8]; let c = C::try_from(&buf[..]).unwrap();
assert_eq!(c, C { head: 9, tail: 77 });
}
#[test]
fn test_trailing_bytes_ignored() {
#[derive(BorshSerialize, BorshDeserializeIncremental, Debug, PartialEq)]
struct Z {
#[incremental(default = 1)]
x: u8,
}
let bytes = [42u8, 99, 100, 101]; let z = Z::try_from(&bytes[..]).unwrap();
assert_eq!(z, Z { x: 42 });
}
#[test]
fn test_option_field_defaults_to_none() {
#[derive(BorshDeserializeIncremental, Debug, PartialEq)]
struct Opty {
v: Option<u32>,
}
let o = Opty::try_from(&[][..]).unwrap();
assert_eq!(o, Opty { v: None });
}