From aba3c83537980d77cbba6e896dfe7af57f2772da Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Thu, 21 Nov 2024 16:44:18 +0800 Subject: [PATCH] json_formatter: Add support for standard range containers This commit extends the JSON formatting capabilities to support all standard range containers through the C++20 ranges concept. Key improvements: - Add generic range support using std::ranges::input_range concept - Enable JSON formatting for any range type without materialization - Maintain existing support for vector, map, and unordered_map - Provide a flexible, type-agnostic serialization approach Example use cases: - Formatting std::span - Serializing range views - Converting filter and transform views directly to JSON Signed-off-by: Kefu Chai --- include/seastar/json/formatter.hh | 55 ++++++++++++++++++++----------- tests/unit/json_formatter_test.cc | 8 +++++ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/include/seastar/json/formatter.hh b/include/seastar/json/formatter.hh index 26c07b505d..fe86d9ed4c 100644 --- a/include/seastar/json/formatter.hh +++ b/include/seastar/json/formatter.hh @@ -22,6 +22,7 @@ #pragma once #ifndef SEASTAR_MODULE +#include #include #include #include @@ -37,6 +38,15 @@ namespace seastar { +namespace internal { + +template +concept is_map = requires { + typename T::mapped_type; +}; + +} + namespace json { SEASTAR_MODULE_EXPORT @@ -64,8 +74,18 @@ class formatter { to_json(p.first) + ":" + to_json(p.second); } - template - static sstring to_json(state s, Iter i, Iter e) { + template + static sstring to_json(state s, const std::tuple& p) { + if (s == state::array) { + return "{" + to_json(state::none, p) + "}"; + } else { + auto& [key, value] = p; + return to_json(key) + ":" + to_json(value); + } + } + + template + static sstring to_json(state s, Iterator i, Sentinel e) { std::stringstream res; res << begin(s); size_t n = 0; @@ -100,8 +120,8 @@ class formatter { } } - template - static future<> write(output_stream& stream, state s, Iter i, Iter e) { + template + static future<> write(output_stream& stream, state s, Iterator i, Sentinel e) { return do_with(true, [&stream, s, i, e] (bool& first) { return stream.write(begin(s)).then([&first, &stream, s, i, e] { return do_for_each(i, e, [&first, &stream, s] (auto& m) { @@ -191,23 +211,18 @@ public: static sstring to_json(bool d); /** - * return a json formatted list of a given vector of params - * @param vec the vector to format - * @return the given vector in a json format + * converts a given range to a JSON-formatted string + * + * @param range A standard range type + * @return A string containing the JSON representation of the input range */ - template - static sstring to_json(const std::vector& vec) { - return to_json(state::array, vec.begin(), vec.end()); - } - - template - static sstring to_json(const std::map& map) { - return to_json(state::map, map.begin(), map.end()); - } - - template - static sstring to_json(const std::unordered_map& map) { - return to_json(state::map, map.begin(), map.end()); + template + static sstring to_json(const Range& range) { + if constexpr (internal::is_map) { + return to_json(state::map, std::ranges::begin(range), std::ranges::end(range)); + } else { + return to_json(state::array, std::ranges::begin(range), std::ranges::end(range)); + } } /** diff --git a/tests/unit/json_formatter_test.cc b/tests/unit/json_formatter_test.cc index 783b29e409..5abaa7f36e 100644 --- a/tests/unit/json_formatter_test.cc +++ b/tests/unit/json_formatter_test.cc @@ -62,6 +62,14 @@ SEASTAR_TEST_CASE(test_collections) { return make_ready_future(); } +SEASTAR_TEST_CASE(test_ranges) { + BOOST_CHECK_EQUAL("[1,2,3,4]", formatter::to_json(std::views::iota(1, 5))); +#ifdef __cpp_lib_ranges_enumerate + BOOST_CHECK_EQUAL("[{0:5},{1:6},{2:7},{3:8}]", formatter::to_json(std::views::iota(5, 9) | std::views::enumerate)); +#endif + return make_ready_future(); +} + struct object_json : public json_base { json_element subject; json_list values;