CCF
Loading...
Searching...
No Matches
json.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4#include "ccf/crypto/base64.h"
6
7#define FMT_HEADER_ONLY
8#include <fmt/format.h>
9#include <sstream>
10
11namespace ccf
12{
16 template <typename T>
17 struct JsonField
18 {
19 using Target = T;
20 char const* name;
21 };
22
23 class JsonParseError : public std::invalid_argument
24 {
25 public:
26 std::vector<std::string> pointer_elements = {};
27
28 using std::invalid_argument::invalid_argument;
29
30 std::string pointer() const
31 {
32 return fmt::format(
33 "#/{}",
34 fmt::join(pointer_elements.crbegin(), pointer_elements.crend(), "/"));
35 }
36
37 std::string describe() const
38 {
39 return fmt::format("At {}: {}", pointer(), what());
40 }
41 };
42}
43
44namespace std
45{
46 template <typename T>
47 inline void to_json(nlohmann::json& j, const std::optional<T>& t)
48 {
49 if (t.has_value())
50 {
51 j = t.value();
52 }
53 }
54
55 template <typename T>
56 inline void from_json(const nlohmann::json& j, std::optional<T>& t)
57 {
58 if (!j.is_null())
59 {
60 t = j.get<T>();
61 }
62 }
63
64 template <typename T>
65 inline void to_json(nlohmann::json& j, const std::vector<T>& t)
66 {
67 if constexpr (std::is_same_v<T, uint8_t>)
68 {
70 }
71 else
72 {
73 j = nlohmann::json::array();
74 for (const auto& e : t)
75 {
76 j.push_back(e);
77 }
78 }
79 }
80
81 template <typename T>
82 inline void from_json(const nlohmann::json& j, std::vector<T>& t)
83 {
84 if constexpr (std::is_same_v<T, uint8_t>)
85 {
86 if (j.is_string())
87 {
88 try
89 {
90 t = ccf::crypto::raw_from_b64(j.get<std::string>());
91 return;
92 }
93 catch (const std::exception& e)
94 {
95 throw ccf::JsonParseError(fmt::format(
96 "Vector of bytes object \"{}\" is not valid base64", j.dump()));
97 }
98 }
99 }
100
101 // Fall-through. So we can convert _from_ [1,2,3] to
102 // std::vector<uint8_t>, but would prefer (and will produce in to_json) a
103 // base64 string
104
105 if (!j.is_array())
106 {
108 fmt::format("Vector object \"{}\" is not an array", j.dump()));
109 }
110
111 for (size_t i = 0; i < j.size(); ++i)
112 {
113 try
114 {
115 t.push_back(j.at(i).template get<T>());
116 }
117 catch (ccf::JsonParseError& jpe)
118 {
119 jpe.pointer_elements.push_back(std::to_string(i));
120 throw;
121 }
122 }
123 }
124}
125
126// FOREACH macro machinery for counting args
127
128// -Wpedantic flags token pasting of __VA_ARGS__
129#pragma clang diagnostic push
130#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
131
132#define __FOR_JSON_COUNT_NN( \
133 _0, \
134 _1, \
135 _2, \
136 _3, \
137 _4, \
138 _5, \
139 _6, \
140 _7, \
141 _8, \
142 _9, \
143 _10, \
144 _11, \
145 _12, \
146 _13, \
147 _14, \
148 _15, \
149 _16, \
150 _17, \
151 _18, \
152 _19, \
153 _20, \
154 _21, \
155 _22, \
156 _23, \
157 _24, \
158 _25, \
159 _26, \
160 _27, \
161 _28, \
162 _29, \
163 _30, \
164 N, \
165 ...) \
166 _FOR_JSON_##N
167#define _FOR_JSON_COUNT_NN_WITH_0(...) \
168 __FOR_JSON_COUNT_NN( \
169 __VA_ARGS__, \
170 30, \
171 29, \
172 28, \
173 27, \
174 26, \
175 25, \
176 24, \
177 23, \
178 22, \
179 21, \
180 20, \
181 19, \
182 18, \
183 17, \
184 16, \
185 15, \
186 14, \
187 13, \
188 12, \
189 11, \
190 10, \
191 9, \
192 8, \
193 7, \
194 6, \
195 5, \
196 4, \
197 3, \
198 2, \
199 1, \
200 0)
201#define _FOR_JSON_COUNT_NN(...) _FOR_JSON_COUNT_NN_WITH_0(DUMMY, ##__VA_ARGS__)
202
203#define _FOR_JSON_0(POP_N) _FOR_JSON_0_##POP_N
204#define _FOR_JSON_1(POP_N) _FOR_JSON_1_##POP_N
205#define _FOR_JSON_2(POP_N) _FOR_JSON_2_##POP_N
206#define _FOR_JSON_3(POP_N) _FOR_JSON_3_##POP_N
207#define _FOR_JSON_4(POP_N) _FOR_JSON_4_##POP_N
208#define _FOR_JSON_5(POP_N) _FOR_JSON_5_##POP_N
209#define _FOR_JSON_6(POP_N) _FOR_JSON_6_##POP_N
210#define _FOR_JSON_7(POP_N) _FOR_JSON_7_##POP_N
211#define _FOR_JSON_8(POP_N) _FOR_JSON_8_##POP_N
212#define _FOR_JSON_9(POP_N) _FOR_JSON_9_##POP_N
213#define _FOR_JSON_10(POP_N) _FOR_JSON_10_##POP_N
214#define _FOR_JSON_11(POP_N) _FOR_JSON_11_##POP_N
215#define _FOR_JSON_12(POP_N) _FOR_JSON_12_##POP_N
216#define _FOR_JSON_13(POP_N) _FOR_JSON_13_##POP_N
217#define _FOR_JSON_14(POP_N) _FOR_JSON_14_##POP_N
218#define _FOR_JSON_15(POP_N) _FOR_JSON_15_##POP_N
219#define _FOR_JSON_16(POP_N) _FOR_JSON_16_##POP_N
220#define _FOR_JSON_17(POP_N) _FOR_JSON_17_##POP_N
221#define _FOR_JSON_18(POP_N) _FOR_JSON_18_##POP_N
222#define _FOR_JSON_19(POP_N) _FOR_JSON_19_##POP_N
223#define _FOR_JSON_20(POP_N) _FOR_JSON_20_##POP_N
224#define _FOR_JSON_21(POP_N) _FOR_JSON_21_##POP_N
225#define _FOR_JSON_22(POP_N) _FOR_JSON_22_##POP_N
226#define _FOR_JSON_23(POP_N) _FOR_JSON_23_##POP_N
227#define _FOR_JSON_24(POP_N) _FOR_JSON_24_##POP_N
228#define _FOR_JSON_25(POP_N) _FOR_JSON_25_##POP_N
229#define _FOR_JSON_26(POP_N) _FOR_JSON_26_##POP_N
230#define _FOR_JSON_27(POP_N) _FOR_JSON_27_##POP_N
231#define _FOR_JSON_28(POP_N) _FOR_JSON_28_##POP_N
232#define _FOR_JSON_29(POP_N) _FOR_JSON_29_##POP_N
233#define _FOR_JSON_30(POP_N) _FOR_JSON_30_##POP_N
234
235// FOREACH macro machinery for forwarding to single arg macros
236#define _FOR_JSON_0_POP1(FUNC, TYPE)
237#define _FOR_JSON_1_POP1(FUNC, TYPE, ARG1) _FOR_JSON_FINAL(FUNC, TYPE, ARG1)
238#define _FOR_JSON_2_POP1(FUNC, TYPE, ARG1, ...) \
239 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
240 _FOR_JSON_1_POP1(FUNC, TYPE, ##__VA_ARGS__)
241#define _FOR_JSON_3_POP1(FUNC, TYPE, ARG1, ...) \
242 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
243 _FOR_JSON_2_POP1(FUNC, TYPE, ##__VA_ARGS__)
244#define _FOR_JSON_4_POP1(FUNC, TYPE, ARG1, ...) \
245 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
246 _FOR_JSON_3_POP1(FUNC, TYPE, ##__VA_ARGS__)
247#define _FOR_JSON_5_POP1(FUNC, TYPE, ARG1, ...) \
248 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
249 _FOR_JSON_4_POP1(FUNC, TYPE, ##__VA_ARGS__)
250#define _FOR_JSON_6_POP1(FUNC, TYPE, ARG1, ...) \
251 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
252 _FOR_JSON_5_POP1(FUNC, TYPE, ##__VA_ARGS__)
253#define _FOR_JSON_7_POP1(FUNC, TYPE, ARG1, ...) \
254 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
255 _FOR_JSON_6_POP1(FUNC, TYPE, ##__VA_ARGS__)
256#define _FOR_JSON_8_POP1(FUNC, TYPE, ARG1, ...) \
257 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
258 _FOR_JSON_7_POP1(FUNC, TYPE, ##__VA_ARGS__)
259#define _FOR_JSON_9_POP1(FUNC, TYPE, ARG1, ...) \
260 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
261 _FOR_JSON_8_POP1(FUNC, TYPE, ##__VA_ARGS__)
262#define _FOR_JSON_10_POP1(FUNC, TYPE, ARG1, ...) \
263 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
264 _FOR_JSON_9_POP1(FUNC, TYPE, ##__VA_ARGS__)
265#define _FOR_JSON_11_POP1(FUNC, TYPE, ARG1, ...) \
266 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
267 _FOR_JSON_10_POP1(FUNC, TYPE, ##__VA_ARGS__)
268#define _FOR_JSON_12_POP1(FUNC, TYPE, ARG1, ...) \
269 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
270 _FOR_JSON_11_POP1(FUNC, TYPE, ##__VA_ARGS__)
271#define _FOR_JSON_13_POP1(FUNC, TYPE, ARG1, ...) \
272 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
273 _FOR_JSON_12_POP1(FUNC, TYPE, ##__VA_ARGS__)
274#define _FOR_JSON_14_POP1(FUNC, TYPE, ARG1, ...) \
275 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
276 _FOR_JSON_13_POP1(FUNC, TYPE, ##__VA_ARGS__)
277#define _FOR_JSON_15_POP1(FUNC, TYPE, ARG1, ...) \
278 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
279 _FOR_JSON_14_POP1(FUNC, TYPE, ##__VA_ARGS__)
280#define _FOR_JSON_16_POP1(FUNC, TYPE, ARG1, ...) \
281 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
282 _FOR_JSON_15_POP1(FUNC, TYPE, ##__VA_ARGS__)
283#define _FOR_JSON_17_POP1(FUNC, TYPE, ARG1, ...) \
284 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
285 _FOR_JSON_16_POP1(FUNC, TYPE, ##__VA_ARGS__)
286#define _FOR_JSON_18_POP1(FUNC, TYPE, ARG1, ...) \
287 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
288 _FOR_JSON_17_POP1(FUNC, TYPE, ##__VA_ARGS__)
289#define _FOR_JSON_19_POP1(FUNC, TYPE, ARG1, ...) \
290 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
291 _FOR_JSON_18_POP1(FUNC, TYPE, ##__VA_ARGS__)
292#define _FOR_JSON_20_POP1(FUNC, TYPE, ARG1, ...) \
293 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
294 _FOR_JSON_19_POP1(FUNC, TYPE, ##__VA_ARGS__)
295#define _FOR_JSON_21_POP1(FUNC, TYPE, ARG1, ...) \
296 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
297 _FOR_JSON_20_POP1(FUNC, TYPE, ##__VA_ARGS__)
298#define _FOR_JSON_22_POP1(FUNC, TYPE, ARG1, ...) \
299 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
300 _FOR_JSON_21_POP1(FUNC, TYPE, ##__VA_ARGS__)
301#define _FOR_JSON_23_POP1(FUNC, TYPE, ARG1, ...) \
302 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
303 _FOR_JSON_22_POP1(FUNC, TYPE, ##__VA_ARGS__)
304#define _FOR_JSON_24_POP1(FUNC, TYPE, ARG1, ...) \
305 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
306 _FOR_JSON_23_POP1(FUNC, TYPE, ##__VA_ARGS__)
307#define _FOR_JSON_25_POP1(FUNC, TYPE, ARG1, ...) \
308 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
309 _FOR_JSON_24_POP1(FUNC, TYPE, ##__VA_ARGS__)
310#define _FOR_JSON_26_POP1(FUNC, TYPE, ARG1, ...) \
311 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
312 _FOR_JSON_25_POP1(FUNC, TYPE, ##__VA_ARGS__)
313#define _FOR_JSON_27_POP1(FUNC, TYPE, ARG1, ...) \
314 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
315 _FOR_JSON_26_POP1(FUNC, TYPE, ##__VA_ARGS__)
316#define _FOR_JSON_28_POP1(FUNC, TYPE, ARG1, ...) \
317 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
318 _FOR_JSON_27_POP1(FUNC, TYPE, ##__VA_ARGS__)
319#define _FOR_JSON_29_POP1(FUNC, TYPE, ARG1, ...) \
320 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
321 _FOR_JSON_28_POP1(FUNC, TYPE, ##__VA_ARGS__)
322#define _FOR_JSON_30_POP1(FUNC, TYPE, ARG1, ...) \
323 _FOR_JSON_NEXT(FUNC, TYPE, ARG1) \
324 _FOR_JSON_29_POP1(FUNC, TYPE, ##__VA_ARGS__)
325
326// FOREACH macro machinery for forwarding to double arg macros
327#define _FOR_JSON_0_POP2(FUNC, TYPE)
328#define _FOR_JSON_1_POP2(FUNC, TYPE, ARG1) INVALID_ODD_ARGS
329#define _FOR_JSON_2_POP2(FUNC, TYPE, ARG1, ARG2) \
330 _FOR_JSON_FINAL(FUNC, TYPE, ARG1, ARG2)
331#define _FOR_JSON_3_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
332#define _FOR_JSON_4_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
333 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
334 _FOR_JSON_2_POP2(FUNC, TYPE, ##__VA_ARGS__)
335#define _FOR_JSON_5_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
336#define _FOR_JSON_6_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
337 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
338 _FOR_JSON_4_POP2(FUNC, TYPE, ##__VA_ARGS__)
339#define _FOR_JSON_7_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
340#define _FOR_JSON_8_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
341 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
342 _FOR_JSON_6_POP2(FUNC, TYPE, ##__VA_ARGS__)
343#define _FOR_JSON_9_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
344#define _FOR_JSON_10_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
345 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
346 _FOR_JSON_8_POP2(FUNC, TYPE, ##__VA_ARGS__)
347#define _FOR_JSON_11_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
348#define _FOR_JSON_12_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
349 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
350 _FOR_JSON_10_POP2(FUNC, TYPE, ##__VA_ARGS__)
351#define _FOR_JSON_13_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
352#define _FOR_JSON_14_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
353 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
354 _FOR_JSON_12_POP2(FUNC, TYPE, ##__VA_ARGS__)
355#define _FOR_JSON_15_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
356#define _FOR_JSON_16_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
357 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
358 _FOR_JSON_14_POP2(FUNC, TYPE, ##__VA_ARGS__)
359#define _FOR_JSON_17_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
360#define _FOR_JSON_18_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
361 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
362 _FOR_JSON_16_POP2(FUNC, TYPE, ##__VA_ARGS__)
363#define _FOR_JSON_19_POP2(FUNC, TYPE, ARG1, ARG2, ...) INVALID_ODD_ARGS
364#define _FOR_JSON_20_POP2(FUNC, TYPE, ARG1, ARG2, ...) \
365 _FOR_JSON_NEXT(FUNC, TYPE, ARG1, ARG2) \
366 _FOR_JSON_18_POP2(FUNC, TYPE, ##__VA_ARGS__)
367
368// Forwarders for macros produced by the machinery above
369#define _FOR_JSON_NEXT(FUNC, ...) FUNC##_FOR_JSON_NEXT(__VA_ARGS__)
370#define _FOR_JSON_FINAL(FUNC, ...) FUNC##_FOR_JSON_FINAL(__VA_ARGS__)
371
372#define WRITE_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD) \
373 { \
374 j[JSON_FIELD] = t.C_FIELD; \
375 }
376#define WRITE_REQUIRED_WITH_RENAMES_FOR_JSON_FINAL(TYPE, C_FIELD, JSON_FIELD) \
377 WRITE_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD)
378
379#define WRITE_REQUIRED_FOR_JSON_NEXT(TYPE, FIELD) \
380 WRITE_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, FIELD, #FIELD)
381#define WRITE_REQUIRED_FOR_JSON_FINAL(TYPE, FIELD) \
382 WRITE_REQUIRED_WITH_RENAMES_FOR_JSON_FINAL(TYPE, FIELD, #FIELD)
383
384#define WRITE_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD) \
385 { \
386 if (t.C_FIELD != t_default.C_FIELD) \
387 { \
388 j[JSON_FIELD] = t.C_FIELD; \
389 } \
390 }
391#define WRITE_OPTIONAL_WITH_RENAMES_FOR_JSON_FINAL(TYPE, C_FIELD, JSON_FIELD) \
392 WRITE_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD)
393
394#define WRITE_OPTIONAL_FOR_JSON_NEXT(TYPE, FIELD) \
395 WRITE_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, FIELD, #FIELD)
396#define WRITE_OPTIONAL_FOR_JSON_FINAL(TYPE, FIELD) \
397 WRITE_OPTIONAL_WITH_RENAMES_FOR_JSON_FINAL(TYPE, FIELD, #FIELD)
398
399#define READ_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD) \
400 { \
401 const auto it = j.find(JSON_FIELD); \
402 if (it == j.end()) \
403 { \
404 throw ccf::JsonParseError( \
405 "Missing required field '" JSON_FIELD "' in object: " + j.dump()); \
406 } \
407 try \
408 { \
409 t.C_FIELD = it->get<decltype(TYPE::C_FIELD)>(); \
410 } \
411 catch (ccf::JsonParseError & jpe) \
412 { \
413 jpe.pointer_elements.push_back(JSON_FIELD); \
414 throw; \
415 } \
416 }
417#define READ_REQUIRED_WITH_RENAMES_FOR_JSON_FINAL(TYPE, C_FIELD, JSON_FIELD) \
418 READ_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD)
419
420#define READ_REQUIRED_FOR_JSON_NEXT(TYPE, FIELD) \
421 READ_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, FIELD, #FIELD)
422#define READ_REQUIRED_FOR_JSON_FINAL(TYPE, FIELD) \
423 READ_REQUIRED_WITH_RENAMES_FOR_JSON_FINAL(TYPE, FIELD, #FIELD)
424
425#define READ_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD) \
426 { \
427 const auto it = j.find(JSON_FIELD); \
428 if (it != j.end()) \
429 { \
430 t.C_FIELD = it->get<decltype(TYPE::C_FIELD)>(); \
431 } \
432 }
433#define READ_OPTIONAL_WITH_RENAMES_FOR_JSON_FINAL(TYPE, C_FIELD, JSON_FIELD) \
434 READ_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD)
435
436#define READ_OPTIONAL_FOR_JSON_NEXT(TYPE, FIELD) \
437 READ_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, FIELD, #FIELD)
438#define READ_OPTIONAL_FOR_JSON_FINAL(TYPE, FIELD) \
439 READ_OPTIONAL_WITH_RENAMES_FOR_JSON_FINAL(TYPE, FIELD, #FIELD)
440
441#define FILL_SCHEMA_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT( \
442 TYPE, C_FIELD, JSON_FIELD) \
443 j["properties"][JSON_FIELD] = \
444 ccf::ds::json::schema_element<decltype(TYPE::C_FIELD)>(); \
445 j["required"].push_back(JSON_FIELD);
446#define FILL_SCHEMA_REQUIRED_WITH_RENAMES_FOR_JSON_FINAL( \
447 TYPE, C_FIELD, JSON_FIELD) \
448 FILL_SCHEMA_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD)
449
450#define FILL_SCHEMA_REQUIRED_FOR_JSON_NEXT(TYPE, FIELD) \
451 FILL_SCHEMA_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, FIELD, #FIELD)
452#define FILL_SCHEMA_REQUIRED_FOR_JSON_FINAL(TYPE, FIELD) \
453 FILL_SCHEMA_REQUIRED_WITH_RENAMES_FOR_JSON_FINAL(TYPE, FIELD, #FIELD)
454
455#define FILL_SCHEMA_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT( \
456 TYPE, C_FIELD, JSON_FIELD) \
457 j["properties"][JSON_FIELD] = \
458 ccf::ds::json::schema_element<decltype(TYPE::C_FIELD)>();
459#define FILL_SCHEMA_OPTIONAL_WITH_RENAMES_FOR_JSON_FINAL( \
460 TYPE, C_FIELD, JSON_FIELD) \
461 FILL_SCHEMA_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, C_FIELD, JSON_FIELD)
462
463#define FILL_SCHEMA_OPTIONAL_FOR_JSON_NEXT(TYPE, FIELD) \
464 FILL_SCHEMA_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, FIELD, #FIELD)
465#define FILL_SCHEMA_OPTIONAL_FOR_JSON_FINAL(TYPE, FIELD) \
466 FILL_SCHEMA_OPTIONAL_WITH_RENAMES_FOR_JSON_FINAL(TYPE, FIELD, #FIELD)
467
468#define ADD_SCHEMA_COMPONENTS_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT( \
469 TYPE, C_FIELD, JSON_FIELD) \
470 j["properties"][JSON_FIELD] = \
471 doc.template add_schema_component<decltype(TYPE::C_FIELD)>(); \
472 j["required"].push_back(JSON_FIELD);
473#define ADD_SCHEMA_COMPONENTS_REQUIRED_WITH_RENAMES_FOR_JSON_FINAL( \
474 TYPE, C_FIELD, JSON_FIELD) \
475 ADD_SCHEMA_COMPONENTS_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT( \
476 TYPE, C_FIELD, JSON_FIELD)
477
478#define ADD_SCHEMA_COMPONENTS_REQUIRED_FOR_JSON_NEXT(TYPE, FIELD) \
479 ADD_SCHEMA_COMPONENTS_REQUIRED_WITH_RENAMES_FOR_JSON_NEXT(TYPE, FIELD, #FIELD)
480#define ADD_SCHEMA_COMPONENTS_REQUIRED_FOR_JSON_FINAL(TYPE, FIELD) \
481 ADD_SCHEMA_COMPONENTS_REQUIRED_WITH_RENAMES_FOR_JSON_FINAL( \
482 TYPE, FIELD, #FIELD)
483
484#define ADD_SCHEMA_COMPONENTS_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT( \
485 TYPE, C_FIELD, JSON_FIELD) \
486 j["properties"][JSON_FIELD] = \
487 doc.template add_schema_component<decltype(TYPE::C_FIELD)>();
488#define ADD_SCHEMA_COMPONENTS_OPTIONAL_WITH_RENAMES_FOR_JSON_FINAL( \
489 TYPE, C_FIELD, JSON_FIELD) \
490 ADD_SCHEMA_COMPONENTS_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT( \
491 TYPE, C_FIELD, JSON_FIELD)
492
493#define ADD_SCHEMA_COMPONENTS_OPTIONAL_FOR_JSON_NEXT(TYPE, FIELD) \
494 ADD_SCHEMA_COMPONENTS_OPTIONAL_WITH_RENAMES_FOR_JSON_NEXT(TYPE, FIELD, #FIELD)
495#define ADD_SCHEMA_COMPONENTS_OPTIONAL_FOR_JSON_FINAL(TYPE, FIELD) \
496 ADD_SCHEMA_COMPONENTS_OPTIONAL_WITH_RENAMES_FOR_JSON_FINAL( \
497 TYPE, FIELD, #FIELD)
498
499#define JSON_FIELD_FOR_JSON_NEXT(TYPE, FIELD) \
500 ccf::JsonField<decltype(TYPE::FIELD)>{#FIELD},
501#define JSON_FIELD_FOR_JSON_FINAL(TYPE, FIELD) \
502 ccf::JsonField<decltype(TYPE::FIELD)> \
503 { \
504# FIELD \
505 }
506
609#define DECLARE_JSON_TYPE_IMPL( \
610 TYPE, \
611 PRE_TO_JSON, \
612 POST_TO_JSON, \
613 PRE_FROM_JSON, \
614 POST_FROM_JSON, \
615 PRE_FILL_SCHEMA, \
616 POST_FILL_SCHEMA, \
617 PRE_ADD_SCHEMA, \
618 POST_ADD_SCHEMA) \
619 void to_json_required_fields(nlohmann::json& j, const TYPE& t); \
620 void to_json_optional_fields(nlohmann::json& j, const TYPE& t); \
621 void from_json_required_fields(const nlohmann::json& j, TYPE& t); \
622 void from_json_optional_fields(const nlohmann::json& j, TYPE& t); \
623 void fill_json_schema_required_fields(nlohmann::json& j, const TYPE*); \
624 void fill_json_schema_optional_fields(nlohmann::json& j, const TYPE*); \
625 template <typename T> \
626 void add_schema_components_required_fields( \
627 T& doc, nlohmann::json& j, const TYPE*); \
628 template <typename T> \
629 void add_schema_components_optional_fields( \
630 T& doc, nlohmann::json& j, const TYPE*); \
631 inline void to_json(nlohmann::json& j, const TYPE& t) \
632 { \
633 PRE_TO_JSON; \
634 to_json_required_fields(j, t); \
635 POST_TO_JSON; \
636 } \
637 inline void from_json(const nlohmann::json& j, TYPE& t) \
638 { \
639 PRE_FROM_JSON; \
640 from_json_required_fields(j, t); \
641 POST_FROM_JSON; \
642 } \
643 inline void fill_json_schema(nlohmann::json& j, const TYPE* t) \
644 { \
645 PRE_FILL_SCHEMA; \
646 fill_json_schema_required_fields(j, t); \
647 POST_FILL_SCHEMA; \
648 } \
649 inline std::string schema_name(const TYPE*) \
650 { \
651 return #TYPE; \
652 } \
653 template <typename T> \
654 void add_schema_components(T& doc, nlohmann::json& j, const TYPE* t) \
655 { \
656 PRE_ADD_SCHEMA; \
657 add_schema_components_required_fields(doc, j, t); \
658 POST_ADD_SCHEMA; \
659 }
660
661#define DECLARE_JSON_TYPE(TYPE) DECLARE_JSON_TYPE_IMPL(TYPE, , , , , , , , )
662
663#define DECLARE_JSON_TYPE_WITH_BASE(TYPE, BASE) \
664 DECLARE_JSON_TYPE_IMPL( \
665 TYPE, \
666 to_json(j, static_cast<const BASE&>(t)), \
667 , \
668 from_json(j, static_cast<BASE&>(t)), \
669 , \
670 fill_json_schema(j, static_cast<const BASE*>(t)), \
671 , \
672 add_schema_components(doc, j, static_cast<const BASE*>(t)), )
673
674#define DECLARE_JSON_TYPE_WITH_2BASES(TYPE, BASE1, BASE2) \
675 DECLARE_JSON_TYPE_IMPL( \
676 TYPE, to_json(j, static_cast<const BASE1&>(t)); \
677 to_json(j, static_cast<const BASE2&>(t)), \
678 , \
679 from_json(j, static_cast<BASE1&>(t)); \
680 from_json(j, static_cast<BASE2&>(t)), \
681 , \
682 fill_json_schema(j, static_cast<const BASE1*>(t)); \
683 fill_json_schema(j, static_cast<const BASE2*>(t)), \
684 , \
685 add_schema_components(doc, j, static_cast<const BASE1*>(t)); \
686 add_schema_components(doc, j, static_cast<const BASE2*>(t)), )
687
688#define DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(TYPE) \
689 DECLARE_JSON_TYPE_IMPL( \
690 TYPE, \
691 , \
692 to_json_optional_fields(j, t), \
693 , \
694 from_json_optional_fields(j, t), \
695 , \
696 fill_json_schema_optional_fields(j, t), \
697 , \
698 add_schema_components_optional_fields(doc, j, t))
699
700#define DECLARE_JSON_TYPE_WITH_BASE_AND_OPTIONAL_FIELDS(TYPE, BASE) \
701 DECLARE_JSON_TYPE_IMPL( \
702 TYPE, \
703 to_json(j, static_cast<const BASE&>(t)), \
704 to_json_optional_fields(j, t), \
705 from_json(j, static_cast<BASE&>(t)), \
706 from_json_optional_fields(j, t), \
707 fill_json_schema(j, static_cast<const BASE*>(t)), \
708 fill_json_schema_optional_fields(j, t), \
709 add_schema_components(doc, j, static_cast<const BASE*>(t)), \
710 add_schema_components_optional_fields(doc, j, t))
711
712#define DECLARE_JSON_REQUIRED_FIELDS(TYPE, ...) \
713 _Pragma("clang diagnostic push"); \
714 _Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\""); \
715 inline void to_json_required_fields( \
716 nlohmann::json& j, [[maybe_unused]] const TYPE& t) \
717 { \
718 if (!j.is_object()) \
719 { \
720 j = nlohmann::json::object(); \
721 } \
722 _FOR_JSON_COUNT_NN(__VA_ARGS__)(POP1)(WRITE_REQUIRED, TYPE, ##__VA_ARGS__) \
723 } \
724 inline void from_json_required_fields( \
725 const nlohmann::json& j, [[maybe_unused]] TYPE& t) \
726 { \
727 if (!j.is_object()) \
728 { \
729 throw ccf::JsonParseError("Expected object, found: " + j.dump()); \
730 } \
731 _FOR_JSON_COUNT_NN(__VA_ARGS__)(POP1)(READ_REQUIRED, TYPE, ##__VA_ARGS__) \
732 } \
733 inline void fill_json_schema_required_fields( \
734 nlohmann::json& j, [[maybe_unused]] const TYPE*) \
735 { \
736 j["type"] = "object"; \
737 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
738 (POP1)(FILL_SCHEMA_REQUIRED, TYPE, ##__VA_ARGS__) \
739 } \
740 template <typename T> \
741 void add_schema_components_required_fields( \
742 [[maybe_unused]] T& doc, nlohmann::json& j, [[maybe_unused]] const TYPE*) \
743 { \
744 j["type"] = "object"; \
745 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
746 (POP1)(ADD_SCHEMA_COMPONENTS_REQUIRED, TYPE, ##__VA_ARGS__); \
747 } \
748 _Pragma("clang diagnostic pop");
749
750#define DECLARE_JSON_REQUIRED_FIELDS_WITH_RENAMES(TYPE, ...) \
751 inline void to_json_required_fields(nlohmann::json& j, const TYPE& t) \
752 { \
753 if (!j.is_object()) \
754 { \
755 j = nlohmann::json::object(); \
756 } \
757 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
758 (POP2)(WRITE_REQUIRED_WITH_RENAMES, TYPE, ##__VA_ARGS__) \
759 } \
760 inline void from_json_required_fields(const nlohmann::json& j, TYPE& t) \
761 { \
762 if (!j.is_object()) \
763 { \
764 throw ccf::JsonParseError("Expected object, found: " + j.dump()); \
765 } \
766 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
767 (POP2)(READ_REQUIRED_WITH_RENAMES, TYPE, ##__VA_ARGS__) \
768 } \
769 inline void fill_json_schema_required_fields(nlohmann::json& j, const TYPE*) \
770 { \
771 j["type"] = "object"; \
772 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
773 (POP2)(FILL_SCHEMA_REQUIRED_WITH_RENAMES, TYPE, ##__VA_ARGS__) \
774 } \
775 template <typename T> \
776 void add_schema_components_required_fields( \
777 T& doc, nlohmann::json& j, const TYPE*) \
778 { \
779 j["type"] = "object"; \
780 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
781 (POP2)(ADD_SCHEMA_COMPONENTS_REQUIRED_WITH_RENAMES, TYPE, ##__VA_ARGS__); \
782 }
783
784#define DECLARE_JSON_OPTIONAL_FIELDS(TYPE, ...) \
785 inline void to_json_optional_fields(nlohmann::json& j, const TYPE& t) \
786 { \
787 const TYPE t_default{}; \
788 _FOR_JSON_COUNT_NN(__VA_ARGS__)(POP1)(WRITE_OPTIONAL, TYPE, ##__VA_ARGS__) \
789 } \
790 inline void from_json_optional_fields(const nlohmann::json& j, TYPE& t) \
791 { \
792 _FOR_JSON_COUNT_NN(__VA_ARGS__)(POP1)(READ_OPTIONAL, TYPE, ##__VA_ARGS__) \
793 } \
794 inline void fill_json_schema_optional_fields(nlohmann::json& j, const TYPE*) \
795 { \
796 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
797 (POP1)(FILL_SCHEMA_OPTIONAL, TYPE, ##__VA_ARGS__) \
798 } \
799 template <typename T> \
800 void add_schema_components_optional_fields( \
801 T& doc, nlohmann::json& j, const TYPE*) \
802 { \
803 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
804 (POP1)(ADD_SCHEMA_COMPONENTS_OPTIONAL, TYPE, ##__VA_ARGS__); \
805 }
806
807#define DECLARE_JSON_OPTIONAL_FIELDS_WITH_RENAMES(TYPE, ...) \
808 inline void to_json_optional_fields(nlohmann::json& j, const TYPE& t) \
809 { \
810 const TYPE t_default{}; \
811 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
812 (POP2)(WRITE_OPTIONAL_WITH_RENAMES, TYPE, ##__VA_ARGS__) \
813 } \
814 inline void from_json_optional_fields(const nlohmann::json& j, TYPE& t) \
815 { \
816 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
817 (POP2)(READ_OPTIONAL_WITH_RENAMES, TYPE, ##__VA_ARGS__) \
818 } \
819 inline void fill_json_schema_optional_fields( \
820 nlohmann::json& j, [[maybe_unused]] const TYPE*) \
821 { \
822 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
823 (POP2)(FILL_SCHEMA_OPTIONAL_WITH_RENAMES, TYPE, ##__VA_ARGS__) \
824 } \
825 template <typename T> \
826 void add_schema_components_optional_fields( \
827 T& doc, nlohmann::json& j, [[maybe_unused]] const TYPE*) \
828 { \
829 _FOR_JSON_COUNT_NN(__VA_ARGS__) \
830 (POP2)(ADD_SCHEMA_COMPONENTS_OPTIONAL_WITH_RENAMES, TYPE, ##__VA_ARGS__); \
831 }
832
833// Enum conversion, based on NLOHMANN_JSON_SERIALIZE_ENUM, but less permissive
834// (throws on unknown JSON values)
835#define DECLARE_JSON_ENUM(TYPE, ...) \
836 template <typename BasicJsonType> \
837 inline void to_json(BasicJsonType& j, const TYPE& e) \
838 { \
839 static_assert(std::is_enum<TYPE>::value, #TYPE " must be an enum!"); \
840 static const std::pair<TYPE, BasicJsonType> m[] = __VA_ARGS__; \
841 auto it = std::find_if( \
842 std::begin(m), \
843 std::end(m), \
844 [e](const std::pair<TYPE, BasicJsonType>& ej_pair) -> bool { \
845 return ej_pair.first == e; \
846 }); \
847 if (it == std::end(m)) \
848 { \
849 throw ccf::JsonParseError(fmt::format( \
850 "Value {} in enum " #TYPE " has no specified JSON conversion", \
851 (size_t)e)); \
852 } \
853 j = it->second; \
854 } \
855 template <typename BasicJsonType> \
856 inline void from_json(const BasicJsonType& j, TYPE& e) \
857 { \
858 static_assert(std::is_enum<TYPE>::value, #TYPE " must be an enum!"); \
859 static const std::pair<TYPE, BasicJsonType> m[] = __VA_ARGS__; \
860 auto it = std::find_if( \
861 std::begin(m), \
862 std::end(m), \
863 [&j](const std::pair<TYPE, BasicJsonType>& ej_pair) -> bool { \
864 return ej_pair.second == j; \
865 }); \
866 if (it == std::end(m)) \
867 { \
868 throw ccf::JsonParseError( \
869 fmt::format("{} is not convertible to " #TYPE, j.dump())); \
870 } \
871 e = it->first; \
872 } \
873 inline std::string schema_name(const TYPE*) \
874 { \
875 return #TYPE; \
876 } \
877 inline void fill_enum_schema(nlohmann::json& j, const TYPE*) \
878 { \
879 static const std::pair<TYPE, nlohmann::json> m[] = __VA_ARGS__; \
880 auto enums = nlohmann::json::array(); \
881 for (const auto& p : m) \
882 { \
883 enums.push_back(p.second); \
884 } \
885 j["enum"] = enums; \
886 j["type"] = "string"; \
887 }
888
889#pragma clang diagnostic pop
Definition json.h:24
std::string describe() const
Definition json.h:37
std::vector< std::string > pointer_elements
Definition json.h:26
std::string pointer() const
Definition json.h:30
std::vector< uint8_t > raw_from_b64(const std::string_view &b64_string)
Definition base64.cpp:12
std::string b64_from_raw(const uint8_t *data, size_t size)
Definition base64.cpp:39
Definition app_interface.h:15
STL namespace.
void to_json(nlohmann::json &j, const std::optional< T > &t)
Definition json.h:47
void from_json(const nlohmann::json &j, std::optional< T > &t)
Definition json.h:56
Definition json.h:18
char const * name
Definition json.h:20
T Target
Definition json.h:19