Add custom matchers for ProxiedWithPresence.

We continue our foray into googletest-rust [1], this time with homegrown matchers.

Our unit tests will now be able to check is_unset() and is_set() for all types that implement ProxiedWithPresence. In practice, this boils down to [u8] and ProtoStr.

Note that we've broken out matchers_upb and matchers_cpp, similar to what was done with aliasing here [2].

[1] https://github.com/google/googletest-rust
[2] https://github.com/protocolbuffers/protobuf/commit/9a0bc392b3011cabc214f01b41314acab33658c2#diff-08e5182ff36ad340a3bfb628995524a2a36a89b59a514ba027b0f25e048dd5c3R90

PiperOrigin-RevId: 573895179
diff --git a/rust/test/shared/BUILD b/rust/test/shared/BUILD
index 32fd7c1..e5be7b7 100644
--- a/rust/test/shared/BUILD
+++ b/rust/test/shared/BUILD
@@ -14,7 +14,31 @@
 # Once we have a couple of these tests we will investigate ways to remove boilerplate (for example
 # by introducing a build macro that registers 2 rust_test targets).
 
-load("@rules_rust//rust:defs.bzl", "rust_test")
+load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+
+rust_library(
+    name = "matchers_upb",
+    srcs = ["matchers.rs"],
+    aliases = {
+        "//rust:protobuf_upb": "protobuf",
+    },
+    deps = [
+        "//third_party/gtest_rust/googletest",
+        "//rust:protobuf_upb",
+    ],
+)
+
+rust_library(
+    name = "matchers_cpp",
+    srcs = ["matchers.rs"],
+    aliases = {
+        "//rust:protobuf_cpp": "protobuf",
+    },
+    deps = [
+        "//third_party/gtest_rust/googletest",
+        "//rust:protobuf_cpp",
+    ],
+)
 
 rust_test(
     name = "child_parent_upb_test",
@@ -125,6 +149,7 @@
     srcs = ["accessors_test.rs"],
     aliases = {
         "//rust:protobuf_cpp": "protobuf",
+        "//rust/test/shared:matchers_cpp": "matchers",
     },
     tags = [
         # TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
@@ -134,6 +159,7 @@
         "//third_party/gtest_rust/googletest",
         "//rust:protobuf_cpp",
         "//rust/test:unittest_cc_rust_proto",
+        "//rust/test/shared:matchers_cpp",
     ],
 )
 
@@ -142,6 +168,7 @@
     srcs = ["accessors_test.rs"],
     aliases = {
         "//rust:protobuf_upb": "protobuf",
+        "//rust/test/shared:matchers_upb": "matchers",
     },
     tags = [
         # TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
@@ -151,6 +178,7 @@
         "//third_party/gtest_rust/googletest",
         "//rust:protobuf_upb",
         "//rust/test:unittest_upb_rust_proto",
+        "//rust/test/shared:matchers_upb",
     ],
 )
 
diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs
index 7f2db2c..40098b1 100644
--- a/rust/test/shared/accessors_test.rs
+++ b/rust/test/shared/accessors_test.rs
@@ -8,6 +8,7 @@
 //! Tests covering accessors for singular bool, int32, int64, and bytes fields.
 
 use googletest::prelude::*;
+use matchers::{is_set, is_unset};
 use protobuf::Optional;
 use unittest_proto::proto2_unittest::{TestAllTypes, TestAllTypes_};
 
@@ -199,10 +200,10 @@
 #[test]
 fn test_optional_bytes_accessors() {
     let mut msg = TestAllTypes::new();
-    assert_that!(msg.optional_bytes(), eq(b""));
+    assert_that!(*msg.optional_bytes(), empty());
     assert_that!(msg.optional_bytes_opt(), eq(Optional::Unset(&b""[..])));
-    assert_that!(msg.optional_bytes_mut().get(), eq(b""));
-    assert_that!(msg.optional_bytes_mut().is_unset(), eq(true));
+    assert_that!(*msg.optional_bytes_mut().get(), empty());
+    assert_that!(msg.optional_bytes_mut(), is_unset());
 
     {
         let s = Vec::from(&b"hello world"[..]);
@@ -210,34 +211,34 @@
     }
     assert_that!(msg.optional_bytes(), eq(b"hello world"));
     assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b"hello world"[..])));
