[yajlpp] flesh things out a bit more

pull/1072/head
Tim Stack 2 years ago
parent 468358a358
commit faeaf477ab

@ -453,8 +453,7 @@
"field": {
"title": "/<format_name>/line-format/field",
"description": "The name of the field to substitute at this position",
"type": "string",
"minLength": 1
"type": "string"
},
"default-value": {
"title": "/<format_name>/line-format/default-value",

@ -232,7 +232,7 @@ extract(const std::string& filename, const extract_cb& cb)
}
auto arc_lock = lnav::filesystem::file_lock(tmp_path);
auto lock_guard = lnav::filesystem::file_lock::guard(arc_lock);
auto lock_guard = lnav::filesystem::file_lock::guard(&arc_lock);
auto done_path = tmp_path;
done_path += ".done";

@ -166,5 +166,18 @@ stat_file(const ghc::filesystem::path& path)
strerror(errno)));
}
file_lock::file_lock(const ghc::filesystem::path& archive_path)
{
auto lock_path = archive_path;
lock_path += ".lck";
auto open_res
= lnav::filesystem::create_file(lock_path, O_RDWR | O_CLOEXEC, 0600);
if (open_res.isErr()) {
throw std::runtime_error(open_res.unwrapErr());
}
this->lh_fd = open_res.unwrap();
}
} // namespace filesystem
} // namespace lnav

@ -82,33 +82,36 @@ class file_lock {
public:
class guard {
public:
explicit guard(file_lock& arc_lock) : g_lock(arc_lock)
explicit guard(file_lock* arc_lock) : g_lock(arc_lock)
{
this->g_lock.lock();
};
this->g_lock->lock();
}
~guard() { this->g_lock.unlock(); };
guard(guard&& other) noexcept
: g_lock(std::exchange(other.g_lock, nullptr))
{
}
~guard()
{
if (this->g_lock != nullptr) {
this->g_lock->unlock();
}
}
guard(const guard&) = delete;
guard& operator=(const guard&) = delete;
guard& operator=(guard&&) = delete;
private:
file_lock& g_lock;
file_lock* g_lock;
};
void lock() const { lockf(this->lh_fd, F_LOCK, 0); }
void unlock() const { lockf(this->lh_fd, F_ULOCK, 0); }
explicit file_lock(const ghc::filesystem::path& archive_path)
{
auto lock_path = archive_path;
lock_path += ".lck";
auto open_res = lnav::filesystem::create_file(
lock_path, O_RDWR | O_CLOEXEC, 0600);
if (open_res.isErr()) {
throw std::runtime_error(open_res.unwrapErr());
}
this->lh_fd = open_res.unwrap();
}
explicit file_lock(const ghc::filesystem::path& archive_path);
auto_fd lh_fd;
};

@ -79,7 +79,8 @@ sql_dirname(const char* path_in)
text_end = strlen(path_in) - 1;
while (text_end >= 0
&& (path_in[text_end] == '/' || path_in[text_end] == '\\')) {
&& (path_in[text_end] == '/' || path_in[text_end] == '\\'))
{
text_end -= 1;
}
@ -249,7 +250,8 @@ fs_extension_functions(struct FuncDef** basic_funcs,
* TODO: add other functions like normpath, ...
*/
{nullptr}};
{nullptr},
};
*basic_funcs = fs_funcs;
*agg_funcs = nullptr;

