Repository: stellar/rs-soroban-env
Pull request #439 (2022-09-15 03:45:53)

Add RawVal <> String conversions for tests
### What
Add RawVal <> String conversions when the std feature is enabled.

### Why
For use in tests for conveniently converting strings to and from `Bytes`.

It's a little unclear on how useful this is, so I might come back and remove it, but I have one use case in some SDK testutils code where it helps simplify SDK code.


Patch:
diff --git a/soroban-env-common/src/lib.rs b/soroban-env-common/src/lib.rs
index afac1189..df57bb9d 100644
--- a/soroban-env-common/src/lib.rs
+++ b/soroban-env-common/src/lib.rs
@@ -33,6 +33,7 @@ mod raw_val;
 mod result;
 mod r#static;
 mod status;
+mod r#str;
 mod symbol;
 mod tuple;
 mod unimplemented_env;
diff --git a/soroban-env-common/src/str.rs b/soroban-env-common/src/str.rs
new file mode 100644
index 00000000..95ca8881
--- /dev/null
+++ b/soroban-env-common/src/str.rs
@@ -0,0 +1,54 @@
+#![cfg(feature = "std")]
+
+use stellar_xdr::ScObjectType;
+
+use crate::{
+    ConversionError, Env, IntoVal, Object, RawVal, RawValConvertible, TryFromVal, TryIntoVal,
+};
+
+impl<E: Env> TryFromVal<E, RawVal> for String {
+    type Error = ConversionError;
+
+    #[inline(always)]
+    fn try_from_val(env: &E, val: RawVal) -> Result<Self, Self::Error> {
+        let obj: Object = val.try_into_val(env)?;
+        if obj.is_obj_type(ScObjectType::Bytes) {
+            let len = unsafe { <u32 as RawValConvertible>::unchecked_from_val(env.bytes_len(obj)) };
+            let mut vec = std::vec![0; len as usize];
+            env.bytes_copy_to_slice(obj, RawVal::U32_ZERO, &mut vec);
+            String::from_utf8(vec).map_err(|_| ConversionError)
+        } else {
+            Err(ConversionError)
+        }
+    }
+}
+
+impl<E: Env> TryIntoVal<E, String> for RawVal {
+    type Error = ConversionError;
+
+    #[inline(always)]
+    fn try_into_val(self, env: &E) -> Result<String, Self::Error> {
+        <_ as TryFromVal<E, RawVal>>::try_from_val(env, self)
+    }
+}
+
+impl<E: Env> IntoVal<E, RawVal> for &str {
+    #[inline(always)]
+    fn into_val(self, env: &E) -> RawVal {
+        env.bytes_new_from_slice(self.as_bytes()).to_raw()
+    }
+}
+
+impl<E: Env> IntoVal<E, RawVal> for String {
+    #[inline(always)]
+    fn into_val(self, env: &E) -> RawVal {
+        <_ as IntoVal<E, RawVal>>::into_val(&self, env)
+    }
+}
+
+impl<E: Env> IntoVal<E, RawVal> for &String {
+    #[inline(always)]
+    fn into_val(self, env: &E) -> RawVal {
+        <&str as IntoVal<E, RawVal>>::into_val(&self, env)
+    }
+}


Test patch:
diff --git a/soroban-env-host/src/test.rs b/soroban-env-host/src/test.rs
index 2d25ea17..6481c0f6 100644
--- a/soroban-env-host/src/test.rs
+++ b/soroban-env-host/src/test.rs
@@ -6,6 +6,7 @@ mod bigint;
 mod bytes;
 mod crypto;
 mod map;
+mod str;
 mod vec;
 
 #[cfg(feature = "vm")]
diff --git a/soroban-env-host/src/test/str.rs b/soroban-env-host/src/test/str.rs
new file mode 100644
index 00000000..742f90a8
--- /dev/null
+++ b/soroban-env-host/src/test/str.rs
@@ -0,0 +1,26 @@
+use std::convert::TryInto;
+
+use soroban_env_common::{IntoVal, TryIntoVal};
+
+use crate::{CheckedEnv, Host, HostError, Object, RawVal};
+
+#[test]
+fn str_conversions() -> Result<(), HostError> {
+    let host = Host::default();
+    let mut obj = host.bytes_new()?;
+    for c in 'a'..='z' {
+        obj = host.bytes_push(obj, (c as u32).into())?;
+    }
+    let raw = obj.to_raw();
+    let s: String = raw.try_into_val(&host)?;
+    assert_eq!(s, "abcdefghijklmnopqrstuvwxyz");
+    let raw: RawVal = s.into_val(&host);
+    let obj: Object = raw.try_into_val(&host)?;
+    for (i, c) in ('a'..='z').enumerate() {
+        let c_raw = host.bytes_get(obj, (i as u32).into())?;
+        let c_u32: u32 = c_raw.try_into()?;
+        let c_char: char = char::from_u32(c_u32).unwrap();
+        assert_eq!((i, c_char), (i, c));
+    }
+    Ok(())
+}