• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.96
/src/db_sub_source.cc
1
/**
2
 * Copyright (c) 2014, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#include <iterator>
31

32
#include "db_sub_source.hh"
33

34
#include "base/ansi_scrubber.hh"
35
#include "base/date_time_scanner.hh"
36
#include "base/humanize.hh"
37
#include "base/itertools.enumerate.hh"
38
#include "base/itertools.hh"
39
#include "base/math_util.hh"
40
#include "base/time_util.hh"
41
#include "base/types.hh"
42
#include "config.h"
43
#include "hist_source_T.hh"
44
#include "scn/scan.h"
45
#include "yajlpp/json_ptr.hh"
46
#include "yajlpp/yajlpp_def.hh"
47

48
using namespace lnav::roles::literals;
49

50
const unsigned char db_label_source::NULL_STR[] = "<NULL>";
51

52
constexpr ssize_t MAX_JSON_WIDTH = 16 * 1024;
53

54
struct user_row_style {
55
    std::map<std::string, style_config> urs_column_config;
56
};
57

58
static const typed_json_path_container<user_row_style>&
59
get_row_style_handlers()
9✔
60
{
61
    static const json_path_container col_style_handlers = {
62
        yajlpp::pattern_property_handler("(?<column_name>[^/]+)")
3✔
63
            .for_field(&user_row_style::urs_column_config)
3✔
64
            .with_children(style_config_handlers),
3✔
65
    };
18✔
66

67
    static const typed_json_path_container<user_row_style> retval
68
        = typed_json_path_container<user_row_style>{
6✔
69
            yajlpp::property_handler("columns")
9✔
70
                .with_children(col_style_handlers),
3✔
71
    }.with_schema_id2("row-style");
18✔
72

73
    return retval;
9✔
74
}
12✔
75

76
line_info
77
db_label_source::text_value_for_line(textview_curses& tc,
928✔
78
                                     int row,
79
                                     std::string& label_out,
80
                                     line_flags_t flags)
81
{
82
    /*
83
     * start_value is the result rowid, each bucket type is a column value
84
     * label_out should be the raw text output.
85
     */
86

87
    label_out.clear();
928✔
88
    this->dls_ansi_attrs.clear();
928✔
89
    label_out.reserve(this->dls_max_column_width * this->dls_headers.size());
928✔
90
    if (row < 0_vl || row >= (int) this->dls_row_cursors.size()) {
928✔
UNCOV
91
        return {};
×
92
    }
93
    std::optional<log_level_t> row_level;
928✔
94
    auto cell_cursor = this->dls_row_cursors[row].sync();
928✔
95
    for (size_t lpc = 0; lpc < this->dls_headers.size();
5,090✔
96
         lpc++, cell_cursor = cell_cursor->next())
4,162✔
97
    {
98
        if (lpc == this->dls_row_style_column
4,162✔
99
            && !this->dls_row_styles_have_errors)
9✔
100
        {
101
            continue;
9✔
102
        }
103
        const auto& hm = this->dls_headers[lpc];
4,156✔
104
        if (hm.hm_hidden) {
4,156✔
105
            continue;
3✔
106
        }
107
        ssize_t actual_col_size
108
            = std::min(this->dls_max_column_width, hm.hm_column_size);
4,153✔
109
        auto align = hm.hm_align;
4,153✔
110

111
        if (row < (int) this->dls_row_styles.size()) {
4,153✔
112
            auto style_iter
113
                = this->dls_row_styles[row].rs_column_config.find(lpc);
18✔
114
            if (style_iter != this->dls_row_styles[row].rs_column_config.end())
18✔
115
            {
116
                if (style_iter->second.ta_align.has_value()) {
6✔
117
                    align = style_iter->second.ta_align.value();
3✔
118
                }
119
            }
120
        }
121

122
        auto sf = cell_cursor->to_string_fragment(this->dls_cell_allocator);
4,153✔
123
        auto al = attr_line_t::from_table_cell_content(
124
            sf, this->dls_max_column_width);
4,153✔
125

126
        if (this->tss_view != nullptr
8,306✔
127
            && cell_cursor->get_type() == lnav::cell_type::CT_TEXT)
4,153✔
128
        {
129
            this->tss_view->apply_highlights(
1,011✔
130
                al, line_range::empty_at(0), line_range::empty_at(0));
2,022✔
131
        }
132
        if (this->dls_level_column && lpc == this->dls_level_column.value()) {
4,153✔
133
            row_level = string2level(sf.data(), sf.length());
119✔
134
        }
135

136
        auto cell_length = al.utf8_length_or_length();
4,153✔
137
        if (actual_col_size < cell_length) {
4,153✔
UNCOV
138
            log_warning(
×
139
                "invalid column size: actual_col_size=%d < cell_length=%d",
140
                actual_col_size,
141
                cell_length);
UNCOV
142
            cell_length = actual_col_size;
×
143
        }
144
        const auto padding = actual_col_size - cell_length;
4,153✔
145
        auto lpadding = 0;
4,153✔
146
        auto rpadding = padding;
4,153✔
147
        switch (align) {
4,153✔
148
            case text_align_t::start:
1,907✔
149
                break;
1,907✔
150
            case text_align_t::center: {
1✔
151
                lpadding = padding / 2;
1✔
152
                rpadding = padding - lpadding;
1✔
153
                break;
1✔
154
            }
155
            case text_align_t::end:
2,245✔
156
                lpadding = padding;
2,245✔
157
                rpadding = 0;
2,245✔
158
                break;
2,245✔
159
        }
160
        this->dls_cell_width[lpc] = al.al_string.length() + padding;
4,153✔
161
        label_out.append(lpadding, ' ');
4,153✔
162
        shift_string_attrs(al.al_attrs, 0, label_out.size());
4,153✔
163
        label_out.append(std::move(al.al_string));
4,153✔
164
        label_out.append(rpadding, ' ');
4,153✔
165
        label_out.push_back(' ');
4,153✔
166

167
        this->dls_ansi_attrs.insert(
8,306✔
168
            this->dls_ansi_attrs.end(),
4,153✔
169
            std::make_move_iterator(al.al_attrs.begin()),
170
            std::make_move_iterator(al.al_attrs.end()));
171
    }
4,153✔
172
    if (row_level.has_value()) {
928✔
173
        this->dls_ansi_attrs.emplace_back(line_range{0, -1},
119✔
174
                                          SA_LEVEL.value(row_level.value()));
238✔
175
    }
176
    this->dls_ansi_attrs.reserve(this->dls_ansi_attrs.size()
928✔
177
                                 + 3 * this->dls_headers.size());
928✔
178
    this->dls_cell_allocator.reset();
928✔
179

180
    return {};
928✔
181
}
182

183
void
184
db_label_source::text_attrs_for_line(textview_curses& tc,
928✔
185
                                     int row,
186
                                     string_attrs_t& sa)
