Repository: dtolnay/cxx (rust)

Issue:
Trait impls for CxxString:  Hash, Ord, PartialOrd, Eq
I need to use `&CxxString` as the key of a BTreeMap or HashMap. However there are some trait impls currently missing, which we should just make delegate to the underlying `&[u8]`. BTreeMap requires `Ord` and HashMap requires `Hash + Eq`.

Pull request description:
Implement Eq, Ord, PartialOrd, Hash for CxxString
Closes #737.

This is implemented by delegation to `&[u8]`, as suggested.

I also implemented `PartialOrd<str>`  for `CxxString` and `PartialOrd<CxxString>` for `str`, since `PartialEq<str>` is already implemented for `CxxString` and vice versa. I added tests for comparisons between `str` and `CxxString`, just in case.


Patch:
diff --git a/src/cxx_string.rs b/src/cxx_string.rs
index 8834b498..c1321892 100644
--- a/src/cxx_string.rs
+++ b/src/cxx_string.rs
@@ -183,6 +183,38 @@ impl PartialEq<str> for CxxString {
     }
 }
 
+impl Eq for CxxString {}
+
+impl PartialOrd for CxxString {
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        self.as_bytes().partial_cmp(other.as_bytes())
+    }
+}
+
+impl PartialOrd<str> for CxxString {
+    fn partial_cmp(&self, other: &str) -> Option<core::cmp::Ordering> {
+        self.as_bytes().partial_cmp(other.as_bytes())
+    }
+}
+
+impl PartialOrd<CxxString> for str {
+    fn partial_cmp(&self, other: &CxxString) -> Option<core::cmp::Ordering> {
+        self.as_bytes().partial_cmp(other.as_bytes())
+    }
+}
+
+impl Ord for CxxString {
+    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+        self.as_bytes().cmp(other.as_bytes())
+    }
+}
+
+impl core::hash::Hash for CxxString {
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        self.as_bytes().hash(state)
+    }
+}
+
 #[doc(hidden)]
 #[repr(C)]
 pub struct StackString {


Test patch:
diff --git a/tests/cxx_string.rs b/tests/cxx_string.rs
index be693cd2..3bc0d913 100644
--- a/tests/cxx_string.rs
+++ b/tests/cxx_string.rs
@@ -13,3 +13,34 @@ fn test_async_cxx_string() {
     fn assert_send(_: impl Send) {}
     assert_send(f());
 }
+
+#[test]
+fn test_cmp_rust_str_cxx_string() {
+    use std::cmp::Ordering;
+    fn test_eq(rust_str: &str) {
+        let_cxx_string!(cxx_string = rust_str);
+        assert_eq!(rust_str.partial_cmp(&*cxx_string), Some(Ordering::Equal));
+        assert_eq!((*cxx_string).partial_cmp(rust_str), Some(Ordering::Equal));
+        assert_eq!(*cxx_string, *rust_str);
+        assert_eq!(*rust_str, *cxx_string);
+    }
+
+    fn test_ne(lhs: &str, rhs: &str) {
+        let_cxx_string!(cxx_lhs = lhs);
+        let_cxx_string!(cxx_rhs = rhs);
+        assert_ne!(lhs.partial_cmp(&*cxx_rhs), Some(Ordering::Equal));
+        assert_ne!(rhs.partial_cmp(&*cxx_lhs), Some(Ordering::Equal));
+        assert_ne!((*cxx_lhs).partial_cmp(rhs), Some(Ordering::Equal));
+        assert_ne!((*cxx_rhs).partial_cmp(lhs), Some(Ordering::Equal));
+        assert_ne!(*cxx_lhs, *rhs);
+        assert_ne!(*cxx_rhs, *lhs);
+        assert_ne!(*rhs, *cxx_lhs);
+        assert_ne!(*lhs, *cxx_rhs);
+    }
+    test_eq("abc");
+    test_eq("");
+    test_ne("abc", "Abc");
+    test_ne("abc", "");
+    // test utf8 character
+    test_eq("♡");
+}