-    assert_that!(msg.optional_bytes_mut().is_set(), eq(true));
+    assert_that!(msg.optional_bytes_mut(), is_set());
     assert_that!(msg.optional_bytes_mut().get(), eq(b"hello world"));
 
     msg.optional_bytes_mut().or_default().set(b"accessors_test");
     assert_that!(msg.optional_bytes(), eq(b"accessors_test"));
     assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b"accessors_test"[..])));
-    assert_that!(msg.optional_bytes_mut().is_set(), eq(true));
+    assert_that!(msg.optional_bytes_mut(), is_set());
     assert_that!(msg.optional_bytes_mut().get(), eq(b"accessors_test"));
     assert_that!(msg.optional_bytes_mut().or_default().get(), eq(b"accessors_test"));
 
     msg.optional_bytes_mut().clear();
-    assert_that!(msg.optional_bytes(), eq(b""));
+    assert_that!(*msg.optional_bytes(), empty());
     assert_that!(msg.optional_bytes_opt(), eq(Optional::Unset(&b""[..])));
-    assert_that!(msg.optional_bytes_mut().is_unset(), eq(true));
+    assert_that!(msg.optional_bytes_mut(), is_unset());
 
     msg.optional_bytes_mut().set(b"");
-    assert_that!(msg.optional_bytes(), eq(b""));
+    assert_that!(*msg.optional_bytes(), empty());
     assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b""[..])));
 
     msg.optional_bytes_mut().clear();
     msg.optional_bytes_mut().or_default();
-    assert_that!(msg.optional_bytes(), eq(b""));
+    assert_that!(*msg.optional_bytes(), empty());
     assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b""[..])));
 
     msg.optional_bytes_mut().or_default().set(b"\xffbinary\x85non-utf8");
     assert_that!(msg.optional_bytes(), eq(b"\xffbinary\x85non-utf8"));
     assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b"\xffbinary\x85non-utf8"[..])));
-    assert_that!(msg.optional_bytes_mut().is_set(), eq(true));
+    assert_that!(msg.optional_bytes_mut(), is_set());
     assert_that!(msg.optional_bytes_mut().get(), eq(b"\xffbinary\x85non-utf8"));
     assert_that!(msg.optional_bytes_mut().or_default().get(), eq(b"\xffbinary\x85non-utf8"));
 }
@@ -248,7 +249,7 @@
     assert_that!(msg.default_bytes(), eq(b"world"));
     assert_that!(msg.default_bytes_opt(), eq(Optional::Unset(&b"world"[..])));
     assert_that!(msg.default_bytes_mut().get(), eq(b"world"));
-    assert_that!(msg.default_bytes_mut().is_unset(), eq(true));
+    assert_that!(msg.default_bytes_mut(), is_unset());
 
     {
         let s = String::from("hello world");
@@ -256,23 +257,23 @@
     }
     assert_that!(msg.default_bytes(), eq(b"hello world"));
     assert_that!(msg.default_bytes_opt(), eq(Optional::Set(&b"hello world"[..])));
-    assert_that!(msg.default_bytes_mut().is_set(), eq(true));
+    assert_that!(msg.default_bytes_mut(), is_set());
     assert_that!(msg.default_bytes_mut().get(), eq(b"hello world"));
 
     msg.default_bytes_mut().or_default().set(b"accessors_test");
     assert_that!(msg.default_bytes(), eq(b"accessors_test"));
     assert_that!(msg.default_bytes_opt(), eq(Optional::Set(&b"accessors_test"[..])));