187
{
188
    static const auto NUM_ATTR = VC_ROLE.value(role_t::VCR_NUMBER);
928✔
189
    static const auto VLINE_ATTR = VC_GRAPHIC.value(NCACS_VLINE);
928✔
190

191
    line_range lr(0, 0);
928✔
192
    const line_range lr2(0, -1);
928✔
193

194
    if (row < 0_vl || row >= (int) this->dls_row_cursors.size()) {
928✔
195
        return;
×
196
    }
197
    sa = std::move(this->dls_ansi_attrs);
928✔
198
    auto alt_row_index = row % 4;
928✔
199
    if (alt_row_index == 2 || alt_row_index == 3) {
928✔
200
        sa.emplace_back(lr2, VC_ROLE.value(role_t::VCR_ALT_ROW));
346✔
201
    }
202
    sa.emplace_back(line_range{0, 0}, SA_ORIGINAL_LINE.value());
928✔
203
    sa.emplace_back(line_range{0, 0}, SA_BODY.value());
928✔
204
    for (size_t lpc = 0; lpc < this->dls_headers.size() - 1; lpc++) {
4,162✔
205
        if (lpc == this->dls_row_style_column
3,234✔
UNCOV
206
            && !this->dls_row_styles_have_errors)
×
207
        {
UNCOV
208
            continue;
×
209
        }
210

211
        const auto& hm = this->dls_headers[lpc];
3,234✔
212
        if (hm.hm_hidden) {
3,234✔
213
            continue;
3✔
214
        }
215

216
        if (hm.is_graphable()) {
3,231✔
217
            lr.lr_end += this->dls_cell_width[lpc];
657✔
218
            sa.emplace_back(lr, NUM_ATTR);
657✔
219
        }
220
        lr.lr_start += this->dls_cell_width[lpc];
3,231✔
221
        lr.lr_end = lr.lr_start + 1;
3,231✔
222
        sa.emplace_back(lr, VLINE_ATTR);
3,231✔
223
        lr.lr_start += 1;
3,231✔
224
    }
225

226
    for (const auto& attr : sa) {
7,288✔
227
        require_ge(attr.sa_range.lr_start, 0);
6,360✔
228
    }
229
    int cell_start = 0;
928✔
230
    auto cursor = this->dls_row_cursors[row].sync();
928✔
231
    for (size_t lpc = 0; lpc < this->dls_headers.size();
5,090✔
232
         lpc++, cursor = cursor->next())
4,162✔
233
    {
234
        std::optional<text_attrs> user_attrs;
4,162✔
235

236
        if (lpc == this->dls_row_style_column) {
4,162✔
237
            if (!this->dls_row_styles_have_errors) {
9✔
238
                continue;
6✔
239
            }
240
        }
241

242
        const auto& hm = this->dls_headers[lpc];
4,156✔
243
        if (hm.hm_hidden) {
4,156✔
244
            continue;
3✔
245
        }
246
        if (row < (ssize_t) this->dls_row_styles.size()) {
4,153✔
247
            auto style_iter
248
                = this->dls_row_styles[row].rs_column_config.find(lpc);
18✔
249
            if (style_iter != this->dls_row_styles[row].rs_column_config.end())
18✔
250
            {
251
                user_attrs = style_iter->second;
6✔
252
            }
253
        }
254

255
        int left = cell_start;
4,153✔
256
        auto stlr = line_range{
257
            cell_start,
258
            (int) (cell_start + this->dls_cell_width[lpc]),
8,306✔
259
        };
4,153✔
260
        if (hm.is_graphable()) {
4,153✔
261
            std::optional<double> get_res;
1,007✔
262
            if (cursor->get_type() == lnav::cell_type::CT_INTEGER) {
1,007✔
263
                get_res = cursor->get_int();
700✔
264
            } else if (cursor->get_type() == lnav::cell_type::CT_FLOAT) {
307✔
265
                get_res = cursor->get_float();
307✔
266
            }
267
            if (get_res.has_value()) {
1,007✔
268
                hm.hm_chart.chart_attrs_for_value(tc,
2,014✔
269
                                                  left,
270
                                                  this->dls_cell_width[lpc],
1,007✔
271
                                                  hm.hm_name,
1,007✔
272
                                                  get_res.value(),
1,007✔
273
                                                  sa,
274
                                                  user_attrs);
275

276
                for (const auto& attr : sa) {
18,089✔
277
                    require_ge(attr.sa_range.lr_start, 0);
17,082✔
278
                }
279
            }
280
        } else if (user_attrs.has_value()) {
3,146✔
281
            sa.emplace_back(stlr, VC_STYLE.value(user_attrs.value()));
3✔
282
        }
283
        auto cell_sf = string_fragment::invalid();
4,153✔
284
        if (cursor->get_type() == lnav::cell_type::CT_TEXT) {
4,153✔
285
            cell_sf = cursor->get_text();
1,567✔
286
        } else if (cursor->get_type() == lnav::cell_type::CT_NULL) {
2,586✔
287
            sa.emplace_back(stlr, VC_ROLE.value(role_t::VCR_NULL));
917✔
288
        }
289
        if (lpc == this->dls_row_style_column) {
4,153✔
290
            sa.emplace_back(stlr, VC_ROLE.value(role_t::VCR_ERROR));
3✔
291
        } else if (cell_sf.is_valid() && cell_sf.length() > 2
5,714✔
292
                   && cell_sf.length() < MAX_JSON_WIDTH
1,329✔
293
                   && ((cell_sf.front() == '{' && cell_sf.back() == '}')
6,986✔
294
                       || (cell_sf.front() == '[' && cell_sf.back() == ']')))
1,272✔
295
        {
296
            json_ptr_walk jpw;
112✔
297

298
            if (jpw.parse(cell_sf.udata(), cell_sf.length()) == yajl_status_ok
112✔
299
                && jpw.complete_parse() == yajl_status_ok)
112✔
300
            {
301
                for (const auto& jpw_value : jpw.jpw_values) {
247✔
302
                    if (jpw_value.wt_type != yajl_t_number) {
181✔
303
                        continue;
118✔
304
                    }
305

306
                    auto num_scan_res = humanize::try_from<double>(
63✔
307
                        string_fragment::from_str(jpw_value.wt_value));
63✔
308

309
                    if (num_scan_res) {
63✔
310
                        hm.hm_chart.chart_attrs_for_value(
126✔
311
                            tc,
312
                            left,
313
                            this->dls_cell_width[lpc],
63✔
314
                            jpw_value.wt_ptr,
63✔
315
                            num_scan_res.value(),
63✔
316
                            sa);
317
                        for (const auto& attr : sa) {
573✔
318
                            require_ge(attr.sa_range.lr_start, 0);
510✔
319
                        }
320
                    }
321
                }
322
            }
323
        }
112✔
324
        cell_start += this->dls_cell_width[lpc] + 1;
4,153✔
325
    }
4,162✔
326

327
    for (const auto& attr : sa) {
9,007✔
328
        require_ge(attr.sa_range.lr_start, 0);
8,079✔
329
    }
330
}
331

