1 /**
2 * Copyright (c) 2018, Timothy Stack
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * * Neither the name of Timothy Stack nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @file yajlpp_def.hh
30 */
31
32 #ifndef yajlpp_def_hh
33 #define yajlpp_def_hh
34
35 #include <chrono>
36
37 #include "yajlpp.hh"
38 #include "relative_time.hh"
39
40 #define FOR_FIELD(T, FIELD) \
41 for_field<T, decltype(T :: FIELD), & T :: FIELD>()
42
assign(intern_string_t & lhs,const string_fragment & rhs)43 inline intern_string_t &assign(intern_string_t &lhs, const string_fragment &rhs) {
44 lhs = intern_string::lookup(rhs.data(), rhs.length());
45
46 return lhs;
47 }
48
assign(std::string & lhs,const string_fragment & rhs)49 inline std::string &assign(std::string &lhs, const string_fragment &rhs) {
50 lhs.assign(rhs.data(), rhs.length());
51
52 return lhs;
53 }
54
55 template<template<typename ...> class Container>
assign(Container<std::string> & lhs,const string_fragment & rhs)56 inline Container<std::string> &assign(Container<std::string> &lhs, const string_fragment &rhs) {
57 lhs.emplace_back(rhs.data(), rhs.length());
58
59 return lhs;
60 }
61
62 struct json_path_container;
63
64 struct json_path_handler : public json_path_handler_base {
65 template<typename P>
json_path_handlerjson_path_handler66 json_path_handler(P path, int(*null_func)(yajlpp_parse_context *))
67 : json_path_handler_base(path)
68 {
69 this->jph_callbacks.yajl_null = (int (*)(void *))null_func;
70 };
71
72 template<typename P>
json_path_handlerjson_path_handler73 json_path_handler(P path, int(*bool_func)(yajlpp_parse_context *, int))
74 : json_path_handler_base(path)
75 {
76 this->jph_callbacks.yajl_boolean = (int (*)(void *, int))bool_func;
77 }
78
79 template<typename P>
json_path_handlerjson_path_handler80 json_path_handler(P path, int(*int_func)(yajlpp_parse_context *, long long))
81 : json_path_handler_base(path)
82 {
83 this->jph_callbacks.yajl_integer = (int (*)(void *, long long))int_func;
84 }
85
86 template<typename P>
json_path_handlerjson_path_handler87 json_path_handler(P path, int(*double_func)(yajlpp_parse_context *, double))
88 : json_path_handler_base(path)
89 {
90 this->jph_callbacks.yajl_double = (int (*)(void *, double))double_func;
91 }
92
93 template<typename P>
json_path_handlerjson_path_handler94 json_path_handler(P path,
95 int(*str_func)(yajlpp_parse_context *, const unsigned char *, size_t))
96 : json_path_handler_base(path)
97 {
98 this->jph_callbacks.yajl_string = (int (*)(void *, const unsigned char *, size_t))str_func;
99 }
100
101 template<typename P>
json_path_handlerjson_path_handler102 json_path_handler(P path) : json_path_handler_base(path) { };
103
json_path_handlerjson_path_handler104 json_path_handler(const std::string &path, const pcrepp &re)
105 : json_path_handler_base(path, re) {
106 };
107
add_cbjson_path_handler108 json_path_handler &add_cb(int(*null_func)(yajlpp_parse_context *)) {
109 this->jph_callbacks.yajl_null = (int (*)(void *))null_func;
110 return *this;
111 };
112
add_cbjson_path_handler113 json_path_handler &add_cb(int(*bool_func)(yajlpp_parse_context *, int))
114 {
115 this->jph_callbacks.yajl_boolean = (int (*)(void *, int))bool_func;
116 return *this;
117 }
118
add_cbjson_path_handler119 json_path_handler &add_cb(int(*int_func)(yajlpp_parse_context *, long long))
120 {
121 this->jph_callbacks.yajl_integer = (int (*)(void *, long long))int_func;
122 return *this;
123 }
124
add_cbjson_path_handler125 json_path_handler &add_cb(int(*double_func)(yajlpp_parse_context *, double))
126 {
127 this->jph_callbacks.yajl_double = (int (*)(void *, double))double_func;
128 return *this;
129 }
130
131
add_cbjson_path_handler132 json_path_handler &add_cb(int(*str_func)(yajlpp_parse_context *, const unsigned char *, size_t))
133 {
134 this->jph_callbacks.yajl_string = (int (*)(void *, const unsigned char *, size_t))str_func;
135 return *this;
136 }
137
with_synopsisjson_path_handler138 json_path_handler &with_synopsis(const char *synopsis) {
139 this->jph_synopsis = synopsis;
140 return *this;
141 }
142
with_descriptionjson_path_handler143 json_path_handler &with_description(const char *description) {
144 this->jph_description = description;
145 return *this;
146 }
147
with_min_lengthjson_path_handler148 json_path_handler &with_min_length(size_t len) {
149 this->jph_min_length = len;
150 return *this;
151 }
152
with_max_lengthjson_path_handler153 json_path_handler &with_max_length(size_t len) {
154 this->jph_max_length = len;
155 return *this;
156 }
157
with_enum_valuesjson_path_handler158 json_path_handler &with_enum_values(const enum_value_t values[]) {
159 this->jph_enum_values = values;
160 return *this;
161 }
162
with_patternjson_path_handler163 json_path_handler &with_pattern(const char *re) {
164 this->jph_pattern_re = re;
165 this->jph_pattern = std::make_shared<pcrepp>(re);
166 return *this;
167 };
168
with_string_validatorjson_path_handler169 json_path_handler &with_string_validator(std::function<void(const string_fragment &)> val) {
170 this->jph_string_validator = val;
171 return *this;
172 };
173
with_min_valuejson_path_handler174 json_path_handler &with_min_value(long long val) {
175 this->jph_min_value = val;
176 return *this;
177 }
178
179 template<typename R, typename T>
with_obj_providerjson_path_handler180 json_path_handler &with_obj_provider(R *(*provider)(const yajlpp_provider_context &pc, T *root)) {
181 this->jph_obj_provider = [provider](const yajlpp_provider_context &ypc, void *root) {
182 return (R *) provider(ypc, (T *) root);
183 };
184 return *this;
185 };
186
187 template<typename T>
with_path_providerjson_path_handler188 json_path_handler &with_path_provider(void (*provider)(T *root, std::vector<std::string> &paths_out)) {
189 this->jph_path_provider = [provider](void *root, std::vector<std::string> &paths_out) {
190 provider((T *) root, paths_out);
191 };
192 return *this;
193 }
194
195 template<typename T, typename MEM_T, MEM_T T::*MEM>
get_field_lvalue_cbjson_path_handler196 static void *get_field_lvalue_cb(void *root, nonstd::optional<std::string> name) {
197 auto obj = (T *) root;
198 auto &mem = obj->*MEM;
199
200 return &mem;
201 };
202
203 template<typename T, typename STR_T, STR_T T::*STR>
string_field_cbjson_path_handler204 static int string_field_cb(yajlpp_parse_context *ypc, const unsigned char *str, size_t len) {
205 auto handler = ypc->ypc_current_handler;
206
207 if (ypc->ypc_locations) {
208 intern_string_t src = intern_string::lookup(ypc->ypc_source);
209
210 (*ypc->ypc_locations)[ypc->get_full_path()] = {
211 src, ypc->get_line_number() };
212 }
213
214 assign(ypc->get_lvalue(ypc->get_obj_member<T, STR_T, STR>()), string_fragment(str, 0, len));
215 handler->jph_validator(*ypc, *handler);
216
217 return 1;
218 };
219
220 template<typename T, typename ENUM_T, ENUM_T T::*ENUM>
enum_field_cbjson_path_handler221 static int enum_field_cb(yajlpp_parse_context *ypc, const unsigned char *str, size_t len) {
222 auto obj = (T *) ypc->ypc_obj_stack.top();
223 auto handler = ypc->ypc_current_handler;
224 auto res = handler->to_enum_value(string_fragment(str, 0, len));
225
226 if (res) {
227 obj->*ENUM = (ENUM_T) res.value();
228 } else {
229 ypc->report_error(lnav_log_level_t::ERROR,
230 "error:%s:line %d\n "
231 "Invalid value, '%.*s', for option:",
232 ypc->ypc_source.c_str(),
233 ypc->get_line_number(),
234 len,
235 str);
236
237 ypc->report_error(lnav_log_level_t::ERROR,
238 " %s %s -- %s\n",
239 &ypc->ypc_path[0],
240 handler->jph_synopsis,
241 handler->jph_description);
242 ypc->report_error(lnav_log_level_t::ERROR,
243 " Allowed values: ");
244 for (int lpc = 0; handler->jph_enum_values[lpc].first; lpc++) {
245 const json_path_handler::enum_value_t &ev = handler->jph_enum_values[lpc];
246
247 ypc->report_error(lnav_log_level_t::ERROR, " %s\n", ev.first);
248 }
249 }
250
251 return 1;
252 };
253
bool_field_cbjson_path_handler254 static int bool_field_cb(yajlpp_parse_context *ypc, int val) {
255 return ypc->ypc_current_handler->jph_bool_cb(ypc, val);
256 };
257
str_field_cb2json_path_handler258 static int str_field_cb2(yajlpp_parse_context *ypc, const unsigned char *str, size_t len) {
259 return ypc->ypc_current_handler->jph_str_cb(ypc, str, len);
260 };
261
int_field_cbjson_path_handler262 static int int_field_cb(yajlpp_parse_context *ypc, long long val) {
263 return ypc->ypc_current_handler->jph_integer_cb(ypc, val);
264 };
265
266 template<typename T, typename NUM_T, NUM_T T::*NUM>
num_field_cbjson_path_handler267 static int num_field_cb(yajlpp_parse_context *ypc, long long num)
268 {
269 auto obj = (T *) ypc->ypc_obj_stack.top();
270
271 obj->*NUM = num;
272
273 return 1;
274 }
275
276 template<typename T, typename NUM_T, NUM_T T::*NUM>
decimal_field_cbjson_path_handler277 static int decimal_field_cb(yajlpp_parse_context *ypc, double num)
278 {
279 auto obj = (T *) ypc->ypc_obj_stack.top();
280
281 obj->*NUM = num;
282
283 return 1;
284 }
285
286 template<typename T, typename STR_T, STR_T T::*STR>
string_field_validatorjson_path_handler287 static void string_field_validator(yajlpp_parse_context &ypc, const json_path_handler_base &jph) {
288 auto &field_ptr = ypc.get_rvalue(ypc.get_obj_member<T, STR_T, STR>());
289
290 if (jph.jph_pattern) {
291 string_fragment sf = to_string_fragment(field_ptr);
292 pcre_input pi(sf);
293 pcre_context_static<30> pc;
294
295 if (!jph.jph_pattern->match(pc, pi)) {
296 ypc.report_error(lnav_log_level_t::ERROR,
297 "Value does not match pattern: %s",
298 jph.jph_pattern_re);
299 }
300 }
301 if (jph.jph_string_validator) {
302 try {
303 jph.jph_string_validator(to_string_fragment(field_ptr));
304 } catch (const std::exception &e) {
305 ypc.report_error(lnav_log_level_t::ERROR,
306 "%s",
307 e.what());
308 }
309 }
310 if (field_ptr.empty() && jph.jph_min_length > 0) {
311 ypc.report_error(lnav_log_level_t::ERROR, "value must not be empty");
312 } else if (field_ptr.size() < jph.jph_min_length) {
313 ypc.report_error(lnav_log_level_t::ERROR, "value must be at least %lu characters long",
314 jph.jph_min_length);
315 }
316 };
317
318 template<typename T, typename NUM_T, NUM_T T::*NUM>
number_field_validatorjson_path_handler319 static void number_field_validator(yajlpp_parse_context &ypc, const json_path_handler_base &jph) {
320 auto &field_ptr = ypc.get_rvalue(ypc.get_obj_member<T, NUM_T, NUM>());
321
322 if (field_ptr < jph.jph_min_value) {
323 ypc.report_error(lnav_log_level_t::ERROR,
324 "value must be greater than %lld",
325 jph.jph_min_value);
326 }
327 }
328
329 template<typename T, typename R, R T::*FIELD>
field_genjson_path_handler330 static yajl_gen_status field_gen(yajlpp_gen_context &ygc,
331 const json_path_handler_base &jph,
332 yajl_gen handle) {
333 auto def_obj = (T *) (ygc.ygc_default_stack.empty() ? nullptr : ygc.ygc_default_stack.top());
334 auto obj = (T *) ygc.ygc_obj_stack.top();
335
336 if (def_obj != nullptr && def_obj->*FIELD == obj->*FIELD) {
337 return yajl_gen_status_ok;
338 }
339
340 if (ygc.ygc_depth) {
341 yajl_gen_string(handle, jph.jph_property);
342 }
343
344 yajlpp_generator gen(handle);
345
346 return gen(obj->*FIELD);
347 };
348
349 template<typename T, typename R, R T::*FIELD>
map_field_genjson_path_handler350 static yajl_gen_status map_field_gen(yajlpp_gen_context &ygc,
351 const json_path_handler_base &jph,
352 yajl_gen handle)
353 {
354 const auto def_container = (T *) (ygc.ygc_default_stack.empty() ?
355 nullptr : ygc.ygc_default_stack.top());
356 auto container = (T *) ygc.ygc_obj_stack.top();
357 auto &obj = container->*FIELD;
358 yajl_gen_status rc;
359
360 for (const auto &pair : obj) {
361 if (def_container != nullptr) {
362 auto &def_obj = def_container->*FIELD;
363 auto iter = def_obj.find(pair.first);
364
365 if (iter != def_obj.end() && iter->second == pair.second) {
366 continue;
367 }
368 }
369
370 if ((rc = yajl_gen_string(handle, pair.first)) !=
371 yajl_gen_status_ok) {
372 return rc;
373 }
374 if ((rc = yajl_gen_string(handle, pair.second)) !=
375 yajl_gen_status_ok) {
376 return rc;
377 }
378 }
379
380 return yajl_gen_status_ok;
381 };
382
383 template<typename T, typename STR_T, std::string T::*STR>
for_fieldjson_path_handler384 json_path_handler &for_field() {
385 this->add_cb(string_field_cb<T, STR_T, STR>);
386 this->jph_gen_callback = field_gen<T, STR_T, STR>;
387 this->jph_validator = string_field_validator<T, STR_T, STR>;
388 this->jph_field_getter = get_field_lvalue_cb<T, STR_T, STR>;
389
390 return *this;
391 };
392
393 template<typename T, typename STR_T, std::map<std::string, std::string> T::*STR>
394 json_path_handler &for_field() {
395 this->add_cb(string_field_cb<T, STR_T, STR>);
396 this->jph_gen_callback = map_field_gen<T, STR_T, STR>;
397 this->jph_validator = string_field_validator<T, STR_T, STR>;
398
399 return *this;
400 };
401
402 template<typename T, typename STR_T, std::map<std::string, std::vector<std::string>> T::*STR>
403 json_path_handler &for_field() {
404 this->add_cb(string_field_cb<T, STR_T, STR>);
405 this->jph_validator = string_field_validator<T, STR_T, STR>;
406
407 return *this;
408 };
409
410 template<typename T, typename STR_T, std::vector<std::string> T::*STR>
411 json_path_handler &for_field() {
412 this->add_cb(string_field_cb<T, STR_T, STR>);
413 this->jph_gen_callback = field_gen<T, STR_T, STR>;
414 this->jph_validator = string_field_validator<T, STR_T, STR>;
415
416 return *this;
417 };
418
419 template<typename T, typename STR_T, intern_string_t T::*STR>
for_fieldjson_path_handler420 json_path_handler &for_field() {
421 this->add_cb(string_field_cb<T, intern_string_t, STR>);
422 this->jph_gen_callback = field_gen<T, intern_string_t, STR>;
423 this->jph_validator = string_field_validator<T, intern_string_t, STR>;
424
425 return *this;
426 };
427
428 template<typename T, typename BOOL_T, bool T::*BOOL>
for_fieldjson_path_handler429 json_path_handler &for_field() {
430 this->add_cb(bool_field_cb);
431 this->jph_bool_cb = [&](yajlpp_parse_context *ypc, int val) {
432 auto obj = (T *) ypc->ypc_obj_stack.top();
433
434 obj->*BOOL = static_cast<bool>(val);
435
436 return 1;
437 };
438 this->jph_gen_callback = field_gen<T, bool, BOOL>;
439
440 return *this;
441 };
442
443 template<typename T, typename U>
get_fieldjson_path_handler444 static inline U& get_field(T& input, U (T::*field)) {
445 return input.*field;
446 }
447
448 template<typename T, typename U, typename... V>
get_fieldjson_path_handler449 static inline auto get_field(T& input, U (T::*field), V... args)
450 -> decltype(get_field(input.*field, args...)) {
451 return get_field(input.*field, args...);
452 }
453
454 template<typename T, typename U, typename... V>
get_fieldjson_path_handler455 static inline auto get_field(void *input, U (T::*field), V... args)
456 -> decltype(get_field(*((T *) input), field, args...)) {
457 return get_field(*((T *) input), field, args...);
458 }
459
460 template<typename R, typename T, typename... Args>
461 struct LastIs {
462 static constexpr bool value = LastIs<R, Args...>::value;
463 };
464
465 template<typename R, typename T>
466 struct LastIs<R, T> {
467 static constexpr bool value = false;
468 };
469
470 template<typename R, typename T>
471 struct LastIs<R, R T::*> {
472 static constexpr bool value = true;
473 };
474
475 template<typename T, typename... Args>
476 struct LastIsEnum {
477 static constexpr bool value = LastIsEnum<Args...>::value;
478 };
479
480 template<typename T, typename U>
481 struct LastIsEnum<U T::*> {
482 static constexpr bool value = std::is_enum<U>::value;
483 };
484
485 template<typename T, typename... Args>
486 struct LastIsNumber {
487 static constexpr bool value = LastIsNumber<Args...>::value;
488 };
489
490 template<typename T, typename U>
491 struct LastIsNumber<U T::*> {
492 static constexpr bool value = std::is_integral<U>::value &&
493 !std::is_same<U, bool>::value;
494 };
495
496 template<
497 typename... Args,
498 std::enable_if_t<LastIs<bool, Args...>::value, bool> = true
499 >
for_fieldjson_path_handler500 json_path_handler &for_field(Args... args) {
501 this->add_cb(bool_field_cb);
502 this->jph_bool_cb = [args...](yajlpp_parse_context *ypc, int val) {
503 auto obj = ypc->ypc_obj_stack.top();
504
505 json_path_handler::get_field(obj, args...) = static_cast<bool>(val);
506
507 return 1;
508 };
509 this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
510 const json_path_handler_base &jph,
511 yajl_gen handle) {
512 const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
513
514 if (!ygc.ygc_default_stack.empty()) {
515 const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
516
517 if (field == field_def) {
518 return yajl_gen_status_ok;
519 }
520 }
521
522 if (ygc.ygc_depth) {
523 yajl_gen_string(handle, jph.jph_property);
524 }
525
526 yajlpp_generator gen(handle);
527
528 return gen(field);
529 };
530 return *this;
531 }
532
533 template<
534 typename... Args,
535 std::enable_if_t<LastIs<std::map<std::string, std::string>, Args...>::value, bool> = true
536 >
for_fieldjson_path_handler537 json_path_handler &for_field(Args... args) {
538 this->add_cb(str_field_cb2);
539 this->jph_str_cb = [args...](yajlpp_parse_context *ypc,
540 const unsigned char *str,
541 size_t len) {
542 auto obj = ypc->ypc_obj_stack.top();
543 auto key = ypc->get_path_fragment(-1);
544
545 json_path_handler::get_field(obj, args...)[key] = std::string((const char *) str, len);
546
547 return 1;
548 };
549 this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
550 const json_path_handler_base &jph,
551 yajl_gen handle) {
552 const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
553
554 if (!ygc.ygc_default_stack.empty()) {
555 const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
556
557 if (field == field_def) {
558 return yajl_gen_status_ok;
559 }
560 }
561
562 {
563 yajlpp_generator gen(handle);
564
565 for (const auto& pair : field) {
566 gen(pair.first);
567 gen(pair.second);
568 }
569 }
570
571 return yajl_gen_status_ok;
572 };
573 return *this;
574 }
575
576 template<
577 typename... Args,
578 std::enable_if_t<LastIs<std::string, Args...>::value, bool> = true
579 >
for_fieldjson_path_handler580 json_path_handler &for_field(Args... args) {
581 this->add_cb(str_field_cb2);
582 this->jph_str_cb = [args...](yajlpp_parse_context *ypc,
583 const unsigned char *str,
584 size_t len) {
585 auto obj = ypc->ypc_obj_stack.top();
586
587 json_path_handler::get_field(obj, args...) = std::string((const char *) str, len);
588
589 return 1;
590 };
591 this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
592 const json_path_handler_base &jph,
593 yajl_gen handle) {
594 const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
595
596 if (!ygc.ygc_default_stack.empty()) {
597 const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
598
599 if (field == field_def) {
600 return yajl_gen_status_ok;
601 }
602 }
603
604 if (ygc.ygc_depth) {
605 yajl_gen_string(handle, jph.jph_property);
606 }
607
608 yajlpp_generator gen(handle);
609
610 return gen(field);
611 };
612 return *this;
613 }
614
615 template<
616 typename... Args,
617 std::enable_if_t<LastIsNumber<Args...>::value, bool> = true
618 >
for_fieldjson_path_handler619 json_path_handler &for_field(Args... args) {
620 this->add_cb(int_field_cb);
621 this->jph_integer_cb = [args...](yajlpp_parse_context *ypc, long long val) {
622 auto obj = ypc->ypc_obj_stack.top();
623
624 if (val < ypc->ypc_current_handler->jph_min_value) {
625 ypc->report_error(lnav_log_level_t::ERROR,
626 "value must be greater than or equal to %lld, found %lld",
627 ypc->ypc_current_handler->jph_min_value,
628 val);
629 return 1;
630 }
631
632 json_path_handler::get_field(obj, args...) = val;
633
634 return 1;
635 };
636 this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
637 const json_path_handler_base &jph,
638 yajl_gen handle) {
639 const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
640
641 if (!ygc.ygc_default_stack.empty()) {
642 const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
643
644 if (field == field_def) {
645 return yajl_gen_status_ok;
646 }
647 }
648
649 if (ygc.ygc_depth) {
650 yajl_gen_string(handle, jph.jph_property);
651 }
652
653 yajlpp_generator gen(handle);
654
655 return gen(field);
656 };
657
658 return *this;
659 }
660
661 template<
662 typename... Args,
663 std::enable_if_t<LastIs<std::chrono::seconds, Args...>::value, bool> = true
664 >
for_fieldjson_path_handler665 json_path_handler &for_field(Args... args) {
666 this->add_cb(str_field_cb2);
667 this->jph_str_cb = [args...](yajlpp_parse_context *ypc,
668 const unsigned char *str,
669 size_t len) {
670 auto obj = ypc->ypc_obj_stack.top();
671 auto handler = ypc->ypc_current_handler;
672 auto parse_res = relative_time::from_str((const char *) str, len);
673
674 if (parse_res.isErr()) {
675 ypc->report_error(lnav_log_level_t::ERROR,
676 "error:%s:line %d\n"
677 " Invalid duration: '%.*s' -- %s",
678 ypc->ypc_source.c_str(),
679 ypc->get_line_number(),
680 len,
681 str,
682 parse_res.unwrapErr().pe_msg.c_str());
683 ypc->report_error(lnav_log_level_t::ERROR,
684 " for option: %s %s -- %s\n",
685 &ypc->ypc_path[0],
686 handler->jph_synopsis,
687 handler->jph_description);
688 return 1;
689 }
690
691 json_path_handler::get_field(obj, args...) = std::chrono::seconds(
692 parse_res.template unwrap().to_timeval().tv_sec);
693
694 return 1;
695 };
696 this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
697 const json_path_handler_base &jph,
698 yajl_gen handle) {
699 const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
700
701 if (!ygc.ygc_default_stack.empty()) {
702 const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
703
704 if (field == field_def) {
705 return yajl_gen_status_ok;
706 }
707 }
708
709 if (ygc.ygc_depth) {
710 yajl_gen_string(handle, jph.jph_property);
711 }
712
713 yajlpp_generator gen(handle);
714
715 return gen(relative_time::from_timeval({static_cast<time_t>(field.count()), 0}).to_string());
716 };
717 this->jph_field_getter = [args...](void *root, nonstd::optional<std::string> name) {
718 return (void *) &json_path_handler::get_field(root, args...);
719 };
720 return *this;
721 }
722
723 template<
724 typename... Args,
725 std::enable_if_t<LastIsEnum<Args...>::value, bool> = true
726 >
for_fieldjson_path_handler727 json_path_handler &for_field(Args... args) {
728 this->add_cb(str_field_cb2);
729 this->jph_str_cb = [args...](yajlpp_parse_context *ypc,
730 const unsigned char *str, size_t len) {
731 auto obj = ypc->ypc_obj_stack.top();
732 auto handler = ypc->ypc_current_handler;
733 auto res = handler->to_enum_value(string_fragment(str, 0, len));
734
735 if (res) {
736 json_path_handler::get_field(obj, args...) =
737 (decltype(json_path_handler::get_field(obj,
738 args...))) res.value();
739 } else {
740 ypc->report_error(lnav_log_level_t::ERROR,
741 "error:%s:line %d\n "
742 "Invalid value, '%.*s', for option:",
743 ypc->ypc_source.c_str(),
744 ypc->get_line_number(),
745 len,
746 str);
747
748 ypc->report_error(lnav_log_level_t::ERROR,
749 " %s %s -- %s\n",
750 &ypc->ypc_path[0],
751 handler->jph_synopsis,
752 handler->jph_description);
753 ypc->report_error(lnav_log_level_t::ERROR,
754 " Allowed values: ");
755 for (int lpc = 0; handler->jph_enum_values[lpc].first; lpc++) {
756 const json_path_handler::enum_value_t &ev = handler->jph_enum_values[lpc];
757
758 ypc->report_error(lnav_log_level_t::ERROR, " %s\n",
759 ev.first);
760 }
761 }
762
763 return 1;
764 };
765
766 return *this;
767 };
768
769
770 template<typename T, typename NUM_T, NUM_T T::*NUM>
for_fieldjson_path_handler771 json_path_handler &for_field(typename std::enable_if<std::is_integral<NUM_T>::value &&
772 !std::is_same<NUM_T, bool>::value>::type* dummy = 0) {
773 this->add_cb(num_field_cb<T, NUM_T, NUM>);
774 this->jph_validator = number_field_validator<T, NUM_T, NUM>;
775 return *this;
776 };
777
778 template<typename T, typename NUM_T, double T::*NUM>
for_fieldjson_path_handler779 json_path_handler &for_field() {
780 this->add_cb(decimal_field_cb<T, NUM_T, NUM>);
781 this->jph_validator = number_field_validator<T, NUM_T, NUM>;
782 return *this;
783 };
784
785 template<typename T, typename ENUM_T, ENUM_T T::*ENUM>
for_fieldjson_path_handler786 json_path_handler &for_field(typename std::enable_if<std::is_enum<ENUM_T>::value>::type* dummy = 0) {
787 this->add_cb(enum_field_cb<T, ENUM_T, ENUM>);
788 return *this;
789 };
790
791 json_path_handler &with_children(const json_path_container &container);
792
with_examplejson_path_handler793 json_path_handler &with_example(const std::string &example) {
794 this->jph_examples.emplace_back(example);
795 return *this;
796 }
797 };
798
799 struct json_path_container {
json_path_containerjson_path_container800 json_path_container(std::initializer_list<json_path_handler> children)
801 : jpc_children(children) {
802 }
803
with_definition_idjson_path_container804 json_path_container &with_definition_id(const std::string& id) {
805 this->jpc_definition_id = id;
806 return *this;
807 }
808
with_schema_idjson_path_container809 json_path_container &with_schema_id(const std::string& id) {
810 this->jpc_schema_id = id;
811 return *this;
812 }
813
with_descriptionjson_path_container814 json_path_container &with_description(std::string desc) {
815 this->jpc_description = std::move(desc);
816 return *this;
817 }
818
819 void gen_schema(yajlpp_gen_context &ygc) const;
820
821 void gen_properties(yajlpp_gen_context &ygc) const;
822
823 std::string jpc_schema_id;
824 std::string jpc_definition_id;
825 std::string jpc_description;
826 std::vector<json_path_handler> jpc_children;
827 };
828
829 namespace yajlpp {
property_handler(const std::string & path)830 inline json_path_handler property_handler(const std::string &path)
831 {
832 return {path};
833 }
834
pattern_property_handler(const std::string & path)835 inline json_path_handler pattern_property_handler(const std::string &path)
836 {
837 return {pcrepp(path)};
838 }
839 }
840
841 #endif
842