Repository: cassandra-rs/cassandra-rs (language: rust)

Issue:
Create cassandra_cpp::Uuid from uuid::Uuid
I'm wondering if there is a better way to convert an [`uuid::Uuid`](https://docs.rs/uuid/0.7.1/uuid/struct.Uuid.html) into `cassandra_cpp::Uuid`.

Currently I'm converting the `uuid::Uuid` into a string and then into `cassandra_cpp::Uuid`. I think the string conversion is an unnecessary intermediary step.

Am I missing something?


Discussion / hints:
Thanks for the feedback! Unfortunately the DataStax Cassandra driver has its own UUID type, and it is only interconvertible with strings - see https://docs.datastax.com/en/developer/cpp-driver/2.11/api/struct.CassUuid/ . So this is unavoidable.

It would be nice if this library did the conversion automatically - I'd be happy to accept a pull request adding this: adding `impl AsRustType<uuid::Uuid> for Row` in row.rs and `impl BindRustType<uuid:Uuid> for Statement` in statement.rs.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f288ff0..f420bbb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ version number is tracked in the file `VERSION`.
 
 ## [Unreleased]
 ### Added
+- Conversion functions between `uuid::Uuid` and this library's `Uuid`.
 ### Changed
 ### Fixed
 
diff --git a/src/cassandra/row.rs b/src/cassandra/row.rs
index 4e3151f..e83decc 100644
--- a/src/cassandra/row.rs
+++ b/src/cassandra/row.rs
@@ -279,6 +279,21 @@ impl AsRustType<Uuid> for Row {
     }
 }
 
+impl AsRustType<uuid::Uuid> for Row {
+    fn get(&self, index: usize) -> Result<uuid::Uuid> {
+        let col = self.get_column(index)?;
+        col.get_uuid().map(|x| x.into())
+    }
+
+    fn get_by_name<S>(&self, name: S) -> Result<uuid::Uuid>
+    where
+        S: Into<String>,
+    {
+        let col = self.get_column_by_name(name)?;
+        col.get_uuid().map(|x| x.into())
+    }
+}
+
 impl AsRustType<Vec<u8>> for Row {
     fn get(&self, index: usize) -> Result<Vec<u8>> {
         let col = self.get_column(index)?;
diff --git a/src/cassandra/statement.rs b/src/cassandra/statement.rs
index dd62142..7ce2970 100644
--- a/src/cassandra/statement.rs
+++ b/src/cassandra/statement.rs
@@ -257,6 +257,16 @@ impl BindRustType<Uuid> for Statement {
     }
 }
 
+impl BindRustType<uuid::Uuid> for Statement {
+    fn bind(&mut self, index: usize, value: uuid::Uuid) -> Result<&mut Self> {
+        self.bind_uuid(index, value.into())
+    }
+
+    fn bind_by_name(&mut self, col: &str, value: uuid::Uuid) -> Result<&mut Self> {
+        self.bind_uuid_by_name(col, value.into())
+    }
+}
+
 impl BindRustType<Inet> for Statement {
     fn bind(&mut self, index: usize, value: Inet) -> Result<&mut Self> {
         self.bind_inet(index, value)
diff --git a/src/cassandra/uuid.rs b/src/cassandra/uuid.rs
index 7061969..2d3c9e0 100644
--- a/src/cassandra/uuid.rs
+++ b/src/cassandra/uuid.rs
@@ -104,6 +104,58 @@ impl Uuid {
     }
 }
 
+impl From<uuid::Uuid> for Uuid {
+    fn from(id: uuid::Uuid) -> Uuid {
+        // implementation taken from Datastax C/C++ driver
+        // serialization.hpp, encode_uuid()
+        let input = id.as_bytes();
+
+        let mut time_and_version = 0u64;
+        time_and_version |= input[3] as u64;
+        time_and_version |= (input[2] as u64) << 8;
+        time_and_version |= (input[1] as u64) << 16;
+        time_and_version |= (input[0] as u64) << 24;
+
+        time_and_version |= (input[5] as u64) << 32;
+        time_and_version |= (input[4] as u64) << 40;
+
+        time_and_version |= (input[7] as u64) << 48;
+        time_and_version |= (input[6] as u64) << 56;
+
+        let mut clock_seq_and_node = 0u64;
+        for i in 0..8 {
+            clock_seq_and_node |= (input[15 - i] as u64) << (8 * i);
+        }
+        Uuid(_Uuid {
+            time_and_version,
+            clock_seq_and_node,
+        })
+    }
+}
+
+impl From<Uuid> for uuid::Uuid {
+    fn from(id: Uuid) -> uuid::Uuid {
+        // implementation taken from Datastax C/C++ driver
+        // serialization.hpp decode_uuid()
+        let mut output = [0u8; 16];
+        output[3] = id.0.time_and_version as u8;
+        output[2] = (id.0.time_and_version >> 8) as u8;
+        output[1] = (id.0.time_and_version >> 16) as u8;
+        output[0] = (id.0.time_and_version >> 24) as u8;
+
+        output[5] = (id.0.time_and_version >> 32) as u8;
+        output[4] = (id.0.time_and_version >> 40) as u8;
+
+        output[7] = (id.0.time_and_version >> 48) as u8;
+        output[6] = (id.0.time_and_version >> 56) as u8;
+
+        for i in 0..8 {
+            output[15 - i] = (id.0.clock_seq_and_node >> (8 * i)) as u8;
+        }
+        uuid::Uuid::from_bytes(&output).unwrap()
+    }
+}
+
 impl str::FromStr for Uuid {
     type Err = Error;
     fn from_str(str: &str) -> Result<Uuid> {


Test patch:
diff --git a/tests/uuids.rs b/tests/uuids.rs
index 7d5e5e7..3acf687 100644
--- a/tests/uuids.rs
+++ b/tests/uuids.rs
@@ -1,6 +1,7 @@
 mod help;
 
 use cassandra_cpp::*;
+use std::str::FromStr;
 
 static TRUNCATE_QUERY: &'static str = "TRUNCATE examples.log;";
 static INSERT_QUERY: &'static str = "INSERT INTO examples.log (key, time, entry) VALUES (?, ?, ?);";
@@ -63,3 +64,15 @@ fn test_uuids() {
     uniques.dedup();
     assert_eq!(4, uniques.len());
 }
+
+#[test]
+fn test_uuids_from() {
+    const TEST_UUID: &'static str = "a0a1a2a3-a4a5-a6a7-a8a9-aaabacadaeaf";
+    let uuid_id_from_str = uuid::Uuid::parse_str(TEST_UUID).unwrap();
+    let cass_id_from_str = Uuid::from_str(TEST_UUID).unwrap();
+
+    let cass_id_from_uuid = Uuid::from(uuid_id_from_str);
+    let uuid_id_from_cass = uuid::Uuid::from(cass_id_from_str);
+    assert_eq!(uuid_id_from_str, uuid_id_from_cass);
+    assert_eq!(cass_id_from_str, cass_id_from_uuid);
+}