332
void
333
db_label_source::set_col_as_graphable(int lpc)
689✔
334
{
335
    static auto& vc = view_colors::singleton();
689✔
336

337
    auto& hm = this->dls_headers[lpc];
689✔
338
    auto name_for_ident_attrs = hm.hm_name;
689✔
339
    auto attrs = vc.attrs_for_ident(name_for_ident_attrs);
689✔
340
    for (size_t attempt = 0; hm.hm_chart.attrs_in_use(attrs) && attempt < 3;
689✔
341
         attempt++)
342
    {
343
        name_for_ident_attrs += " ";
×
UNCOV
344
        attrs = vc.attrs_for_ident(name_for_ident_attrs);
×
345
    }
346
    hm.hm_graphable = true;
689✔
347
    hm.hm_chart.with_attrs_for_ident(hm.hm_name, attrs);
689✔
348
    hm.hm_title_attrs = attrs | text_attrs::with_reverse();
689✔
349
    hm.hm_column_size = std::max(hm.hm_column_size, size_t{10});
689✔
350
}
689✔
351

352
void
353
db_label_source::push_header(const std::string& colstr, int type)
2,908✔
354
{
355
    this->dls_headers.emplace_back(colstr);
2,908✔
356
    this->dls_cell_width.push_back(0);
2,908✔
357

358
    auto& hm = this->dls_headers.back();
2,908✔
359

360
    hm.hm_column_size = utf8_string_length(colstr).unwrapOr(colstr.length());
2,908✔
361
    hm.hm_column_type = type;
2,908✔
362
    if (colstr == "log_time" || colstr == "min(log_time)"
5,749✔
363
        || colstr == "log_time_msecs")
5,749✔
364
    {
365
        this->dls_time_column_index = this->dls_headers.size() - 1;
73✔
366
    }
367
    if (colstr == "__lnav_style__") {
2,908✔
368
        this->dls_row_style_column = this->dls_headers.size() - 1;
3✔
369
    }
370
    if (colstr == "log_level") {
2,908✔
371
        this->dls_level_column = this->dls_headers.size() - 1;
53✔
372
    }
373
    hm.hm_chart.with_show_state(stacked_bar_chart_base::show_all{});
2,908✔
374
}
2,908✔
375

376
void
377
db_label_source::update_time_column(const timeval& tv)
314✔
378
{
379
    if (!this->dls_time_column.empty() && tv < this->dls_time_column.back()) {
314✔
380
        this->dls_time_column_invalidated_at = this->dls_time_column.size();
1✔
381
        this->dls_time_column_index = SIZE_MAX;
1✔
382
        this->dls_time_column.clear();
1✔
383
    } else {
384
        this->dls_time_column.push_back(tv);
313✔
385
    }
386
}
314✔
387

388
void
389
db_label_source::update_time_column(const string_fragment& sf)
293✔
390
{
391
    date_time_scanner dts;
293✔
392
    timeval tv;
393

394
    if (!dts.convert_to_timeval(sf.data(), sf.length(), nullptr, tv)) {
293✔
395
        tv.tv_sec = -1;
×
396
        tv.tv_usec = -1;
×
397
    }
398
    this->update_time_column(tv);
293✔
399
}
293✔
400

