Repository: stellar/rs-soroban-env
Pull request #441 (2022-09-15 17:49:18)

Add tuple to [RawVal; N] conversions
### What
Add tuple to [RawVal; N] conversions.

### Why
These conversions can be convenient.


Patch:
diff --git a/soroban-env-common/src/tuple.rs b/soroban-env-common/src/tuple.rs
index 6a724f3c..9366dd22 100644
--- a/soroban-env-common/src/tuple.rs
+++ b/soroban-env-common/src/tuple.rs
@@ -6,7 +6,10 @@ use crate::{
 };
 
 macro_rules! impl_for_tuple {
-    ( $count:literal $($typ:ident $idx:tt)+ ) => {
+    ( $count:literal $count_usize:literal $($typ:ident $idx:tt)+ ) => {
+
+        // Conversions to and from RawVal.
+
         impl<E: Env, $($typ),*> TryFromVal<E, RawVal> for ($($typ,)*)
         where
             $($typ: TryFromVal<E, RawVal>),*
@@ -32,6 +35,17 @@ macro_rules! impl_for_tuple {
             }
         }
 
+        impl<E: Env, $($typ),*> TryIntoVal<E, ($($typ,)*)> for RawVal
+        where
+            $($typ: TryFromVal<E, RawVal>),*
+        {
+            type Error = ConversionError;
+            #[inline(always)]
+            fn try_into_val(self, env: &E) -> Result<($($typ,)*), Self::Error> {
+                <_ as TryFromVal<_, _>>::try_from_val(env, self)
+            }
+        }
+
         impl<E: Env, $($typ),*> IntoVal<E, RawVal> for ($($typ,)*)
         where
             $($typ: IntoVal<E, RawVal>),*
@@ -69,7 +83,24 @@ macro_rules! impl_for_tuple {
             }
         }
 
-        impl<E: Env, $($typ),*> TryIntoVal<E, ($($typ,)*)> for RawVal
+        // Conversions to and from Array of RawVal.
+
+        impl<E: Env, $($typ),*, const N: usize> TryFromVal<E, &[RawVal; N]> for ($($typ,)*)
+        where
+            $($typ: TryFromVal<E, RawVal>),*
+        {
+            type Error = ConversionError;
+
+            fn try_from_val(env: &E, val: &[RawVal; N]) -> Result<Self, Self::Error> {
+                Ok((
+                    $({
+                        $typ::try_from_val(&env, val[$idx]).map_err(|_| ConversionError)?
+                    },)*
+                ))
+            }
+        }
+
+        impl<E: Env, $($typ),*, const N: usize> TryIntoVal<E, ($($typ,)*)> for &[RawVal; N]
         where
             $($typ: TryFromVal<E, RawVal>),*
         {
@@ -79,18 +110,74 @@ macro_rules! impl_for_tuple {
                 <_ as TryFromVal<_, _>>::try_from_val(env, self)
             }
         }
+
+        impl<E: Env, $($typ),*, const N: usize> IntoVal<E, [RawVal; N]> for &($($typ,)*)
+        where
+            $(for<'a> &'a $typ: IntoVal<E, RawVal>),*
+        {
+            fn into_val(self, env: &E) -> [RawVal; N] {
+                let mut arr = [RawVal::VOID; N];
+                $(arr[$idx] = self.$idx.into_val(&env);)*
+                arr
+            }
+        }
+
+        impl<E: Env, $($typ),*, const N: usize> IntoVal<E, [RawVal; N]> for ($($typ,)*)
+        where
+            $($typ: IntoVal<E, RawVal>),*
+        {
+            fn into_val(self, env: &E) -> [RawVal; N] {
+                let mut arr = [RawVal::VOID; N];
+                $(arr[$idx] = self.$idx.into_val(&env);)*
+                arr
+            }
+        }
     };
 }
 
