diff options
Diffstat (limited to 'src/yajlpp')
-rw-r--r-- | src/yajlpp/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/yajlpp/Makefile.am | 2 | ||||
-rw-r--r-- | src/yajlpp/json_ptr.cc | 46 | ||||
-rw-r--r-- | src/yajlpp/json_ptr.hh | 11 | ||||
-rw-r--r-- | src/yajlpp/yajlpp.cc | 82 | ||||
-rw-r--r-- | src/yajlpp/yajlpp.hh | 63 | ||||
-rw-r--r-- | src/yajlpp/yajlpp_def.hh | 467 |
7 files changed, 567 insertions, 106 deletions
diff --git a/src/yajlpp/CMakeLists.txt b/src/yajlpp/CMakeLists.txt index 73168ba..260e6e1 100644 --- a/src/yajlpp/CMakeLists.txt +++ b/src/yajlpp/CMakeLists.txt @@ -13,7 +13,7 @@ add_library( target_include_directories(yajlpp PUBLIC . .. ../fmtlib ${CMAKE_CURRENT_BINARY_DIR}/..) -target_link_libraries(yajlpp pcrepp yajl ncurses::libcurses) +target_link_libraries(yajlpp pcrepp yajl ncurses::libcurses datepp) add_executable(test_yajlpp test_yajlpp.cc) target_link_libraries(test_yajlpp yajlpp base ${lnav_LIBS}) diff --git a/src/yajlpp/Makefile.am b/src/yajlpp/Makefile.am index cc6bcf6..35688a0 100644 --- a/src/yajlpp/Makefile.am +++ b/src/yajlpp/Makefile.am @@ -11,6 +11,7 @@ AM_CPPFLAGS = \ $(PCRE_CFLAGS) \ -I$(top_srcdir)/src/ \ -I$(top_srcdir)/src/fmtlib \ + -I$(top_srcdir)/src/third-party/date/include \ -I$(top_srcdir)/src/third-party/scnlib/include AM_LDFLAGS = \ @@ -53,6 +54,7 @@ LDADD = \ libyajlpp.a \ $(top_builddir)/src/base/libbase.a \ $(top_builddir)/src/fmtlib/libcppfmt.a \ + $(top_builddir)/src/third-party/date/src/libdatepp.a \ $(top_builddir)/src/third-party/scnlib/src/libscnlib.a \ $(top_builddir)/src/pcrepp/libpcrepp.a \ $(top_builddir)/src/yajl/libyajl.a diff --git a/src/yajlpp/json_ptr.cc b/src/yajlpp/json_ptr.cc index 4cc0273..3336f4e 100644 --- a/src/yajlpp/json_ptr.cc +++ b/src/yajlpp/json_ptr.cc @@ -226,6 +226,19 @@ json_ptr::encode(char* dst, size_t dst_len, const char* src, size_t src_len) return retval; } +std::string +json_ptr::encode_str(const char* src, size_t src_len) +{ + if (src_len == (size_t) -1) { + src_len = strlen(src); + } + + char retval[src_len * 2 + 1]; + auto rc = encode(retval, sizeof(retval), src, src_len); + + return std::string(retval, rc); +} + size_t json_ptr::decode(char* dst, const char* src, ssize_t src_len) { @@ -268,6 +281,39 @@ json_ptr::decode(char* dst, const char* src, ssize_t src_len) return retval; } +std::string +json_ptr::decode(const string_fragment& sf) +{ + std::string retval; + auto in_escape = false; + + retval.reserve(sf.length()); + for (const auto ch : sf) { + if (in_escape) { + switch (ch) { + case '0': + retval.push_back('~'); + break; + case '1': + retval.push_back('/'); + break; + case '2': + retval.push_back('#'); + break; + default: + break; + } + in_escape = false; + } else if (ch == '~') { + in_escape = true; + } else { + retval.push_back(ch); + } + } + + return retval; +} + bool json_ptr::expect_map(int32_t& depth, int32_t& index) { diff --git a/src/yajlpp/json_ptr.hh b/src/yajlpp/json_ptr.hh index f7822ab..463048c 100644 --- a/src/yajlpp/json_ptr.hh +++ b/src/yajlpp/json_ptr.hh @@ -41,6 +41,7 @@ #include <sys/types.h> #include "base/auto_mem.hh" +#include "base/intern_string.hh" #include "yajl/api/yajl_parse.h" #include "yajl/api/yajl_tree.h" @@ -64,7 +65,8 @@ public: void inc_array_index() { if (!this->jpw_array_indexes.empty() - && this->jpw_array_indexes.back() != -1) { + && this->jpw_array_indexes.back() != -1) + { this->jpw_array_indexes.back() += 1; } } @@ -108,7 +110,14 @@ public: const char* src, size_t src_len = -1); + static std::string encode_str(const char* src, size_t src_len = -1); + static std::string encode_str(const std::string& src) + { + return encode_str(src.c_str(), src.size()); + } + static size_t decode(char* dst, const char* src, ssize_t src_len = -1); + static std::string decode(const string_fragment& sf); json_ptr(const char* value) : jp_value(value), jp_pos(value) {} diff --git a/src/yajlpp/yajlpp.cc b/src/yajlpp/yajlpp.cc index b1362ab..0fc0001 100644 --- a/src/yajlpp/yajlpp.cc +++ b/src/yajlpp/yajlpp.cc @@ -34,8 +34,6 @@ #include "yajlpp.hh" -#include "base/fs_util.hh" -#include "base/snippet_highlighters.hh" #include "config.h" #include "fmt/format.h" #include "ghc/filesystem.hpp" @@ -213,10 +211,7 @@ json_path_handler_base::gen(yajlpp_gen_context& ygc, yajl_gen handle) const if (this->jph_children) { for (const auto& lpath : local_paths) { - std::string full_path = lpath; - if (this->jph_path_provider) { - full_path += "/"; - } + std::string full_path = json_ptr::encode_str(lpath); int start_depth = ygc.ygc_depth; yajl_gen_string(handle, lpath); @@ -455,8 +450,8 @@ json_path_handler_base::gen_schema_type(yajlpp_gen_context& ygc) const void json_path_handler_base::walk( - const std::function< - void(const json_path_handler_base&, const std::string&, void*)>& cb, + const std::function<void( + const json_path_handler_base&, const std::string&, const void*)>& cb, void* root, const std::string& base) const { @@ -465,11 +460,11 @@ json_path_handler_base::walk( if (this->jph_path_provider) { this->jph_path_provider(root, local_paths); - for (auto& lpath : local_paths) { + for (const auto& lpath : local_paths) { cb(*this, fmt::format(FMT_STRING("{}{}{}"), base, - lpath, + json_ptr::encode_str(lpath), this->jph_children ? "/" : ""), nullptr); } @@ -477,6 +472,12 @@ json_path_handler_base::walk( local_paths.clear(); this->jph_path_provider(root, local_paths); } + if (this->jph_field_getter) { + const auto* field = this->jph_field_getter(root, nonstd::nullopt); + if (field != nullptr) { + cb(*this, base, field); + } + } } else { local_paths.emplace_back(this->jph_property); @@ -484,6 +485,7 @@ json_path_handler_base::walk( if (this->jph_children) { full_path += "/"; } + cb(*this, full_path, nullptr); } @@ -493,7 +495,7 @@ json_path_handler_base::walk( static const intern_string_t POSS_SRC = intern_string::lookup("possibilities"); - std::string full_path = base + lpath; + std::string full_path = base + json_ptr::encode_str(lpath); if (this->jph_children) { full_path += "/"; } @@ -509,13 +511,18 @@ json_path_handler_base::walk( static thread_local auto md = lnav::pcre2pp::match_data::unitialized(); - std::string full_path = lpath + "/"; + const auto short_path = json_ptr::encode_str(lpath) + "/"; - if (!this->jph_regex->capture_from(full_path) + if (!this->jph_regex->capture_from(short_path) .into(md) .matches() .ignore_error()) { + log_error( + "path-handler regex (%s) does not match path: " + "%s", + this->jph_regex->get_pattern().c_str(), + full_path.c_str()); ensure(false); } child_root = this->jph_obj_provider( @@ -527,7 +534,7 @@ json_path_handler_base::walk( } } else { for (auto& lpath : local_paths) { - void* field = nullptr; + const void* field = nullptr; if (this->jph_field_getter) { field = this->jph_field_getter(root, lpath); @@ -1370,6 +1377,33 @@ json_path_handler_base::report_pattern_error(yajlpp_parse_context* ypc, .with_help(this->get_help_text(ypc))); } +void +json_path_handler_base::report_tz_error(yajlpp_parse_context* ypc, + const std::string& value_str, + const char* msg) const +{ + auto help_al = attr_line_t() + .append(lnav::roles::h2("Available time zones")) + .append("\n"); + + try { + for (const auto& tz : date::get_tzdb().zones) { + help_al.append(" ") + .append(lnav::roles::symbol(tz.name())) + .append("\n"); + } + } catch (const std::runtime_error& e) { + log_error("unable to load timezones: %s", e.what()); + } + + ypc->report_error(lnav::console::user_message::error( + attr_line_t().append_quoted(value_str).append( + " is not a valid timezone")) + .with_snippet(ypc->get_snippet()) + .with_reason(msg) + .with_help(help_al)); +} + attr_line_t json_path_handler_base::get_help_text(const std::string& full_path) const { @@ -1495,6 +1529,7 @@ json_path_container::gen_schema(yajlpp_gen_context& ygc) const void json_path_container::gen_properties(yajlpp_gen_context& ygc) const { + static const auto FWD_SLASH = lnav::pcre2pp::code::from_const(R"(\[\^/\])"); auto pattern_count = count_if( this->jpc_children.begin(), this->jpc_children.end(), [](auto& jph) { return jph.jph_is_pattern_property; @@ -1524,7 +1559,10 @@ json_path_container::gen_properties(yajlpp_gen_context& ygc) const if (!child_handler.jph_is_pattern_property) { continue; } - properties.gen(child_handler.jph_property); + + auto pattern = child_handler.jph_property; + pattern = FWD_SLASH.replace(pattern, "."); + properties.gen(fmt::format(FMT_STRING("^{}$"), pattern)); child_handler.gen_schema(ygc); } } @@ -1572,3 +1610,17 @@ yajlpp_gen::to_string_fragment() return string_fragment::from_bytes(buf, len); } + +namespace yajlpp { + +auto_mem<yajl_handle_t> +alloc_handle(const yajl_callbacks* cb, void* cu) +{ + auto_mem<yajl_handle_t> retval(yajl_free); + + retval = yajl_alloc(cb, nullptr, cu); + + return retval; +} + +} // namespace yajlpp
\ No newline at end of file diff --git a/src/yajlpp/yajlpp.hh b/src/yajlpp/yajlpp.hh index 7632329..ecf9734 100644 --- a/src/yajlpp/yajlpp.hh +++ b/src/yajlpp/yajlpp.hh @@ -112,6 +112,14 @@ struct factory_container : public positioned_property<std::shared_ptr<T>> { return Err( lnav::console::to_user_message(src, from_res.unwrapErr())); } + + std::string to_string() const + { + if (this->pp_value != nullptr) { + return this->pp_value->to_string(); + } + return ""; + } }; template<typename... Args> @@ -131,6 +139,14 @@ struct factory_container : public positioned_property<std::shared_ptr<T>> { return Err(lnav::console::to_user_message(src, from_res.unwrapErr())); } + + std::string to_string() const + { + if (this->pp_value != nullptr) { + return this->pp_value->to_string(); + } + return ""; + } }; class yajlpp_gen_context; @@ -228,11 +244,11 @@ struct json_path_handler_base { yajl_gen_status gen(yajlpp_gen_context& ygc, yajl_gen handle) const; yajl_gen_status gen_schema(yajlpp_gen_context& ygc) const; yajl_gen_status gen_schema_type(yajlpp_gen_context& ygc) const; - void walk( - const std::function< - void(const json_path_handler_base&, const std::string&, void*)>& cb, - void* root = nullptr, - const std::string& base = "/") const; + void walk(const std::function<void(const json_path_handler_base&, + const std::string&, + const void*)>& cb, + void* root = nullptr, + const std::string& base = "/") const; enum class schema_type_t : std::uint32_t { ANY, @@ -255,7 +271,7 @@ struct json_path_handler_base { std::function<void(yajlpp_parse_context& ypc, const json_path_handler_base& jph)> jph_validator; - std::function<void*(void* root, nonstd::optional<std::string> name)> + std::function<const void*(void* root, nonstd::optional<std::string> name)> jph_field_getter; std::function<void*(const yajlpp_provider_context& pe, void* root)> jph_obj_provider; @@ -283,8 +299,7 @@ struct json_path_handler_base { 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)> + std::function<int(yajlpp_parse_context*, const string_fragment& sf)> jph_str_cb; void validate_string(yajlpp_parse_context& ypc, string_fragment sf) const; @@ -301,6 +316,9 @@ struct json_path_handler_base { void report_error(yajlpp_parse_context* ypc, const std::string& value_str, lnav::console::user_message um) const; + void report_tz_error(yajlpp_parse_context* ypc, + const std::string& value_str, + const char* msg) const; attr_line_t get_help_text(const std::string& full_path) const; attr_line_t get_help_text(yajlpp_parse_context* ypc) const; @@ -452,6 +470,16 @@ public: return obj->*MEM; } + void fill_in_source() + { + if (this->ypc_locations != nullptr) { + (*this->ypc_locations)[this->get_full_path()] = source_location{ + this->ypc_source, + this->get_line_number(), + }; + } + } + const intern_string_t ypc_source; int ypc_line_number{1}; const struct json_path_container* ypc_handlers; @@ -673,10 +701,29 @@ struct json_string { memcpy((void*) this->js_content.in(), buf, this->js_len); } + explicit json_string(auto_buffer&& buf) + { + auto buf_pair = buf.release(); + + this->js_content = (const unsigned char*) buf_pair.first; + this->js_len = buf_pair.second; + } + + string_fragment to_string_fragment() const + { + return string_fragment::from_bytes(this->js_content, this->js_len); + } + auto_mem<const unsigned char> js_content; size_t js_len{0}; }; void dump_schema_to(const json_path_container& jpc, const char* internals_dir); +namespace yajlpp { + +auto_mem<yajl_handle_t> alloc_handle(const yajl_callbacks* cb, void* cu); + +} // namespace yajlpp + #endif diff --git a/src/yajlpp/yajlpp_def.hh b/src/yajlpp/yajlpp_def.hh index 22454b1..7bcb687 100644 --- a/src/yajlpp/yajlpp_def.hh +++ b/src/yajlpp/yajlpp_def.hh @@ -241,11 +241,13 @@ struct json_path_handler : public json_path_handler_base { static int null_field_cb(yajlpp_parse_context* ypc) { + ypc->fill_in_source(); return ypc->ypc_current_handler->jph_null_cb(ypc); } static int bool_field_cb(yajlpp_parse_context* ypc, int val) { + ypc->fill_in_source(); return ypc->ypc_current_handler->jph_bool_cb(ypc, val); } @@ -253,20 +255,36 @@ struct json_path_handler : public json_path_handler_base { const unsigned char* str, size_t len) { - return ypc->ypc_current_handler->jph_str_cb(ypc, str, len); + ypc->fill_in_source(); + return ypc->ypc_current_handler->jph_str_cb( + ypc, string_fragment::from_bytes(str, len)); } static int int_field_cb(yajlpp_parse_context* ypc, long long val) { + ypc->fill_in_source(); return ypc->ypc_current_handler->jph_integer_cb(ypc, val); } static int dbl_field_cb(yajlpp_parse_context* ypc, double val) { + ypc->fill_in_source(); return ypc->ypc_current_handler->jph_double_cb(ypc, val); } template<typename T, typename U> + static inline U& get_field(T& input, std::shared_ptr<U>(T::*field)) + { + auto& ptr = input.*field; + + if (ptr.get() == nullptr) { + ptr = std::make_shared<U>(); + } + + return *ptr; + } + + template<typename T, typename U> static inline U& get_field(T& input, U(T::*field)) { return input.*field; @@ -366,11 +384,64 @@ struct json_path_handler : public json_path_handler_base { }; template<typename T, typename U> + struct LastIsVector<std::shared_ptr<std::vector<U>> T::*> { + using value_type = U; + static constexpr bool value = true; + }; + + template<typename T, typename U> struct LastIsVector<U T::*> { using value_type = void; static constexpr bool value = false; }; + template<typename T, typename... Args> + struct LastIsIntegerVector { + using value_type = typename LastIsIntegerVector<Args...>::value_type; + static constexpr bool value = LastIsIntegerVector<Args...>::value; + }; + + template<typename T, typename U> + struct LastIsIntegerVector<std::vector<U> T::*> { + using value_type = U; + static constexpr bool value + = std::is_integral<U>::value && !std::is_same<U, bool>::value; + }; + + template<typename T, typename U> + struct LastIsIntegerVector<U T::*> { + using value_type = void; + static constexpr bool value = false; + }; + + template<typename T, typename... Args> + struct LastIsMap { + using key_type = typename LastIsMap<Args...>::key_type; + using value_type = typename LastIsMap<Args...>::value_type; + static constexpr bool value = LastIsMap<Args...>::value; + }; + + template<typename T, typename K, typename U> + struct LastIsMap<std::shared_ptr<std::map<K, U>> T::*> { + using key_type = K; + using value_type = U; + static constexpr bool value = true; + }; + + template<typename T, typename K, typename U> + struct LastIsMap<std::map<K, U> T::*> { + using key_type = K; + using value_type = U; + static constexpr bool value = true; + }; + + template<typename T, typename U> + struct LastIsMap<U T::*> { + using key_type = void; + using value_type = void; + static constexpr bool value = false; + }; + template<typename T> static bool is_field_set(const nonstd::optional<T>& field) { @@ -434,15 +505,13 @@ struct json_path_handler : public json_path_handler_base { { 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; + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* jph = ypc->ypc_current_handler; jph->validate_string(*ypc, value_str); json_path_handler::get_field(obj, args...) - .emplace_back(std::move(value_str)); + .emplace_back(value_str.to_string()); return 1; }; @@ -450,10 +519,33 @@ struct json_path_handler : public json_path_handler_base { } template<typename... Args, + std::enable_if_t<LastIsIntegerVector<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) { + const 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...).emplace_back(val); + + return 1; + }; + return *this; + } + + template<typename... Args, std::enable_if_t<LastIsVector<Args...>::value, bool> = true, std::enable_if_t< !std::is_same<typename LastIsVector<Args...>::value_type, - std::string>::value, + std::string>::value + && !LastIsIntegerVector<Args...>::value, bool> = true> json_path_handler& for_field(Args... args) @@ -515,16 +607,37 @@ struct json_path_handler : public json_path_handler_base { { 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(); + const string_fragment& value_str) { + 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); + = value_str.to_string(); return 1; }; + this->jph_path_provider = + [args...](void* root, std::vector<std::string>& paths_out) { + const auto& field = json_path_handler::get_field(root, args...); + + for (const auto& pair : field) { + paths_out.emplace_back(pair.first); + } + }; + this->jph_field_getter + = [args...](void* root, + nonstd::optional<std::string> name) -> const void* { + const auto& field = json_path_handler::get_field(root, args...); + if (!name) { + return &field; + } + + auto iter = field.find(name.value()); + if (iter == field.end()) { + return nullptr; + } + return (void*) &iter->second; + }; this->jph_gen_callback = [args...](yajlpp_gen_context& ygc, const json_path_handler_base& jph, yajl_gen handle) { @@ -555,6 +668,70 @@ struct json_path_handler : public json_path_handler_base { } template<typename... Args, + std::enable_if_t<LastIsMap<Args...>::value, bool> = true, + std::enable_if_t< + std::is_same<intern_string_t, + typename LastIsMap<Args...>::key_type>::value, + bool> + = true> + json_path_handler& for_field(Args... args) + { + this->jph_path_provider = + [args...](void* root, std::vector<std::string>& paths_out) { + const auto& field = json_path_handler::get_field(root, args...); + + for (const auto& pair : field) { + paths_out.emplace_back(std::to_string(pair.first)); + } + }; + this->jph_obj_provider + = [args...](const yajlpp_provider_context& ypc, void* root) { + auto& field = json_path_handler::get_field(root, args...); + + return &(field[ypc.get_substr_i(0)]); + }; + return *this; + } + + template< + typename... Args, + std::enable_if_t<LastIsMap<Args...>::value, bool> = true, + std::enable_if_t< + std::is_same<std::string, + typename LastIsMap<Args...>::key_type>::value, + bool> + = true, + std::enable_if_t< + !std::is_same<json_any_t, + typename LastIsMap<Args...>::value_type>::value + && !std::is_same<std::string, + typename LastIsMap<Args...>::value_type>::value + && !std::is_same< + nonstd::optional<std::string>, + typename LastIsMap<Args...>::value_type>::value, + bool> + = true> + json_path_handler& for_field(Args... args) + { + this->jph_path_provider = + [args...](void* root, std::vector<std::string>& paths_out) { + const auto& field = json_path_handler::get_field(root, args...); + + for (const auto& pair : field) { + paths_out.emplace_back(pair.first); + } + }; + this->jph_obj_provider + = [args...](const yajlpp_provider_context& ypc, void* root) { + auto& field = json_path_handler::get_field(root, args...); + auto key = ypc.get_substr(0); + + return &(field[key]); + }; + return *this; + } + + template<typename... Args, std::enable_if_t< LastIs<std::map<std::string, nonstd::optional<std::string>>, Args...>::value, @@ -564,13 +741,12 @@ struct json_path_handler : public json_path_handler_base { { this->add_cb(str_field_cb2); this->jph_str_cb = [args...](yajlpp_parse_context* ypc, - const unsigned char* str, - size_t len) { + const string_fragment& value_str) { 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); + = value_str.to_string(); return 1; }; @@ -642,13 +818,12 @@ struct json_path_handler : public json_path_handler_base { }; this->add_cb(str_field_cb2); this->jph_str_cb = [args...](yajlpp_parse_context* ypc, - const unsigned char* str, - size_t len) { + const string_fragment& value_str) { 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); + = value_str.to_string(); return 1; }; @@ -691,14 +866,12 @@ struct json_path_handler : public json_path_handler_base { { 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; + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* jph = ypc->ypc_current_handler; jph->validate_string(*ypc, value_str); - json_path_handler::get_field(obj, args...) = std::move(value_str); + json_path_handler::get_field(obj, args...) = value_str.to_string(); return 1; }; @@ -738,23 +911,22 @@ struct json_path_handler : public json_path_handler_base { { 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 jph = ypc->ypc_current_handler; + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* jph = ypc->ypc_current_handler; date_time_scanner dts; timeval tv{}; exttm tm; - if (dts.scan((char*) str, len, nullptr, &tm, tv) == nullptr) { - ypc->report_error( - lnav::console::user_message::error( - attr_line_t("unrecognized timestamp ") - .append_quoted( - string_fragment::from_bytes(str, len))) - .with_snippet(ypc->get_snippet()) - .with_help(jph->get_help_text(ypc))); + if (dts.scan(value_str.data(), value_str.length(), nullptr, &tm, tv) + == nullptr) + { + ypc->report_error(lnav::console::user_message::error( + attr_line_t("unrecognized timestamp ") + .append_quoted(value_str)) + .with_snippet(ypc->get_snippet()) + .with_help(jph->get_help_text(ypc))); } else { json_path_handler::get_field(obj, args...) = tv; } @@ -804,14 +976,20 @@ struct json_path_handler : public json_path_handler_base { { 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; + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* jph = ypc->ypc_current_handler; jph->validate_string(*ypc, value_str); - json_path_handler::get_field(obj, args...) = std::move(value_str); + json_path_handler::get_field(obj, args...) = value_str.to_string(); + + return 1; + }; + this->add_cb(null_field_cb); + this->jph_null_cb = [args...](yajlpp_parse_context* ypc) { + auto* obj = ypc->ypc_obj_stack.top(); + + json_path_handler::get_field(obj, args...) = nonstd::nullopt; return 1; }; @@ -858,11 +1036,9 @@ struct json_path_handler : public json_path_handler_base { { 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; + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* jph = ypc->ypc_current_handler; jph->validate_string(*ypc, value_str); auto& field = json_path_handler::get_field(obj, args...); @@ -870,7 +1046,7 @@ struct json_path_handler : public json_path_handler_base { 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); + field.pp_value = value_str.to_string(); return 1; }; @@ -897,6 +1073,10 @@ struct json_path_handler : public json_path_handler_base { return gen(field.pp_value); }; + this->jph_field_getter + = [args...](void* root, nonstd::optional<std::string> name) { + return (void*) &json_path_handler::get_field(root, args...); + }; return *this; } @@ -907,11 +1087,9 @@ struct json_path_handler : public json_path_handler_base { { 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; + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* jph = ypc->ypc_current_handler; jph->validate_string(*ypc, value_str); json_path_handler::get_field(obj, args...) @@ -945,6 +1123,60 @@ struct json_path_handler : public json_path_handler_base { return *this; } + template< + typename... Args, + std::enable_if_t< + LastIs<positioned_property<const date::time_zone*>, 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 string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* jph = ypc->ypc_current_handler; + + try { + const auto* tz + = date::get_tzdb().locate_zone(value_str.to_string()); + 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 = tz; + } catch (const std::runtime_error& e) { + jph->report_tz_error(ypc, value_str.to_string(), e.what()); + } + + 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->name()); + }; + return *this; + } + template<typename... Args, std::enable_if_t< LastIs<positioned_property<intern_string_t>, Args...>::value, @@ -954,11 +1186,9 @@ struct json_path_handler : public json_path_handler_base { { 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; + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* jph = ypc->ypc_current_handler; jph->validate_string(*ypc, value_str); auto& field = json_path_handler::get_field(obj, args...); @@ -997,7 +1227,7 @@ struct json_path_handler : public json_path_handler_base { template<typename> struct int_ { - typedef int type; + using type = int; }; template< typename C, @@ -1009,11 +1239,10 @@ struct json_path_handler : public json_path_handler_base { 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) { + this->jph_str_cb = [args..., ptr_arg]( + yajlpp_parse_context* ypc, + const string_fragment& value_frag) { auto* obj = ypc->ypc_obj_stack.top(); - auto value_frag = string_fragment::from_bytes(str, len); const auto* jph = ypc->ypc_current_handler; auto loc = source_location{ypc->ypc_source, ypc->get_line_number()}; @@ -1028,6 +1257,30 @@ struct json_path_handler : public json_path_handler_base { return 1; }; + this->jph_gen_callback + = [args..., ptr_arg](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..., ptr_arg); + + if (!ygc.ygc_default_stack.empty()) { + const auto& field_def = json_path_handler::get_field( + ygc.ygc_default_stack.top(), args..., ptr_arg); + + 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.to_string()); + }; return *this; } @@ -1038,7 +1291,7 @@ struct json_path_handler : public json_path_handler_base { this->add_cb(int_field_cb); this->jph_integer_cb = [args...](yajlpp_parse_context* ypc, long long val) { - auto jph = ypc->ypc_current_handler; + const auto* jph = ypc->ypc_current_handler; auto* obj = ypc->ypc_obj_stack.top(); if (val < jph->jph_min_value) { @@ -1091,7 +1344,7 @@ struct json_path_handler : public json_path_handler_base { { this->add_cb(dbl_field_cb); this->jph_double_cb = [args...](yajlpp_parse_context* ypc, double val) { - auto jph = ypc->ypc_current_handler; + const auto* jph = ypc->ypc_current_handler; auto* obj = ypc->ypc_obj_stack.top(); if (val < jph->jph_min_value) { @@ -1146,18 +1399,16 @@ struct json_path_handler : public json_path_handler_base { { 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)); + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* handler = ypc->ypc_current_handler; + auto parse_res = relative_time::from_str(value_str); 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); + handler->report_duration_error( + ypc, value_str.to_string(), parse_error); return 1; } @@ -1204,18 +1455,16 @@ struct json_path_handler : public json_path_handler_base { { 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)); + const string_fragment& value_str) { + auto* obj = ypc->ypc_obj_stack.top(); + const auto* handler = ypc->ypc_current_handler; + auto res = handler->to_enum_value(value_str); if (res) { json_path_handler::get_field(obj, args...) = (typename LastIsEnum<Args...>::value_type) res.value(); } else { - handler->report_enum_error(ypc, - std::string((const char*) str, len)); + handler->report_enum_error(ypc, value_str.to_string()); } return 1; @@ -1325,11 +1574,34 @@ public: return *this; } + Result<void, std::vector<lnav::console::user_message>> consume( + const string_fragment& json) + { + if (this->yp_parse_context.parse(json) == yajl_status_ok) { + if (this->yp_errors.empty()) { + return Ok(); + } + } + + return Err(std::move(this->yp_errors)); + } + + Result<T, std::vector<lnav::console::user_message>> complete() + { + if (this->yp_parse_context.complete_parse() == yajl_status_ok) { + return Ok(std::move(this->yp_obj)); + } + + return Err(std::move(this->yp_errors)); + } + 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)); + if (this->yp_errors.empty()) { + return Ok(std::move(this->yp_obj)); + } } return Err(std::move(this->yp_errors)); @@ -1343,6 +1615,34 @@ private: }; template<typename T> +struct typed_json_path_container; + +template<typename T> +struct yajlpp_formatter { + const T& yf_obj; + const typed_json_path_container<T>& yf_container; + yajlpp_gen yf_gen; + + template<typename... Args> + yajlpp_formatter<T> with_config(Args... args) && + { + yajl_gen_config(this->yf_gen.get_handle(), args...); + + return std::move(*this); + } + + std::string to_string() && + { + yajlpp_gen_context ygc(this->yf_gen, this->yf_container); + ygc.template with_obj(this->yf_obj); + ygc.ygc_depth = 1; + ygc.gen(); + + return this->yf_gen.to_string_fragment().to_string(); + } +}; + +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) @@ -1366,6 +1666,11 @@ struct typed_json_path_container : public json_path_container { return yajlpp_parser<T>{src, this}; } + yajlpp_formatter<T> formatter_for(const T& obj) const + { + return yajlpp_formatter<T>{obj, *this}; + } + std::string to_string(const T& obj) const { yajlpp_gen gen; |