401
void
402
db_label_source::push_column(const column_value_t& sv)
8,740✔
403
{
404
    auto row_index = this->dls_row_cursors.size() - 1;
8,740✔
405
    auto& vc = view_colors::singleton();
8,740✔
406
    auto col = this->dls_push_column++;
8,740✔
407
    auto& hm = this->dls_headers[col];
8,740✔
408
    size_t width = 1;
8,740✔
409
    auto cv_sf = string_fragment::invalid();
8,740✔
410

411
    sv.match(
8,740✔
UNCOV
412
        [this, &col, &width, &cv_sf, &hm, &row_index](
×
413
            const string_fragment& sf) {
414
            if (this->dls_row_style_column == col) {
3,919✔
415
                return;
9✔
416
            }
417
            if (col == this->dls_time_column_index) {
3,910✔
418
                this->update_time_column(sf);
293✔
419
            } else if (this->dls_level_column
7,234✔
420
                       && this->dls_level_column.value() == col
4,891✔
421
                       && this->tss_view != nullptr)
4,891✔
422
            {
423
                auto& bm = this->tss_view->get_bookmarks();
213✔
424
                auto lev = string2level(sf.data(), sf.length());
213✔
425
                switch (lev) {
213✔
426
                    case log_level_t::LEVEL_FATAL:
37✔
427
                    case log_level_t::LEVEL_CRITICAL:
428
                    case log_level_t::LEVEL_ERROR:
429
                        bm[&textview_curses::BM_ERRORS].insert_once(
74✔
430
                            vis_line_t(row_index));
37✔
431
                        break;
37✔
432
                    case log_level_t::LEVEL_WARNING:
6✔
433
                        bm[&textview_curses::BM_WARNINGS].insert_once(
12✔
434
                            vis_line_t(row_index));
6✔
435
                        break;
6✔
436
                    default:
170✔
437
                        break;
170✔
438
                }
439
            }
440
            width = utf8_string_length(sf.data(), sf.length())
3,910✔
441
                        .unwrapOr(sf.length());
3,910✔
442
            if (hm.is_graphable()
3,910✔
443
                && sf.length() < lnav::cell_container::SHORT_TEXT_LENGTH)
3,910✔
444
            {
445
                auto from_res = humanize::try_from<double>(sf);
382✔
446
                if (from_res.has_value()) {
382✔
447
                    this->dls_cell_container.push_float_with_units_cell(
764✔
448
                        from_res.value(), sf);
382✔
449
                } else {
UNCOV
450
                    this->dls_cell_container.push_text_cell(sf);
×
451
                }
452
            } else {
453
                this->dls_cell_container.push_text_cell(sf);
3,528✔
454
            }
455
            cv_sf = sf;
3,910✔
456
        },
UNCOV
457
        [this, col, &width](int64_t i) {
×
458
            width = count_digits(i);
2,539✔
459
            this->dls_cell_container.push_int_cell(i);
2,539✔
460
            if (col == this->dls_time_column_index) {
2,539✔
461
                auto ms = std::chrono::milliseconds{i};
21✔
462
                auto us
463
                    = std::chrono::duration_cast<std::chrono::microseconds>(ms);
21✔
464

465
                this->update_time_column(to_timeval(us));
21✔
466
            }
467
        },
2,539✔
UNCOV
468
        [this, &width](double d) {
×
469
            char buffer[1];
470
            auto fmt_res = fmt::format_to_n(buffer, 0, FMT_STRING("{}"), d);
693✔
471
            width = fmt_res.size;
231✔
472
            this->dls_cell_container.push_float_cell(d);
231✔
473
        },
231✔
474
        [this, &width](null_value_t) {
8,740✔
475
            width = 6;
2,051✔
476
            this->dls_cell_container.push_null_cell();
2,051✔
477
        });
2,051✔
478

479
    if (col == this->dls_row_style_column) {
8,740✔
480
        auto col_sf = string_fragment::invalid();
9✔
481
        if (sv.is<null_value_t>()) {
9✔
UNCOV
482
            this->dls_row_styles.emplace_back(row_style{});
×
483
        } else if (sv.is<string_fragment>()) {
9✔
484
            static const intern_string_t SRC
485
                = intern_string::lookup("__lnav_style__");
15✔
486
            auto frag = sv.get<string_fragment>();
9✔
487
            if (frag.empty()) {
9✔
UNCOV
488
                this->dls_row_styles.emplace_back(row_style{});
×
489
            } else {
490
                auto parse_res
491
                    = get_row_style_handlers().parser_for(SRC).of(frag);
9✔
492
                if (parse_res.isErr()) {
9✔
UNCOV
493
                    log_error("DB row %d JSON is invalid:", row_index);
×
UNCOV
494
                    auto errors = parse_res.unwrapErr();
×
UNCOV
495
                    for (const auto& err : errors) {
×
UNCOV
496
                        log_error("  %s", err.to_attr_line().al_string.c_str());
×
497
                    }
UNCOV
498
                    col_sf = string_fragment::from_str(
×
UNCOV
499
                                 errors[0].to_attr_line().al_string)
×
UNCOV
500
                                 .to_owned(this->dls_cell_allocator);
×
UNCOV
501
                    this->dls_row_styles_have_errors = true;
×
UNCOV
502
                } else {
×
503
                    auto urs = parse_res.unwrap();
9✔
504
                    auto rs = row_style{};
9✔
505
                    for (const auto& [col_name, col_style] :
18✔
506
                         urs.urs_column_config)
27✔
507
                    {
508
                        auto col_index_opt
509
                            = this->column_name_to_index(col_name);
9✔
510
                        if (!col_index_opt) {
9✔
511
                            log_error("DB row %d column name '%s' not found",
3✔
512
                                      row_index,
513
                                      col_name.c_str());
UNCOV
514
                            col_sf = string_fragment::from_str(
×
515
                                         fmt::format(
3✔
516
                                             FMT_STRING(
9✔
517
                                                 "column name '{}' not found"),
518
                                             col_name))
519
                                         .to_owned(this->dls_cell_allocator);
3✔
520
                            this->dls_row_styles_have_errors = true;
3✔
521
                        } else {
522
                            text_attrs ta;
6✔
523

524
                            auto fg_res = styling::color_unit::from_str(
525
                                col_style.sc_color);
6✔
526
                            if (fg_res.isErr()) {
6✔
UNCOV
527
                                log_error("DB row %d color is invalid: %s",
×
528
                                          row_index,
529
                                          fg_res.unwrapErr().c_str());
530
                                col_sf
UNCOV
531
                                    = string_fragment::from_str(
×
UNCOV
532
                                          fmt::format(
×
UNCOV
533
                                              FMT_STRING("invalid color: {}"),
×
UNCOV
534
                                              fg_res.unwrapErr()))
×
UNCOV
535
                                          .to_owned(this->dls_cell_allocator);
×
UNCOV
536
                                this->dls_row_styles_have_errors = true;
×
537
                            } else {
538
                                ta.ta_fg_color
539
                                    = vc.match_color(fg_res.unwrap());
6✔
540
                            }
541
                            auto bg_res = styling::color_unit::from_str(
542
                                col_style.sc_background_color);
6✔
543
                            if (bg_res.isErr()) {
6✔
UNCOV
544
                                log_error(
×
545
                                    "DB row %d background-color is invalid: %s",
546
                                    row_index,
547
                                    bg_res.unwrapErr().c_str());
548
                                col_sf
UNCOV
549
                                    = string_fragment::from_str(
×
UNCOV
550
                                          fmt::format(
×
UNCOV
551
                                              FMT_STRING(
×
552
                                                  "invalid "
553
                                                  "background-color: {}"),
UNCOV
554
                                              fg_res.unwrapErr()))
×
UNCOV
555
                                          .to_owned(this->dls_cell_allocator);
×
UNCOV
556
                                this->dls_row_styles_have_errors = true;
×
557
                            } else {
558
                                ta.ta_bg_color
559
                                    = vc.match_color(bg_res.unwrap());
6✔
560
                            }
561
                            ta.ta_align = col_style.sc_text_align;
6✔
562
                            if (col_style.sc_underline) {
6✔
UNCOV
563
                                ta |= text_attrs::style::underline;
×
564
                            }
565
                            if (col_style.sc_bold) {
6✔
UNCOV
566
                                ta |= text_attrs::style::bold;
×
567
                            }
568
                            if (col_style.sc_italic) {
6✔
UNCOV
569
                                ta |= text_attrs::style::italic;
×
570
                            }
571
                            if (col_style.sc_strike) {
6✔
UNCOV
572
                                ta |= text_attrs::style::struck;
×
573
                            }
574
                            if (this->dls_headers[col_index_opt.value()]
6✔
575
                                    .is_graphable())
6✔
576
                            {
577
                                this->dls_headers[col_index_opt.value()]
3✔
578
                                    .hm_title_attrs
579
                                    = text_attrs::with_underline();
6✔
580
                            }
581
                            rs.rs_column_config[col_index_opt.value()] = ta;
6✔
582
                        }
6✔
583
                    }
584
                    this->dls_row_styles.emplace_back(std::move(rs));
9✔
585
                }
9✔
586
            }
9✔
587
        } else {
UNCOV
588
            log_error("DB row %d is not a string -- %s",
×
589
                      row_index,
590
                      mapbox::util::apply_visitor(type_visitor(), sv));
591

592
            col_sf
UNCOV
593
                = string_fragment::from_str("expecting a JSON object for style")
×
UNCOV
594
                      .to_owned(this->dls_cell_allocator);
×
UNCOV
595
            this->dls_row_styles_have_errors = true;
×
596
        }
597

598
        if (col_sf.empty()) {
9✔
599
            this->dls_cell_container.push_null_cell();
6✔
600
        } else {
601
            this->dls_cell_container.push_text_cell(col_sf);
3✔
602
            width = utf8_string_length(col_sf.data(), col_sf.length())
3✔
603
                        .unwrapOr(col_sf.length());
3✔
604
            this->dls_cell_allocator.reset();
3✔
605
        }
606
    }
607

608
    hm.hm_column_size = std::max(this->dls_headers[col].hm_column_size, width);
8,740✔
609
    if (hm.is_graphable()) {
8,740✔
610
        if (sv.is<int64_t>()) {
1,940✔
611
            hm.hm_chart.add_value(hm.hm_name, sv.get<int64_t>());
1,333✔
612
        } else if (sv.is<double>()) {
607✔
613
            hm.hm_chart.add_value(hm.hm_name, sv.get<double>());
221✔
614
        } else if (sv.is<string_fragment>()) {
386✔
615
            auto sf = sv.get<string_fragment>();
382✔
616
            auto num_from_res = humanize::try_from<double>(sf);
382✔
617
            if (num_from_res) {
382✔
618
                hm.hm_chart.add_value(hm.hm_name, num_from_res.value());
382✔
619
            }
620
        }
621
    } else if (cv_sf.is_valid() && cv_sf.length() > 2
10,328✔
622
               && ((cv_sf.startswith("{") && cv_sf.endswith("}"))
13,244✔
623
                   || (cv_sf.startswith("[") && cv_sf.endswith("]"))))
2,916✔
624
    {
625
        json_ptr_walk jpw;
370✔
626

627
        if (jpw.parse(cv_sf.data(), cv_sf.length()) == yajl_status_ok
370✔
628
            && jpw.complete_parse() == yajl_status_ok)
370✔
629
        {
630
            for (const auto& jpw_value : jpw.jpw_values) {
1,316✔
631
                if (jpw_value.wt_type != yajl_t_number) {
998✔
632
                    continue;
659✔
633
                }
634

635
                auto num_scan_res = scn::scan_value<double>(jpw_value.wt_value);
339✔
636
                if (num_scan_res) {
339✔
637
                    hm.hm_chart.add_value(jpw_value.wt_ptr,
339✔
638
                                          num_scan_res->value());
339✔
639
                    hm.hm_chart.with_attrs_for_ident(
678✔
640
                        jpw_value.wt_ptr, vc.attrs_for_ident(jpw_value.wt_ptr));
339✔
641
                }
642
            }
643
        }
644
    }
370✔
645
    hm.hm_chart.next_row();
8,740✔
646
}
8,740✔
647

