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