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