648
void
649
db_label_source::clear()
2,624✔
650
{
651
    this->dls_generation += 1;
2,624✔
652
    this->dls_query_start = std::nullopt;
2,624✔
653
    this->dls_query_end = std::nullopt;
2,624✔
654
    this->dls_headers.clear();
2,624✔
655
    this->dls_row_cursors.clear();
2,624✔
656
    this->dls_cell_container.reset();
2,624✔
657
    this->dls_time_column.clear();
2,624✔
658
    this->dls_time_column_index = SIZE_MAX;
2,624✔
659
    this->dls_cell_width.clear();
2,624✔
660
    this->dls_row_styles.clear();
2,624✔
661
    this->dls_row_styles_have_errors = false;
2,624✔
662
    this->dls_row_style_column = SIZE_MAX;
2,624✔
663
    this->dls_level_column = std::nullopt;
2,624✔
664
    this->dls_cell_allocator.reset();
2,624✔
665
    if (this->tss_view != nullptr) {
2,624✔
666
        this->tss_view->get_bookmarks().clear();
316✔
667
    }
668
}
2,624✔
669

670
std::optional<size_t>
671
db_label_source::column_name_to_index(const std::string& name) const
17✔
672
{
673
    return this->dls_headers | lnav::itertools::find(name);
17✔
674
}
675

676
std::optional<vis_line_t>
677
db_label_source::row_for_time(timeval time_bucket)
5✔
678
{
679
    const auto iter = std::lower_bound(this->dls_time_column.begin(),
5✔
680
                                       this->dls_time_column.end(),
681
                                       time_bucket);
682
    if (iter != this->dls_time_column.end()) {
5✔
683
        return vis_line_t(std::distance(this->dls_time_column.begin(), iter));
8✔
684
    }
685
    return std::nullopt;
1✔
686
}
687

688
std::optional<text_time_translator::row_info>
689
db_label_source::time_for_row(vis_line_t row)
292✔
690
{
691
    if ((row < 0_vl) || (((size_t) row) >= this->dls_time_column.size())) {
292✔
692
        return std::nullopt;
236✔
693
    }
694

695
    return row_info{this->dls_time_column[row], row};
56✔
696
}
697

698
bool
UNCOV
699
db_label_source::text_handle_mouse(
×
700
    textview_curses& tc,
701
    const listview_curses::display_line_content_t&,
702
    mouse_event& me)
703
{
UNCOV
704
    if (tc.get_overlay_selection()) {
×
UNCOV
705
        auto nci = ncinput{};
×
UNCOV
706
        if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 0, 3)) {
×
UNCOV
707
            nci.id = ' ';
×
UNCOV
708
            nci.eff_text[0] = ' ';
×
UNCOV
709
            this->list_input_handle_key(tc, nci);
×
710
        }
711
    }
UNCOV
712
    return true;
×
713
}
714

715
static constexpr string_attr_type<std::string> DBA_DETAILS("details");
716
static constexpr string_attr_type<std::string> DBA_COLUMN_NAME("column-name");
717

718
bool
UNCOV
719
db_label_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
×
720
{
UNCOV
721
    switch (ch.eff_text[0]) {
×
UNCOV
722
        case ' ': {
×
UNCOV
723
            auto ov_sel = lv.get_overlay_selection();
×
UNCOV
724
            if (ov_sel) {
×
UNCOV
725
                std::vector<attr_line_t> rows;
×
UNCOV
726
                auto* ov_source = lv.get_overlay_source();
×
UNCOV
727
                ov_source->list_value_for_overlay(
×
UNCOV
728
                    lv, lv.get_selection().value(), rows);
×
UNCOV
729
                if (ov_sel.value() < (ssize_t) rows.size()) {
×
UNCOV
730
                    auto& row_al = rows[ov_sel.value()];
×
731
                    auto col_attr
UNCOV
732
                        = get_string_attr(row_al.al_attrs, DBA_COLUMN_NAME);
×
UNCOV
733
                    if (col_attr) {
×
UNCOV
734
                        auto col_name = col_attr.value().get();
×
UNCOV
735
                        auto col_opt = this->column_name_to_index(col_name);
×
UNCOV
736
                        if (col_opt) {
×
UNCOV
737
                            this->dls_headers[col_opt.value()].hm_hidden
×
UNCOV
738
                                = !this->dls_headers[col_opt.value()].hm_hidden;
×
739
                        }
740
                    }
741
                }
UNCOV
742
                lv.set_needs_update();
×
743

UNCOV
744
                return true;
×
745
            }
UNCOV
746
            break;
×
747
        }
748
    }
749

UNCOV
750
    return false;
×
751
}
752

