Seastar
High performance C++ framework for concurrent servers
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
formatter.hh
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18/*
19 * Copyright 2015 Cloudius Systems
20 */
21
22#pragma once
23
24#ifndef SEASTAR_MODULE
25#include <ranges>
26#include <string>
27#include <tuple>
28#include <vector>
29#include <unordered_map>
30#include <map>
31#include <time.h>
32#include <sstream>
33#endif
34
35#include <seastar/core/loop.hh>
36#include <seastar/core/sstring.hh>
37#include <seastar/core/iostream.hh>
38#include <seastar/util/modules.hh>
39
40namespace seastar {
41
42namespace internal {
43
44template<typename T>
45concept is_map = requires {
46 typename T::mapped_type;
47};
48
49template<typename T>
50concept is_pair_like = requires {
51 typename std::tuple_size<T>::type;
52 requires std::tuple_size_v<T> == 2;
53};
54
55template<typename T>
56concept is_string_like =
57 std::convertible_to<const T&, std::string_view> &&
58 requires (T s) {
59 { s.data() } -> std::same_as<char*>;
60 // sstring::length() and sstring::find() return size_t instead of
61 // size_type (i.e., uint32_t), so we cannot check their return type
62 // with T::size_type
63 s.find('a');
64 s.length();
65 };
66
67}
68
69namespace json {
70
71SEASTAR_MODULE_EXPORT
72class jsonable;
73
74typedef struct tm date_time;
75
81SEASTAR_MODULE_EXPORT
82class formatter {
83 enum class state {
84 none, array, map
85 };
86 static sstring begin(state);
87 static sstring end(state);
88
89 template<internal::is_pair_like T>
90 static sstring to_json(state s, const T& p) {
91 auto& [key, value] = p;
92 return s == state::array ?
93 "{" + to_json(state::none, p) + "}" :
94 to_json(key) + ":" + to_json(value);
95 }
96
97 template<typename Iterator, typename Sentinel>
98 static sstring to_json(state s, Iterator i, Sentinel e) {
99 std::stringstream res;
100 res << begin(s);
101 size_t n = 0;
102 while (i != e) {
103 if (n++ != 0) {
104 res << ",";
105 }
106 res << to_json(s, *i++);
107 }
108 res << end(s);
109 return res.str();
110 }
111
112 // fallback template
113 template<typename T>
114 static sstring to_json(state, const T& t) {
115 return to_json(t);
116 }
117
118 template<internal::is_pair_like T>
119 static future<> write(output_stream<char>& stream, state s, T&& p) {
120 if (s == state::array) {
121 return stream.write("{").then([&stream, &p] {
122 return write(stream, state::none, p).then([&stream] {
123 return stream.write("}");
124 });
125 });
126 } else {
127 auto& [key, value] = p;
128 return stream.write(to_json(key) + ":").then([&value, &stream] {
129 return write(stream, value);
130 });
131 }
132 }
133
134 template<internal::is_pair_like T>
135 static future<> write(output_stream<char>& stream, state s, const T& p) {
136 if (s == state::array) {
137 return stream.write("{").then([&stream, p] {
138 return write(stream, state::none, p).then([&stream] {
139 return stream.write("}");
140 });
141 });
142 } else {
143 auto& [key, value] = p;
144 return stream.write(to_json(key) + ":").then([&stream, value] {
145 return write(stream, value);
146 });
147 }
148 }
149
150 template<typename Iterator, typename Sentinel>
151 static future<> write(output_stream<char>& stream, state s, Iterator i, Sentinel e) {
152 return do_with(true, [&stream, s, i, e] (bool& first) {
153 return stream.write(begin(s)).then([&first, &stream, s, i, e] {
154 using ref_t = std::iter_reference_t<Iterator>;
155 return do_for_each(i, e, [&first, &stream, s] (ref_t m) {
156 auto f = (first) ? make_ready_future<>() : stream.write(",");
157 first = false;
158 if constexpr (std::is_lvalue_reference_v<ref_t>) {
159 return f.then([&m, &stream, s] {
160 return write(stream, s, m);
161 });
162 } else {
163 using value_t = std::iter_value_t<Iterator>;
164 return f.then([m = std::forward<value_t>(m), &stream, s] {
165 return write(stream, s, m);
166 });
167 }
168 }).then([&stream, s] {
169 return stream.write(end(s));
170 });
171 });
172 });
173 }
174
175 // fallback template
176 template<typename T>
177 static future<> write(output_stream<char>& stream, state, const T& t) {
178 return write(stream, t);
179 }
180
181public:
182
188 static sstring to_json(const sstring& str);
189
195 static sstring to_json(int n);
196
202 static sstring to_json(unsigned n);
203
209 static sstring to_json(long n);
210
216 static sstring to_json(float f);
217
223 static sstring to_json(double d);
224
231 static sstring to_json(const char* str, size_t len);
232
239 static sstring to_json(const char* str);
240
246 static sstring to_json(bool d);
247
253 template<std::ranges::input_range Range>
254 requires (!internal::is_string_like<Range>)
255 static sstring to_json(const Range& range) {
256 if constexpr (internal::is_map<Range>) {
257 return to_json(state::map, std::ranges::begin(range), std::ranges::end(range));
258 } else {
259 return to_json(state::array, std::ranges::begin(range), std::ranges::end(range));
260 }
261 }
262
268 static sstring to_json(const date_time& d);
269
275 static sstring to_json(const jsonable& obj);
276
282 static sstring to_json(unsigned long l);
283
284
285
291 static future<> write(output_stream<char>& s, const sstring& str) {
292 return s.write(to_json(str));
293 }
294
301 return s.write(to_json(n));
302 }
303
309 static future<> write(output_stream<char>& s, long n) {
310 return s.write(to_json(n));
311 }
312
318 static future<> write(output_stream<char>& s, float f) {
319 return s.write(to_json(f));
320 }
321
327 static future<> write(output_stream<char>& s, double d) {
328 return s.write(to_json(d));
329 }
330
336 static future<> write(output_stream<char>& s, const char* str) {
337 return s.write(to_json(str));
338 }
339
345 static future<> write(output_stream<char>& s, bool d) {
346 return s.write(to_json(d));
347 }
348
358 template<std::ranges::input_range Range>
359 requires (!internal::is_string_like<Range>)
360 static future<> write(output_stream<char>& s, const Range& range) {
361 return do_with(std::move(range), [&s] (const auto& range) {
362 if constexpr (internal::is_map<Range>) {
363 return write(s, state::map, std::ranges::begin(range), std::ranges::end(range));
364 } else {
365 return write(s, state::array, std::ranges::begin(range), std::ranges::end(range));
366 }
367 });
368 }
369
375 static future<> write(output_stream<char>& s, const date_time& d) {
376 return s.write(to_json(d));
377 }
378
384 template <std::derived_from<jsonable> Jsonable>
385 static future<> write(output_stream<char>& s, Jsonable obj) {
386 return do_with(std::move(obj), [&s] (const auto& obj) {
387 return obj.write(s);
388 });
389 }
390
396 static future<> write(output_stream<char>& s, unsigned long l) {
397 return s.write(to_json(l));
398 }
399};
400
401}
402
403}
A representation of a possibly not-yet-computed value.
Definition: future.hh:1240
Result then(Func &&func) noexcept
Schedule a block of code to run when the future is ready.
Definition: future.hh:1425
Definition: formatter.hh:82
static sstring to_json(double d)
static future write(output_stream< char > &s, const date_time &d)
Definition: formatter.hh:375
static future write(output_stream< char > &s, double d)
Definition: formatter.hh:327
static future write(output_stream< char > &s, long n)
Definition: formatter.hh:309
static future write(output_stream< char > &s, unsigned long l)
Definition: formatter.hh:396
static sstring to_json(const sstring &str)
static future write(output_stream< char > &s, float f)
Definition: formatter.hh:318
static future write(output_stream< char > &s, Jsonable obj)
Definition: formatter.hh:385
static sstring to_json(long n)
static sstring to_json(const Range &range)
Definition: formatter.hh:255
static sstring to_json(const date_time &d)
static future write(output_stream< char > &s, const Range &range)
Definition: formatter.hh:360
static sstring to_json(int n)
static future write(output_stream< char > &s, const sstring &str)
Definition: formatter.hh:291
static future write(output_stream< char > &s, bool d)
Definition: formatter.hh:345
static sstring to_json(unsigned long l)
static future write(output_stream< char > &s, const char *str)
Definition: formatter.hh:336
static future write(output_stream< char > &s, int n)
Definition: formatter.hh:300
static sstring to_json(const jsonable &obj)
static sstring to_json(bool d)
static sstring to_json(unsigned n)
static sstring to_json(const char *str)
static sstring to_json(float f)
static sstring to_json(const char *str, size_t len)
Definition: json_elements.hh:195
Definition: stream.hh:60
future do_for_each(Iterator begin, Sentinel end, AsyncAction action) noexcept
Call a function for each item in a range, sequentially (iterator version).
Definition: loop.hh:465
auto do_with(T1 &&rv1, T2 &&rv2, More &&... more) noexcept
Definition: do_with.hh:135
Seastar API namespace.
Definition: abort_on_ebadf.hh:26