From 5a05b37838f7baf52419b0caf004cc6ac5a311b5 Mon Sep 17 00:00:00 2001 From: Yat Ho Date: Mon, 16 Dec 2024 06:50:19 +0800 Subject: [PATCH] feat: support the JSON `null` type in `tr_variant` (#7255) --- libtransmission/variant-benc.cc | 6 ++++++ libtransmission/variant-json.cc | 8 +++++++- libtransmission/variant.cc | 8 ++++++++ libtransmission/variant.h | 27 +++++++++++++++++---------- tests/libtransmission/json-test.cc | 25 +++++++++++++++++++++---- 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/libtransmission/variant-benc.cc b/libtransmission/variant-benc.cc index 59901f9aa..f210b1495 100644 --- a/libtransmission/variant-benc.cc +++ b/libtransmission/variant-benc.cc @@ -277,6 +277,11 @@ namespace to_string_helpers { using OutBuf = libtransmission::StackBuffer<1024U * 8U, std::byte>; +void saveNullFunc(tr_variant const& /*var*/, std::nullptr_t /*val*/, void* vout) +{ + static_cast(vout)->add("0:"sv); +} + void saveIntFunc(tr_variant const& /*var*/, int64_t const val, void* vout) { auto out = static_cast(vout); @@ -338,6 +343,7 @@ std::string tr_variant_serde::to_benc_string(tr_variant const& var) using namespace to_string_helpers; static auto constexpr Funcs = WalkFuncs{ + saveNullFunc, // saveIntFunc, // saveBoolFunc, // saveRealFunc, // diff --git a/libtransmission/variant-json.cc b/libtransmission/variant-json.cc index 1080ee3c5..4ac47cc08 100644 --- a/libtransmission/variant-json.cc +++ b/libtransmission/variant-json.cc @@ -50,7 +50,7 @@ struct json_to_variant_handler : public rapidjson::BaseReaderHandler<> bool Null() { - *get_leaf() = tr_variant::unmanaged_string(""); + *get_leaf() = nullptr; return true; } @@ -311,6 +311,11 @@ private: using writer_var_t = std::variant, rapidjson::PrettyWriter>; +void jsonNullFunc(tr_variant const& /*var*/, std::nullptr_t /*val*/, void* vdata) +{ + std::visit([](auto&& writer) { writer.Null(); }, *static_cast(vdata)); +} + void jsonIntFunc(tr_variant const& /*var*/, int64_t const val, void* vdata) { std::visit([val](auto&& writer) { writer.Int64(val); }, *static_cast(vdata)); @@ -363,6 +368,7 @@ std::string tr_variant_serde::to_json_string(tr_variant const& var) const using namespace to_string_helpers; static auto constexpr Funcs = WalkFuncs{ + jsonNullFunc, // jsonIntFunc, // jsonBoolFunc, // jsonRealFunc, // diff --git a/libtransmission/variant.cc b/libtransmission/variant.cc index 4c6b15257..a3c14405a 100644 --- a/libtransmission/variant.cc +++ b/libtransmission/variant.cc @@ -208,6 +208,10 @@ void tr_variant::Merge::operator()(std::monostate const& src) { tgt_ = src; } +void tr_variant::Merge::operator()(std::nullptr_t const& src) +{ + tgt_ = src; +} void tr_variant::Merge::operator()(bool const& src) { tgt_ = src; @@ -761,6 +765,10 @@ void tr_variant_serde::walk(tr_variant const& top, WalkFuncs const& walk_funcs, switch (variant_index(v)) { + case tr_variant::NullIndex: + walk_funcs.null_func(*v, *v->get_if(), user_data); + break; + case tr_variant::BoolIndex: walk_funcs.bool_func(*v, *v->get_if(), user_data); break; diff --git a/libtransmission/variant.h b/libtransmission/variant.h index 6f68ba849..e5e86df5a 100644 --- a/libtransmission/variant.h +++ b/libtransmission/variant.h @@ -30,15 +30,16 @@ struct tr_variant { public: - enum Type : size_t + enum Type : uint8_t { - NoneIndex = 0, - BoolIndex = 1, - IntIndex = 2, - DoubleIndex = 3, - StringIndex = 4, - VectorIndex = 5, - MapIndex = 6 + NoneIndex, + NullIndex, + BoolIndex, + IntIndex, + DoubleIndex, + StringIndex, + VectorIndex, + MapIndex }; using Vector = std::vector; @@ -239,7 +240,11 @@ public: template tr_variant& operator=(Val value) { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + val_.emplace(value); + } + else if constexpr (std::is_same_v) { val_.emplace(std::string{ value }); } @@ -392,6 +397,7 @@ private: public: explicit Merge(tr_variant& tgt); void operator()(std::monostate const& src); + void operator()(std::nullptr_t const& src); void operator()(bool const& src); void operator()(int64_t const& src); void operator()(double const& src); @@ -403,7 +409,7 @@ private: tr_variant& tgt_; }; - std::variant val_; + std::variant val_; }; template<> @@ -567,6 +573,7 @@ private: struct WalkFuncs { + void (*null_func)(tr_variant const& var, std::nullptr_t val, void* user_data); void (*int_func)(tr_variant const& var, int64_t val, void* user_data); void (*bool_func)(tr_variant const& var, bool val, void* user_data); void (*double_func)(tr_variant const& var, double val, void* user_data); diff --git a/tests/libtransmission/json-test.cc b/tests/libtransmission/json-test.cc index ef6445cab..264073143 100644 --- a/tests/libtransmission/json-test.cc +++ b/tests/libtransmission/json-test.cc @@ -61,7 +61,23 @@ TEST_P(JSONTest, testElements) " \"null\": null }" }; - auto const var = tr_variant_serde::json().inplace().parse(In).value_or(tr_variant{}); + // Same as In, just formatted differently + static auto constexpr Out = std::string_view{ + // clang-format off + "{" + "\"escaped\":\"bell \\b formfeed \\f linefeed \\n carriage return \\r tab \\t\"," + "\"false\":false," + "\"float\":6.5," + "\"int\":5," + "\"null\":null," + "\"string\":\"hello world\"," + "\"true\":true" + "}" + // clang-format on + }; + + auto serde = tr_variant_serde::json().inplace().compact(); + auto var = serde.parse(In).value_or(tr_variant{}); auto const* const map = var.get_if(); ASSERT_NE(map, nullptr); @@ -89,9 +105,10 @@ TEST_P(JSONTest, testElements) ASSERT_TRUE(b); EXPECT_FALSE(*b); - sv = map->value_if(tr_quark_new("null"sv)); - ASSERT_TRUE(sv); - EXPECT_EQ(""sv, *sv); + auto n = map->value_if(tr_quark_new("null"sv)); + EXPECT_TRUE(n); + + EXPECT_EQ(serde.to_string(var), Out); } TEST_P(JSONTest, testUtf8)