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