You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lnav/src/yajlpp/yajlpp_def.hh

1467 lines
47 KiB
C++

/**
* Copyright (c) 2018, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @file yajlpp_def.hh
*/
#ifndef yajlpp_def_hh
#define yajlpp_def_hh
#include <chrono>
#include "config.h"
#include "mapbox/variant.hpp"
#include "relative_time.hh"
#include "yajlpp.hh"
#define FOR_FIELD(T, FIELD) for_field<T, decltype(T ::FIELD), &T ::FIELD>()
inline intern_string_t&
assign(intern_string_t& lhs, const string_fragment& rhs)
{
lhs = intern_string::lookup(rhs.data(), rhs.length());
return lhs;
}
inline std::string&
assign(std::string& lhs, const string_fragment& rhs)
{
lhs.assign(rhs.data(), rhs.length());
return lhs;
}
template<template<typename...> class Container>
inline Container<std::string>&
assign(Container<std::string>& lhs, const string_fragment& rhs)
{
lhs.emplace_back(rhs.data(), rhs.length());
return lhs;
}
struct json_null_t {
bool operator==(const json_null_t& other) const { return true; }
};
using json_any_t
= mapbox::util::variant<json_null_t, bool, int64_t, double, std::string>;
struct json_path_container;
struct json_path_handler : public json_path_handler_base {
template<typename P>
json_path_handler(P path, int (*null_func)(yajlpp_parse_context*))
: json_path_handler_base(path)
{
this->jph_callbacks.yajl_null = (int (*)(void*)) null_func;
}
template<typename P>
json_path_handler(P path, int (*bool_func)(yajlpp_parse_context*, int))
: json_path_handler_base(path)
{
this->jph_callbacks.yajl_boolean = (int (*)(void*, int)) bool_func;
}
template<typename P>
json_path_handler(P path, int (*int_func)(yajlpp_parse_context*, long long))
: json_path_handler_base(path)
{
this->jph_callbacks.yajl_integer = (int (*)(void*, long long)) int_func;
}
template<typename P>
json_path_handler(P path, int (*double_func)(yajlpp_parse_context*, double))
: json_path_handler_base(path)
{
this->jph_callbacks.yajl_double = (int (*)(void*, double)) double_func;
}
template<typename P>
json_path_handler(P path) : json_path_handler_base(path)
{
}
template<typename P>
json_path_handler(P path,
int (*str_func)(yajlpp_parse_context*,
const unsigned char*,
size_t))
: json_path_handler_base(path)
{
this->jph_callbacks.yajl_string
= (int (*)(void*, const unsigned char*, size_t)) str_func;
}
json_path_handler(const std::string& path,
const std::shared_ptr<const lnav::pcre2pp::code>& re)
: json_path_handler_base(path, re)
{
}
json_path_handler& add_cb(int (*null_func)(yajlpp_parse_context*))
{
this->jph_callbacks.yajl_null = (int (*)(void*)) null_func;
return *this;
}
json_path_handler& add_cb(int (*bool_func)(yajlpp_parse_context*, int))
{
this->jph_callbacks.yajl_boolean = (int (*)(void*, int)) bool_func;
return *this;
}
json_path_handler& add_cb(int (*int_func)(yajlpp_parse_context*, long long))
{
this->jph_callbacks.yajl_integer = (int (*)(void*, long long)) int_func;
return *this;
}
json_path_handler& add_cb(int (*double_func)(yajlpp_parse_context*, double))
{
this->jph_callbacks.yajl_double = (int (*)(void*, double)) double_func;
return *this;
}
json_path_handler& add_cb(int (*str_func)(yajlpp_parse_context*,
const unsigned char*,
size_t))
{
this->jph_callbacks.yajl_string
= (int (*)(void*, const unsigned char*, size_t)) str_func;
return *this;
}
json_path_handler& with_synopsis(const char* synopsis)
{
this->jph_synopsis = synopsis;
return *this;
}
json_path_handler& with_description(const char* description)
{
this->jph_description = description;
return *this;
}
json_path_handler& with_min_length(size_t len)
{
this->jph_min_length = len;
return *this;
}
json_path_handler& with_max_length(size_t len)
{
this->jph_max_length = len;
return *this;
}
json_path_handler& with_enum_values(const enum_value_t values[])
{
this->jph_enum_values = values;
return *this;
}
template<typename T, std::size_t N>
json_path_handler& with_pattern(const T (&re)[N])
{
this->jph_pattern_re = re;
this->jph_pattern = lnav::pcre2pp::code::from_const(re).to_shared();
return *this;
}
json_path_handler& with_min_value(long long val)
{
this->jph_min_value = val;
return *this;
}
template<typename R, typename T>
json_path_handler& with_obj_provider(
R* (*provider)(const yajlpp_provider_context& pc, T* root))
{
this->jph_obj_provider
= [provider](const yajlpp_provider_context& ypc, void* root) {
return (R*) provider(ypc, (T*) root);
};
return *this;
}
template<typename R>
json_path_handler& with_size_provider(size_t (*provider)(const R* root))
{
this->jph_size_provider
= [provider](const void* root) { return provider((R*) root); };
return *this;
}
template<typename T>
json_path_handler& with_path_provider(
void (*provider)(T* root, std::vector<std::string>& paths_out))
{
this->jph_path_provider
= [provider](void* root, std::vector<std::string>& paths_out) {
provider((T*) root, paths_out);
};
return *this;
}
template<typename T>
json_path_handler& with_obj_deleter(
void (*provider)(const yajlpp_provider_context& pc, T* root))
{
this->jph_obj_deleter
= [provider](const yajlpp_provider_context& ypc, void* root) {
provider(ypc, (T*) root);
};
return *this;
}
template<typename T, typename MEM_T, MEM_T T::*MEM>
static void* get_field_lvalue_cb(void* root,
nonstd::optional<std::string> name)
{
auto obj = (T*) root;
auto& mem = obj->*MEM;
return &mem;
}
template<typename T, typename STR_T, STR_T T::*STR>
static int string_field_cb(yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len)
{
auto handler = ypc->ypc_current_handler;
if (ypc->ypc_locations) {
(*ypc->ypc_locations)[ypc->get_full_path()]
= source_location{ypc->ypc_source, ypc->get_line_number()};
}
assign(ypc->get_lvalue(ypc->get_obj_member<T, STR_T, STR>()),
string_fragment(str, 0, len));
handler->jph_validator(*ypc, *handler);
return 1;
}
template<typename T, typename ENUM_T, ENUM_T T::*ENUM>
static int enum_field_cb(yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len)
{
auto obj = (T*) ypc->ypc_obj_stack.top();
auto handler = ypc->ypc_current_handler;
auto res = handler->to_enum_value(string_fragment(str, 0, len));
if (res) {
obj->*ENUM = (ENUM_T) res.value();
} else {
handler->report_enum_error(ypc,
std::string((const char*) str, len));
}
return 1;
}
static int bool_field_cb(yajlpp_parse_context* ypc, int val)
{
return ypc->ypc_current_handler->jph_bool_cb(ypc, val);
}
static int str_field_cb2(yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len)
{
return ypc->ypc_current_handler->jph_str_cb(ypc, str, len);
}
static int int_field_cb(yajlpp_parse_context* ypc, long long val)
{
return ypc->ypc_current_handler->jph_integer_cb(ypc, val);
}
template<typename T, typename NUM_T, NUM_T T::*NUM>
static int num_field_cb(yajlpp_parse_context* ypc, long long num)
{
auto obj = (T*) ypc->ypc_obj_stack.top();
obj->*NUM = num;
return 1;
}
template<typename T, typename NUM_T, NUM_T T::*NUM>
static int decimal_field_cb(yajlpp_parse_context* ypc, double num)
{
auto obj = (T*) ypc->ypc_obj_stack.top();
obj->*NUM = num;
return 1;
}
template<typename T, typename STR_T, STR_T T::*STR>
static void string_field_validator(yajlpp_parse_context& ypc,
const json_path_handler_base& jph)
{
auto& field_ptr = ypc.get_rvalue(ypc.get_obj_member<T, STR_T, STR>());
if (jph.jph_pattern) {
auto sf = to_string_fragment(field_ptr);
if (!jph.jph_pattern->find_in(sf).ignore_error()) {
jph.report_pattern_error(&ypc, sf.to_string());
}
}
if (field_ptr.empty() && jph.jph_min_length > 0) {
ypc.report_error(
lnav::console::user_message::error(
attr_line_t("invalid value for option ")
.template append_quoted(lnav::roles::symbol(
ypc.get_full_path().to_string())))
.with_reason("empty values are not allowed")
.with_snippet(ypc.get_snippet())
.with_help(jph.get_help_text(&ypc)));
} else if (field_ptr.size() < jph.jph_min_length) {
ypc.report_error(
lnav::console::user_message::error(
attr_line_t()
.template append_quoted(field_ptr)
.append(" is not a valid value for option ")
.append_quoted(lnav::roles::symbol(
ypc.get_full_path().to_string())))
.with_reason(attr_line_t("value must be at least ")
.append(lnav::roles::number(
fmt::to_string(jph.jph_min_length)))
.append(" characters long"))
.with_snippet(ypc.get_snippet())
.with_help(jph.get_help_text(&ypc)));
}
}
template<typename T, typename NUM_T, NUM_T T::*NUM>
static void number_field_validator(yajlpp_parse_context& ypc,
const json_path_handler_base& jph)
{
auto& field_ptr = ypc.get_rvalue(ypc.get_obj_member<T, NUM_T, NUM>());
if (field_ptr < jph.jph_min_value) {
jph.report_min_value_error(&ypc, field_ptr);
}
}
template<typename T, typename R, R T::*FIELD>
static yajl_gen_status field_gen(yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle)
{
auto def_obj = (T*) (ygc.ygc_default_stack.empty()
? nullptr
: ygc.ygc_default_stack.top());
auto obj = (T*) ygc.ygc_obj_stack.top();
if (def_obj != nullptr && def_obj->*FIELD == obj->*FIELD) {
return yajl_gen_status_ok;
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(obj->*FIELD);
}
template<typename T, typename R, R T::*FIELD>
static yajl_gen_status map_field_gen(yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle)
{
const auto def_container = (T*) (ygc.ygc_default_stack.empty()
? nullptr
: ygc.ygc_default_stack.top());
auto container = (T*) ygc.ygc_obj_stack.top();
auto& obj = container->*FIELD;
yajl_gen_status rc;
for (const auto& pair : obj) {
if (def_container != nullptr) {
auto& def_obj = def_container->*FIELD;
auto iter = def_obj.find(pair.first);
if (iter != def_obj.end() && iter->second == pair.second) {
continue;
}
}
if ((rc = yajl_gen_string(handle, pair.first))
!= yajl_gen_status_ok)
{
return rc;
}
if ((rc = yajl_gen_string(handle, pair.second))
!= yajl_gen_status_ok)
{
return rc;
}
}
return yajl_gen_status_ok;
}
template<typename T, typename STR_T, std::string T::*STR>
json_path_handler& for_field()
{
this->add_cb(string_field_cb<T, STR_T, STR>);
this->jph_gen_callback = field_gen<T, STR_T, STR>;
this->jph_validator = string_field_validator<T, STR_T, STR>;
this->jph_field_getter = get_field_lvalue_cb<T, STR_T, STR>;
return *this;
}
template<typename T,
typename STR_T,
std::map<std::string, std::string> T::*STR>
json_path_handler& for_field()
{
this->add_cb(string_field_cb<T, STR_T, STR>);
this->jph_gen_callback = map_field_gen<T, STR_T, STR>;
this->jph_validator = string_field_validator<T, STR_T, STR>;
return *this;
}
template<typename T,
typename STR_T,
std::map<std::string, std::vector<std::string>> T::*STR>
json_path_handler& for_field()
{
this->add_cb(string_field_cb<T, STR_T, STR>);
this->jph_validator = string_field_validator<T, STR_T, STR>;
return *this;
}
template<typename T, typename STR_T, std::vector<std::string> T::*STR>
json_path_handler& for_field()
{
this->add_cb(string_field_cb<T, STR_T, STR>);
this->jph_gen_callback = field_gen<T, STR_T, STR>;
this->jph_validator = string_field_validator<T, STR_T, STR>;
return *this;
}
template<typename T, typename STR_T, intern_string_t T::*STR>
json_path_handler& for_field()
{
this->add_cb(string_field_cb<T, intern_string_t, STR>);
this->jph_gen_callback = field_gen<T, intern_string_t, STR>;
this->jph_validator = string_field_validator<T, intern_string_t, STR>;
return *this;
}
template<typename T, typename BOOL_T, bool T::*BOOL>
json_path_handler& for_field()
{
this->add_cb(bool_field_cb);
this->jph_bool_cb = [&](yajlpp_parse_context* ypc, int val) {
auto obj = (T*) ypc->ypc_obj_stack.top();
obj->*BOOL = static_cast<bool>(val);
return 1;
};
this->jph_gen_callback = field_gen<T, bool, BOOL>;
return *this;
}
template<typename T, typename U>
static inline U& get_field(T& input, U(T::*field))
{
return input.*field;
}
template<typename T, typename U, typename... V>
static inline auto get_field(T& input, U(T::*field), V... args)
-> decltype(get_field(input.*field, args...))
{
return get_field(input.*field, args...);
}
template<typename T, typename U, typename... V>
static inline auto get_field(void* input, U(T::*field), V... args)
-> decltype(get_field(*((T*) input), field, args...))
{
return get_field(*((T*) input), field, args...);
}
template<typename R, typename T, typename... Args>
struct LastIs {
static constexpr bool value = LastIs<R, Args...>::value;
};
template<typename R, typename T>
struct LastIs<R, T> {
static constexpr bool value = false;
};
template<typename R, typename T>
struct LastIs<R, R T::*> {
static constexpr bool value = true;
};
template<typename T, typename... Args>
struct LastIsEnum {
static constexpr bool value = LastIsEnum<Args...>::value;
};
template<typename T, typename U>
struct LastIsEnum<U T::*> {
static constexpr bool value = std::is_enum<U>::value;
};
template<typename T, typename... Args>
struct LastIsNumber {
static constexpr bool value = LastIsNumber<Args...>::value;
};
template<typename T, typename U>
struct LastIsNumber<U T::*> {
static constexpr bool value
= std::is_integral<U>::value && !std::is_same<U, bool>::value;
};
template<typename T, typename U>
struct LastIsNumber<nonstd::optional<U> T::*> {
static constexpr bool value
= std::is_integral<U>::value && !std::is_same<U, bool>::value;
};
template<typename T, typename... Args>
struct LastIsVector {
static constexpr bool value = LastIsVector<Args...>::value;
};
template<typename T, typename U>
struct LastIsVector<std::vector<U> T::*> {
static constexpr bool value = true;
};
template<typename T, typename U>
struct LastIsVector<U T::*> {
static constexpr bool value = false;
};
template<typename T>
static bool is_field_set(const nonstd::optional<T>& field)
{
return field.has_value();
}
template<typename T>
static bool is_field_set(const T&)
{
return true;
}
template<typename... Args,
std::enable_if_t<LastIs<bool, Args...>::value, bool> = true>
json_path_handler& for_field(Args... args)
{
this->add_cb(bool_field_cb);
this->jph_bool_cb = [args...](yajlpp_parse_context* ypc, int val) {
auto* obj = ypc->ypc_obj_stack.top();
json_path_handler::get_field(obj, args...) = static_cast<bool>(val);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field);
};
this->jph_field_getter
= [args...](void* root, nonstd::optional<std::string> name) {
return (void*) &json_path_handler::get_field(root, args...);
};
return *this;
}
template<typename... Args,
std::enable_if_t<LastIsVector<Args...>::value, bool> = true>
json_path_handler& for_field(Args... args)
{
this->jph_obj_provider
= [args...](const yajlpp_provider_context& ypc, void* root) {
auto& vec = json_path_handler::get_field(root, args...);
if (ypc.ypc_index >= vec.size()) {
vec.resize(ypc.ypc_index + 1);
}
return &vec[ypc.ypc_index];
};
this->jph_size_provider = [args...](void* root) {
auto& vec = json_path_handler::get_field(root, args...);
return vec.size();
};
return *this;
}
template<typename T, typename U>
json_path_handler& for_child(positioned_property<U>(T::*field))
{
this->jph_obj_provider
= [field](const yajlpp_provider_context& ypc, void* root) -> void* {
auto& child = json_path_handler::get_field(root, field);
if (ypc.ypc_parse_context != nullptr && child.pp_path.empty()) {
child.pp_path = ypc.ypc_parse_context->get_full_path();
}
return &child.pp_value;
};
return *this;
}
template<typename... Args>
json_path_handler& for_child(Args... args)
{
this->jph_obj_provider = [args...](const yajlpp_provider_context& ypc,
void* root) -> void* {
auto& child = json_path_handler::get_field(root, args...);
return &child;
};
return *this;
}
template<typename... Args,
std::enable_if_t<
LastIs<std::map<std::string, std::string>, Args...>::value,
bool>
= true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto obj = ypc->ypc_obj_stack.top();
auto key = ypc->get_path_fragment(-1);
json_path_handler::get_field(obj, args...)[key]
= std::string((const char*) str, len);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
{
yajlpp_generator gen(handle);
for (const auto& pair : field) {
gen(pair.first);
gen(pair.second);
}
}
return yajl_gen_status_ok;
};
return *this;
}
template<typename... Args,
std::enable_if_t<
LastIs<std::map<std::string, json_any_t>, Args...>::value,
bool>
= true>
json_path_handler& for_field(Args... args)
{
this->add_cb(bool_field_cb);
this->jph_bool_cb = [args...](yajlpp_parse_context* ypc, int val) {
auto* obj = ypc->ypc_obj_stack.top();
auto key = ypc->get_path_fragment(-1);
json_path_handler::get_field(obj, args...)[key] = val ? true
: false;
return 1;
};
this->add_cb(int_field_cb);
this->jph_integer_cb
= [args...](yajlpp_parse_context* ypc, long long val) {
auto* obj = ypc->ypc_obj_stack.top();
auto key = ypc->get_path_fragment(-1);
json_path_handler::get_field(obj, args...)[key]
= static_cast<int64_t>(val);
return 1;
};
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto* obj = ypc->ypc_obj_stack.top();
auto key = ypc->get_path_fragment(-1);
json_path_handler::get_field(obj, args...)[key]
= std::string((const char*) str, len);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
{
yajlpp_generator gen(handle);
for (const auto& pair : field) {
gen(pair.first);
pair.second.match([&gen](json_null_t v) { gen(); },
[&gen](bool v) { gen(v); },
[&gen](int64_t v) { gen(v); },
[&gen](double v) { gen(v); },
[&gen](const std::string& v) { gen(v); });
}
}
return yajl_gen_status_ok;
};
return *this;
}
template<typename... Args,
std::enable_if_t<LastIs<std::string, Args...>::value, bool> = true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto obj = ypc->ypc_obj_stack.top();
auto value_str = std::string((const char*) str, len);
auto jph = ypc->ypc_current_handler;
if (jph->jph_pattern) {
if (!jph->jph_pattern->find_in(value_str).ignore_error()) {
jph->report_pattern_error(ypc, value_str);
}
}
json_path_handler::get_field(obj, args...) = std::move(value_str);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field);
};
this->jph_field_getter
= [args...](void* root, nonstd::optional<std::string> name) {
return (void*) &json_path_handler::get_field(root, args...);
};
return *this;
}
template<
typename... Args,
std::enable_if_t<LastIs<nonstd::optional<std::string>, Args...>::value,
bool>
= true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto obj = ypc->ypc_obj_stack.top();
auto value_str = std::string((const char*) str, len);
auto jph = ypc->ypc_current_handler;
if (jph->jph_pattern) {
if (!jph->jph_pattern->find_in(value_str).ignore_error()) {
jph->report_pattern_error(ypc, value_str);
}
}
json_path_handler::get_field(obj, args...) = std::move(value_str);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (!field) {
return yajl_gen_status_ok;
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field.value());
};
this->jph_field_getter
= [args...](void* root, nonstd::optional<std::string> name) {
return (void*) &json_path_handler::get_field(root, args...);
};
return *this;
}
template<typename... Args,
std::enable_if_t<
LastIs<positioned_property<std::string>, Args...>::value,
bool>
= true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto obj = ypc->ypc_obj_stack.top();
auto value_str = std::string((const char*) str, len);
auto jph = ypc->ypc_current_handler;
if (jph->jph_pattern) {
if (!jph->jph_pattern->find_in(value_str).ignore_error()) {
jph->report_pattern_error(ypc, value_str);
}
}
auto& field = json_path_handler::get_field(obj, args...);
field.pp_path = ypc->get_full_path();
field.pp_location.sl_source = ypc->ypc_source;
field.pp_location.sl_line_number = ypc->get_line_number();
field.pp_value = std::move(value_str);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field.pp_value == field_def.pp_value) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field.pp_value);
};
return *this;
}
template<typename... Args,
std::enable_if_t<LastIs<intern_string_t, Args...>::value, bool>
= true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto obj = ypc->ypc_obj_stack.top();
auto value_str = std::string((const char*) str, len);
auto jph = ypc->ypc_current_handler;
if (jph->jph_pattern) {
if (!jph->jph_pattern->find_in(value_str).ignore_error()) {
jph->report_pattern_error(ypc, value_str);
}
}
json_path_handler::get_field(obj, args...)
= intern_string::lookup(value_str);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field);
};
return *this;
}
template<typename... Args,
std::enable_if_t<
LastIs<positioned_property<intern_string_t>, Args...>::value,
bool>
= true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto obj = ypc->ypc_obj_stack.top();
auto value_str = std::string((const char*) str, len);
auto jph = ypc->ypc_current_handler;
if (jph->jph_pattern) {
if (!jph->jph_pattern->find_in(value_str).ignore_error()) {
jph->report_pattern_error(ypc, value_str);
}
}
auto& field = json_path_handler::get_field(obj, args...);
field.pp_path = ypc->get_full_path();
field.pp_location.sl_source = ypc->ypc_source;
field.pp_location.sl_line_number = ypc->get_line_number();
field.pp_value = intern_string::lookup(value_str);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field.pp_value == field_def.pp_value) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field.pp_value);
};
return *this;
}
template<typename>
struct int_ {
typedef int type;
};
template<typename C,
typename T,
typename int_<decltype(T::from(intern_string_t{}))>::type = 0,
typename... Args>
json_path_handler& for_field(Args... args, T C::*ptr_arg)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args..., ptr_arg](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto* obj = ypc->ypc_obj_stack.top();
auto value_frag = string_fragment::from_bytes(str, len);
const auto* jph = ypc->ypc_current_handler;
auto from_res = T::from(ypc->get_full_path(), value_frag);
if (from_res.isErr()) {
jph->report_error(
ypc, value_frag.to_string(), from_res.unwrapErr());
} else {
json_path_handler::get_field(obj, args..., ptr_arg)
= from_res.unwrap();
}
return 1;
};
return *this;
}
template<typename... Args,
std::enable_if_t<LastIsNumber<Args...>::value, bool> = true>
json_path_handler& for_field(Args... args)
{
this->add_cb(int_field_cb);
this->jph_integer_cb
= [args...](yajlpp_parse_context* ypc, long long val) {
auto jph = ypc->ypc_current_handler;
auto* obj = ypc->ypc_obj_stack.top();
if (val < jph->jph_min_value) {
jph->report_min_value_error(ypc, val);
return 1;
}
json_path_handler::get_field(obj, args...) = val;
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (!is_field_set(field)) {
return yajl_gen_status_ok;
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field);
};
this->jph_field_getter
= [args...](void* root, nonstd::optional<std::string> name) {
return (void*) &json_path_handler::get_field(root, args...);
};
return *this;
}
template<
typename... Args,
std::enable_if_t<LastIs<std::chrono::seconds, Args...>::value, bool>
= true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto obj = ypc->ypc_obj_stack.top();
auto handler = ypc->ypc_current_handler;
auto parse_res = relative_time::from_str(
string_fragment::from_bytes(str, len));
if (parse_res.isErr()) {
auto parse_error = parse_res.unwrapErr();
auto value_str = std::string((const char*) str, len);
handler->report_duration_error(ypc, value_str, parse_error);
return 1;
}
json_path_handler::get_field(obj, args...) = std::chrono::seconds(
parse_res.template unwrap().to_timeval().tv_sec);
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(relative_time::from_timeval(
{static_cast<time_t>(field.count()), 0})
.to_string());
};
this->jph_field_getter
= [args...](void* root, nonstd::optional<std::string> name) {
return (void*) &json_path_handler::get_field(root, args...);
};
return *this;
}
template<typename... Args,
std::enable_if_t<LastIsEnum<Args...>::value, bool> = true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len) {
auto obj = ypc->ypc_obj_stack.top();
auto handler = ypc->ypc_current_handler;
auto res = handler->to_enum_value(string_fragment(str, 0, len));
if (res) {
json_path_handler::get_field(obj, args...)
= (decltype(json_path_handler::get_field(
obj, args...))) res.value();
} else {
handler->report_enum_error(ypc,
std::string((const char*) str, len));
}
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(jph.to_enum_string((int) field));
};
this->jph_field_getter
= [args...](void* root, nonstd::optional<std::string> name) {
return (void*) &json_path_handler::get_field(root, args...);
};
return *this;
};
template<typename T, typename NUM_T, NUM_T T::*NUM>
json_path_handler& for_field(
typename std::enable_if<std::is_integral<NUM_T>::value
&& !std::is_same<NUM_T, bool>::value>::type*
dummy
= 0)
{
this->add_cb(num_field_cb<T, NUM_T, NUM>);
this->jph_validator = number_field_validator<T, NUM_T, NUM>;
return *this;
}
template<typename T, typename NUM_T, double T::*NUM>
json_path_handler& for_field()
{
this->add_cb(decimal_field_cb<T, NUM_T, NUM>);
this->jph_validator = number_field_validator<T, NUM_T, NUM>;
return *this;
}
template<typename T, typename ENUM_T, ENUM_T T::*ENUM>
json_path_handler& for_field(
typename std::enable_if<std::is_enum<ENUM_T>::value>::type* dummy = 0)
{
this->add_cb(enum_field_cb<T, ENUM_T, ENUM>);
return *this;
}
json_path_handler& with_children(const json_path_container& container);
json_path_handler& with_example(const std::string& example)
{
this->jph_examples.emplace_back(example);
return *this;
}
};
struct json_path_container {
json_path_container(std::initializer_list<json_path_handler> children)
: jpc_children(children)
{
}
json_path_container& with_definition_id(const std::string& id)
{
this->jpc_definition_id = id;
return *this;
}
json_path_container& with_schema_id(const std::string& id)
{
this->jpc_schema_id = id;
return *this;
}
json_path_container& with_description(std::string desc)
{
this->jpc_description = std::move(desc);
return *this;
}
void gen_schema(yajlpp_gen_context& ygc) const;
void gen_properties(yajlpp_gen_context& ygc) const;
std::string jpc_schema_id;
std::string jpc_definition_id;
std::string jpc_description;
std::vector<json_path_handler> jpc_children;
};
template<typename T>
class yajlpp_parser {
public:
yajlpp_parser(intern_string_t src, const json_path_container* container)
: yp_parse_context(src, container)
{
this->yp_handle = yajl_alloc(&this->yp_parse_context.ypc_callbacks,
nullptr,
&this->yp_parse_context);
this->yp_parse_context.with_handle(this->yp_handle);
this->yp_parse_context.template with_obj(this->yp_obj);
this->yp_parse_context.ypc_userdata = this;
this->yp_parse_context.with_error_reporter(
[](const auto& ypc, const auto& um) {
auto* yp = static_cast<yajlpp_parser<T>*>(ypc.ypc_userdata);
yp->yp_errors.template emplace_back(um);
});
}
yajlpp_parser& with_ignore_unused(bool value)
{
this->yp_parse_context.with_ignore_unused(value);
return *this;
}
Result<T, std::vector<lnav::console::user_message>> of(
const string_fragment& json)
{
if (this->yp_parse_context.parse_doc(json)) {
return Ok(std::move(this->yp_obj));
}
return Err(std::move(this->yp_errors));
}
private:
yajlpp_parse_context yp_parse_context;
auto_mem<yajl_handle_t> yp_handle{yajl_free};
std::vector<lnav::console::user_message> yp_errors;
T yp_obj;
};
template<typename T>
struct typed_json_path_container : public json_path_container {
typed_json_path_container(std::initializer_list<json_path_handler> children)
: json_path_container(children)
{
}
typed_json_path_container<T>& with_schema_id2(const std::string& id)
{
this->jpc_schema_id = id;
return *this;
}
typed_json_path_container<T>& with_description2(std::string desc)
{
this->jpc_description = std::move(desc);
return *this;
}
yajlpp_parser<T> parser_for(intern_string_t src) const
{
return yajlpp_parser<T>{src, this};
}
std::string to_string(const T& obj) const
{
yajlpp_gen gen;
yajlpp_gen_context ygc(gen, *this);
ygc.template with_obj(obj);
ygc.ygc_depth = 1;
ygc.gen();
return gen.to_string_fragment().to_string();
}
json_string to_json_string(T& obj) const
{
yajlpp_gen gen;
yajlpp_gen_context ygc(gen, *this);
ygc.template with_obj(obj);
ygc.ygc_depth = 1;
ygc.gen();
return json_string{gen.get_handle()};
}
};
namespace yajlpp {
inline json_path_handler
property_handler(const std::string& path)
{
return {path};
}
template<typename T, std::size_t N>
inline json_path_handler
pattern_property_handler(const T (&path)[N])
{
return {lnav::pcre2pp::code::from_const(path).to_shared()};
}
} // namespace yajlpp
#endif