-    assert_that!(msg.default_bytes_mut().is_set(), eq(true));
+    assert_that!(msg.default_bytes_mut(), is_set());
     assert_that!(msg.default_bytes_mut().get(), eq(b"accessors_test"));
     assert_that!(msg.default_bytes_mut().or_default().get(), eq(b"accessors_test"));
 
     msg.default_bytes_mut().clear();
     assert_that!(msg.default_bytes(), eq(b"world"));
     assert_that!(msg.default_bytes_opt(), eq(Optional::Unset(&b"world"[..])));
-    assert_that!(msg.default_bytes_mut().is_unset(), eq(true));
+    assert_that!(msg.default_bytes_mut(), is_unset());
 
     msg.default_bytes_mut().set(b"");
-    assert_that!(msg.default_bytes(), eq(b""));
+    assert_that!(*msg.default_bytes(), empty());
     assert_that!(msg.default_bytes_opt(), eq(Optional::Set(&b""[..])));
 
     msg.default_bytes_mut().clear();
@@ -283,7 +284,7 @@
     msg.default_bytes_mut().or_default().set(b"\xffbinary\x85non-utf8");
     assert_that!(msg.default_bytes(), eq(b"\xffbinary\x85non-utf8"));
     assert_that!(msg.default_bytes_opt(), eq(Optional::Set(&b"\xffbinary\x85non-utf8"[..])));
-    assert_that!(msg.default_bytes_mut().is_set(), eq(true));
+    assert_that!(msg.default_bytes_mut(), is_set());
     assert_that!(msg.default_bytes_mut().get(), eq(b"\xffbinary\x85non-utf8"));
     assert_that!(msg.default_bytes_mut().or_default().get(), eq(b"\xffbinary\x85non-utf8"));
 }
@@ -294,7 +295,7 @@
     assert_that!(msg.optional_string(), eq(""));
     assert_that!(msg.optional_string_opt(), eq(Optional::Unset("".into())));
     assert_that!(msg.optional_string_mut().get(), eq(""));
-    assert_that!(msg.optional_string_mut().is_unset(), eq(true));
+    assert_that!(msg.optional_string_mut(), is_unset());
 
     {
         let s = String::from("hello world");
@@ -302,20 +303,20 @@
     }
     assert_that!(msg.optional_string(), eq("hello world"));
     assert_that!(msg.optional_string_opt(), eq(Optional::Set("hello world".into())));
-    assert_that!(msg.optional_string_mut().is_set(), eq(true));
+    assert_that!(msg.optional_string_mut(), is_set());
     assert_that!(msg.optional_string_mut().get(), eq("hello world"));
 
     msg.optional_string_mut().or_default().set("accessors_test");
     assert_that!(msg.optional_string(), eq("accessors_test"));
     assert_that!(msg.optional_string_opt(), eq(Optional::Set("accessors_test".into())));
-    assert_that!(msg.optional_string_mut().is_set(), eq(true));
+    assert_that!(msg.optional_string_mut(), is_set());
     assert_that!(msg.optional_string_mut().get(), eq("accessors_test"));
     assert_that!(msg.optional_string_mut().or_default().get(), eq("accessors_test"));
 
     msg.optional_string_mut().clear();
     assert_that!(msg.optional_string(), eq(""));
     assert_that!(msg.optional_string_opt(), eq(Optional::Unset("".into())));
-    assert_that!(msg.optional_string_mut().is_unset(), eq(true));
+    assert_that!(msg.optional_string_mut(), is_unset());
 
     msg.optional_string_mut().set("");
     assert_that!(msg.optional_string(), eq(""));
@@ -333,7 +334,7 @@
     assert_that!(msg.default_string(), eq("hello"));
     assert_that!(msg.default_string_opt(), eq(Optional::Unset("hello".into())));
     assert_that!(msg.default_string_mut().get(), eq("hello"));
