feat: add tr_variant::Map methods (#7910)

* feat: add tr_variant::Map::contains()

* feat: add tr_variant::Map::replace_key()

* fixup! feat: add tr_variant::Map::replace_key()

fix readability-braces-around-statements

* fixup! feat: add tr_variant::Map::contains()

readability-container-contains
This commit is contained in:
Charles Kerr
2025-12-09 21:16:59 -06:00
committed by GitHub
parent 9f911d4d52
commit e671c0346c
2 changed files with 154 additions and 0 deletions

View File

@@ -99,6 +99,11 @@ public:
return Vector::const_iterator{ const_cast<Map*>(this)->find(key) };
}
[[nodiscard]] auto contains(tr_quark const key) const noexcept
{
return find(key) != end(); // NOLINT(readability-container-contains)
}
[[nodiscard]] TR_CONSTEXPR20 auto find(std::initializer_list<tr_quark> keys) noexcept
{
auto constexpr Predicate = [](auto const& item, tr_quark key)
@@ -139,6 +144,23 @@ public:
return 0U;
}
bool replace_key(tr_quark const old_key, tr_quark const new_key)
{
if (contains(new_key))
{
return false;
}
auto iter = find(old_key);
if (iter == end())
{
return false;
}
iter->first = new_key;
return true;
}
[[nodiscard]] tr_variant& operator[](tr_quark const& key)
{
if (auto const iter = find(key); iter != end())

View File

@@ -556,6 +556,138 @@ TEST_F(VariantTest, dictFindType)
EXPECT_EQ(ExpectedInt, *i);
}
TEST_F(VariantTest, mapContains)
{
auto const key_bool = tr_quark_new("contains-bool"sv);
auto const key_int = tr_quark_new("contains-int"sv);
auto const key_double = tr_quark_new("contains-double"sv);
auto const key_string = tr_quark_new("contains-string"sv);
auto const key_vector = tr_quark_new("contains-vector"sv);
auto const key_map = tr_quark_new("contains-map"sv);
auto const key_missing = tr_quark_new("contains-missing"sv);
auto const nested_key = tr_quark_new("contains-nested"sv);
// populate a test map
auto top = tr_variant::make_map(6U);
auto* const map = top.get_if<tr_variant::Map>();
ASSERT_NE(map, nullptr);
map->try_emplace(key_bool, true);
map->try_emplace(key_int, int64_t{ 42 });
map->try_emplace(key_double, 4.2);
map->try_emplace(key_string, "needle"sv);
auto vec = tr_variant::Vector{};
vec.emplace_back(true);
vec.emplace_back(int64_t{ 7 });
map->try_emplace(key_vector, std::move(vec));
auto nested = tr_variant::make_map(1U);
auto* nested_map = nested.get_if<tr_variant::Map>();
ASSERT_NE(nested_map, nullptr);
nested_map->try_emplace(nested_key, "nested"sv);
map->try_emplace(key_map, std::move(nested));
// ---
// test: returns true for entries that exist
EXPECT_TRUE(map->contains(key_bool));
EXPECT_TRUE(map->contains(key_double));
EXPECT_TRUE(map->contains(key_int));
EXPECT_TRUE(map->contains(key_map));
EXPECT_TRUE(map->contains(key_string));
EXPECT_TRUE(map->contains(key_vector));
// test: returns false for entries that never existed
EXPECT_FALSE(map->contains(key_missing));
// test: returns false for entries that were removed
EXPECT_EQ(1U, map->erase(key_vector));
EXPECT_FALSE(map->contains(key_vector));
}
TEST_F(VariantTest, mapReplaceKey)
{
auto constexpr IntVal = int64_t{ 73 };
auto const key_bool = tr_quark_new("replace-bool"sv);
auto const key_int = tr_quark_new("replace-int"sv);
auto const key_double = tr_quark_new("replace-double"sv);
auto const key_string = tr_quark_new("replace-string"sv);
auto const key_vector = tr_quark_new("replace-vector"sv);
auto const key_map = tr_quark_new("replace-map"sv);
auto const key_duplicate = tr_quark_new("replace-duplicate"sv);
auto const key_missing_src = tr_quark_new("replace-missing-src"sv);
auto const key_missing_tgt = tr_quark_new("replace-missing-tgt"sv);
auto const key_replacement = tr_quark_new("replace-string-new"sv);
auto const key_nested = tr_quark_new("replace-nested"sv);
// populate a sample map
auto top = tr_variant::make_map(7U);
auto* const map = top.get_if<tr_variant::Map>();
ASSERT_NE(map, nullptr);
map->try_emplace(key_bool, true);
map->try_emplace(key_int, IntVal);
map->try_emplace(key_double, 7.3);
map->try_emplace(key_string, "string"sv);
auto vec = tr_variant::Vector{};
vec.emplace_back(false);
vec.emplace_back(int64_t{ 99 });
map->try_emplace(key_vector, std::move(vec));
auto nested = tr_variant::make_map(1U);
auto* nested_map = nested.get_if<tr_variant::Map>();
ASSERT_NE(nested_map, nullptr);
nested_map->try_emplace(key_nested, "nested"sv);
map->try_emplace(key_map, std::move(nested));
map->try_emplace(key_duplicate, "occupied"sv);
// ---
// test: neither src nor tgt exist
auto const serde = tr_variant_serde::json();
auto expected = serde.to_string(top);
EXPECT_FALSE(map->contains(key_missing_src));
EXPECT_FALSE(map->contains(key_missing_tgt));
EXPECT_FALSE(map->replace_key(key_missing_src, key_missing_tgt));
EXPECT_FALSE(map->contains(key_missing_src));
EXPECT_FALSE(map->contains(key_missing_tgt));
auto actual = serde.to_string(top);
EXPECT_EQ(expected, actual); // confirm variant is unchanged
// test: src doesn't exist
expected = serde.to_string(top);
EXPECT_FALSE(map->contains(key_missing_src));
EXPECT_EQ(IntVal, map->value_if<int64_t>(key_int).value_or(!IntVal));
EXPECT_FALSE(map->replace_key(key_missing_src, key_int));
EXPECT_FALSE(map->contains(key_missing_src));
EXPECT_EQ(IntVal, map->value_if<int64_t>(key_int).value_or(!IntVal));
actual = serde.to_string(top);
EXPECT_EQ(expected, actual); // confirm variant is unchanged
// test: tgt already exists
expected = serde.to_string(top);
EXPECT_TRUE(map->contains(key_int));
EXPECT_TRUE(map->contains(key_string));
EXPECT_FALSE(map->replace_key(key_int, key_string));
EXPECT_TRUE(map->contains(key_int));
EXPECT_TRUE(map->contains(key_string));
actual = serde.to_string(top);
EXPECT_EQ(expected, actual); // confirm variant is unchanged
// test: successful replacement
EXPECT_TRUE(map->contains(key_int));
EXPECT_FALSE(map->contains(key_replacement));
EXPECT_TRUE(map->replace_key(key_int, key_replacement));
EXPECT_FALSE(map->contains(key_int));
EXPECT_TRUE(map->contains(key_replacement));
EXPECT_EQ(IntVal, map->value_if<int64_t>(key_replacement).value_or(!IntVal));
}
TEST_F(VariantTest, variantFromBufFuzz)
{
auto benc_serde = tr_variant_serde::json();