|
|
|
/**
|
|
|
|
* Copyright (c) 2015, 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.cc
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <regex>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include "yajlpp.hh"
|
|
|
|
|
|
|
|
#include "base/fs_util.hh"
|
|
|
|
#include "base/snippet_highlighters.hh"
|
|
|
|
#include "config.h"
|
|
|
|
#include "fmt/format.h"
|
|
|
|
#include "ghc/filesystem.hpp"
|
|
|
|
#include "yajl/api/yajl_parse.h"
|
|
|
|
#include "yajlpp_def.hh"
|
|
|
|
|
|
|
|
const json_path_handler_base::enum_value_t
|
|
|
|
json_path_handler_base::ENUM_TERMINATOR((const char*) nullptr, 0);
|
|
|
|
|
|
|
|
yajl_gen_status
|
|
|
|
yajl_gen_tree(yajl_gen hand, yajl_val val)
|
|
|
|
{
|
|
|
|
switch (val->type) {
|
|
|
|
case yajl_t_string: {
|
|
|
|
return yajl_gen_string(hand, YAJL_GET_STRING(val));
|
|
|
|
}
|
|
|
|
case yajl_t_number: {
|
|
|
|
if (YAJL_IS_INTEGER(val)) {
|
|
|
|
return yajl_gen_integer(hand, YAJL_GET_INTEGER(val));
|
|
|
|
}
|
|
|
|
if (YAJL_IS_DOUBLE(val)) {
|
|
|
|
return yajl_gen_double(hand, YAJL_GET_DOUBLE(val));
|
|
|
|
}
|
|
|
|
return yajl_gen_number(
|
|
|
|
hand, YAJL_GET_NUMBER(val), strlen(YAJL_GET_NUMBER(val)));
|
|
|
|
}
|
|
|
|
case yajl_t_object: {
|
|
|
|
auto rc = yajl_gen_map_open(hand);
|
|
|
|
if (rc != yajl_gen_status_ok) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
for (size_t lpc = 0; lpc < YAJL_GET_OBJECT(val)->len; lpc++) {
|
|
|
|
rc = yajl_gen_string(hand, YAJL_GET_OBJECT(val)->keys[lpc]);
|
|
|
|
if (rc != yajl_gen_status_ok) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
rc = yajl_gen_tree(hand, YAJL_GET_OBJECT(val)->values[lpc]);
|
|
|
|
if (rc != yajl_gen_status_ok) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rc = yajl_gen_map_close(hand);
|
|
|
|
if (rc != yajl_gen_status_ok) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
return yajl_gen_status_ok;
|
|
|
|
}
|
|
|
|
case yajl_t_array: {
|
|
|
|
auto rc = yajl_gen_array_open(hand);
|
|
|
|
if (rc != yajl_gen_status_ok) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
for (size_t lpc = 0; lpc < YAJL_GET_ARRAY(val)->len; lpc++) {
|
|
|
|
rc = yajl_gen_tree(hand, YAJL_GET_ARRAY(val)->values[lpc]);
|
|
|
|
if (rc != yajl_gen_status_ok) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rc = yajl_gen_array_close(hand);
|
|
|
|
if (rc != yajl_gen_status_ok) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
return yajl_gen_status_ok;
|
|
|
|
}
|
|
|
|
case yajl_t_true: {
|
|
|
|
return yajl_gen_bool(hand, true);
|
|
|
|
}
|
|
|
|
case yajl_t_false: {
|
|
|
|
return yajl_gen_bool(hand, false);
|
|
|
|
}
|
|
|
|
case yajl_t_null: {
|
|
|
|
return yajl_gen_null(hand);
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return yajl_gen_status_ok;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
yajl_cleanup_tree(yajl_val val)
|
|
|
|
{
|
|
|
|
if (YAJL_IS_OBJECT(val)) {
|
|
|
|
auto* val_as_obj = YAJL_GET_OBJECT(val);
|
|
|
|
|
|
|
|
for (size_t lpc = 0; lpc < val_as_obj->len;) {
|
|
|
|
auto* child_val = val_as_obj->values[lpc];
|
|
|
|
|
|
|
|
yajl_cleanup_tree(child_val);
|
|
|
|
if (YAJL_IS_OBJECT(child_val)
|
|
|
|
&& YAJL_GET_OBJECT(child_val)->len == 0) {
|
|
|
|
free((char*) val_as_obj->keys[lpc]);
|
|
|
|
yajl_tree_free(val_as_obj->values[lpc]);
|
|
|
|
val_as_obj->len -= 1;
|
|
|
|
for (auto lpc2 = lpc; lpc2 < val_as_obj->len; lpc2++) {
|
|
|
|
val_as_obj->keys[lpc2] = val_as_obj->keys[lpc2 + 1];
|
|
|
|
val_as_obj->values[lpc2] = val_as_obj->values[lpc2 + 1];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lpc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
json_path_handler_base::json_path_handler_base(const std::string& property)
|
|
|
|
: jph_property(property.back() == '#'
|
|
|
|
? property.substr(0, property.size() - 1)
|
|
|
|
: property),
|
|
|
|
jph_regex(
|
|
|
|
std::make_shared<pcrepp>(pcrepp::quote(property), PCRE_ANCHORED)),
|
|
|
|
jph_is_array(property.back() == '#')
|
|
|
|
{
|
|
|
|
memset(&this->jph_callbacks, 0, sizeof(this->jph_callbacks));
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string
|
|
|
|
scrub_pattern(const std::string& pattern)
|
|
|
|
{
|
|
|
|
static const std::regex CAPTURE(R"(\(\?\<\w+\>)");
|
|
|
|
|
|
|
|
return std::regex_replace(pattern, CAPTURE, "(");
|
|
|
|
}
|
|
|
|
|
|
|
|
json_path_handler_base::json_path_handler_base(const pcrepp& property)
|
|
|
|
: jph_property(scrub_pattern(property.p_pattern)),
|
|
|
|
jph_regex(std::make_shared<pcrepp>(property)),
|
|
|
|
jph_is_array(property.p_pattern.back() == '#'),
|
|
|
|
jph_is_pattern_property(true)
|
|
|
|
{
|
|
|
|
memset(&this->jph_callbacks, 0, sizeof(this->jph_callbacks));
|
|
|
|
}
|
|
|
|
|
|
|
|
json_path_handler_base::json_path_handler_base(std::string property,
|
|
|
|
const pcrepp& property_re)
|
|
|
|
: jph_property(std::move(property)),
|
|
|
|
jph_regex(std::make_shared<pcrepp>(property_re)),
|
|
|
|
jph_is_array(property_re.p_pattern.find('#') != std::string::npos)
|
|
|
|
{
|
|
|
|
memset(&this->jph_callbacks, 0, sizeof(this->jph_callbacks));
|
|
|
|
}
|
|
|
|
|
|
|
|
json_path_handler_base::json_path_handler_base(
|
|
|
|
std::string property, const std::shared_ptr<pcrepp>& property_re)
|
|
|
|
: jph_property(std::move(property)), jph_regex(property_re),
|
|
|
|
jph_is_array(property_re->p_pattern.find('#') != std::string::npos)
|
|
|
|
{
|
|
|
|
memset(&this->jph_callbacks, 0, sizeof(this->jph_callbacks));
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_gen_status
|
|
|
|
json_path_handler_base::gen(yajlpp_gen_context& ygc, yajl_gen handle) const
|
|
|
|
{
|
|
|
|
if (this->jph_is_array) {
|
|
|
|
auto size = this->jph_size_provider(ygc.ygc_obj_stack.top());
|
|
|
|
|
|
|
|
yajl_gen_string(handle, this->jph_property);
|
|
|
|
yajl_gen_array_open(handle);
|
|
|
|
for (size_t index = 0; index < size; index++) {
|
|
|
|
pcre_context_static<30> pc;
|
|
|
|
pcre_input pi("");
|
|
|
|
|
|
|
|
yajlpp_provider_context ypc{{pc, pi}, index};
|
|
|
|
yajlpp_gen_context elem_ygc(handle, *this->jph_children);
|
|
|
|
elem_ygc.ygc_depth = 1;
|
|
|
|
elem_ygc.ygc_obj_stack.push(
|
|
|
|
this->jph_obj_provider(ypc, ygc.ygc_obj_stack.top()));
|
|
|
|
|
|
|
|
elem_ygc.gen();
|
|
|
|
}
|
|
|
|
yajl_gen_array_close(handle);
|
|
|
|
|
|
|
|
return yajl_gen_status_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> local_paths;
|
|
|
|
|
|
|
|
if (this->jph_path_provider) {
|
|
|
|
this->jph_path_provider(ygc.ygc_obj_stack.top(), local_paths);
|
|
|
|
} else {
|
|
|
|
local_paths.emplace_back(this->jph_property);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->jph_children) {
|
|
|
|
for (const auto& lpath : local_paths) {
|
|
|
|
std::string full_path = lpath;
|
|
|
|
if (this->jph_path_provider) {
|
|
|
|
full_path += "/";
|
|
|
|
}
|
|
|
|
int start_depth = ygc.ygc_depth;
|
|
|
|
|
|
|
|
yajl_gen_string(handle, lpath);
|
|
|
|
yajl_gen_map_open(handle);
|
|
|
|
ygc.ygc_depth += 1;
|
|
|
|
|
|
|
|
if (this->jph_obj_provider) {
|
|
|
|
pcre_context_static<30> pc;
|
|
|
|
pcre_input pi(full_path);
|
|
|
|
|
|
|
|
this->jph_regex->match(pc, pi);
|
|
|
|
ygc.ygc_obj_stack.push(this->jph_obj_provider(
|
|
|
|
{{pc, pi}, yajlpp_provider_context::nindex},
|
|
|
|
ygc.ygc_obj_stack.top()));
|
|
|
|
if (!ygc.ygc_default_stack.empty()) {
|
|
|
|
ygc.ygc_default_stack.push(this->jph_obj_provider(
|
|
|
|
{{pc, pi}, yajlpp_provider_context::nindex},
|
|
|
|
ygc.ygc_default_stack.top()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& jph : this->jph_children->jpc_children) {
|
|
|
|
yajl_gen_status status = jph.gen(ygc, handle);
|
|
|
|
|
|
|
|
const unsigned char* buf;
|
|
|
|
size_t len;
|
|
|
|
yajl_gen_get_buf(handle, &buf, &len);
|
|
|
|
if (status != yajl_gen_status_ok) {
|
|
|
|
log_error("yajl_gen failure for: %s -- %d",
|
|
|
|
jph.jph_property.c_str(),
|
|
|
|
status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->jph_obj_provider) {
|
|
|
|
ygc.ygc_obj_stack.pop();
|
|
|
|
if (!ygc.ygc_default_stack.empty()) {
|
|
|
|
ygc.ygc_default_stack.pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (ygc.ygc_depth > start_depth) {
|
|
|
|
yajl_gen_map_close(handle);
|
|
|
|
ygc.ygc_depth -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (this->jph_gen_callback != nullptr) {
|
|
|
|
return this->jph_gen_callback(ygc, *this, handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
return yajl_gen_status_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* const SCHEMA_TYPE_STRINGS[] = {
|
|
|
|
"any",
|
|
|
|
"boolean",
|
|
|
|
"integer",
|
|
|
|
"number",
|
|
|
|
"string",
|
|
|
|
"array",
|
|
|
|
"object",
|
|
|
|
};
|
|
|
|
|
|
|
|
yajl_gen_status
|
|
|
|
json_path_handler_base::gen_schema(yajlpp_gen_context& ygc) const
|
|
|
|
{
|
|
|
|
if (this->jph_children) {
|
|
|
|
{
|
|
|
|
yajlpp_map schema(ygc.ygc_handle);
|
|
|
|
|
|
|
|
if (this->jph_description && this->jph_description[0]) {
|
|
|
|
schema.gen("description");
|
|
|
|
schema.gen(this->jph_description);
|
|
|
|
}
|
|
|
|
if (this->jph_is_pattern_property) {
|
|
|
|
ygc.ygc_path.emplace_back(fmt::format(
|
|
|
|
FMT_STRING("<{}>"), this->jph_regex->name_for_capture(0)));
|
|
|
|
} else {
|
|
|
|
ygc.ygc_path.emplace_back(this->jph_property);
|
|
|
|
}
|
|
|
|
if (this->jph_children->jpc_definition_id.empty()) {
|
|
|
|
schema.gen("title");
|
|
|
|
schema.gen(fmt::format(FMT_STRING("/{}"),
|
|
|
|
fmt::join(ygc.ygc_path, "/")));
|
|
|
|
schema.gen("type");
|
|
|
|
if (this->jph_is_array) {
|
|
|
|
if (this->jph_regex->p_pattern.find("#?")
|
|
|
|
== std::string::npos) {
|
|
|
|
schema.gen("array");
|
|
|
|
} else {
|
|
|
|
yajlpp_array type_array(ygc.ygc_handle);
|
|
|
|
|
|
|
|
type_array.gen("array");
|
|
|
|
for (auto schema_type : this->get_types()) {
|
|
|
|
type_array.gen(
|
|
|
|
SCHEMA_TYPE_STRINGS[(int) schema_type]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
schema.gen("items");
|
|
|
|
yajl_gen_map_open(ygc.ygc_handle);
|
|
|
|
yajl_gen_string(ygc.ygc_handle, "type");
|
|
|
|
this->gen_schema_type(ygc);
|
|
|
|
} else {
|
|
|
|
this->gen_schema_type(ygc);
|
|
|
|
}
|
|
|
|
this->jph_children->gen_schema(ygc);
|
|
|
|
if (this->jph_is_array) {
|
|
|
|
yajl_gen_map_close(ygc.ygc_handle);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
schema.gen("title");
|
|
|
|
schema.gen(fmt::format(FMT_STRING("/{}"),
|
|
|
|
fmt::join(ygc.ygc_path, "/")));
|
|
|
|
this->jph_children->gen_schema(ygc);
|
|
|
|
}
|
|
|
|
ygc.ygc_path.pop_back();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
yajlpp_map schema(ygc.ygc_handle);
|
|
|
|
|
|
|
|
if (this->jph_is_pattern_property) {
|
|
|
|
ygc.ygc_path.emplace_back(fmt::format(
|
|
|
|
FMT_STRING("<{}>"), this->jph_regex->name_for_capture(0)));
|
|
|
|
} else {
|
|
|
|
ygc.ygc_path.emplace_back(this->jph_property);
|
|
|
|
}
|
|
|
|
|
|
|
|
schema.gen("title");
|
|
|
|
schema.gen(
|
|
|
|
fmt::format(FMT_STRING("/{}"), fmt::join(ygc.ygc_path, "/")));
|
|
|
|
if (this->jph_description && this->jph_description[0]) {
|
|
|
|
schema.gen("description");
|
|
|
|
schema.gen(this->jph_description);
|
|
|
|
}
|
|
|
|
|
|
|
|
schema.gen("type");
|
|
|
|
|
|
|
|
if (this->jph_is_array) {
|
|
|
|
if (this->jph_regex->p_pattern.find("#?") == std::string::npos) {
|
|
|
|
schema.gen("array");
|
|
|
|
} else {
|
|
|
|
yajlpp_array type_array(ygc.ygc_handle);
|
|
|
|
|
|
|
|
type_array.gen("array");
|
|
|
|
for (auto schema_type : this->get_types()) {
|
|
|
|
type_array.gen(SCHEMA_TYPE_STRINGS[(int) schema_type]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
yajl_gen_string(ygc.ygc_handle, "items");
|
|
|
|
yajl_gen_map_open(ygc.ygc_handle);
|
|
|
|
yajl_gen_string(ygc.ygc_handle, "type");
|
|
|
|
}
|
|
|
|
|
|
|
|
this->gen_schema_type(ygc);
|
|
|
|
|
|
|
|
if (!this->jph_examples.empty()) {
|
|
|
|
schema.gen("examples");
|
|
|
|
|
|
|
|
yajlpp_array example_array(ygc.ygc_handle);
|
|
|
|
for (auto& ex : this->jph_examples) {
|
|
|
|
example_array.gen(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->jph_is_array) {
|
|
|
|
yajl_gen_map_close(ygc.ygc_handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
ygc.ygc_path.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
return yajl_gen_status_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_gen_status
|
|
|
|
json_path_handler_base::gen_schema_type(yajlpp_gen_context& ygc) const
|
|
|
|
{
|
|
|
|
yajlpp_generator schema(ygc.ygc_handle);
|
|
|
|
|
|
|
|
auto types = this->get_types();
|
|
|
|
if (types.size() == 1) {
|
|
|
|
yajl_gen_string(ygc.ygc_handle, SCHEMA_TYPE_STRINGS[(int) types[0]]);
|
|
|
|
} else {
|
|
|
|
yajlpp_array type_array(ygc.ygc_handle);
|
|
|
|
|
|
|
|
for (auto schema_type : types) {
|
|
|
|
type_array.gen(SCHEMA_TYPE_STRINGS[(int) schema_type]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& schema_type : types) {
|
|
|
|
switch (schema_type) {
|
|
|
|
case schema_type_t::STRING:
|
|
|
|
if (this->jph_min_length > 0) {
|
|
|
|
schema("minLength");
|
|
|
|
schema(this->jph_min_length);
|
|
|
|
}
|
|
|
|
if (this->jph_max_length < INT_MAX) {
|
|
|
|
schema("maxLength");
|
|
|
|
schema(this->jph_max_length);
|
|
|
|
}
|
|
|
|
if (this->jph_pattern_re) {
|
|
|
|
schema("pattern");
|
|
|
|
schema(this->jph_pattern_re);
|
|
|
|
}
|
|
|
|
if (this->jph_enum_values) {
|
|
|
|
schema("enum");
|
|
|
|
|
|
|
|
yajlpp_array enum_array(ygc.ygc_handle);
|
|
|
|
for (int lpc = 0; this->jph_enum_values[lpc].first; lpc++) {
|
|
|
|
enum_array.gen(this->jph_enum_values[lpc].first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case schema_type_t::INTEGER:
|
|
|
|
case schema_type_t::NUMBER:
|
|
|
|
if (this->jph_min_value > LLONG_MIN) {
|
|
|
|
schema("minimum");
|
|
|
|
schema(this->jph_min_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return yajl_gen_keys_must_be_strings;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
json_path_handler_base::walk(
|
|
|
|
const std::function<
|
|
|
|
void(const json_path_handler_base&, const std::string&, void*)>& cb,
|
|
|
|
void* root,
|
|
|
|
const std::string& base) const
|
|
|
|
{
|
|
|
|
std::vector<std::string> local_paths;
|
|
|
|
|
|
|
|
if (this->jph_path_provider) {
|
|
|
|
this->jph_path_provider(root, local_paths);
|
|
|
|
|
|
|
|
for (auto& lpath : local_paths) {
|
|
|
|
cb(*this,
|
|
|
|
fmt::format(FMT_STRING("{}{}{}"),
|
|
|
|
base,
|
|
|
|
lpath,
|
|
|
|
this->jph_children ? "/" : ""),
|
|
|
|
nullptr);
|
|
|
|
}
|
|
|
|
if (this->jph_obj_deleter) {
|
|
|
|
local_paths.clear();
|
|
|
|
this->jph_path_provider(root, local_paths);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
local_paths.emplace_back(this->jph_property);
|
|
|
|
|
|
|
|
std::string full_path = base + this->jph_property;
|
|
|
|
if (this->jph_children) {
|
|
|
|
full_path += "/";
|
|
|
|
}
|
|
|
|
cb(*this, full_path, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->jph_children) {
|
|
|
|
for (const auto& lpath : local_paths) {
|
|
|
|
for (const auto& jph : this->jph_children->jpc_children) {
|
|
|
|
static const auto POSS_SRC
|
|
|
|
= intern_string::lookup("possibilities");
|
|
|
|
|
|
|
|
std::string full_path = base + lpath;
|
|
|
|
if (this->jph_children) {
|
|
|
|
full_path += "/";
|
|
|
|
}
|
|
|
|
json_path_container dummy{
|
|
|
|
json_path_handler(this->jph_property, this->jph_regex),
|
|
|
|
};
|
|
|
|
|
|
|
|
yajlpp_parse_context ypc(POSS_SRC, &dummy);
|
|
|
|
void* child_root = root;
|
|
|
|
|
|
|
|
ypc.set_path(full_path).with_obj(root).update_callbacks();
|
|
|
|
if (this->jph_obj_provider) {
|
|
|
|
std::string full_path = lpath + "/";
|
|
|
|
pcre_input pi(full_path);
|
|
|
|
|
|
|
|
if (!this->jph_regex->match(ypc.ypc_pcre_context, pi)) {
|
|
|
|
ensure(false);
|
|
|
|
}
|
|
|
|
child_root = this->jph_obj_provider(
|
|
|
|
{{ypc.ypc_pcre_context, pi},
|
|
|
|
yajlpp_provider_context::nindex},
|
|
|
|
root);
|
|
|
|
}
|
|
|
|
|
|
|
|
jph.walk(cb, child_root, full_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (auto& lpath : local_paths) {
|
|
|
|
void* field = nullptr;
|
|
|
|
|
|
|
|
if (this->jph_field_getter) {
|
|
|
|
field = this->jph_field_getter(root, lpath);
|
|
|
|
}
|
|
|
|
cb(*this, base + lpath, field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nonstd::optional<int>
|
|
|
|
json_path_handler_base::to_enum_value(const string_fragment& sf) const
|
|
|
|
{
|
|
|
|
for (int lpc = 0; this->jph_enum_values[lpc].first; lpc++) {
|
|
|
|
const enum_value_t& ev = this->jph_enum_values[lpc];
|
|
|
|
|
|
|
|
if (sf == ev.first) {
|
|
|
|
return ev.second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nonstd::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char*
|
|
|
|
json_path_handler_base::to_enum_string(int value) const
|
|
|
|
{
|
|
|
|
for (int lpc = 0; this->jph_enum_values[lpc].first; lpc++) {
|
|
|
|
const auto& ev = this->jph_enum_values[lpc];
|
|
|
|
|
|
|
|
if (ev.second == value) {
|
|
|
|
return ev.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<json_path_handler_base::schema_type_t>
|
|
|
|
json_path_handler_base::get_types() const
|
|
|
|
{
|
|
|
|
std::vector<schema_type_t> retval;
|
|
|
|
|
|
|
|
if (this->jph_callbacks.yajl_boolean) {
|
|
|
|
retval.push_back(schema_type_t::BOOLEAN);
|
|
|
|
}
|
|
|
|
if (this->jph_callbacks.yajl_integer) {
|
|
|
|
retval.push_back(schema_type_t::INTEGER);
|
|
|
|
}
|
|
|
|
if (this->jph_callbacks.yajl_double || this->jph_callbacks.yajl_number) {
|
|
|
|
retval.push_back(schema_type_t::NUMBER);
|
|
|
|
}
|
|
|
|
if (this->jph_callbacks.yajl_string) {
|
|
|
|
retval.push_back(schema_type_t::STRING);
|
|
|
|
}
|
|
|
|
if (this->jph_children) {
|
|
|
|
retval.push_back(schema_type_t::OBJECT);
|
|
|
|
}
|
|
|
|
if (retval.empty()) {
|
|
|
|
retval.push_back(schema_type_t::ANY);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
yajlpp_parse_context::yajlpp_parse_context(
|
|
|
|
intern_string_t source, const struct json_path_container* handlers)
|
|
|
|
: ypc_source(source), ypc_handlers(handlers)
|
|
|
|
{
|
|
|
|
this->ypc_path.reserve(4096);
|
|
|
|
this->ypc_path.push_back('/');
|
|
|
|
this->ypc_path.push_back('\0');
|
|
|
|
this->ypc_callbacks = DEFAULT_CALLBACKS;
|
|
|
|
memset(&this->ypc_alt_callbacks, 0, sizeof(this->ypc_alt_callbacks));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
yajlpp_parse_context::map_start(void* ctx)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
|
|
|
|
int retval = 1;
|
|
|
|
|
|
|
|
require(ypc->ypc_path.size() >= 2);
|
|
|
|
|
|
|
|
ypc->ypc_path_index_stack.push_back(ypc->ypc_path.size() - 1);
|
|
|
|
|
|
|
|
if (ypc->ypc_path.size() > 1
|
|
|
|
&& ypc->ypc_path[ypc->ypc_path.size() - 2] == '#') {
|
|
|
|
ypc->ypc_array_index.back() += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ypc->ypc_alt_callbacks.yajl_start_map != nullptr) {
|
|
|
|
retval = ypc->ypc_alt_callbacks.yajl_start_map(ypc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
yajlpp_parse_context::map_key(void* ctx, const unsigned char* key, size_t len)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
|
|
|
|
int retval = 1;
|
|
|
|
|
|
|
|
require(ypc->ypc_path.size() >= 2);
|
|
|
|
|
|
|
|
ypc->ypc_path.resize(ypc->ypc_path_index_stack.back());
|
|
|
|
if (ypc->ypc_path.back() != '/') {
|
|
|
|
ypc->ypc_path.push_back('/');
|
|
|
|
}
|
|
|
|
for (size_t lpc = 0; lpc < len; lpc++) {
|
|
|
|
switch (key[lpc]) {
|
|
|
|
case '~':
|
|
|
|
ypc->ypc_path.push_back('~');
|
|
|
|
ypc->ypc_path.push_back('0');
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
ypc->ypc_path.push_back('~');
|
|
|
|
ypc->ypc_path.push_back('1');
|
|
|
|
break;
|
|
|
|
case '#':
|
|
|
|
ypc->ypc_path.push_back('~');
|
|
|
|
ypc->ypc_path.push_back('2');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ypc->ypc_path.push_back(key[lpc]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ypc->ypc_path.push_back('\0');
|
|
|
|
|
|
|
|
if (ypc->ypc_alt_callbacks.yajl_map_key != nullptr) {
|
|
|
|
retval = ypc->ypc_alt_callbacks.yajl_map_key(ctx, key, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ypc->ypc_handlers != nullptr) {
|
|
|
|
ypc->update_callbacks();
|
|
|
|
}
|
|
|
|
|
|
|
|
ensure(ypc->ypc_path.size() >= 2);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
yajlpp_parse_context::update_callbacks(const json_path_container* orig_handlers,
|
|
|
|
int child_start)
|
|
|
|
{
|
|
|
|
const json_path_container* handlers = orig_handlers;
|
|
|
|
|
|
|
|
this->ypc_current_handler = nullptr;
|
|
|
|
|
|
|
|
if (this->ypc_handlers == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->ypc_sibling_handlers = orig_handlers;
|
|
|
|
|
|
|
|
pcre_input pi(&this->ypc_path[0], 0, this->ypc_path.size() - 1);
|
|
|
|
|
|
|
|
this->ypc_callbacks = DEFAULT_CALLBACKS;
|
|
|
|
|
|
|
|
if (handlers == nullptr) {
|
|
|
|
handlers = this->ypc_handlers;
|
|
|
|
this->ypc_handler_stack.clear();
|
|
|
|
this->ypc_array_handler_count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this->ypc_active_paths.empty()) {
|
|
|
|
std::string curr_path(&this->ypc_path[0], this->ypc_path.size() - 1);
|
|
|
|
|
|
|
|
if (this->ypc_active_paths.find(curr_path)
|
|
|
|
== this->ypc_active_paths.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child_start == 0 && !this->ypc_obj_stack.empty()) {
|
|
|
|
while (this->ypc_obj_stack.size() > 1) {
|
|
|
|
this->ypc_obj_stack.pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& jph : handlers->jpc_children) {
|
|
|
|
pi.reset(&this->ypc_path[1 + child_start],
|
|
|
|
0,
|
|
|
|
this->ypc_path.size() - 2 - child_start);
|
|
|
|
if (jph.jph_regex->match(this->ypc_pcre_context, pi)) {
|
|
|
|
pcre_context::capture_t* cap = this->ypc_pcre_context.all();
|
|
|
|
|
|
|
|
if (jph.jph_is_array) {
|
|
|
|
this->ypc_array_handler_count += 1;
|
|
|
|
}
|
|
|
|
if (jph.jph_obj_provider) {
|
|
|
|
auto index = this->ypc_array_handler_count == 0
|
|
|
|
? static_cast<size_t>(-1)
|
|
|
|
: this->ypc_array_index[this->ypc_array_handler_count - 1];
|
|
|
|
|
|
|
|
if ((1 + child_start + cap->c_end
|
|
|
|
!= (int) this->ypc_path.size() - 1)
|
|
|
|
&& (!jph.is_array()
|
|
|
|
|| index != yajlpp_provider_context::nindex))
|
|
|
|
{
|
|
|
|
this->ypc_obj_stack.push(jph.jph_obj_provider(
|
|
|
|
{{this->ypc_pcre_context, pi}, index},
|
|
|
|
this->ypc_obj_stack.top()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jph.jph_children) {
|
|
|
|
this->ypc_handler_stack.emplace_back(&jph);
|
|
|
|
|
|
|
|
if (1 + child_start + cap->c_end
|
|
|
|
!= (int) this->ypc_path.size() - 1) {
|
|
|
|
this->update_callbacks(jph.jph_children,
|
|
|
|
1 + child_start + cap->c_end);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (1 + child_start + cap->c_end
|
|
|
|
!= (int) this->ypc_path.size() - 1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->ypc_current_handler = &jph;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jph.jph_callbacks.yajl_null != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_null = jph.jph_callbacks.yajl_null;
|
|
|
|
}
|
|
|
|
if (jph.jph_callbacks.yajl_boolean != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_boolean
|
|
|
|
= jph.jph_callbacks.yajl_boolean;
|
|
|
|
}
|
|
|
|
if (jph.jph_callbacks.yajl_integer != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_integer
|
|
|
|
= jph.jph_callbacks.yajl_integer;
|
|
|
|
}
|
|
|
|
if (jph.jph_callbacks.yajl_double != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_double = jph.jph_callbacks.yajl_double;
|
|
|
|
}
|
|
|
|
if (jph.jph_callbacks.yajl_string != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_string = jph.jph_callbacks.yajl_string;
|
|
|
|
}
|
|
|
|
if (jph.jph_is_array) {
|
|
|
|
this->ypc_array_handler_count -= 1;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
yajlpp_parse_context::map_end(void* ctx)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
|
|
|
|
int retval = 1;
|
|
|
|
|
|
|
|
ypc->ypc_path.resize(ypc->ypc_path_index_stack.back());
|
|
|
|
ypc->ypc_path.push_back('\0');
|
|
|
|
ypc->ypc_path_index_stack.pop_back();
|
|
|
|
|
|
|
|
if (ypc->ypc_alt_callbacks.yajl_end_map != nullptr) {
|
|
|
|
retval = ypc->ypc_alt_callbacks.yajl_end_map(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
ypc->update_callbacks();
|
|
|
|
|
|
|
|
ensure(ypc->ypc_path.size() >= 2);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
yajlpp_parse_context::array_start(void* ctx)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
|
|
|
|
int retval = 1;
|
|
|
|
|
|
|
|
ypc->ypc_path_index_stack.push_back(ypc->ypc_path.size() - 1);
|
|
|
|
ypc->ypc_path[ypc->ypc_path.size() - 1] = '#';
|
|
|
|
ypc->ypc_path.push_back('\0');
|
|
|
|
ypc->ypc_array_index.push_back(-1);
|
|
|
|
|
|
|
|
if (ypc->ypc_alt_callbacks.yajl_start_array != nullptr) {
|
|
|
|
retval = ypc->ypc_alt_callbacks.yajl_start_array(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
ypc->update_callbacks();
|
|
|
|
|
|
|
|
ensure(ypc->ypc_path.size() >= 2);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
yajlpp_parse_context::array_end(void* ctx)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
|
|
|
|
int retval = 1;
|
|
|
|
|
|
|
|
ypc->ypc_path.resize(ypc->ypc_path_index_stack.back());
|
|
|
|
ypc->ypc_path.push_back('\0');
|
|
|
|
ypc->ypc_path_index_stack.pop_back();
|
|
|
|
ypc->ypc_array_index.pop_back();
|
|
|
|
|
|
|
|
if (ypc->ypc_alt_callbacks.yajl_end_array != nullptr) {
|
|
|
|
retval = ypc->ypc_alt_callbacks.yajl_end_array(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
ypc->update_callbacks();
|
|
|
|
|
|
|
|
ensure(ypc->ypc_path.size() >= 2);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
yajlpp_parse_context::handle_unused(void* ctx)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
|
|
|
|
|
|
|
|
if (ypc->ypc_ignore_unused) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const json_path_handler_base* handler = ypc->ypc_current_handler;
|
|
|
|
lnav::console::user_message msg;
|
|
|
|
|
|
|
|
if (handler != nullptr && strlen(handler->jph_synopsis) > 0
|
|
|
|
&& strlen(handler->jph_description) > 0)
|
|
|
|
{
|
|
|
|
auto help_text = handler->get_help_text(ypc);
|
|
|
|
std::vector<std::string> expected_types;
|
|
|
|
|
|
|
|
if (ypc->ypc_callbacks.yajl_boolean
|
|
|
|
!= (int (*)(void*, int)) yajlpp_parse_context::handle_unused)
|
|
|
|
{
|
|
|
|
expected_types.emplace_back("boolean");
|
|
|
|
}
|
|
|
|
if (ypc->ypc_callbacks.yajl_integer
|
|
|
|
!= (int (*)(void*, long long)) yajlpp_parse_context::handle_unused)
|
|
|
|
{
|
|
|
|
expected_types.emplace_back("integer");
|
|
|
|
}
|
|
|
|
if (ypc->ypc_callbacks.yajl_double
|
|
|
|
!= (int (*)(void*, double)) yajlpp_parse_context::handle_unused)
|
|
|
|
{
|
|
|
|
expected_types.emplace_back("float");
|
|
|
|
}
|
|
|
|
if (ypc->ypc_callbacks.yajl_string
|
|
|
|
!= (int (*)(void*, const unsigned char*, size_t))
|
|
|
|
yajlpp_parse_context::handle_unused)
|
|
|
|
{
|
|
|
|
expected_types.emplace_back("string");
|
|
|
|
}
|
|
|
|
if (!expected_types.empty()) {
|
|
|
|
help_text.appendf(
|
|
|
|
FMT_STRING(" expecting one of the following types: {}"),
|
|
|
|
fmt::join(expected_types, ", "));
|
|
|
|
}
|
|
|
|
msg = lnav::console::user_message::warning(
|
|
|
|
attr_line_t("unexpected data for property ")
|
|
|
|
.append_quoted(lnav::roles::symbol(
|
|
|
|
ypc->get_full_path().to_string())))
|
|
|
|
.with_help(help_text);
|
|
|
|
} else if (ypc->ypc_path[1]) {
|
|
|
|
msg = lnav::console::user_message::warning(
|
|
|
|
attr_line_t("unexpected value for property ")
|
|
|
|
.append_quoted(
|
|
|
|
lnav::roles::symbol(ypc->get_full_path().to_string())));
|
|
|
|
} else {
|
|
|
|
msg = lnav::console::user_message::error("unexpected JSON value");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handler == nullptr) {
|
|
|
|
const json_path_container* accepted_handlers;
|
|
|
|
|
|
|
|
if (ypc->ypc_sibling_handlers) {
|
|
|
|
accepted_handlers = ypc->ypc_sibling_handlers;
|
|
|
|
} else {
|
|
|
|
accepted_handlers = ypc->ypc_handlers;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr_line_t help_text;
|
|
|
|
|
|
|
|
if (accepted_handlers->jpc_children.size() == 1
|
|
|
|
&& accepted_handlers->jpc_children.front().jph_is_array)
|
|
|
|
{
|
|
|
|
const auto& jph = accepted_handlers->jpc_children.front();
|
|
|
|
|
|
|
|
help_text.append("expecting an array of ")
|
|
|
|
.append(lnav::roles::variable(jph.jph_synopsis))
|
|
|
|
.append(" values");
|
|
|
|
} else {
|
|
|
|
help_text.append(lnav::roles::h2("Available Properties"))
|
|
|
|
.append("\n");
|
|
|
|
for (const auto& jph : accepted_handlers->jpc_children) {
|
|
|
|
help_text.append(" ")
|
|
|
|
.append(lnav::roles::symbol(jph.jph_property))
|
|
|
|
.append(lnav::roles::symbol(
|
|
|
|
jph.jph_children != nullptr ? "/" : ""))
|
|
|
|
.append(" ")
|
|
|
|
.append(lnav::roles::variable(jph.jph_synopsis))
|
|
|
|
.append("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
msg.with_help(help_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
msg.with_snippet(ypc->get_snippet());
|
|
|
|
|
|
|
|
ypc->report_error(msg);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
yajlpp_parse_context::handle_unused_or_delete(void* ctx)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
|
|
|
|
|
|
|
|
if (!ypc->ypc_handler_stack.empty()
|
|
|
|
&& ypc->ypc_handler_stack.back()->jph_obj_deleter)
|
|
|
|
{
|
|
|
|
pcre_context_static<30> pc;
|
|
|
|
auto key_start = ypc->ypc_path_index_stack.back();
|
|
|
|
pcre_input pi(&ypc->ypc_path[key_start + 1],
|
|
|
|
0,
|
|
|
|
ypc->ypc_path.size() - key_start - 2);
|
|
|
|
yajlpp_provider_context provider_ctx{{pc, pi}, static_cast<size_t>(-1)};
|
|
|
|
ypc->ypc_handler_stack.back()->jph_regex->match(pc, pi);
|
|
|
|
|
|
|
|
ypc->ypc_handler_stack.back()->jph_obj_deleter(
|
|
|
|
provider_ctx, ypc->ypc_obj_stack.top());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return handle_unused(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
const yajl_callbacks yajlpp_parse_context::DEFAULT_CALLBACKS = {
|
|
|
|
yajlpp_parse_context::handle_unused_or_delete,
|
|
|
|
(int (*)(void*, int)) yajlpp_parse_context::handle_unused,
|
|
|
|
(int (*)(void*, long long)) yajlpp_parse_context::handle_unused,
|
|
|
|
(int (*)(void*, double)) yajlpp_parse_context::handle_unused,
|
|
|
|
nullptr,
|
|
|
|
(int (*)(void*, const unsigned char*, size_t))
|
|
|
|
yajlpp_parse_context::handle_unused,
|
|
|
|
yajlpp_parse_context::map_start,
|
|
|
|
yajlpp_parse_context::map_key,
|
|
|
|
yajlpp_parse_context::map_end,
|
|
|
|
yajlpp_parse_context::array_start,
|
|
|
|
yajlpp_parse_context::array_end,
|
|
|
|
};
|
|
|
|
|
|
|
|
yajl_status
|
|
|
|
yajlpp_parse_context::parse(const unsigned char* jsonText, size_t jsonTextLen)
|
|
|
|
{
|
|
|
|
this->ypc_json_text = jsonText;
|
|
|
|
this->ypc_json_text_len = jsonTextLen;
|
|
|
|
|
|
|
|
yajl_status retval = yajl_parse(this->ypc_handle, jsonText, jsonTextLen);
|
|
|
|
|
|
|
|
size_t consumed = yajl_get_bytes_consumed(this->ypc_handle);
|
|
|
|
|
|
|
|
this->ypc_line_number
|
|
|
|
+= std::count(&jsonText[0], &jsonText[consumed], '\n');
|
|
|
|
|
|
|
|
this->ypc_json_text = nullptr;
|
|
|
|
|
|
|
|
if (retval != yajl_status_ok && this->ypc_error_reporter) {
|
|
|
|
auto* msg = yajl_get_error(this->ypc_handle, 1, jsonText, jsonTextLen);
|
|
|
|
|
|
|
|
this->report_error(
|
|
|
|
lnav::console::user_message::error("invalid JSON")
|
|
|
|
.with_snippet(lnav::console::snippet::from(this->ypc_source,
|
|
|
|
(const char*) msg)
|
|
|
|
.with_line(this->get_line_number())));
|
|
|
|
yajl_free_error(this->ypc_handle, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_status
|
|
|
|
yajlpp_parse_context::complete_parse()
|
|
|
|
{
|
|
|
|
yajl_status retval = yajl_complete_parse(this->ypc_handle);
|
|
|
|
|
|
|
|
if (retval != yajl_status_ok && this->ypc_error_reporter) {
|
|
|
|
auto* msg = yajl_get_error(this->ypc_handle, 0, nullptr, 0);
|
|
|
|
|
|
|
|
this->report_error(lnav::console::user_message::error("invalid JSON")
|
|
|
|
.with_reason((const char*) msg)
|
|
|
|
.with_snippet(this->get_snippet()));
|
|
|
|
yajl_free_error(this->ypc_handle, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
yajlpp_parse_context::parse_doc(const string_fragment& sf)
|
|
|
|
{
|
|
|
|
bool retval = true;
|
|
|
|
|
|
|
|
this->ypc_json_text = (const unsigned char*) sf.data();
|
|
|
|
this->ypc_json_text_len = sf.length();
|
|
|
|
|
|
|
|
auto rc = yajl_parse(this->ypc_handle, this->ypc_json_text, sf.length());
|
|
|
|
size_t consumed = yajl_get_bytes_consumed(this->ypc_handle);
|
|
|
|
this->ypc_line_number += std::count(
|
|
|
|
&this->ypc_json_text[0], &this->ypc_json_text[consumed], '\n');
|
|
|
|
|
|
|
|
if (rc != yajl_status_ok) {
|
|
|
|
if (this->ypc_error_reporter) {
|
|
|
|
auto* msg = yajl_get_error(this->ypc_handle,
|
|
|
|
1,
|
|
|
|
this->ypc_json_text,
|
|
|
|
this->ypc_json_text_len);
|
|
|
|
|
|
|
|
this->report_error(
|
|
|
|
lnav::console::user_message::error("invalid JSON")
|
|
|
|
.with_reason((const char*) msg)
|
|
|
|
.with_snippet(this->get_snippet()));
|
|
|
|
yajl_free_error(this->ypc_handle, msg);
|
|
|
|
}
|
|
|
|
retval = false;
|
|
|
|
} else if (this->complete_parse() != yajl_status_ok) {
|
|
|
|
retval = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->ypc_json_text = nullptr;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
const intern_string_t
|
|
|
|
yajlpp_parse_context::get_path() const
|
|
|
|
{
|
|
|
|
if (this->ypc_path.size() <= 1) {
|
|
|
|
return intern_string_t();
|
|
|
|
}
|
|
|
|
return intern_string::lookup(&this->ypc_path[1], this->ypc_path.size() - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
const intern_string_t
|
|
|
|
yajlpp_parse_context::get_full_path() const
|
|
|
|
{
|
|
|
|
if (this->ypc_path.size() <= 1) {
|
|
|
|
static intern_string_t SLASH = intern_string::lookup("/");
|
|
|
|
|
|
|
|
return SLASH;
|
|
|
|
}
|
|
|
|
return intern_string::lookup(&this->ypc_path[0], this->ypc_path.size() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
yajlpp_parse_context::reset(const struct json_path_container* handlers)
|
|
|
|
{
|
|
|
|
this->ypc_handlers = handlers;
|
|
|
|
this->ypc_path.clear();
|
|
|
|
this->ypc_path.push_back('/');
|
|
|
|
this->ypc_path.push_back('\0');
|
|
|
|
this->ypc_path_index_stack.clear();
|
|
|
|
this->ypc_array_index.clear();
|
|
|
|
this->ypc_array_handler_count = 0;
|
|
|
|
this->ypc_callbacks = DEFAULT_CALLBACKS;
|
|
|
|
memset(&this->ypc_alt_callbacks, 0, sizeof(this->ypc_alt_callbacks));
|
|
|
|
this->ypc_sibling_handlers = nullptr;
|
|
|
|
this->ypc_current_handler = nullptr;
|
|
|
|
while (!this->ypc_obj_stack.empty()) {
|
|
|
|
this->ypc_obj_stack.pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
yajlpp_parse_context::set_static_handler(json_path_handler_base& jph)
|
|
|
|
{
|
|
|
|
this->ypc_path.clear();
|
|
|
|
this->ypc_path.push_back('/');
|
|
|
|
this->ypc_path.push_back('\0');
|
|
|
|
this->ypc_path_index_stack.clear();
|
|
|
|
this->ypc_array_index.clear();
|
|
|
|
this->ypc_array_handler_count = 0;
|
|
|
|
if (jph.jph_callbacks.yajl_null != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_null = jph.jph_callbacks.yajl_null;
|
|
|
|
}
|
|
|
|
if (jph.jph_callbacks.yajl_boolean != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_boolean = jph.jph_callbacks.yajl_boolean;
|
|
|
|
}
|
|
|
|
if (jph.jph_callbacks.yajl_integer != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_integer = jph.jph_callbacks.yajl_integer;
|
|
|
|
}
|
|
|
|
if (jph.jph_callbacks.yajl_double != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_double = jph.jph_callbacks.yajl_double;
|
|
|
|
}
|
|
|
|
if (jph.jph_callbacks.yajl_string != nullptr) {
|
|
|
|
this->ypc_callbacks.yajl_string = jph.jph_callbacks.yajl_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
yajlpp_parse_context&
|
|
|
|
yajlpp_parse_context::set_path(const std::string& path)
|
|
|
|
{
|
|
|
|
this->ypc_path.resize(path.size() + 1);
|
|
|
|
std::copy(path.begin(), path.end(), this->ypc_path.begin());
|
|
|
|
this->ypc_path[path.size()] = '\0';
|
|
|
|
for (size_t lpc = 0; lpc < path.size(); lpc++) {
|
|
|
|
switch (path[lpc]) {
|
|
|
|
case '/':
|
|
|
|
this->ypc_path_index_stack.push_back(
|
|
|
|
this->ypc_path_index_stack.empty() ? 1 : 0 + lpc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char*
|
|
|
|
yajlpp_parse_context::get_path_fragment(int offset,
|
|
|
|
char* frag_in,
|
|
|
|
size_t& len_out) const
|
|
|
|
{
|
|
|
|
const char* retval;
|
|
|
|
size_t start, end;
|
|
|
|
|
|
|
|
if (offset < 0) {
|
|
|
|
offset = ((int) this->ypc_path_index_stack.size()) + offset;
|
|
|
|
}
|
|
|
|
start = this->ypc_path_index_stack[offset] + ((offset == 0) ? 0 : 1);
|
|
|
|
if ((offset + 1) < (int) this->ypc_path_index_stack.size()) {
|
|
|
|
end = this->ypc_path_index_stack[offset + 1];
|
|
|
|
} else {
|
|
|
|
end = this->ypc_path.size() - 1;
|
|
|
|
}
|
|
|
|
if (this->ypc_handlers) {
|
|
|
|
len_out
|
|
|
|
= json_ptr::decode(frag_in, &this->ypc_path[start], end - start);
|
|
|
|
retval = frag_in;
|
|
|
|
} else {
|
|
|
|
retval = &this->ypc_path[start];
|
|
|
|
len_out = end - start;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
yajlpp_parse_context::get_line_number() const
|
|
|
|
{
|
|
|
|
if (this->ypc_handle != nullptr && this->ypc_json_text) {
|
|
|
|
size_t consumed = yajl_get_bytes_consumed(this->ypc_handle);
|
|
|
|
long current_count = std::count(
|
|
|
|
&this->ypc_json_text[0], &this->ypc_json_text[consumed], '\n');
|
|
|
|
|
|
|
|
return this->ypc_line_number + current_count;
|
|
|
|
}
|
|
|
|
return this->ypc_line_number;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
yajlpp_gen_context::gen()
|
|
|
|
{
|
|
|
|
yajlpp_map root(this->ygc_handle);
|
|
|
|
|
|
|
|
for (const auto& jph : this->ygc_handlers->jpc_children) {
|
|
|
|
jph.gen(*this, this->ygc_handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
yajlpp_gen_context::gen_schema(const json_path_container* handlers)
|
|
|
|
{
|
|
|
|
if (handlers == nullptr) {
|
|
|
|
handlers = this->ygc_handlers;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
yajlpp_map schema(this->ygc_handle);
|
|
|
|
|
|
|
|
if (!handlers->jpc_schema_id.empty()) {
|
|
|
|
schema.gen("$id");
|
|
|
|
schema.gen(handlers->jpc_schema_id);
|
|
|
|
schema.gen("title");
|
|
|
|
schema.gen(handlers->jpc_schema_id);
|
|
|
|
}
|
|
|
|
schema.gen("$schema");
|
|
|
|
schema.gen("http://json-schema.org/draft-07/schema#");
|
|
|
|
if (!handlers->jpc_description.empty()) {
|
|
|
|
schema.gen("description");
|
|
|
|
schema.gen(handlers->jpc_description);
|
|
|
|
}
|
|
|
|
handlers->gen_schema(*this);
|
|
|
|
|
|
|
|
if (!this->ygc_schema_definitions.empty()) {
|
|
|
|
schema.gen("definitions");
|
|
|
|
|
|
|
|
yajlpp_map defs(this->ygc_handle);
|
|
|
|
for (auto& container : this->ygc_schema_definitions) {
|
|
|
|
defs.gen(container.first);
|
|
|
|
|
|
|
|
yajlpp_map def(this->ygc_handle);
|
|
|
|
|
|
|
|
def.gen("title");
|
|
|
|
def.gen(container.first);
|
|
|
|
if (!container.second->jpc_description.empty()) {
|
|
|
|
def.gen("description");
|
|
|
|
def.gen(container.second->jpc_description);
|
|
|
|
}
|
|
|
|
def.gen("type");
|
|
|
|
def.gen("object");
|
|
|
|
def.gen("$$target");
|
|
|
|
def.gen(fmt::format(FMT_STRING("#/definitions/{}"),
|
|
|
|
container.first));
|
|
|
|
container.second->gen_properties(*this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
yajlpp_gen_context&
|
|
|
|
yajlpp_gen_context::with_context(yajlpp_parse_context& ypc)
|
|
|
|
{
|
|
|
|
this->ygc_obj_stack = ypc.ypc_obj_stack;
|
|
|
|
if (ypc.ypc_current_handler == nullptr && !ypc.ypc_handler_stack.empty()
|
|
|
|
&& ypc.ypc_handler_stack.back() != nullptr)
|
|
|
|
{
|
|
|
|
this->ygc_handlers = ypc.ypc_handler_stack.back()->jph_children;
|
|
|
|
this->ygc_depth += 1;
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
json_path_handler&
|
|
|
|
json_path_handler::with_children(const json_path_container& container)
|
|
|
|
{
|
|
|
|
this->jph_children = &container;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
lnav::console::snippet
|
|
|
|
yajlpp_parse_context::get_snippet() const
|
|
|
|
{
|
|
|
|
auto line_number = this->get_line_number();
|
|
|
|
attr_line_t content;
|
|
|
|
|
|
|
|
if (this->ypc_json_text != nullptr) {
|
|
|
|
auto in_text_line = line_number - this->ypc_line_number;
|
|
|
|
const auto* line_start = this->ypc_json_text;
|
|
|
|
auto text_len_remaining = this->ypc_json_text_len;
|
|
|
|
|
|
|
|
while (in_text_line > 0) {
|
|
|
|
const auto* line_end = (const unsigned char*) memchr(
|
|
|
|
line_start, '\n', text_len_remaining);
|
|
|
|
if (line_end == nullptr) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
text_len_remaining -= (line_end - line_start) + 1;
|
|
|
|
line_start = line_end + 1;
|
|
|
|
in_text_line -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (text_len_remaining > 0) {
|
|
|
|
const auto* line_end = (const unsigned char*) memchr(
|
|
|
|
line_start, '\n', text_len_remaining);
|
|
|
|
if (line_end) {
|
|
|
|
text_len_remaining = (line_end - line_start);
|
|
|
|
}
|
|
|
|
content.append(
|
|
|
|
string_fragment::from_bytes(line_start, text_len_remaining));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
content.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
|
|
|
|
return lnav::console::snippet::from(this->ypc_source, content)
|
|
|
|
.with_line(line_number);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
json_path_handler_base::report_pattern_error(yajlpp_parse_context* ypc,
|
|
|
|
const std::string& value_str) const
|
|
|
|
{
|
|
|
|
ypc->report_error(
|
|
|
|
lnav::console::user_message::error(
|
|
|
|
attr_line_t()
|
|
|
|
.append_quoted(value_str)
|
|
|
|
.append(" is not a valid value for option ")
|
|
|
|
.append_quoted(
|
|
|
|
lnav::roles::symbol(ypc->get_full_path().to_string())))
|
|
|
|
.with_snippet(ypc->get_snippet())
|
|
|
|
.with_reason(attr_line_t("value does not match pattern: ")
|
|
|
|
.append(lnav::roles::symbol(this->jph_pattern_re)))
|
|
|
|
.with_help(this->get_help_text(ypc)));
|
|
|
|
}
|
|
|
|
|
|
|
|
attr_line_t
|
|
|
|
json_path_handler_base::get_help_text(const std::string& full_path) const
|
|
|
|
{
|
|
|
|
attr_line_t retval;
|
|
|
|
|
|
|
|
retval.append(lnav::roles::h2("Property Synopsis"))
|
|
|
|
.append("\n ")
|
|
|
|
.append(lnav::roles::symbol(full_path))
|
|
|
|
.append(" ")
|
|
|
|
.append(lnav::roles::variable(this->jph_synopsis))
|
|
|
|
.append("\n")
|
|
|
|
.append(lnav::roles::h2("Description"))
|
|
|
|
.append("\n ")
|
|
|
|
.append(this->jph_description)
|
|
|
|
.append("\n");
|
|
|
|
|
|
|
|
if (this->jph_enum_values != nullptr) {
|
|
|
|
retval.append(lnav::roles::h2("Allowed Values")).append("\n ");
|
|
|
|
|
|
|
|
for (int lpc = 0; this->jph_enum_values[lpc].first; lpc++) {
|
|
|
|
const auto& ev = this->jph_enum_values[lpc];
|
|
|
|
|
|
|
|
retval.append(lpc == 0 ? "" : ", ")
|
|
|
|
.append(lnav::roles::symbol(ev.first));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this->jph_examples.empty()) {
|
|
|
|
retval
|
|
|
|
.append(lnav::roles::h2(
|
|
|
|
this->jph_examples.size() == 1 ? "Example" : "Examples"))
|
|
|
|
.append("\n");
|
|
|
|
|
|
|
|
for (const auto& ex : this->jph_examples) {
|
|
|
|
retval.appendf(FMT_STRING(" {}\n"), ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr_line_t
|
|
|
|
json_path_handler_base::get_help_text(yajlpp_parse_context* ypc) const
|
|
|
|
{
|
|
|
|
return this->get_help_text(ypc->get_full_path().to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
json_path_handler_base::report_min_value_error(yajlpp_parse_context* ypc,
|
|
|
|
long long value) const
|
|
|
|
{
|
|
|
|
const auto* jph = ypc->ypc_current_handler;
|
|
|
|
|
|
|
|
ypc->report_error(
|
|
|
|
lnav::console::user_message::error(
|
|
|
|
attr_line_t()
|
|
|
|
.append_quoted(fmt::to_string(value))
|
|
|
|
.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 greater than or equal to ")
|
|
|
|
.append(lnav::roles::number(
|
|
|
|
fmt::to_string(jph->jph_min_value))))
|
|
|
|
.with_snippet(ypc->get_snippet())
|
|
|
|
.with_help(jph->get_help_text(ypc)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
json_path_handler_base::report_duration_error(
|
|
|
|
yajlpp_parse_context* ypc,
|
|
|
|
const std::string& value_str,
|
|
|
|
const relative_time::parse_error& pe) const
|
|
|
|
{
|
|
|
|
ypc->report_error(lnav::console::user_message::error(
|
|
|
|
attr_line_t()
|
|
|
|
.append_quoted(value_str)
|
|
|
|
.append(" is not a valid duration value "
|
|
|
|
"for option ")
|
|
|
|
.append_quoted(lnav::roles::symbol(
|
|
|
|
ypc->get_full_path().to_string())))
|
|
|
|
.with_snippet(ypc->get_snippet())
|
|
|
|
.with_reason(pe.pe_msg)
|
|
|
|
.with_help(this->get_help_text(ypc)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
json_path_handler_base::report_enum_error(yajlpp_parse_context* ypc,
|
|
|
|
const std::string& value_str) const
|
|
|
|
{
|
|
|
|
ypc->report_error(lnav::console::user_message::error(
|
|
|
|
attr_line_t()
|
|
|
|
.append_quoted(value_str)
|
|
|
|
.append(" is not a valid value for option ")
|
|
|
|
.append_quoted(lnav::roles::symbol(
|
|
|
|
ypc->get_full_path().to_string())))
|
|
|
|
.with_snippet(ypc->get_snippet())
|
|
|
|
.with_help(this->get_help_text(ypc)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
json_path_handler_base::report_regex_value_error(
|
|
|
|
yajlpp_parse_context* ypc,
|
|
|
|
const std::string& value,
|
|
|
|
const pcrepp::compile_error& pcre_error) const
|
|
|
|
{
|
|
|
|
attr_line_t pcre_error_content{value};
|
|
|
|
|
|
|
|
lnav::snippets::regex_highlighter(pcre_error_content,
|
|
|
|
pcre_error_content.length(),
|
|
|
|
line_range{
|
|
|
|
0,
|
|
|
|
(int) pcre_error_content.length(),
|
|
|
|
});
|
|
|
|
pcre_error_content.append("\n")
|
|
|
|
.append(pcre_error.ce_offset, ' ')
|
|
|
|
.append(lnav::roles::error("^ "))
|
|
|
|
.append(lnav::roles::error(pcre_error.ce_msg));
|
|
|
|
ypc->report_error(lnav::console::user_message::error(
|
|
|
|
attr_line_t()
|
|
|
|
.append_quoted(value)
|
|
|
|
.append(" is not a valid regular expression for "
|
|
|
|
"property ")
|
|
|
|
.append_quoted(
|
|
|
|
lnav::roles::symbol(ypc->get_full_path().to_string())))
|
|
|
|
.with_reason(pcre_error.ce_msg)
|
|
|
|
.with_snippet(ypc->get_snippet())
|
|
|
|
.with_snippet(lnav::console::snippet::from(
|
|
|
|
ypc->get_full_path(), pcre_error_content))
|
|
|
|
.with_help(this->get_help_text(ypc)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
json_path_container::gen_schema(yajlpp_gen_context& ygc) const
|
|
|
|
{
|
|
|
|
if (!this->jpc_definition_id.empty()) {
|
|
|
|
ygc.ygc_schema_definitions[this->jpc_definition_id] = this;
|
|
|
|
|
|
|
|
yajl_gen_string(ygc.ygc_handle, "$ref");
|
|
|
|
yajl_gen_string(ygc.ygc_handle,
|
|
|
|
fmt::format(FMT_STRING("#/definitions/{}"),
|
|
|
|
this->jpc_definition_id));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->gen_properties(ygc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
json_path_container::gen_properties(yajlpp_gen_context& ygc) const
|
|
|
|
{
|
|
|
|
auto pattern_count = count_if(
|
|
|
|
this->jpc_children.begin(), this->jpc_children.end(), [](auto& jph) {
|
|
|
|
return jph.jph_is_pattern_property;
|
|
|
|
});
|
|
|
|
auto plain_count = this->jpc_children.size() - pattern_count;
|
|
|
|
|
|
|
|
if (plain_count > 0) {
|
|
|
|
yajl_gen_string(ygc.ygc_handle, "properties");
|
|
|
|
{
|
|
|
|
yajlpp_map properties(ygc.ygc_handle);
|
|
|
|
|
|
|
|
for (auto& child_handler : this->jpc_children) {
|
|
|
|
if (child_handler.jph_is_pattern_property) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
properties.gen(child_handler.jph_property);
|
|
|
|
child_handler.gen_schema(ygc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pattern_count > 0) {
|
|
|
|
yajl_gen_string(ygc.ygc_handle, "patternProperties");
|
|
|
|
{
|
|
|
|
yajlpp_map properties(ygc.ygc_handle);
|
|
|
|
|
|
|
|
for (const auto& child_handler : this->jpc_children) {
|
|
|
|
if (!child_handler.jph_is_pattern_property) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
properties.gen(child_handler.jph_property);
|
|
|
|
child_handler.gen_schema(ygc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_gen_string(ygc.ygc_handle, "additionalProperties");
|
|
|
|
yajl_gen_bool(ygc.ygc_handle, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
schema_printer(FILE* file, const char* str, size_t len)
|
|
|
|
{
|
|
|
|
fwrite(str, len, 1, file);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dump_schema_to(const json_path_container& jpc, const char* internals_dir)
|
|
|
|
{
|
|
|
|
yajlpp_gen genner;
|
|
|
|
yajlpp_gen_context ygc(genner, jpc);
|
|
|
|
auto internals_dir_path = ghc::filesystem::path(internals_dir);
|
|
|
|
auto schema_file_name = ghc::filesystem::path(jpc.jpc_schema_id).filename();
|
|
|
|
auto schema_path = internals_dir_path / schema_file_name;
|
|
|
|
auto file = std::unique_ptr<FILE, decltype(&fclose)>(
|
|
|
|
fopen(schema_path.c_str(), "w+"), fclose);
|
|
|
|
|
|
|
|
if (!file.get()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_gen_config(genner, yajl_gen_beautify, true);
|
|
|
|
yajl_gen_config(
|
|
|
|
genner, yajl_gen_print_callback, schema_printer, file.get());
|
|
|
|
|
|
|
|
ygc.gen_schema();
|
|
|
|
}
|
|
|
|
|
|
|
|
string_fragment
|
|
|
|
yajlpp_gen::to_string_fragment()
|
|
|
|
{
|
|
|
|
const unsigned char* buf;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
yajl_gen_get_buf(this->yg_handle.in(), &buf, &len);
|
|
|
|
|
|
|
|
return string_fragment((const char*) buf, 0, len);
|
|
|
|
}
|