Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add (de)serialisation + schema for more core::ops::Range... types (full, open-ended, inclusive) #213

Merged
merged 4 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions borsh/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,19 +315,6 @@ impl BorshDeserialize for bool {
}
}

impl<T> BorshDeserialize for core::ops::Range<T>
where
T: BorshDeserialize,
{
#[inline]
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
Ok(Self {
start: T::deserialize_reader(reader)?,
end: T::deserialize_reader(reader)?,
})
}
}

impl<T> BorshDeserialize for Option<T>
where
T: BorshDeserialize,
Expand Down Expand Up @@ -802,13 +789,16 @@ fn array_deserialization_doesnt_leak() {
}
}

impl BorshDeserialize for () {
fn deserialize_reader<R: Read>(_reader: &mut R) -> Result<Self> {
Ok(())
}
}

macro_rules! impl_tuple {
(@unit $name:ty) => {
impl BorshDeserialize for $name {
#[inline]
fn deserialize_reader<R: Read>(_reader: &mut R) -> Result<Self> {
Ok(<$name>::default())
}
}
};

($($name:ident)+) => {
impl<$($name),+> BorshDeserialize for ($($name,)+)
where $($name: BorshDeserialize,)+
Expand All @@ -822,6 +812,9 @@ macro_rules! impl_tuple {
};
}

impl_tuple!(@unit ());
impl_tuple!(@unit core::ops::RangeFull);

impl_tuple!(T0);
impl_tuple!(T0 T1);
impl_tuple!(T0 T1 T2);
Expand All @@ -843,6 +836,24 @@ impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17);
impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18);
impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19);

macro_rules! impl_range {
($type:ident, $make:expr, $($side:ident),*) => {
impl<T: BorshDeserialize> BorshDeserialize for core::ops::$type<T> {
#[inline]
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
let ($($side,)*) = <_>::deserialize_reader(reader)?;
Ok($make)
}
}
};
}

impl_range!(Range, start..end, start, end);
impl_range!(RangeInclusive, start..=end, start, end);
impl_range!(RangeFrom, start.., start);
impl_range!(RangeTo, ..end, end);
impl_range!(RangeToInclusive, ..=end, end);

#[cfg(feature = "rc")]
impl<T: ?Sized> BorshDeserialize for Rc<T>
where
Expand Down
102 changes: 90 additions & 12 deletions borsh/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,30 +256,22 @@ where
}
}

impl BorshSchema for () {
fn add_definitions_recursively(_definitions: &mut BTreeMap<Declaration, Definition>) {}

fn declaration() -> Declaration {
"nil".to_string()
}
}

macro_rules! impl_for_renamed_primitives {
($($type: ty : $name: ident)+) => {
$(
impl BorshSchema for $type {
#[inline]
fn add_definitions_recursively(_definitions: &mut BTreeMap<Declaration, Definition>) {}
fn declaration() -> Declaration {
stringify!($name).to_string()
}
#[inline]
fn declaration() -> Declaration { stringify!($name).into() }
}
)+
};
}

macro_rules! impl_for_primitives {
($($type: ident)+) => {
impl_for_renamed_primitives!{$($type : $type)+}
impl_for_renamed_primitives!{$($type : $type)+}
};
}

Expand All @@ -302,6 +294,46 @@ impl_for_renamed_primitives!(core::num::NonZeroU128: nonzero_u128);
// see 12 lines above
impl_for_renamed_primitives!(core::num::NonZeroUsize: nonzero_u64);

impl_for_renamed_primitives!((): nil);

impl BorshSchema for core::ops::RangeFull {
#[inline]
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
let fields = Fields::Empty;
let def = Definition::Struct { fields };
add_definition(Self::declaration(), def, definitions);
}
#[inline]
fn declaration() -> Declaration {
"RangeFull".into()
}
}

macro_rules! impl_for_range {
($type:ident, $($name:ident),*) => {
impl<T: BorshSchema> BorshSchema for core::ops::$type<T> {
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
let decl = T::declaration();
let fields = Fields::NamedFields(vec![$(
(FieldName::from(stringify!($name)), decl.clone())
),*]);
let def = Definition::Struct { fields };
add_definition(Self::declaration(), def, definitions);
T::add_definitions_recursively(definitions);
}
fn declaration() -> Declaration {
format!("{}<{}>", stringify!($type), T::declaration())
}
}
};
}

impl_for_range!(Range, start, end);
impl_for_range!(RangeInclusive, start, end);
impl_for_range!(RangeFrom, start);
impl_for_range!(RangeTo, end);
impl_for_range!(RangeToInclusive, end);

impl<T, const N: usize> BorshSchema for [T; N]
where
T: BorshSchema,
Expand Down Expand Up @@ -792,4 +824,50 @@ mod tests {
let phantom_declaration = PhantomData::<Vec<u8>>::declaration();
assert_eq!("nil", phantom_declaration);
}