-impl_for_tuple! {  1_u32 T0 0 }
-impl_for_tuple! {  2_u32 T0 0 T1 1 }
-impl_for_tuple! {  3_u32 T0 0 T1 1 T2 2 }
-impl_for_tuple! {  4_u32 T0 0 T1 1 T2 2 T3 3 }
-impl_for_tuple! {  5_u32 T0 0 T1 1 T2 2 T3 3 T4 4 }
-impl_for_tuple! {  6_u32 T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 }
-impl_for_tuple! {  7_u32 T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 }
-impl_for_tuple! {  8_u32 T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 }
-impl_for_tuple! {  9_u32 T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 }
-impl_for_tuple! { 10_u32 T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T9 9 }
-impl_for_tuple! { 11_u32 T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T9 9 T10 10 }
-impl_for_tuple! { 12_u32 T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T9 9 T10 10 T11 11 }
+impl_for_tuple! {  1_u32  1_usize T0 0 }
+impl_for_tuple! {  2_u32  2_usize T0 0 T1 1 }
+impl_for_tuple! {  3_u32  3_usize T0 0 T1 1 T2 2 }
+impl_for_tuple! {  4_u32  4_usize T0 0 T1 1 T2 2 T3 3 }
+impl_for_tuple! {  5_u32  5_usize T0 0 T1 1 T2 2 T3 3 T4 4 }
+impl_for_tuple! {  6_u32  6_usize T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 }
+impl_for_tuple! {  7_u32  7_usize T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 }
+impl_for_tuple! {  8_u32  8_usize T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 }
+impl_for_tuple! {  9_u32  9_usize T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 }
+impl_for_tuple! { 10_u32 10_usize T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T9 9 }
+impl_for_tuple! { 11_u32 11_usize T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T9 9 T10 10 }
+impl_for_tuple! { 12_u32 12_usize T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T9 9 T10 10 T11 11 }
+
+// Unit types are not tuples, but when people write out what they think is an
+// empty tuple, it is a unit type. The following conversions have unit types
+// behave like tuples in some conversions that are safe to do so, like
+// conversions to and from arrays. Note that unit typles convert to
+// RawVal::VOID, see raw_val.rs for those conversions.
+
+impl<E: Env> TryFromVal<E, &[RawVal; 0]> for () {
+    type Error = ConversionError;
+
+    fn try_from_val(_env: &E, _val: &[RawVal; 0]) -> Result<Self, Self::Error> {
+        Ok(())
+    }
+}
+
+impl<E: Env> TryIntoVal<E, ()> for &[RawVal; 0] {
+    type Error = ConversionError;
+    #[inline(always)]
+    fn try_into_val(self, env: &E) -> Result<(), Self::Error> {
+        <_ as TryFromVal<_, _>>::try_from_val(env, self)
+    }
+}
+
+impl<E: Env> IntoVal<E, [RawVal; 0]> for &() {
+    fn into_val(self, _env: &E) -> [RawVal; 0] {
+        [RawVal::VOID; 0]
+    }
+}
+
+impl<E: Env> IntoVal<E, [RawVal; 0]> for () {
+    fn into_val(self, _env: &E) -> [RawVal; 0] {
+        [RawVal::VOID; 0]
+    }
+}


Test patch:
diff --git a/soroban-env-host/src/test.rs b/soroban-env-host/src/test.rs
index 6481c0f6..b6ee8164 100644
--- a/soroban-env-host/src/test.rs
+++ b/soroban-env-host/src/test.rs
@@ -17,3 +17,4 @@ mod contract_event;
 mod invocation;
 #[cfg(all(feature = "vm", feature = "testutils"))]
 mod lifecycle;
+mod tuple;
diff --git a/soroban-env-host/src/test/tuple.rs b/soroban-env-host/src/test/tuple.rs
new file mode 100644
index 00000000..e9561fdd
--- /dev/null
+++ b/soroban-env-host/src/test/tuple.rs
@@ -0,0 +1,36 @@
+use soroban_env_common::{IntoVal, TryIntoVal};
+
+use crate::{CheckedEnv, Host, HostError, RawVal};
+
+#[test]
+fn tuple_conversions() -> Result<(), HostError> {
+    let host = Host::default();
+
+    let raw: RawVal = (1u32, 1i32).into_val(&host);
+
+    let mut obj = host.vec_new(RawVal::VOID)?;
+    obj = host.vec_push_back(obj, (1u32).into())?;
+    obj = host.vec_push_back(obj, (1i32).into())?;
+
+    assert_eq!(host.obj_cmp(raw, obj.to_raw())?, 0);
+
+    let roundtrip: (u32, i32) = raw.try_into_val(&host)?;
+    assert_eq!(roundtrip, (1u32, 1i32));
+
+    Ok(())
+}
+
+#[test]
+fn tuple_array_conversions() -> Result<(), HostError> {
+    let host = Host::default();
+
+    let raw: [RawVal; 0] = ().into_val(&host);
+    let unit: () = raw.try_into_val(&host)?;
+    assert_eq!(unit, ());
+
+    let raw: [RawVal; 2] = (1u32, 1i32).into_val(&host);
+    let roundtrip: (u32, i32) = raw.try_into_val(&host)?;
+    assert_eq!(roundtrip, (1u32, 1i32));
+
+    Ok(())
+}