-    assert_that!(msg.default_string_mut().is_unset(), eq(true));
+    assert_that!(msg.default_string_mut(), is_unset());
 
     {
         let s = String::from("hello world");
@@ -341,20 +342,20 @@
     }
     assert_that!(msg.default_string(), eq("hello world"));
     assert_that!(msg.default_string_opt(), eq(Optional::Set("hello world".into())));
-    assert_that!(msg.default_string_mut().is_set(), eq(true));
+    assert_that!(msg.default_string_mut(), is_set());
     assert_that!(msg.default_string_mut().get(), eq("hello world"));
 
     msg.default_string_mut().or_default().set("accessors_test");
     assert_that!(msg.default_string(), eq("accessors_test"));
     assert_that!(msg.default_string_opt(), eq(Optional::Set("accessors_test".into())));
-    assert_that!(msg.default_string_mut().is_set(), eq(true));
+    assert_that!(msg.default_string_mut(), is_set());
     assert_that!(msg.default_string_mut().get(), eq("accessors_test"));
     assert_that!(msg.default_string_mut().or_default().get(), eq("accessors_test"));
 
     msg.default_string_mut().clear();
     assert_that!(msg.default_string(), eq("hello"));
     assert_that!(msg.default_string_opt(), eq(Optional::Unset("hello".into())));
-    assert_that!(msg.default_string_mut().is_unset(), eq(true));
+    assert_that!(msg.default_string_mut(), is_unset());
 
     msg.default_string_mut().set("");
     assert_that!(msg.default_string(), eq(""));
diff --git a/rust/test/shared/matchers.rs b/rust/test/shared/matchers.rs
new file mode 100644
index 0000000..f89f1dd
--- /dev/null
+++ b/rust/test/shared/matchers.rs
@@ -0,0 +1,58 @@
+use googletest::matcher::MatcherResult;
+use googletest::prelude::*;
+use protobuf::{AbsentField, Optional, PresentField, ProxiedWithPresence};
+use std::marker::PhantomData;
+
+/// ===============================
+///               IS_UNSET
+/// ===============================
+pub fn is_unset<'a, T: std::fmt::Debug + ProxiedWithPresence + ?Sized + 'a>()
+-> impl Matcher<ActualT = Optional<PresentField<'a, T>, AbsentField<'a, T>>> {
+    UnsetMatcher::<T> { _phantom: PhantomData }
+}
+
+struct UnsetMatcher<'a, T: ProxiedWithPresence + ?Sized> {
+    _phantom: PhantomData<PresentField<'a, T>>,
+}
+
+impl<'a, T: std::fmt::Debug + ProxiedWithPresence + ?Sized> Matcher for UnsetMatcher<'a, T> {
+    type ActualT = Optional<PresentField<'a, T>, AbsentField<'a, T>>;
+
+    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+        actual.is_unset().into()
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> String {
+        match matcher_result {
+            MatcherResult::Match => "is not set".to_string(),
+            MatcherResult::NoMatch => "is set".to_string(),
+        }
+    }
+}
+
+/// ===============================
+///               IS_SET
+/// ===============================
+pub fn is_set<'a, T: std::fmt::Debug + ProxiedWithPresence + ?Sized + 'a>()
+-> impl Matcher<ActualT = Optional<PresentField<'a, T>, AbsentField<'a, T>>> {
+    SetMatcher::<T> { _phantom: PhantomData }
+}
+
+struct SetMatcher<'a, T: ProxiedWithPresence + ?Sized> {
+    _phantom: PhantomData<PresentField<'a, T>>,
+}
+
+impl<'a, T: std::fmt::Debug + ProxiedWithPresence + ?Sized> Matcher for SetMatcher<'a, T> {
+    type ActualT = Optional<PresentField<'a, T>, AbsentField<'a, T>>;
+
+    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+        actual.is_set().into()
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> String {
+        match matcher_result {
+            MatcherResult::Match => "is set".to_string(),
+            MatcherResult::NoMatch => "is not set".to_string(),
+        }
+    }
+}