diff options
Diffstat (limited to '')
-rw-r--r-- | src/string-extension-functions.cc | 167 |
1 files changed, 102 insertions, 65 deletions
diff --git a/src/string-extension-functions.cc b/src/string-extension-functions.cc index fccc7a9..59c5e83 100644 --- a/src/string-extension-functions.cc +++ b/src/string-extension-functions.cc @@ -35,6 +35,7 @@ #include "spookyhash/SpookyV2.h" #include "sqlite-extension-func.hh" #include "text_anonymizer.hh" +#include "view_curses.hh" #include "vtab_module.hh" #include "vtab_module_json.hh" #include "yajl/api/yajl_gen.h" @@ -48,6 +49,35 @@ using namespace mapbox; +enum class encode_algo { + base64, + hex, + uri, +}; + +template<> +struct from_sqlite<encode_algo> { + inline encode_algo operator()(int argc, sqlite3_value** val, int argi) + { + const char* algo_name = (const char*) sqlite3_value_text(val[argi]); + + if (strcasecmp(algo_name, "base64") == 0) { + return encode_algo::base64; + } + if (strcasecmp(algo_name, "hex") == 0) { + return encode_algo::hex; + } + if (strcasecmp(algo_name, "uri") == 0) { + return encode_algo::uri; + } + + throw from_sqlite_conversion_error("value of 'base64', 'hex', or 'uri'", + argi); + } +}; + +namespace { + struct cache_entry { std::shared_ptr<lnav::pcre2pp::code> re2; std::shared_ptr<column_namer> cn{ @@ -176,24 +206,7 @@ regexp_match(string_fragment re, string_fragment str) #endif } -json_string -extract(const char* str) -{ - data_scanner ds(str); - data_parser dp(&ds); - - dp.parse(); - // dp.print(stderr, dp.dp_pairs); - - yajlpp_gen gen; - yajl_gen_config(gen, yajl_gen_beautify, false); - - elements_to_json(gen, dp, &dp.dp_pairs); - - return json_string(gen); -} - -json_string +static json_string logfmt2json(string_fragment line) { logfmt::parser p(line); @@ -365,8 +378,8 @@ sparkline_final(sqlite3_context* context) return; } - auto* retval = (char*) malloc(sc->sc_values.size() * 3 + 1); - auto* start = retval; + auto retval = auto_mem<char>::malloc(sc->sc_values.size() * 3 + 1); + auto* start = retval.in(); for (const auto& value : sc->sc_values) { auto bar = humanize::sparkline(value, sc->sc_max_value); @@ -376,7 +389,7 @@ sparkline_final(sqlite3_context* context) } *start = '\0'; - sqlite3_result_text(context, retval, -1, free); + to_sqlite(context, std::move(retval)); sc->~sparkline_context(); } @@ -448,33 +461,6 @@ sql_gzip(sqlite3_value* val) return nonstd::nullopt; } -enum class encode_algo { - base64, - hex, - uri, -}; - -template<> -struct from_sqlite<encode_algo> { - inline encode_algo operator()(int argc, sqlite3_value** val, int argi) - { - const char* algo_name = (const char*) sqlite3_value_text(val[argi]); - - if (strcasecmp(algo_name, "base64") == 0) { - return encode_algo::base64; - } - if (strcasecmp(algo_name, "hex") == 0) { - return encode_algo::hex; - } - if (strcasecmp(algo_name, "uri") == 0) { - return encode_algo::uri; - } - - throw from_sqlite_conversion_error("value of 'base64', 'hex', or 'uri'", - argi); - } -}; - #if defined(HAVE_LIBCURL) static CURL* get_curl_easy() @@ -549,7 +535,7 @@ sql_encode(sqlite3_value* value, encode_algo algo) for (int lpc = 0; lpc < text_len; lpc++) { fmt::format_to(std::back_inserter(buf), - FMT_STRING("{:x}"), + FMT_STRING("{:02x}"), text[lpc]); } @@ -635,17 +621,18 @@ const char* curl_url_strerror(CURLUcode error); #endif static json_string -sql_parse_url(string_fragment url_frag) +sql_parse_url(std::string url) { static auto* CURL_HANDLE = get_curl_easy(); auto_mem<CURLU> cu(curl_url_cleanup); cu = curl_url(); - auto rc = curl_url_set(cu, CURLUPART_URL, url_frag.data(), 0); + auto rc = curl_url_set( + cu, CURLUPART_URL, url.c_str(), CURLU_NON_SUPPORT_SCHEME); if (rc != CURLUE_OK) { throw lnav::console::user_message::error( - attr_line_t("invalid URL: ").append_quoted(url_frag.to_string())) + attr_line_t("invalid URL: ").append(lnav::roles::file(url))) .with_reason(curl_url_strerror(rc)); } @@ -663,7 +650,7 @@ sql_parse_url(string_fragment url_frag) } else { root.gen(); } - root.gen("user"); + root.gen("username"); rc = curl_url_get(cu, CURLUPART_USER, url_part.out(), CURLU_URLDECODE); if (rc == CURLUE_OK) { root.gen(string_fragment::from_c_str(url_part.in())); @@ -708,25 +695,26 @@ sql_parse_url(string_fragment url_frag) robin_hood::unordered_set<std::string> seen_keys; yajlpp_map query_map(gen); + for (size_t lpc = 0; url_part.in()[lpc]; lpc++) { + if (url_part.in()[lpc] == '+') { + url_part.in()[lpc] = ' '; + } + } auto query_frag = string_fragment::from_c_str(url_part.in()); auto remaining = query_frag; while (true) { auto split_res = remaining.split_when(string_fragment::tag1{'&'}); - - if (!split_res) { - break; - } - auto_mem<char> kv_pair(curl_free); - auto kv_pair_encoded = split_res->first; + auto kv_pair_encoded = split_res.first; int out_len = 0; kv_pair = curl_easy_unescape(CURL_HANDLE, kv_pair_encoded.data(), kv_pair_encoded.length(), &out_len); + auto kv_pair_frag = string_fragment::from_bytes(kv_pair.in(), out_len); auto eq_index_opt = kv_pair_frag.find('='); @@ -741,20 +729,20 @@ sql_parse_url(string_fragment url_frag) query_map.gen(val); } } else { - auto val_str = split_res->first.to_string(); + auto val_str = split_res.first.to_string(); if (seen_keys.count(val_str) == 0) { seen_keys.insert(val_str); - query_map.gen(split_res->first); + query_map.gen(split_res.first); query_map.gen(); } } - if (split_res->second.empty()) { + if (split_res.second.empty()) { break; } - remaining = split_res->second; + remaining = split_res.second; } } else { root.gen("query"); @@ -788,7 +776,7 @@ struct url_parts { }; static const json_path_container url_params_handlers = { - yajlpp::pattern_property_handler("(?<param>.+)") + yajlpp::pattern_property_handler("(?<param>.*)") .for_field(&url_parts::up_parameters), }; @@ -812,7 +800,7 @@ sql_unparse_url(string_fragment in) auto parse_res = url_parts_handlers.parser_for(SRC).of(in); if (parse_res.isErr()) { - throw parse_res.unwrapErr(); + throw parse_res.unwrapErr()[0]; } auto up = parse_res.unwrap(); @@ -873,6 +861,36 @@ sql_unparse_url(string_fragment in) return retval; } +} // namespace + +json_string +extract(const char* str) +{ + data_scanner ds(str); + data_parser dp(&ds); + + dp.parse(); + // dp.print(stderr, dp.dp_pairs); + + yajlpp_gen gen; + yajl_gen_config(gen, yajl_gen_beautify, false); + + elements_to_json(gen, dp, &dp.dp_pairs); + + return json_string(gen); +} + +static std::string +sql_humanize_id(string_fragment id) +{ + auto& vc = view_colors::singleton(); + auto attrs = vc.attrs_for_ident(id.data(), id.length()); + + return fmt::format(FMT_STRING("\x1b[38;5;{}m{}\x1b[0m"), + attrs.ta_fg_color.value_or(COLOR_CYAN), + id); +} + int string_extension_functions(struct FuncDef** basic_funcs, struct FuncDefAgg** agg_funcs) @@ -892,6 +910,7 @@ string_extension_functions(struct FuncDef** basic_funcs, "Match a string against a regular expression and return " "the capture groups as JSON.") .sql_function() + .with_prql_path({"text", "regexp_match"}) .with_parameter({"re", "The regular expression to use"}) .with_parameter({ "str", @@ -919,6 +938,7 @@ string_extension_functions(struct FuncDef** basic_funcs, "Replace the parts of a string that match a regular " "expression.") .sql_function() + .with_prql_path({"text", "regexp_replace"}) .with_parameter( {"str", "The string to perform replacements on"}) .with_parameter({"re", "The regular expression to match"}) @@ -947,6 +967,7 @@ string_extension_functions(struct FuncDef** basic_funcs, "humanize_file_size", "Format the given file size as a human-friendly string") .sql_function() + .with_prql_path({"humanize", "file_size"}) .with_parameter({"value", "The file size to format"}) .with_tags({"string"}) .with_example({ @@ -954,6 +975,18 @@ string_extension_functions(struct FuncDef** basic_funcs, "SELECT humanize_file_size(10 * 1024 * 1024)", })), + sqlite_func_adapter<decltype(&sql_humanize_id), sql_humanize_id>:: + builder(help_text("humanize_id", + "Colorize the given ID using ANSI escape codes.") + .sql_function() + .with_prql_path({"humanize", "id"}) + .with_parameter({"id", "The identifier to color"}) + .with_tags({"string"}) + .with_example({ + "To colorize the ID 'cluster1'", + "SELECT humanize_id('cluster1')", + })), + sqlite_func_adapter<decltype(&humanize::sparkline), humanize::sparkline>:: builder( @@ -964,6 +997,7 @@ string_extension_functions(struct FuncDef** basic_funcs, "aggregate version returns a string with a bar " "character for every numeric input") .sql_function() + .with_prql_path({"text", "sparkline"}) .with_parameter({"value", "The numeric value to convert"}) .with_parameter(help_text("upper", "The upper bound of the numeric " @@ -989,6 +1023,7 @@ string_extension_functions(struct FuncDef** basic_funcs, help_text("anonymize", "Replace identifying information with random values.") .sql_function() + .with_prql_path({"text", "anonymize"}) .with_parameter({"value", "The text to anonymize"}) .with_tags({"string"}) .with_example({ @@ -1000,6 +1035,7 @@ string_extension_functions(struct FuncDef** basic_funcs, help_text("extract", "Automatically Parse and extract data from a string") .sql_function() + .with_prql_path({"text", "discover"}) .with_parameter({"str", "The string to parse"}) .with_tags({"string"}) .with_example({ @@ -1015,6 +1051,7 @@ string_extension_functions(struct FuncDef** basic_funcs, help_text("logfmt2json", "Convert a logfmt-encoded string into JSON") .sql_function() + .with_prql_path({"logfmt", "to_json"}) .with_parameter({"str", "The logfmt message to parse"}) .with_tags({"string"}) .with_example({ |