]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
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 | #include <string> | |
25 | #include <vector> | |
26 | #include <time.h> | |
27 | #include <sstream> | |
f67539c2 TL |
28 | #include <seastar/core/do_with.hh> |
29 | #include <seastar/core/loop.hh> | |
11fdf7f2 TL |
30 | #include <seastar/json/formatter.hh> |
31 | #include <seastar/core/sstring.hh> | |
32 | #include <seastar/core/iostream.hh> | |
33 | ||
34 | namespace seastar { | |
35 | ||
36 | namespace json { | |
37 | ||
38 | /** | |
39 | * The base class for all json element. | |
40 | * Every json element has a name | |
41 | * An indication if it was set or not | |
42 | * And is this element is mandatory. | |
43 | * When a mandatory element is not set | |
44 | * this is not a valid object | |
45 | */ | |
46 | class json_base_element { | |
47 | public: | |
48 | /** | |
49 | * The constructors | |
50 | */ | |
20effc67 | 51 | json_base_element() noexcept |
11fdf7f2 TL |
52 | : _mandatory(false), _set(false) { |
53 | } | |
54 | ||
55 | virtual ~json_base_element() = default; | |
56 | ||
57 | /** | |
58 | * Check if it's a mandatory parameter | |
59 | * and if it's set. | |
60 | * @return true if this is not a mandatory parameter | |
61 | * or if it is and it's value is set | |
62 | */ | |
20effc67 | 63 | virtual bool is_verify() noexcept { |
11fdf7f2 TL |
64 | return !(_mandatory && !_set); |
65 | } | |
66 | ||
20effc67 | 67 | json_base_element& operator=(const json_base_element& o) noexcept { |
11fdf7f2 TL |
68 | // Names and mandatory are never changed after creation |
69 | _set = o._set; | |
70 | return *this; | |
71 | } | |
72 | ||
73 | /** | |
74 | * returns the internal value in a json format | |
75 | * Each inherit class must implement this method | |
76 | * @return formated internal value | |
77 | */ | |
78 | virtual std::string to_string() = 0; | |
79 | ||
80 | virtual future<> write(output_stream<char>& s) const = 0; | |
81 | std::string _name; | |
82 | bool _mandatory; | |
83 | bool _set; | |
84 | }; | |
85 | ||
86 | /** | |
87 | * Basic json element instantiate | |
88 | * the json_element template. | |
89 | * it adds a value to the base definition | |
90 | * and the to_string implementation using the formatter | |
91 | */ | |
92 | template<class T> | |
93 | class json_element : public json_base_element { | |
94 | public: | |
95 | ||
96 | /** | |
97 | * the assignment operator also set | |
98 | * the set value to true. | |
99 | * @param new_value the new value | |
100 | * @return the value itself | |
101 | */ | |
102 | json_element &operator=(const T& new_value) { | |
103 | _value = new_value; | |
104 | _set = true; | |
105 | return *this; | |
106 | } | |
107 | /** | |
108 | * the assignment operator also set | |
109 | * the set value to true. | |
110 | * @param new_value the new value | |
111 | * @return the value itself | |
112 | */ | |
113 | template<class C> | |
114 | json_element &operator=(const C& new_value) { | |
115 | _value = new_value; | |
116 | _set = true; | |
117 | return *this; | |
118 | } | |
119 | /** | |
120 | * The brackets operator | |
121 | * @return the value | |
122 | */ | |
20effc67 | 123 | const T& operator()() const noexcept { |
11fdf7f2 TL |
124 | return _value; |
125 | } | |
126 | ||
127 | /** | |
128 | * The to_string return the value | |
129 | * formated as a json value | |
130 | * @return the value foramted for json | |
131 | */ | |
132 | virtual std::string to_string() override | |
133 | { | |
134 | return formatter::to_json(_value); | |
135 | } | |
136 | ||
137 | virtual future<> write(output_stream<char>& s) const override { | |
138 | return formatter::write(s, _value); | |
139 | } | |
140 | private: | |
141 | T _value; | |
142 | }; | |
143 | ||
144 | /** | |
145 | * json_list is based on std vector implementation. | |
146 | * | |
147 | * When values are added with push it is set the "set" flag to true | |
148 | * hence will be included in the parsed object | |
149 | */ | |
150 | template<class T> | |
151 | class json_list : public json_base_element { | |
152 | public: | |
153 | ||
154 | /** | |
155 | * Add an element to the list. | |
156 | * @param element a new element that will be added to the list | |
157 | */ | |
158 | void push(const T& element) { | |
159 | _set = true; | |
160 | _elements.push_back(element); | |
161 | } | |
162 | ||
163 | virtual std::string to_string() override | |
164 | { | |
165 | return formatter::to_json(_elements); | |
166 | } | |
167 | ||
168 | /** | |
169 | * Assignment can be done from any object that support const range | |
170 | * iteration and that it's elements can be assigned to the list elements | |
171 | */ | |
172 | template<class C> | |
173 | json_list& operator=(const C& list) { | |
174 | _elements.clear(); | |
175 | for (auto i : list) { | |
176 | push(i); | |
177 | } | |
178 | return *this; | |
179 | } | |
180 | virtual future<> write(output_stream<char>& s) const override { | |
181 | return formatter::write(s, _elements); | |
182 | } | |
183 | std::vector<T> _elements; | |
184 | }; | |
185 | ||
186 | class jsonable { | |
187 | public: | |
188 | virtual ~jsonable() = default; | |
189 | /** | |
190 | * create a foramted string of the object. | |
191 | * @return the object formated. | |
192 | */ | |
193 | virtual std::string to_json() const = 0; | |
194 | ||
195 | /*! | |
196 | * \brief write an object to the output stream | |
197 | * | |
198 | * The defult implementation uses the to_json | |
199 | * Object implementation override it. | |
200 | */ | |
201 | virtual future<> write(output_stream<char>& s) const { | |
202 | return s.write(to_json()); | |
203 | } | |
204 | }; | |
205 | ||
206 | /** | |
207 | * The base class for all json objects | |
208 | * It holds a list of all the element in it, | |
209 | * allowing it implement the to_json method. | |
210 | * | |
211 | * It also allows iterating over the element | |
212 | * in the object, even if not all the member | |
213 | * are known in advance and in practice mimic | |
214 | * reflection | |
215 | */ | |
216 | struct json_base : public jsonable { | |
217 | ||
218 | virtual ~json_base() = default; | |
219 | ||
220 | json_base() = default; | |
221 | ||
222 | json_base(const json_base&) = delete; | |
223 | ||
224 | json_base operator=(const json_base&) = delete; | |
225 | ||
226 | /** | |
227 | * create a foramted string of the object. | |
228 | * @return the object formated. | |
229 | */ | |
230 | virtual std::string to_json() const; | |
231 | ||
232 | /*! | |
233 | * \brief write to an output stream | |
234 | */ | |
235 | virtual future<> write(output_stream<char>&) const; | |
236 | ||
237 | /** | |
238 | * Check that all mandatory elements are set | |
239 | * @return true if all mandatory parameters are set | |
240 | */ | |
241 | virtual bool is_verify() const; | |
242 | ||
243 | /** | |
244 | * Register an element in an object | |
245 | * @param element the element to be added | |
246 | * @param name the element name | |
247 | * @param mandatory is this element mandatory. | |
248 | */ | |
249 | virtual void add(json_base_element* element, std::string name, | |
250 | bool mandatory = false); | |
251 | ||
252 | std::vector<json_base_element*> _elements; | |
253 | }; | |
254 | ||
255 | /** | |
256 | * There are cases where a json request needs to return a successful | |
257 | * empty reply. | |
258 | * The json_void class will be used to mark that the reply should be empty. | |
259 | * | |
260 | */ | |
261 | struct json_void : public jsonable{ | |
262 | virtual std::string to_json() const { | |
263 | return ""; | |
264 | } | |
265 | ||
266 | /*! | |
267 | * \brief write to an output stream | |
268 | */ | |
269 | virtual future<> write(output_stream<char>& s) const { | |
270 | return s.close(); | |
271 | } | |
272 | }; | |
273 | ||
274 | ||
275 | /** | |
276 | * The json return type, is a helper class to return a json | |
277 | * formatted string. | |
278 | * It uses autoboxing in its constructor so when a function return | |
279 | * type is json_return_type, it could return a type that would be converted | |
280 | * ie. | |
281 | * json_return_type foo() { | |
282 | * return "hello"; | |
283 | * } | |
284 | * | |
285 | * would return a json formatted string: "hello" (rather then hello) | |
286 | */ | |
287 | struct json_return_type { | |
288 | sstring _res; | |
289 | std::function<future<>(output_stream<char>&&)> _body_writer; | |
290 | json_return_type(std::function<future<>(output_stream<char>&&)>&& body_writer) : _body_writer(std::move(body_writer)) { | |
291 | } | |
292 | template<class T> | |
293 | json_return_type(const T& res) { | |
294 | _res = formatter::to_json(res); | |
295 | } | |
296 | ||
297 | json_return_type(json_return_type&& o) noexcept : _res(std::move(o._res)), _body_writer(std::move(o._body_writer)) { | |
298 | } | |
9f95a23c | 299 | json_return_type& operator=(json_return_type&& o) noexcept { |
1e59de90 TL |
300 | if (this != &o) { |
301 | _res = std::move(o._res); | |
302 | _body_writer = std::move(o._body_writer); | |
303 | } | |
9f95a23c TL |
304 | return *this; |
305 | } | |
20effc67 TL |
306 | |
307 | json_return_type(const json_return_type&) = default; | |
308 | json_return_type& operator=(const json_return_type&) = default; | |
11fdf7f2 TL |
309 | }; |
310 | ||
311 | /*! | |
312 | * \brief capture a range and return a serialize function for it as a json array. | |
313 | * | |
314 | * To use it, pass a range and a mapping function. | |
315 | * For example, if res is a map: | |
316 | * | |
317 | * return make_ready_future<json::json_return_type>(stream_range_as_array(res, [](const auto&i) {return i.first})); | |
318 | */ | |
319 | template<typename Container, typename Func> | |
f67539c2 | 320 | SEASTAR_CONCEPT( requires requires (Container c, Func aa, output_stream<char> s) { { formatter::write(s, aa(*c.begin())) } -> std::same_as<future<>>; } ) |
11fdf7f2 | 321 | std::function<future<>(output_stream<char>&&)> stream_range_as_array(Container val, Func fun) { |
1e59de90 | 322 | return [val = std::move(val), fun = std::move(fun)](output_stream<char>&& s) mutable { |
11fdf7f2 TL |
323 | return do_with(output_stream<char>(std::move(s)), Container(std::move(val)), Func(std::move(fun)), true, [](output_stream<char>& s, const Container& val, const Func& f, bool& first){ |
324 | return s.write("[").then([&val, &s, &first, &f] () { | |
325 | return do_for_each(val, [&s, &first, &f](const typename Container::value_type& v){ | |
326 | auto fut = first ? make_ready_future<>() : s.write(", "); | |
327 | first = false; | |
328 | return fut.then([&s, &f, &v]() { | |
329 | return formatter::write(s, f(v)); | |
330 | }); | |
331 | }); | |
332 | }).then([&s](){ | |
1e59de90 TL |
333 | return s.write("]"); |
334 | }).finally([&s] { | |
335 | return s.close(); | |
11fdf7f2 TL |
336 | }); |
337 | }); | |
338 | }; | |
339 | } | |
340 | ||
341 | /*! | |
342 | * \brief capture an object and return a serialize function for it. | |
343 | * | |
344 | * To use it: | |
345 | * return make_ready_future<json::json_return_type>(stream_object(res)); | |
346 | */ | |
347 | template<class T> | |
348 | std::function<future<>(output_stream<char>&&)> stream_object(T val) { | |
1e59de90 | 349 | return [val = std::move(val)](output_stream<char>&& s) mutable { |
11fdf7f2 | 350 | return do_with(output_stream<char>(std::move(s)), T(std::move(val)), [](output_stream<char>& s, const T& val){ |
1e59de90 | 351 | return formatter::write(s, val).finally([&s] { |
11fdf7f2 TL |
352 | return s.close(); |
353 | }); | |
354 | }); | |
355 | }; | |
356 | } | |
357 | ||
358 | } | |
359 | ||
360 | } |