753
std::optional<json_string>
754
db_label_source::text_row_details(const textview_curses& tc)
3✔
755
{
756
    if (this->dls_row_cursors.empty()) {
3✔
UNCOV
757
        log_trace("db_label_source::text_row_details: empty");
×
UNCOV
758
        return std::nullopt;
×
759
    }
760
    if (!this->dls_query_end.has_value()) {
3✔
761
        log_trace("db_label_source::text_row_details: query in progress");
2✔
762
        return std::nullopt;
2✔
763
    }
764

765
    auto ov_sel = tc.get_overlay_selection();
1✔
766

767
    if (ov_sel.has_value()) {
1✔
UNCOV
768
        std::vector<attr_line_t> rows;
×
UNCOV
769
        auto* ov_source = tc.get_overlay_source();
×
UNCOV
770
        ov_source->list_value_for_overlay(tc, tc.get_selection().value(), rows);
×
UNCOV
771
        if (ov_sel.value() < (ssize_t) rows.size()) {
×
UNCOV
772
            auto& row_al = rows[ov_sel.value()];
×
UNCOV
773
            auto deets_attr = get_string_attr(row_al.al_attrs, DBA_DETAILS);
×
UNCOV
774
            if (deets_attr) {
×
UNCOV
775
                auto deets = deets_attr->get();
×
UNCOV
776
                if (!deets.empty()) {
×
UNCOV
777
                    return json_string(
×
UNCOV
778
                        auto_buffer::from(deets.c_str(), deets.length()));
×
779
                }
780
            }
781
        }
UNCOV
782
    } else {
×
783
        yajlpp_gen gen;
1✔
784

785
        {
786
            yajlpp_map root(gen);
1✔
787

788
            auto sel = tc.get_selection();
1✔
789
            if (sel) {
1✔
790
                root.gen("value");
1✔
791

792
                {
793
                    yajlpp_map value_map(gen);
1✔
794

795
                    auto cursor
796
                        = this->dls_row_cursors[tc.get_selection().value()]
1✔
797
                              .sync();
1✔
798
                    for (const auto& [col, hm] :
22✔
799
                         lnav::itertools::enumerate(this->dls_headers))
23✔
800
                    {
801
                        value_map.gen(hm.hm_name);
21✔
802

803
                        switch (cursor->get_type()) {
21✔
804
                            case lnav::cell_type::CT_NULL:
9✔
805
                                value_map.gen();
9✔
806
                                break;
9✔
807
                            case lnav::cell_type::CT_INTEGER:
5✔
808
                                value_map.gen(cursor->get_int());
5✔
809
                                break;
5✔
UNCOV
810
                            case lnav::cell_type::CT_FLOAT:
×
UNCOV
811
                                if (cursor->get_sub_value() == 0) {
×
UNCOV
812
                                    value_map.gen(cursor->get_float());
×
813
                                } else {
UNCOV
814
                                    value_map.gen(cursor->get_float_as_text());
×
815
                                }
UNCOV
816
                                break;
×
817
                            case lnav::cell_type::CT_TEXT:
7✔
818
                                value_map.gen(cursor->get_text());
7✔
819
                                break;
7✔
820
                        }
821
                        cursor = cursor->next();
21✔
822
                    }
823
                }
1✔
824
            }
825
        }
1✔
826

827
        return json_string{gen};
1✔
828
    }
1✔
829

UNCOV
830
    return std::nullopt;
×
831
}
832

833
std::string
834
db_label_source::get_row_as_string(vis_line_t row)
1,473✔
835
{
836
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())) {
1,473✔
UNCOV
837
        return "";
×
838
    }
839

840
    if (this->dls_headers.size() == 1) {
1,473✔
841
        return this->dls_row_cursors[row]
1,465✔
842
            .sync()
1,465✔
843
            .value()
1,465✔
844
            .to_string_fragment(this->dls_cell_allocator)
2,930✔
845
            .to_string();
1,465✔
846
    }
847

848
    std::string retval;
8✔
849
    size_t lpc = 0;
8✔
850
    auto cursor = this->dls_row_cursors[row].sync();
8✔
851
    while (lpc < this->dls_headers.size() && cursor.has_value()) {
32✔
852
        const auto& hm = this->dls_headers[lpc];
24✔
853

854
        if (!retval.empty()) {
24✔
855
            retval.append("; ");
16✔
856
        }
857
        retval.append(hm.hm_name);
24✔
858
        retval.push_back('=');
24✔
859
        auto sf = cursor->to_string_fragment(this->dls_cell_allocator);
24✔
860
        retval += sf;
24✔
861

862
        cursor = cursor->next();
24✔
863
        lpc += 1;
24✔
864
    }
865
    this->dls_cell_allocator.reset();
8✔
866

867
    return retval;
8✔
868
}
8✔
869

870
std::string
UNCOV
871
db_label_source::get_cell_as_string(vis_line_t row, size_t col)
×
872
{
UNCOV
873
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())
×
UNCOV
874
        || col >= this->dls_headers.size())
×
875
    {
UNCOV
876
        return "";
×
877
    }
878

UNCOV
879
    this->dls_cell_allocator.reset();
×
UNCOV
880
    size_t lpc = 0;
×
UNCOV
881
    auto cursor = this->dls_row_cursors[row].sync();
×
UNCOV
882
    while (cursor.has_value()) {
×
UNCOV
883
        if (lpc == col) {
×
UNCOV
884
            return cursor->to_string_fragment(this->dls_cell_allocator)
×
UNCOV
885
                .to_string();
×
886
        }
887

UNCOV
888
        cursor = cursor->next();
×
UNCOV
889
        lpc += 1;
×
890
    }
891

UNCOV
892
    return "";
×
893
}
894

895
std::optional<int64_t>
UNCOV
896
db_label_source::get_cell_as_int64(vis_line_t row, size_t col)
×
897
{
UNCOV
898
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())
×
UNCOV
899
        || col >= this->dls_headers.size())
×
900
    {
UNCOV
901
        return std::nullopt;
×
902
    }
903

UNCOV
904
    size_t lpc = 0;
×
UNCOV
905
    auto cursor = this->dls_row_cursors[row].sync();
×
UNCOV
906
    while (cursor.has_value()) {
×
UNCOV
907
        if (lpc == col) {
×
UNCOV
908
            if (cursor->get_type() == lnav::cell_type::CT_INTEGER) {
×
UNCOV
909
                return cursor->get_int();
×
910
            }
UNCOV
911
            return std::nullopt;
×
912
        }
913

UNCOV
914
        cursor = cursor->next();
×
UNCOV
915
        lpc += 1;
×
916
    }
917

UNCOV
918
    return std::nullopt;
×
919
}
920

921
std::optional<double>
UNCOV
922
db_label_source::get_cell_as_double(vis_line_t row, size_t col)
×
923
{
UNCOV
924
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())
×
UNCOV
925
        || col >= this->dls_headers.size())
×
926
    {
UNCOV
927
        return std::nullopt;
×
928
    }
929

UNCOV
930
    size_t lpc = 0;
×
UNCOV
931
    auto cursor = this->dls_row_cursors[row].sync();
×
UNCOV
932
    while (cursor.has_value()) {
×
UNCOV
933
        if (lpc == col) {
×
UNCOV
934
            switch (cursor->get_type()) {
×
UNCOV
935
                case lnav::cell_type::CT_INTEGER:
×
UNCOV
936
                    return cursor->get_int();
×
UNCOV
937
                case lnav::cell_type::CT_FLOAT:
×
UNCOV
938
                    return cursor->get_float();
×
UNCOV
939
                default:
×
UNCOV
940
                    return std::nullopt;
×
941
            }
942
        }
943

UNCOV
944
        cursor = cursor->next();
×
UNCOV
945
        lpc += 1;
×
946
    }
947

UNCOV
948
    return std::nullopt;
×
949
}
950

951
void
952
db_label_source::reset_user_state()
6✔
953
{
954
    for (auto& hm : this->dls_headers) {
6✔
UNCOV
955
        hm.hm_hidden = false;
×
956
    }
957
}
6✔
958

959
std::optional<attr_line_t>
UNCOV
960
db_overlay_source::list_header_for_overlay(const listview_curses& lv,
×
961
                                           vis_line_t line)
