Skip to content

Commit

Permalink
Merge 'json_formatter: Make formatter::write work for std::pair' from…
Browse files Browse the repository at this point in the history
… Stephan Dollberg

Previously the un/associative container overloads for `formatter::write`
were broken because it failed to find an overload for
`write(output_stream, pair)`.

To fix this we make the `write(output_stream<char>&, state, Iter, Iter)`
overload actually `write(output_stream, state, pair)` so that the
existing overload that handles `pair` can be found.

Further we fix the fallback `write(output_stream, state, T)` overload to
call `formatter::write` recursively with the state stripped instead of
calling `to_json`. This keeps the recursive zero-copy nature of
`formatter::write` intact.

All of the above mirrors how the existing overloads for `to_json`
already work.

Adds some tests as well.

Closes #2440

* github.com:scylladb/seastar:
  json_formatter: Add tests for formatter::write
  json_formatter: Make formatter::write work for std::pair
  • Loading branch information
avikivity committed Sep 25, 2024
2 parents 3b85633 + 67fe1a9 commit e5b80b4
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 14 deletions.
8 changes: 4 additions & 4 deletions include/seastar/json/formatter.hh
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ class formatter {
static future<> write(output_stream<char>& stream, state s, Iter i, Iter 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] (auto& m) {
return do_for_each(i, e, [&first, &stream, s] (auto& m) {
auto f = (first) ? make_ready_future<>() : stream.write(",");
first = false;
return f.then([&m, &stream] {
return write(stream, m);
return f.then([&m, &stream, s] {
return write(stream, s, m);
});
}).then([&stream, s] {
return stream.write(end(s));
Expand All @@ -120,7 +120,7 @@ class formatter {
// fallback template
template<typename T>
static future<> write(output_stream<char>& stream, state, const T& t) {
return stream.write(to_json(t));
return write(stream, t);
}

public:
Expand Down
64 changes: 54 additions & 10 deletions tests/unit/json_formatter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,15 @@ SEASTAR_TEST_CASE(test_jsonable) {
return make_ready_future();
}

SEASTAR_THREAD_TEST_CASE(test_stream_range_as_array) {
template<typename F>
void formatter_check_expected(sstring expected, F f, bool close = true) {
auto vec = std::vector<net::packet>{};
auto out = output_stream<char>(data_sink(std::make_unique<vector_data_sink>(vec)), 8);

auto mapper = stream_range_as_array(std::vector<int>{1,2,3}, [] (auto i) {
object_json obj;
obj.subject = std::to_string(i);
obj.values.push(i);
return obj;
});

mapper(std::move(out)).get();
f(out);
if (close) {
out.close().get();
}

auto packets = net::packet{};
for (auto &p : vec) {
Expand All @@ -112,6 +109,53 @@ SEASTAR_THREAD_TEST_CASE(test_stream_range_as_array) {
auto buf = packets.release();

sstring result(buf.front().get(), buf.front().size());
sstring expected = "[{\"subject\":\"1\",\"values\":[1]}, {\"subject\":\"2\",\"values\":[2]}, {\"subject\":\"3\",\"values\":[3]}]";
BOOST_CHECK_EQUAL(expected, result);
}


SEASTAR_THREAD_TEST_CASE(test_stream_range_as_array) {
sstring expected = R"([{"subject":"1","values":[1]}, {"subject":"2","values":[2]}, {"subject":"3","values":[3]}])";
formatter_check_expected(expected, [] (auto& out) {
auto mapper = stream_range_as_array(std::vector<int>{1,2,3}, [] (auto i) {
object_json obj;
obj.subject = std::to_string(i);
obj.values.push(i);
return obj;
});

mapper(std::move(out)).get();
}, false);
}

SEASTAR_THREAD_TEST_CASE(formatter_write) {

formatter_check_expected("3", [] (auto &out) {
json::formatter::write(out, 3).get();
});
formatter_check_expected("false", [] (auto &out) {
json::formatter::write(out, false).get();
});
formatter_check_expected("\"foo\"", [] (auto &out) {
json::formatter::write(out, "foo").get();
});

formatter_check_expected("{1:2,3:4}", [] (auto& out) {
json::formatter::write(out, std::map<int, int>({{1, 2}, {3, 4}})).get();
});
formatter_check_expected("{3:4,1:2}", [] (auto& out) {
json::formatter::write(out, std::unordered_map<int, int>({{1, 2}, {3, 4}})).get();
});
formatter_check_expected("[1,2,3,4]", [] (auto &out) {
json::formatter::write(out, std::vector<int>({1, 2, 3, 4})).get();
});

formatter_check_expected("[{1:2},{3:4}]", [] (auto &out) {
json::formatter::write(out, std::vector<std::pair<int, int>>({{1, 2}, {3, 4}})).get();
});
formatter_check_expected("[{1:2},{3:4}]", [] (auto &out) {
json::formatter::write(out, std::vector<std::map<int, int>>({{{1, 2}}, {{3, 4}}})).get();
});
formatter_check_expected("[[1,2],[3,4]]", [] (auto &out) {
json::formatter::write(out, std::vector<std::vector<int>>({{1, 2}, {3, 4}})).get();
});
}

0 comments on commit e5b80b4

Please sign in to comment.