#[test]
fn range() {
assert_eq!("RangeFull", <core::ops::RangeFull>::declaration());
let mut actual_defs = map!();
<core::ops::RangeFull>::add_definitions_recursively(&mut actual_defs);
assert_eq!(
map! {
"RangeFull" => Definition::Struct {
fields: Fields::Empty
}
},
actual_defs
);

let actual_name = <core::ops::Range<u64>>::declaration();
let mut actual_defs = map!();
<core::ops::Range<u64>>::add_definitions_recursively(&mut actual_defs);
assert_eq!("Range<u64>", actual_name);
assert_eq!(
map! {
"Range<u64>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("start".into(), "u64".into()),
("end".into(), "u64".into()),
])
}
},
actual_defs
);

let actual_name = <core::ops::RangeTo<u64>>::declaration();
let mut actual_defs = map!();
<core::ops::RangeTo<u64>>::add_definitions_recursively(&mut actual_defs);
assert_eq!("RangeTo<u64>", actual_name);
assert_eq!(
map! {
"RangeTo<u64>" => Definition::Struct {
fields: Fields::NamedFields(vec![
("end".into(), "u64".into()),
])
}
},
actual_defs
);
}
}
4 changes: 4 additions & 0 deletions borsh/src/schema_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ mod tests {
test_ok::<core::num::NonZeroI16>(2);
test_ok::<core::num::NonZeroU32>(4);

test_ok::<core::ops::RangeFull>(0);
test_ok::<core::ops::RangeInclusive<u8>>(2);
test_ok::<core::ops::RangeToInclusive<u64>>(8);

test_ok::<Option<()>>(1);
test_ok::<Option<u8>>(2);
test_ok::<core::result::Result<u8, usize>>(9);
Expand Down
48 changes: 31 additions & 17 deletions borsh/src/ser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,6 @@ impl BorshSerialize for bool {
}
}

impl<T> BorshSerialize for core::ops::Range<T>
where
T: BorshSerialize,
{
#[inline]
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
self.start.serialize(writer)?;
self.end.serialize(writer)
}
}

impl<T> BorshSerialize for Option<T>
where
T: BorshSerialize,
Expand Down Expand Up @@ -522,13 +511,16 @@ where
}
}

impl BorshSerialize for () {
fn serialize<W: Write>(&self, _writer: &mut W) -> Result<()> {
Ok(())
}
}

macro_rules! impl_tuple {
(@unit $name:ty) => {
impl BorshSerialize for $name {
#[inline]
fn serialize<W: Write>(&self, _writer: &mut W) -> Result<()> {
Ok(())
}
}
};

($($idx:tt $name:ident)+) => {
impl<$($name),+> BorshSerialize for ($($name,)+)
where $($name: BorshSerialize,)+
Expand All @@ -542,6 +534,9 @@ macro_rules! impl_tuple {
};
}

impl_tuple!(@unit ());
impl_tuple!(@unit core::ops::RangeFull);

impl_tuple!(0 T0);
impl_tuple!(0 T0 1 T1);
impl_tuple!(0 T0 1 T1 2 T2);
Expand All @@ -563,6 +558,25 @@ impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15 16 T16 17 T17 18 T18);
impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15 16 T16 17 T17 18 T18 19 T19);

macro_rules! impl_range {
($type:ident, $this:ident, $($field:expr),*) => {
impl<T: BorshSerialize> BorshSerialize for core::ops::$type<T> {
#[inline]
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
let $this = self;
$( $field.serialize(writer)?; )*
Ok(())
}
}
};
}

impl_range!(Range, this, &this.start, &this.end);
impl_range!(RangeInclusive, this, this.start(), this.end());
impl_range!(RangeFrom, this, &this.start);
impl_range!(RangeTo, this, &this.end);
impl_range!(RangeToInclusive, this, &this.end);

#[cfg(feature = "rc")]
impl<T: BorshSerialize + ?Sized> BorshSerialize for Rc<T> {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
Expand Down
34 changes: 34 additions & 0 deletions borsh/tests/snapshots/test_range__ranges.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
source: borsh/tests/test_range.rs
expression: encoded
---
[
1,
0,
0,
0,
2,
0,
0,
0,
3,
0,
0,
0,
4,
0,
0,
0,
5,
0,
0,
0,
6,
0,
0,
0,
7,
0,
0,
0,
]
13 changes: 13 additions & 0 deletions borsh/tests/test_range.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![cfg_attr(not(feature = "std"), no_std)]

#[test]
fn test_ranges() {
let want = (1..2, 3..=4, 5.., ..6, ..=7, ..);

let encoded = borsh::to_vec(&want).unwrap();
#[cfg(feature = "std")]
insta::assert_debug_snapshot!(encoded);

let got = borsh::from_slice(&encoded).unwrap();
assert_eq!(want, got);
}
Loading