@ -1359,7 +1359,7 @@ line_buffer::enable_cache()
"%d:cache file path: %s", this->lb_fd.get(), cached_file_path.c_str());
auto fl = lnav::filesystem::file_lock(cached_file_path);
auto guard = lnav::filesystem::file_lock::guard(fl);
auto guard = lnav::filesystem::file_lock::guard(&fl);
if (ghc::filesystem::exists(cached_done_path)) {
log_info("%d:using existing cache file");

@ -338,11 +338,12 @@ update_installs_from_git()
if (glob(git_formats.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {
for (int lpc = 0; lpc < (int) gl->gl_pathc; lpc++) {
char* git_dir = dirname(gl->gl_pathv[lpc]);
auto git_dir
= ghc::filesystem::path(gl->gl_pathv[lpc]).parent_path();
printf("Updating formats in %s\n", git_dir);
auto pull_cmd
= fmt::format(FMT_STRING("cd '{}' && git pull"), git_dir);
printf("Updating formats in %s\n", git_dir.c_str());
auto pull_cmd = fmt::format(FMT_STRING("cd '{}' && git pull"),
git_dir.string());
int ret = system(pull_cmd.c_str());
if (ret == -1) {
std::cerr << "Failed to spawn command "
@ -519,7 +520,7 @@ static const struct json_path_container global_var_handlers = {
paths_out.emplace_back(iter.first);
}
})
.FOR_FIELD(_lnav_config, lc_global_vars),
.for_field(&_lnav_config::lc_global_vars),
};
static const struct json_path_container style_config_handlers =
@ -868,7 +869,7 @@ static const struct json_path_container highlighter_handlers = {
yajlpp::property_handler("pattern")
.with_synopsis("regular expression")
.with_description("The regular expression to highlight")
.FOR_FIELD(highlighter_config, hc_regex),
.for_field(&highlighter_config::hc_regex),
yajlpp::property_handler("style")
.with_description(
@ -906,7 +907,7 @@ static const struct json_path_container theme_vars_handlers = {
paths_out.emplace_back(iter.first);
}
})
.FOR_FIELD(lnav_theme, lt_vars),
.for_field(&lnav_theme::lt_vars),
};
static const struct json_path_container theme_def_handlers = {

@ -595,7 +595,7 @@ json_array_end(void* ctx)
return 1;
}
static struct json_path_container json_log_handlers = {
static const struct json_path_container json_log_handlers = {
yajlpp::pattern_property_handler("\\w+")
.add_cb(read_json_null)
.add_cb(read_json_bool)
@ -671,7 +671,7 @@ rewrite_json_double(yajlpp_parse_context* ypc, double val)
return 1;
}
static struct json_path_container json_log_rewrite_handlers = {
static const struct json_path_container json_log_rewrite_handlers = {
yajlpp::pattern_property_handler("\\w+")
.add_cb(rewrite_json_null)
.add_cb(rewrite_json_bool)

@ -79,10 +79,8 @@ static external_log_format*
ensure_format(const yajlpp_provider_context& ypc, loader_userdata* ud)
{
const intern_string_t name = ypc.get_substr_i(0);
std::vector<intern_string_t>* formats = ud->ud_format_names;
external_log_format* retval;
retval = LOG_FORMATS[name].get();
auto* formats = ud->ud_format_names;
auto* retval = LOG_FORMATS[name].get();
if (retval == nullptr) {
LOG_FORMATS[name] = std::make_shared<external_log_format>(name);
retval = LOG_FORMATS[name].get();
@ -95,7 +93,8 @@ ensure_format(const yajlpp_provider_context& ypc, loader_userdata* ud)
}
if (!ud->ud_format_path.empty()) {
auto i_src_path = intern_string::lookup(ud->ud_format_path.string());
const intern_string_t i_src_path
= intern_string::lookup(ud->ud_format_path.string());
auto srcs_iter = retval->elf_format_sources.find(i_src_path);
if (srcs_iter == retval->elf_format_sources.end()) {
retval->elf_format_source_order.emplace_back(ud->ud_format_path);
@ -175,7 +174,7 @@ scaling_factor_provider(const yajlpp_provider_context& ypc,
external_log_format::value_def* value_def)
{
auto scale_name = ypc.get_substr_i(0);
scaling_factor& retval = value_def->vd_unit_scaling[scale_name];
auto& retval = value_def->vd_unit_scaling[scale_name];
return &retval;
}
@ -383,7 +382,7 @@ ensure_sample(external_log_format* elf, int index)
static external_log_format::sample*
sample_provider(const yajlpp_provider_context& ypc, external_log_format* elf)
{
external_log_format::sample& sample = ensure_sample(elf, ypc.ypc_index);
auto& sample = ensure_sample(elf, ypc.ypc_index);
return &sample;
}
@ -404,7 +403,7 @@ read_json_constant(yajlpp_parse_context* ypc,
return 1;
}
static struct json_path_container pattern_handlers = {
static const struct json_path_container pattern_handlers = {
yajlpp::property_handler("pattern")
.with_synopsis("<message-regex>")
.with_description(
@ -455,12 +454,11 @@ static const json_path_handler_base::enum_value_t TRANSFORM_ENUM[] = {
json_path_handler_base::ENUM_TERMINATOR,
};
static struct json_path_container line_format_handlers = {
static const struct json_path_container line_format_handlers = {
yajlpp::property_handler("field")
.with_synopsis("<field-name>")
.with_description(
"The name of the field to substitute at this position")
.with_min_length(1)
.for_field(&external_log_format::json_format_element::jfe_value),
yajlpp::property_handler("default-value")
@ -530,22 +528,22 @@ static const json_path_handler_base::enum_value_t SCALE_OP_ENUM[] = {
json_path_handler_base::ENUM_TERMINATOR,
};
static struct json_path_container scaling_factor_handlers = {
static const struct json_path_container scaling_factor_handlers = {
yajlpp::pattern_property_handler("op")
.with_enum_values(SCALE_OP_ENUM)
.for_field(&scaling_factor::sf_op),
yajlpp::pattern_property_handler("value").FOR_FIELD(scaling_factor,
sf_value),
yajlpp::pattern_property_handler("value").for_field(
&scaling_factor::sf_value),
};
static struct json_path_container scale_handlers = {
static const struct json_path_container scale_handlers = {
yajlpp::pattern_property_handler("(?<scale>[^/]+)")
.with_obj_provider(scaling_factor_provider)
.with_children(scaling_factor_handlers),
};
static struct json_path_container unit_handlers = {
static const struct json_path_container unit_handlers = {
yajlpp::property_handler("field")
.with_synopsis("<field-name>")
.with_description(
@ -557,7 +555,7 @@ static struct json_path_container unit_handlers = {
.with_children(scale_handlers),
};
static struct json_path_container value_def_handlers = {
static const struct json_path_container value_def_handlers = {
yajlpp::property_handler("kind")
.with_synopsis("<data-type>")
.with_description("The type of data in the field")
@ -597,7 +595,7 @@ static struct json_path_container value_def_handlers = {
yajlpp::property_handler("action-list#")
.with_synopsis("<string>")
.with_description("Actions to execute when this field is clicked on")
.FOR_FIELD(external_log_format::value_def, vd_action_list),
.for_field(&external_log_format::value_def::vd_action_list),
yajlpp::property_handler("rewriter")
.with_synopsis("<command>")
@ -613,7 +611,7 @@ static struct json_path_container value_def_handlers = {
.for_field(&external_log_format::value_def::vd_description),
};
static struct json_path_container highlighter_def_handlers = {
static const struct json_path_container highlighter_def_handlers = {
yajlpp::property_handler("pattern")
.with_synopsis("<regex>")
.with_description(
@ -660,7 +658,7 @@ static const json_path_handler_base::enum_value_t LEVEL_ENUM[] = {
json_path_handler_base::ENUM_TERMINATOR,
};
static struct json_path_container sample_handlers = {
static const struct json_path_container sample_handlers = {
yajlpp::property_handler("description")
.with_synopsis("<text>")
.with_description("A description of this sample.")
@ -685,14 +683,14 @@ static const json_path_handler_base::enum_value_t TYPE_ENUM[] = {
json_path_handler_base::ENUM_TERMINATOR,
};
static struct json_path_container regex_handlers = {
static const struct json_path_container regex_handlers = {
yajlpp::pattern_property_handler(R"((?<pattern_name>[^/]+))")
.with_description("The set of patterns used to match log messages")
.with_obj_provider(pattern_provider)
.with_children(pattern_handlers),
};
static struct json_path_container level_handlers = {
static const struct json_path_container level_handlers = {
yajlpp::pattern_property_handler("(?<level>trace|debug[2345]?|info|stats|"
"notice|warning|error|critical|fatal)")
.add_cb(read_levels)
@ -704,7 +702,7 @@ static struct json_path_container level_handlers = {
"the number for the corresponding level."),
};
static struct json_path_container value_handlers = {
static const struct json_path_container value_handlers = {
yajlpp::pattern_property_handler("(?<value_name>[^/]+)")
.with_description(
"The set of values captured by the log message patterns")
@ -712,7 +710,7 @@ static struct json_path_container value_handlers = {
.with_children(value_def_handlers),
};
static struct json_path_container tag_path_handlers = {
static const struct json_path_container tag_path_handlers = {
yajlpp::property_handler("glob")
.with_synopsis("<glob>")
.with_description("The glob to match against file paths")
@ -720,7 +718,7 @@ static struct json_path_container tag_path_handlers = {
.for_field(&format_tag_def::path_restriction::p_glob),
};
static struct json_path_container format_tag_def_handlers = {
static const struct json_path_container format_tag_def_handlers = {
yajlpp::property_handler("paths#")
.with_description("Restrict tagging to the given paths")
.for_field(&format_tag_def::ftd_paths)
@ -742,14 +740,14 @@ static struct json_path_container format_tag_def_handlers = {
.for_field(&format_tag_def::ftd_level),
};
static struct json_path_container tag_handlers = {
static const struct json_path_container tag_handlers = {
yajlpp::pattern_property_handler(R"((?<tag_name>[\w:;\._\-]+))")
.with_description("The name of the tag to apply")
.with_obj_provider(format_tag_def_provider)
.with_children(format_tag_def_handlers),
};
static struct json_path_container highlight_handlers = {
static const struct json_path_container highlight_handlers = {
yajlpp::pattern_property_handler(R"((?<highlight_name>[^/]+))")
.with_description("The definition of a highlight")
.with_obj_provider<external_log_format::highlighter_def,
@ -763,20 +761,20 @@ static struct json_path_container highlight_handlers = {
.with_children(highlighter_def_handlers),
};
static struct json_path_container action_def_handlers = {
static const struct json_path_container action_def_handlers = {
json_path_handler("label", read_action_def),
json_path_handler("capture-output", read_action_bool),
json_path_handler("cmd#", read_action_cmd),
};
static struct json_path_container action_handlers = {
static const struct json_path_container action_handlers = {
json_path_handler(
lnav::pcre2pp::code::from_const("(?<action_name>\\w+)").to_shared(),
read_action_def)
.with_children(action_def_handlers),
};
static struct json_path_container search_table_def_handlers = {
static const struct json_path_container search_table_def_handlers = {
json_path_handler("pattern")
.with_synopsis("<regex>")
.with_description("The regular expression for this search table.")
@ -793,7 +791,7 @@ static struct json_path_container search_table_def_handlers = {
.for_field(&external_log_format::search_table_def::std_level),
};
static struct json_path_container search_table_handlers = {
static const struct json_path_container search_table_handlers = {
yajlpp::pattern_property_handler("(?<table_name>\\w+)")
.with_description(
"The set of search tables to be automatically defined")
@ -816,7 +814,7 @@ static const json_path_handler_base::enum_value_t MIME_TYPE_ENUM[] = {
json_path_handler_base::ENUM_TERMINATOR,
};
struct json_path_container format_handlers = {
const struct json_path_container format_handlers = {
yajlpp::property_handler("regex")
.with_description(
"The set of regular expressions used to match log messages")
@ -978,7 +976,7 @@ read_id(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
return 1;
}
struct json_path_container root_format_handler = json_path_container{
const struct json_path_container root_format_handler = json_path_container{
json_path_handler("$schema", read_id)
.with_synopsis("The URI of the schema for this file")
.with_description("Specifies the type of this file"),
@ -1034,15 +1032,13 @@ write_sample_file()
struct script_metadata meta;
auto sf = bsf.to_string_fragment();
auto_fd script_fd;
struct stat st;
extract_metadata(sf, meta);
auto path
= fmt::format(FMT_STRING("formats/default/{}.lnav"), meta.sm_name);
auto script_path = lnav::paths::dotlnav() / path;
if (lnav::filesystem::statp(script_path, &st) == 0
&& st.st_size == sf.length())
{
auto stat_res = lnav::filesystem::stat_file(script_path);
if (stat_res.isOk() && stat_res.unwrap().st_size == sf.length()) {
// Assume it's the right contents and move on...
continue;
}
@ -1167,19 +1163,19 @@ load_from_path(const ghc::filesystem::path& path,
log_info("loading formats from path: %s", format_path.c_str());
if (glob(format_path.c_str(), 0, nullptr, gl.inout()) == 0) {
for (int lpc = 0; lpc < (int) gl->gl_pathc; lpc++) {
const char* base = basename(gl->gl_pathv[lpc]);
auto filepath = ghc::filesystem::path(gl->gl_pathv[lpc]);
if (startswith(base, "config.")) {
if (startswith(filepath.filename().string(), "config.")) {
log_info(" not loading config as format: %s",
filepath.c_str());
continue;
}
std::string filename(gl->gl_pathv[lpc]);
std::vector<intern_string_t> format_list;
format_list = load_format_file(filename, errors);
auto format_list = load_format_file(filepath, errors);
if (format_list.empty()) {
log_warning("Empty format file: %s", filename.c_str());
log_warning("Empty format file: %s", filepath.c_str());
} else {
log_info("contents of format file '%s':", filepath.c_str());
for (auto iter = format_list.begin(); iter != format_list.end();
++iter)
{
@ -1217,8 +1213,7 @@ load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
yajl_config(handle, yajl_allow_comments, 1);
auto sf = bsf.to_string_fragment();
if (ypc_builtin.parse(sf) != yajl_status_ok) {
unsigned char* msg = yajl_get_error(
handle, 1, (const unsigned char*) sf.data(), sf.length());
auto* msg = yajl_get_error(handle, 1, sf.udata(), sf.length());
errors.emplace_back(
lnav::console::user_message::error("invalid json")
@ -1402,19 +1397,34 @@ extract_metadata(string_fragment contents, struct script_metadata& meta_out)
void
extract_metadata_from_file(struct script_metadata& meta_inout)
{
auto stat_res = lnav::filesystem::stat_file(meta_inout.sm_path);
if (stat_res.isErr()) {
log_warning("unable to open script: %s -- %s",
meta_inout.sm_path.c_str(),
stat_res.unwrapErr().c_str());
return;
}
auto st = stat_res.unwrap();
if (!S_ISREG(st.st_mode)) {
log_warning("script is not a regular file -- %s",
meta_inout.sm_path.c_str());
return;
}
auto open_res = lnav::filesystem::open_file(meta_inout.sm_path, O_RDONLY);
if (open_res.isErr()) {
log_warning("unable to open script file: %s -- %s",
meta_inout.sm_path.c_str(),
open_res.unwrapErr().c_str());
return;
}
auto fd = open_res.unwrap();
char buffer[8 * 1024];
auto_mem<FILE> fp(fclose);
struct stat st;
if (lnav::filesystem::statp(meta_inout.sm_path, &st) == -1) {
log_warning("unable to open script -- %s", meta_inout.sm_path.c_str());
} else if (!S_ISREG(st.st_mode)) {
log_warning("not a regular file -- %s", meta_inout.sm_path.c_str());
} else if ((fp = fopen(meta_inout.sm_path.c_str(), "r")) != nullptr) {
size_t len;
len = fread(buffer, 1, sizeof(buffer), fp.in());
extract_metadata(string_fragment::from_bytes(buffer, len), meta_inout);
auto rc = read(fd, buffer, sizeof(buffer));
if (rc > 0) {
extract_metadata(string_fragment::from_bytes(buffer, rc), meta_inout);
}
}

@ -75,7 +75,7 @@ struct available_scripts {
void find_format_scripts(const std::vector<ghc::filesystem::path>& extra_paths,
available_scripts& scripts);
extern struct json_path_container format_handlers;
extern struct json_path_container root_format_handler;
extern const struct json_path_container format_handlers;
extern const struct json_path_container root_format_handler;
#endif

@ -1890,12 +1890,12 @@ vt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info)
return SQLITE_OK;
}
static struct json_path_container tags_handler = {
static const struct json_path_container tags_handler = {
json_path_handler("#")
.with_synopsis("tag")
.with_description("A tag for the log line")
.with_pattern(R"(^#[^\s]+$)")
.FOR_FIELD(bookmark_metadata, bm_tags),
.for_field(&bookmark_metadata::bm_tags),
};
static int

@ -153,8 +153,6 @@ logfile::~logfile() {}
bool
logfile::exists() const
{
struct stat st;
if (!this->lf_actual_path) {
return true;
}
@ -163,13 +161,15 @@ logfile::exists() const
return true;
}
if (lnav::filesystem::statp(this->lf_actual_path.value(), &st) == -1) {
auto stat_res = lnav::filesystem::stat_file(this->lf_actual_path.value());
if (stat_res.isErr()) {
log_error("%s: stat failed -- %s",
this->lf_actual_path.value().c_str(),
strerror(errno));
stat_res.unwrapErr().c_str());
return false;
}
auto st = stat_res.unwrap();
return this->lf_stat.st_dev == st.st_dev
&& this->lf_stat.st_ino == st.st_ino
&& this->lf_stat.st_size <= st.st_size;

@ -862,7 +862,7 @@ read_commands(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
return 1;
}
static struct json_path_container view_def_handlers = {
static const struct json_path_container view_def_handlers = {
json_path_handler("top_line", read_top_line),
json_path_handler("search", read_current_search),
json_path_handler("word_wrap", read_word_wrap),
@ -870,18 +870,18 @@ static struct json_path_container view_def_handlers = {
json_path_handler("commands#", read_commands),
};
static struct json_path_container view_handlers = {
static const struct json_path_container view_handlers = {
yajlpp::pattern_property_handler("([^/]+)").with_children(
view_def_handlers),
};
static struct json_path_container file_state_handlers = {
static const struct json_path_container file_state_handlers = {
yajlpp::property_handler("visible")
.with_description("Indicates whether the file is visible or not")
.for_field(&file_state::fs_is_visible),
};
static struct json_path_container file_states_handlers = {
static const struct json_path_container file_states_handlers = {
yajlpp::pattern_property_handler(R"((?<filename>[^/]+))")
.with_description("Map of file names to file state objects")
.with_obj_provider<file_state, void>([](const auto& ypc, auto* root) {
@ -891,7 +891,7 @@ static struct json_path_container file_states_handlers = {
.with_children(file_state_handlers),
};
static struct json_path_container view_info_handlers = {
static const struct json_path_container view_info_handlers = {
yajlpp::property_handler("save-time")
.for_field(&session_data_t::sd_save_time),
yajlpp::property_handler("time-offset")

@ -269,7 +269,7 @@ tailer::looper::complete_path(const network::path& path)
static std::vector<std::string>
create_ssh_args_from_config(const std::string& dest)
{
auto& cfg = injector::get<const tailer::config&>();
const auto& cfg = injector::get<const tailer::config&>();
std::vector<std::string> retval;
retval.emplace_back(cfg.c_ssh_cmd);

@ -383,7 +383,7 @@ json_path_handler_base::gen_schema(yajlpp_gen_context& ygc) const
schema.gen("examples");
yajlpp_array example_array(ygc.ygc_handle);
for (auto& ex : this->jph_examples) {
for (const auto& ex : this->jph_examples) {
example_array.gen(ex);
}
}
@ -1107,7 +1107,7 @@ yajlpp_parse_context::reset(const struct json_path_container* handlers)
}
void
yajlpp_parse_context::set_static_handler(json_path_handler_base& jph)
yajlpp_parse_context::set_static_handler(const json_path_handler_base& jph)
{
this->ypc_path.clear();
this->ypc_path.push_back('/');
@ -1310,6 +1310,40 @@ yajlpp_parse_context::get_snippet() const
.with_line(line_number);
}
void
json_path_handler_base::validate_string(yajlpp_parse_context& ypc,
string_fragment sf) const
{
if (this->jph_pattern) {
if (!this->jph_pattern->find_in(sf).ignore_error()) {
this->report_pattern_error(&ypc, sf.to_string());
}
}
if (sf.empty() && this->jph_min_length > 0) {
ypc.report_error(lnav::console::user_message::error(
attr_line_t("invalid value for option ")
.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(this->get_help_text(&ypc)));
} else if (sf.length() < this->jph_min_length) {
ypc.report_error(
lnav::console::user_message::error(
attr_line_t()
.append_quoted(sf)
.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(this->jph_min_length)))
.append(" characters long"))
.with_snippet(ypc.get_snippet())
.with_help(this->get_help_text(&ypc)));
}
}
void
json_path_handler_base::report_pattern_error(yajlpp_parse_context* ypc,
const std::string& value_str) const
@ -1378,8 +1412,6 @@ 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()
@ -1389,9 +1421,9 @@ json_path_handler_base::report_min_value_error(yajlpp_parse_context* ypc,
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))))
fmt::to_string(this->jph_min_value))))
.with_snippet(ypc->get_snippet())
.with_help(jph->get_help_text(ypc)));
.with_help(this->get_help_text(ypc)));
}
void
@ -1465,7 +1497,7 @@ json_path_container::gen_properties(yajlpp_gen_context& ygc) const
{
yajlpp_map properties(ygc.ygc_handle);
for (auto& child_handler : this->jpc_children) {
for (const auto& child_handler : this->jpc_children) {
if (child_handler.jph_is_pattern_property) {
continue;
}
@ -1529,5 +1561,5 @@ yajlpp_gen::to_string_fragment()
yajl_gen_get_buf(this->yg_handle.in(), &buf, &len);
return string_fragment((const char*) buf, 0, len);
return string_fragment::from_bytes(buf, len);
}

@ -282,10 +282,13 @@ struct json_path_handler_base {
std::function<int(yajlpp_parse_context*)> jph_null_cb;
std::function<int(yajlpp_parse_context*, int)> jph_bool_cb;
std::function<int(yajlpp_parse_context*, long long)> jph_integer_cb;
std::function<int(yajlpp_parse_context*, double)> jph_double_cb;
std::function<int(
yajlpp_parse_context*, const unsigned char* str, size_t len)>
jph_str_cb;
void validate_string(yajlpp_parse_context& ypc, string_fragment sf) const;
void report_pattern_error(yajlpp_parse_context* ypc,
const std::string& value_str) const;
void report_min_value_error(yajlpp_parse_context* ypc,
@ -350,7 +353,7 @@ public:
void reset(const struct json_path_container* handlers);
void set_static_handler(struct json_path_handler_base& jph);
void set_static_handler(const struct json_path_handler_base& jph);
template<typename T>
yajlpp_parse_context& with_obj(T& obj)

@ -41,33 +41,6 @@
#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; }
};
@ -246,54 +219,6 @@ struct json_path_handler : public json_path_handler_base {
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 null_field_cb(yajlpp_parse_context* ypc)
{
return ypc->ypc_current_handler->jph_null_cb(ypc);
@ -316,204 +241,9 @@ struct json_path_handler : public json_path_handler_base {
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)
static int dbl_field_cb(yajlpp_parse_context* ypc, double val)
{
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;
return ypc->ypc_current_handler->jph_double_cb(ypc, val);
}
template<typename T, typename U>
@ -572,22 +302,37 @@ struct json_path_handler : public json_path_handler_base {
};
template<typename T, typename... Args>
struct LastIsNumber {
static constexpr bool value = LastIsNumber<Args...>::value;
struct LastIsInteger {
static constexpr bool value = LastIsInteger<Args...>::value;
};
template<typename T, typename U>
struct LastIsNumber<U T::*> {
struct LastIsInteger<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::*> {
struct LastIsInteger<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 LastIsFloat {
static constexpr bool value = LastIsFloat<Args...>::value;
};
template<typename T, typename U>
struct LastIsFloat<U T::*> {
static constexpr bool value = std::is_same<U, double>::value;
};
template<typename T, typename U>
struct LastIsFloat<nonstd::optional<U> T::*> {
static constexpr bool value = std::is_same<U, double>::value;
};
template<typename T, typename... Args>
struct LastIsVector {
using value_type = typename LastIsVector<Args...>::value_type;
@ -675,12 +420,7 @@ struct json_path_handler : public json_path_handler_base {
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);
}
}
jph->validate_string(*ypc, value_str);
json_path_handler::get_field(obj, args...)
.emplace_back(std::move(value_str));
@ -937,12 +677,7 @@ struct json_path_handler : public json_path_handler_base {
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);
}
}
jph->validate_string(*ypc, value_str);
json_path_handler::get_field(obj, args...) = std::move(value_str);
return 1;
@ -1055,12 +790,7 @@ struct json_path_handler : public json_path_handler_base {
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);
}
}
jph->validate_string(*ypc, value_str);
json_path_handler::get_field(obj, args...) = std::move(value_str);
return 1;
@ -1114,12 +844,7 @@ struct json_path_handler : public json_path_handler_base {
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);
}
}
jph->validate_string(*ypc, value_str);
auto& field = json_path_handler::get_field(obj, args...);
field.pp_path = ypc->get_full_path();
@ -1168,12 +893,7 @@ struct json_path_handler : public json_path_handler_base {
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);
}
}
jph->validate_string(*ypc, value_str);
json_path_handler::get_field(obj, args...)
= intern_string::lookup(value_str);
@ -1220,12 +940,7 @@ struct json_path_handler : public json_path_handler_base {
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);
}
}
jph->validate_string(*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;
@ -1297,7 +1012,7 @@ struct json_path_handler : public json_path_handler_base {
}
template<typename... Args,
std::enable_if_t<LastIsNumber<Args...>::value, bool> = true>
std::enable_if_t<LastIsInteger<Args...>::value, bool> = true>
json_path_handler& for_field(Args... args)
{
this->add_cb(int_field_cb);
@ -1350,6 +1065,59 @@ struct json_path_handler : public json_path_handler_base {
return *this;
}
template<typename... Args,
std::enable_if_t<LastIsFloat<Args...>::value, bool> = true>
json_path_handler& for_field(Args... args)
{
this->add_cb(dbl_field_cb);
this->jph_double_cb = [args...](yajlpp_parse_context* ypc, double 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>
@ -1465,34 +1233,6 @@ struct json_path_handler : public json_path_handler_base {
};
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);
@ -1617,7 +1357,7 @@ struct typed_json_path_container : public json_path_container {
return gen.to_string_fragment().to_string();
}
json_string to_json_string(T& obj) const
json_string to_json_string(const T& obj) const
{
yajlpp_gen gen;
yajlpp_gen_context ygc(gen, *this);

@ -226,6 +226,7 @@ dist_noinst_DATA = \
expected/test_tailer.sh_12f539e535df04364316699f9edeac461aa9f9de.err \
expected/test_tailer.sh_12f539e535df04364316699f9edeac461aa9f9de.out \
ansi-colors.0.in \
bad-config/formats/invalid-json-format/format.json \
bad-config/formats/invalid-properties/format.json \
bad-config/formats/invalid-regex/format.json \
bad-config/formats/invalid-sample/format.json \

@ -0,0 +1,17 @@
{
"$schema": "https://lnav.org/schemas/format-v1.schema.json",
"bad_json_log": {
"json": true,
"line-format": [
{
"field": ""
},
{
"field": "__timestamp__",
"timestamp-format": ""
}
],
"value": {
}
}
}

@ -1,3 +1,11 @@
✘ error: invalid value for option “/bad_json_log/line-format#/timestamp-format”
reason: empty values are not allowed
 --> {test_dir}/bad-config/formats/invalid-json-format/format.json:11
 |  "timestamp-format": "" 
 = help: Property Synopsis
/bad_json_log/line-format#/timestamp-format <string>
Description
The strftime(3) format for this field
✘ error: “invalid(abc” is not a valid regular expression
reason: missing closing parenthesis
 --> /invalid_props_log/tags/badtag3/pattern
@ -78,6 +86,9 @@
/$schema The URI of the schema for this file
Description
Specifies the type of this file
✘ error: invalid line format element “/bad_json_log/line-format/0/field”
reason: “” is not a defined value
 --> {test_dir}/bad-config/formats/invalid-json-format/format.json:7
✘ error: invalid pattern: “incomplete-match”
reason: pattern does not match entire message
 --> {test_dir}/bad-config/formats/invalid-regex/format.json:20

Loading…
Cancel
Save