962
{
UNCOV
963
    attr_line_t retval;
×
964

UNCOV
965
    retval.append("  Details for row ")
×
UNCOV
966
        .append(
×
UNCOV
967
            lnav::roles::number(fmt::format(FMT_STRING("{:L}"), (int) line)))
×
UNCOV
968
        .append(". Press ")
×
UNCOV
969
        .append("p"_hotkey)
×
UNCOV
970
        .append(" to hide this panel.");
×
UNCOV
971
    if (lv.get_overlay_selection()) {
×
UNCOV
972
        retval.append(" Controls: ")
×
UNCOV
973
            .append("c"_hotkey)
×
UNCOV
974
            .append(" to copy a column value; ")
×
UNCOV
975
            .append("SPC"_hotkey)
×
UNCOV
976
            .append(" to hide/show a column");
×
977
    } else {
UNCOV
978
        retval.append("  Press ")
×
UNCOV
979
            .append("CTRL-]"_hotkey)
×
UNCOV
980
            .append(" to focus on this panel");
×
981
    }
UNCOV
982
    return retval;
×
983
}
984

985
void
986
db_overlay_source::list_value_for_overlay(const listview_curses& lv,
630✔
987
                                          vis_line_t row,
988
                                          std::vector<attr_line_t>& value_out)
989
{
990
    if (!this->dos_active || lv.get_inner_height() == 0) {
630✔
991
        return;
630✔
992
    }
993

UNCOV
994
    auto sel = lv.get_selection();
×
UNCOV
995
    if (!sel || row != sel.value()) {
×
UNCOV
996
        return;
×
997
    }
998

UNCOV
999
    auto& vc = view_colors::singleton();
×
1000
    unsigned long width;
UNCOV
1001
    vis_line_t height;
×
1002

UNCOV
1003
    lv.get_dimensions(height, width);
×
1004

UNCOV
1005
    auto max_name_width = this->dos_labels->dls_headers
×
UNCOV
1006
        | lnav::itertools::map([](const auto& hm) { return hm.hm_name.size(); })
×
UNCOV
1007
        | lnav::itertools::max();
×
1008

UNCOV
1009
    auto cursor = this->dos_labels->dls_row_cursors[row].sync();
×
UNCOV
1010
    for (const auto& [col, hm] :
×
UNCOV
1011
         lnav::itertools::enumerate(this->dos_labels->dls_headers))
×
1012
    {
UNCOV
1013
        auto al = attr_line_t()
×
UNCOV
1014
                      .append(lnav::roles::h3(hm.hm_name))
×
UNCOV
1015
                      .right_justify(max_name_width.value_or(0) + 2);
×
1016

UNCOV
1017
        if (hm.hm_hidden) {
×
UNCOV
1018
            al.insert(1, "\u25c7"_comment);
×
1019
        } else {
UNCOV
1020
            al.insert(1, "\u25c6"_ok);
×
1021
        }
1022

1023
        auto sf
UNCOV
1024
            = cursor->to_string_fragment(this->dos_labels->dls_cell_allocator);
×
1025

UNCOV
1026
        al.al_attrs.emplace_back(line_range{0, -1},
×
UNCOV
1027
                                 DBA_COLUMN_NAME.value(hm.hm_name));
×
UNCOV
1028
        if (cursor->get_type() == lnav::cell_type::CT_TEXT
×
UNCOV
1029
            && (sf.startswith("[") || sf.startswith("{")))
×
1030
        {
UNCOV
1031
            json_ptr_walk jpw;
×
1032

UNCOV
1033
            if (jpw.parse(sf.udata(), sf.length()) == yajl_status_ok
×
UNCOV
1034
                && jpw.complete_parse() == yajl_status_ok)
×
1035
            {
1036
                {
UNCOV
1037
                    yajlpp_gen gen;
×
1038

1039
                    {
UNCOV
1040
                        yajlpp_map root(gen);
×
1041

UNCOV
1042
                        root.gen("key");
×
UNCOV
1043
                        root.gen(hm.hm_name);
×
UNCOV
1044
                        root.gen("value");
×
UNCOV
1045
                        root.gen(sf);
×
1046
                    }
UNCOV
1047
                    al.al_attrs.emplace_back(
×
UNCOV
1048
                        line_range{0, -1},
×
UNCOV
1049
                        DBA_DETAILS.value(
×
UNCOV
1050
                            gen.to_string_fragment().to_string()));
×
1051
                }
UNCOV
1052
                value_out.emplace_back(al);
×
UNCOV
1053
                al.clear();
×
1054

UNCOV
1055
                stacked_bar_chart<std::string> chart;
×
UNCOV
1056
                int start_line = value_out.size();
×
1057

UNCOV
1058
                auto indent = 3 + max_name_width.value() - hm.hm_name.size();
×
UNCOV
1059
                chart.with_stacking_enabled(false)
×
UNCOV
1060
                    .with_margins(indent + 2, 0)
×
UNCOV
1061
                    .with_show_state(stacked_bar_chart_base::show_all{});
×
1062

UNCOV
1063
                for (const auto& [walk_index, jpw_value] :
×
UNCOV
1064
                     lnav::itertools::enumerate(jpw.jpw_values))
×
1065
                {
1066
                    {
UNCOV
1067
                        yajlpp_gen gen;
×
1068

1069
                        {
UNCOV
1070
                            yajlpp_map root(gen);
×
1071

UNCOV
1072
                            root.gen("key");
×
UNCOV
1073
                            root.gen(jpw_value.wt_ptr);
×
UNCOV
1074
                            root.gen("value");
×
UNCOV
1075
                            root.gen(jpw_value.wt_value);
×
1076
                        }
UNCOV
1077
                        al.al_attrs.emplace_back(
×
UNCOV
1078
                            line_range{0, -1},
×
UNCOV
1079
                            DBA_DETAILS.value(
×
UNCOV
1080
                                gen.to_string_fragment().to_string()));
×
1081
                    }
1082

UNCOV
1083
                    al.append(indent + 2, ' ')
×
UNCOV
1084
                        .append(lnav::roles::h5(jpw_value.wt_ptr))
×
UNCOV
1085
                        .append(" = ")
×
UNCOV
1086
                        .append(jpw_value.wt_value);
×
1087

UNCOV
1088
                    auto& sa = al.al_attrs;
×
UNCOV
1089
                    line_range lr(indent, indent + 1);
×
1090

UNCOV
1091
                    sa.emplace_back(
×
1092
                        lr,
UNCOV
1093
                        VC_GRAPHIC.value(walk_index < jpw.jpw_values.size() - 1
×
1094
                                             ? NCACS_LTEE
1095
                                             : NCACS_LLCORNER));
UNCOV
1096
                    lr.lr_start = indent + 2 + jpw_value.wt_ptr.size() + 3;
×
UNCOV
1097
                    lr.lr_end = -1;
×
1098

UNCOV
1099
                    if (jpw_value.wt_type == yajl_t_number) {
×
1100
                        auto num_scan_res
UNCOV
1101
                            = scn::scan_value<double>(jpw_value.wt_value);
×
1102

UNCOV
1103
                        if (num_scan_res) {
×
UNCOV
1104
                            auto attrs = vc.attrs_for_ident(jpw_value.wt_ptr);
×
1105

UNCOV
1106
                            chart.add_value(jpw_value.wt_ptr,
×
UNCOV
1107
                                            num_scan_res->value());
×
UNCOV
1108
                            chart.with_attrs_for_ident(jpw_value.wt_ptr, attrs);
×
1109
                        }
UNCOV
1110
                        sa.emplace_back(lr, VC_ROLE.value(role_t::VCR_NUMBER));
×
1111
                    }
UNCOV
1112
                    value_out.emplace_back(al);
×
UNCOV
1113
                    al.clear();
×
1114
                }
1115

UNCOV
1116
                int curr_line = start_line;
×
UNCOV
1117
                for (auto iter = jpw.jpw_values.begin();
×
UNCOV
1118
                     iter != jpw.jpw_values.end();
×
UNCOV
1119
                     ++iter, curr_line++)
×
1120
                {
UNCOV
1121
                    if (iter->wt_type != yajl_t_number) {
×
UNCOV
1122
                        continue;
×
1123
                    }
1124

1125
                    auto num_scan_res
UNCOV
1126
                        = humanize::try_from<double>(iter->wt_value);
×
UNCOV
1127
                    if (num_scan_res) {
×
UNCOV
1128
                        auto& sa = value_out[curr_line].get_attrs();
×
UNCOV
1129
                        int left = indent + 2;
×
1130

UNCOV
1131
                        chart.chart_attrs_for_value(lv,
×
1132
                                                    left,
1133
                                                    width,
UNCOV
1134
                                                    iter->wt_ptr,
×
UNCOV
1135
                                                    num_scan_res.value(),
×
1136
                                                    sa);
1137
                    }
1138
                }
UNCOV
1139
            } else {
×
UNCOV
1140
                yajlpp_gen gen;
×
1141

1142
                {
UNCOV
1143
                    yajlpp_map root(gen);
×
1144

UNCOV
1145
                    root.gen("key");
×
UNCOV
1146
                    root.gen(hm.hm_name);
×
UNCOV
1147
                    root.gen("value");
×
UNCOV
1148
                    root.gen(sf);
×
1149
                }
UNCOV
1150
                al.append(": ").append(sf);
×
UNCOV
1151
                al.al_attrs.emplace_back(
×
UNCOV
1152
                    line_range{0, -1},
×
UNCOV
1153
                    DBA_DETAILS.value(gen.to_string_fragment().to_string()));
×
1154
            }
UNCOV
1155
        } else {
×
UNCOV
1156
            yajlpp_gen gen;
×
1157

1158
            {
UNCOV
1159
                yajlpp_map root(gen);
×
1160

UNCOV
1161
                root.gen("key");
×
UNCOV
1162
                root.gen(hm.hm_name);
×
UNCOV
1163
                root.gen("value");
×
UNCOV
1164
                switch (cursor->get_type()) {
×
UNCOV
1165
                    case lnav::cell_type::CT_NULL:
×
UNCOV
1166
                        root.gen();
×
UNCOV
1167
                        break;
×
UNCOV
1168
                    case lnav::cell_type::CT_INTEGER:
×
UNCOV
1169
                        root.gen(cursor->get_int());
×
UNCOV
1170
                        break;
×
UNCOV
1171
                    case lnav::cell_type::CT_FLOAT:
×
UNCOV
1172
                        if (cursor->get_sub_value() == 0) {
×
UNCOV
1173
                            root.gen(cursor->get_float());
×
1174
                        } else {
UNCOV
1175
                            root.gen(cursor->get_float_as_text());
×
1176
                        }
UNCOV
1177
                        break;
×
UNCOV
1178
                    case lnav::cell_type::CT_TEXT:
×
UNCOV
1179
                        root.gen(cursor->get_text());
×
UNCOV
1180
                        break;
×
1181
                }
1182
            }
1183

UNCOV
1184
            auto value_al = attr_line_t::from_table_cell_content(sf, 1000);
×
UNCOV
1185
            al.append(": ").append(value_al);
×
UNCOV
1186
            al.al_attrs.emplace_back(
×
UNCOV
1187
                line_range{0, -1},
×
UNCOV
1188
                DBA_DETAILS.value(gen.to_string_fragment().to_string()));
×
1189
        }
1190

UNCOV
1191
        if (!al.empty()) {
×
UNCOV
1192
            value_out.emplace_back(al);
×
1193
        }
UNCOV
1194
        cursor = cursor->next();
×
1195
    }
1196

UNCOV
1197
    this->dos_labels->dls_cell_allocator.reset();
×
1198
}
1199

