Repository: near/borsh-rs
Pull request #97 (2022-07-29 13:38:07)

feat: Add clone impls to borsh schema types
I don't think there is any reason we shouldn't be able to clone schema definition types.

Motivation: https://github.com/near/near-sdk-rs/pull/872


Patch:
diff --git a/borsh/src/de/mod.rs b/borsh/src/de/mod.rs
index eedf6c87..f4b2d940 100644
--- a/borsh/src/de/mod.rs
+++ b/borsh/src/de/mod.rs
@@ -23,6 +23,7 @@ mod hint;
 
 const ERROR_NOT_ALL_BYTES_READ: &str = "Not all bytes read";
 const ERROR_UNEXPECTED_LENGTH_OF_INPUT: &str = "Unexpected length of input";
+const ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_ISIZE: &str = "Overflow on machine with 32 bit isize";
 const ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_USIZE: &str = "Overflow on machine with 32 bit usize";
 const ERROR_INVALID_ZERO_VALUE: &str = "Expected a non-zero value";
 
@@ -157,6 +158,19 @@ impl_for_nonzero_integer!(core::num::NonZeroU64);
 impl_for_nonzero_integer!(core::num::NonZeroU128);
 impl_for_nonzero_integer!(core::num::NonZeroUsize);
 
+impl BorshDeserialize for isize {
+    fn deserialize(buf: &mut &[u8]) -> Result<Self> {
+        let i: i64 = BorshDeserialize::deserialize(buf)?;
+        let i = isize::try_from(i).map_err(|_| {
+            Error::new(
+                ErrorKind::InvalidInput,
+                ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_ISIZE,
+            )
+        })?;
+        Ok(i)
+    }
+}
+
 impl BorshDeserialize for usize {
     fn deserialize(buf: &mut &[u8]) -> Result<Self> {
         let u: u64 = BorshDeserialize::deserialize(buf)?;
@@ -574,7 +588,7 @@ impl BorshDeserialize for () {
 
 macro_rules! impl_tuple {
     ($($name:ident)+) => {
-      impl<$($name),+> BorshDeserialize for ($($name),+)
+      impl<$($name),+> BorshDeserialize for ($($name,)+)
       where $($name: BorshDeserialize,)+
       {
         #[inline]
@@ -586,6 +600,7 @@ macro_rules! impl_tuple {
     };
 }
 
+impl_tuple!(T0);
 impl_tuple!(T0 T1);
 impl_tuple!(T0 T1 T2);
 impl_tuple!(T0 T1 T2 T3);
diff --git a/borsh/src/schema.rs b/borsh/src/schema.rs
index 80921d0a..f0ea4db0 100644
--- a/borsh/src/schema.rs
+++ b/borsh/src/schema.rs
@@ -29,7 +29,7 @@ pub type VariantName = String;
 /// The name of the field in the struct (can be used to convert JSON to Borsh using the schema).
 pub type FieldName = String;
 /// The type that we use to represent the definition of the Borsh type.
-#[derive(PartialEq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)]
+#[derive(Clone, PartialEq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)]
 pub enum Definition {
     /// A fixed-size array with the length known at the compile time and the same-type elements.
     Array { length: u32, elements: Declaration },
@@ -47,7 +47,7 @@ pub enum Definition {
 }
 
 /// The collection representing the fields of a struct.
-#[derive(PartialEq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)]
+#[derive(Clone, PartialEq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)]
 pub enum Fields {
     /// The struct with named fields.
     NamedFields(Vec<(FieldName, Declaration)>),
@@ -58,7 +58,7 @@ pub enum Fields {
 }
 
 /// All schema information needed to deserialize a single type.
-#[derive(PartialEq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)]
+#[derive(Clone, PartialEq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)]
 pub struct BorshSchemaContainer {
     /// Declaration of the type.
     pub declaration: Declaration,
@@ -142,9 +142,11 @@ macro_rules! impl_for_primitives {
     };
 }
 
-impl_for_primitives!(bool char f32 f64 i8 i16 i32 i64 i128 u8 u16 u32 u64 u128);
+impl_for_primitives!(bool f32 f64 i8 i16 i32 i64 i128 u8 u16 u32 u64 u128);
 impl_for_renamed_primitives!(String: string);
 impl_for_renamed_primitives!(str: string);
+impl_for_renamed_primitives!(isize: i64);
+impl_for_renamed_primitives!(usize: u64);
 
 #[cfg(not(feature = "const-generics"))]
 const _: () = {
@@ -301,7 +303,7 @@ where
 
 macro_rules! impl_tuple {
     ($($name:ident),+) => {
-    impl<$($name),+> BorshSchema for ($($name),+)
+    impl<$($name),+> BorshSchema for ($($name,)+)
     where
         $($name: BorshSchema),+
     {
@@ -323,6 +325,7 @@ macro_rules! impl_tuple {
     };
 }
 
+impl_tuple!(T0);
 impl_tuple!(T0, T1);
 impl_tuple!(T0, T1, T2);
 impl_tuple!(T0, T1, T2, T3);
diff --git a/borsh/src/ser/mod.rs b/borsh/src/ser/mod.rs
index 212d0463..b3244c00 100644
--- a/borsh/src/ser/mod.rs
+++ b/borsh/src/ser/mod.rs
@@ -118,6 +118,12 @@ impl_for_nonzero_integer!(core::num::NonZeroU64);
 impl_for_nonzero_integer!(core::num::NonZeroU128);
 impl_for_nonzero_integer!(core::num::NonZeroUsize);
 
+impl BorshSerialize for isize {
+    fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
+        BorshSerialize::serialize(&(*self as i64), writer)
+    }
+}
+
 impl BorshSerialize for usize {
     fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
         BorshSerialize::serialize(&(*self as u64), writer)
@@ -505,7 +511,7 @@ impl BorshSerialize for () {
 
 macro_rules! impl_tuple {
     ($($idx:tt $name:ident)+) => {
-      impl<$($name),+> BorshSerialize for ($($name),+)
+      impl<$($name),+> BorshSerialize for ($($name,)+)
       where $($name: BorshSerialize,)+
       {
         #[inline]
@@ -517,6 +523,7 @@ macro_rules! impl_tuple {
     };
 }
 
+impl_tuple!(0 T0);
 impl_tuple!(0 T0 1 T1);
 impl_tuple!(0 T0 1 T1 2 T2);
 impl_tuple!(0 T0 1 T1 2 T2 3 T3);


Test patch:
diff --git a/borsh/tests/test_primitives.rs b/borsh/tests/test_primitives.rs
new file mode 100644
index 00000000..df3dd274
--- /dev/null
+++ b/borsh/tests/test_primitives.rs
@@ -0,0 +1,22 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+
+macro_rules! test_primitive {
+    ($test_name: ident, $v: expr, $t: ty) => {
+        #[test]
+        fn $test_name() {
+            let expected: $t = $v;
+            let buf = expected.try_to_vec().unwrap();
+            let actual = <$t>::try_from_slice(&buf).expect("failed to deserialize");
+            assert_eq!(actual, expected);
+        }
+    };
+}
+
+test_primitive!(test_isize_neg, -100isize, isize);
+test_primitive!(test_isize_pos, 100isize, isize);
+test_primitive!(test_isize_min, isize::min_value(), isize);
+test_primitive!(test_isize_max, isize::max_value(), isize);
+
+test_primitive!(test_usize, 100usize, usize);
+test_primitive!(test_usize_min, usize::min_value(), usize);
+test_primitive!(test_usize_max, usize::max_value(), usize);
diff --git a/borsh/tests/test_schema_primitives.rs b/borsh/tests/test_schema_primitives.rs
new file mode 100644
index 00000000..bacaf1e6
--- /dev/null
+++ b/borsh/tests/test_schema_primitives.rs
@@ -0,0 +1,25 @@
+use borsh::schema::*;
+
+#[test]
+fn isize_schema() {
+    let schema = isize::schema_container();
+    assert_eq!(
+        schema,
+        BorshSchemaContainer {
+            declaration: "i64".to_string(),
+            definitions: Default::default()
+        }
+    )
+}
+
+#[test]
+fn usize_schema() {
+    let schema = usize::schema_container();
+    assert_eq!(
+        schema,
+        BorshSchemaContainer {
+            declaration: "u64".to_string(),
+            definitions: Default::default()
+        }
+    )
+}
diff --git a/borsh/tests/test_schema_tuple.rs b/borsh/tests/test_schema_tuple.rs
new file mode 100644
index 00000000..c5e6367a
--- /dev/null
+++ b/borsh/tests/test_schema_tuple.rs
@@ -0,0 +1,28 @@
+use borsh::maybestd::collections::HashMap;
+use borsh::schema::*;
+
+macro_rules! map(
+    () => { HashMap::new() };
+    { $($key:expr => $value:expr),+ } => {
+        {
+            let mut m = HashMap::new();
+            $(
+                m.insert($key.to_string(), $value);
+            )+
+            m
+        }
+     };
+);
+
+#[test]
+fn test_unary_tuple_schema() {
+    assert_eq!("Tuple<bool>", <(bool,)>::declaration());
+    let mut defs = Default::default();
+    <(bool,)>::add_definitions_recursively(&mut defs);
+    assert_eq!(
+        map! {
+        "Tuple<bool>" => Definition::Tuple { elements: vec!["bool".to_string()] }
+        },
+        defs
+    );
+}
diff --git a/borsh/tests/test_tuple.rs b/borsh/tests/test_tuple.rs
new file mode 100644
index 00000000..c629dcfc
--- /dev/null
+++ b/borsh/tests/test_tuple.rs
@@ -0,0 +1,9 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+
+#[test]
+fn test_unary_tuple() {
+    let expected = (true,);
+    let buf = expected.try_to_vec().unwrap();
+    let actual = <(bool,)>::try_from_slice(&buf).expect("failed to deserialize");
+    assert_eq!(actual, expected);
+}