1200
bool
1201
db_overlay_source::list_static_overlay(const listview_curses& lv,
293✔
1202
                                       int y,
1203
                                       int bottom,
1204
                                       attr_line_t& value_out)
1205
{
1206
    if (y != 0) {
293✔
1207
        return false;
91✔
1208
    }
1209

1210
    auto& line = value_out.get_string();
202✔
1211
    const auto* dls = this->dos_labels;
202✔
1212
    auto& sa = value_out.get_attrs();
202✔
1213

1214
    for (size_t lpc = 0; lpc < this->dos_labels->dls_headers.size(); lpc++) {
1,115✔
1215
        if (lpc == this->dos_labels->dls_row_style_column
913✔
1216
            && !this->dos_labels->dls_row_styles_have_errors)
3✔
1217
        {
1218
            continue;
3✔
1219
        }
1220

1221
        const auto& hm = dls->dls_headers[lpc];
911✔
1222
        if (hm.hm_hidden) {
911✔
1223
            continue;
1✔
1224
        }
1225
        auto actual_col_size
1226
            = std::min(dls->dls_max_column_width, hm.hm_column_size);
910✔
1227
        auto cell_title = hm.hm_name;
910✔
1228
        string_attrs_t cell_attrs;
910✔
1229
        scrub_ansi_string(cell_title, &cell_attrs);
910✔
1230
        truncate_to(cell_title, dls->dls_max_column_width);
910✔
1231

1232
        auto cell_length
1233
            = utf8_string_length(cell_title).unwrapOr(actual_col_size);
910✔
1234
        int total_fill = actual_col_size - cell_length;
910✔
1235
        auto line_len_before = line.length();
910✔
1236

1237
        int before = total_fill / 2;
910✔
1238
        total_fill -= before;
910✔
1239
        line.append(before, ' ');
910✔
1240
        shift_string_attrs(cell_attrs, 0, line.size());
910✔
1241
        line.append(cell_title);
910✔
1242
        line.append(total_fill, ' ');
910✔
1243
        auto header_range = line_range(line_len_before, line.length());
910✔
1244

1245
        line.append(1, ' ');
910✔
1246

1247
        require_ge(header_range.lr_start, 0);
910✔
1248

1249
        sa.emplace_back(header_range, VC_STYLE.value(hm.hm_title_attrs));
910✔
1250
        sa.insert(sa.end(), cell_attrs.begin(), cell_attrs.end());
910✔
1251
    }
910✔
1252

1253
    line_range lr(0);
202✔
1254

1255
    sa.emplace_back(
202✔
1256
        lr,
1257
        VC_STYLE.value(text_attrs::with_styles(text_attrs::style::bold,
404✔
1258
                                               text_attrs::style::underline)));
1259
    return true;
202✔
1260
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc