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

tstack / lnav / 18577582398-2579

16 Oct 2025 11:24PM UTC coverage: 69.212% (-0.9%) from 70.063%
18577582398-2579

push

github

tstack
[build] fix type

1 of 1 new or added line in 1 file covered. (100.0%)

2573 existing lines in 65 files now uncovered.

50113 of 72405 relevant lines covered (69.21%)

415651.95 hits per line

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

61.54
/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
#include <map>
32

33
#include "db_sub_source.hh"
34

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

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

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

53
constexpr ssize_t MAX_JSON_WIDTH = 16 * 1024;
54

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

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

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

74
    return retval;
9✔
75
}
12✔
76

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

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

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

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

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

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

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

181
    return {};
945✔
182
}
183

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

192
    line_range lr(0, 0);
945✔
193
    const line_range lr2(0, -1);
945✔
194

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

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

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

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

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

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

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

277
                for (const auto& attr : sa) {
18,817✔
278
                    require_ge(attr.sa_range.lr_start, 0);
17,759✔
279
                }
280
            }
281
        } else if (user_attrs.has_value()) {
3,257✔
282
            sa.emplace_back(stlr, VC_STYLE.value(user_attrs.value()));
3✔
283
        }
284
        auto cell_sf = string_fragment::invalid();
4,315✔
285
        if (cursor->get_type() == lnav::cell_type::CT_TEXT) {
4,315✔
286
            cell_sf = cursor->get_text();
1,625✔
287
        } else if (cursor->get_type() == lnav::cell_type::CT_NULL) {
2,690✔
288
            sa.emplace_back(stlr, VC_ROLE.value(role_t::VCR_NULL));
970✔
289
        }
290
        if (lpc == this->dls_row_style_column) {
4,315✔
291
            sa.emplace_back(stlr, VC_ROLE.value(role_t::VCR_ERROR));
3✔
292
        } else if (cell_sf.is_valid() && cell_sf.length() > 2
5,934✔
293
                   && cell_sf.length() < MAX_JSON_WIDTH
1,386✔
294
                   && ((cell_sf.front() == '{' && cell_sf.back() == '}')
7,263✔
295
                       || (cell_sf.front() == '[' && cell_sf.back() == ']')))
1,329✔
296
        {
297
            auto cb = [&](const std::string& ptr, const scoped_value_t& sv) {
181✔
298
                auto val_opt = to_double(sv);
181✔
299
                if (val_opt) {
181✔
300
                    hm.hm_chart.chart_attrs_for_value(tc,
126✔
301
                                                      left,
302
                                                      this->dls_cell_width[lpc],
63✔
303
                                                      ptr,
304
                                                      val_opt.value(),
63✔
305
                                                      sa);
306
                    for (const auto& attr : sa) {
573✔
307
                        require_ge(attr.sa_range.lr_start, 0);
510✔
308
                    }
309
                }
310
            };
181✔
311
            json_ptr_walk jpw(cb);
112✔
312

313
            jpw.parse_fully(cell_sf);
112✔
314
        }
112✔
315
        cell_start += this->dls_cell_width[lpc] + 1;
4,315✔
316
    }
4,324✔
317

318
    for (const auto& attr : sa) {
9,328✔
319
        require_ge(attr.sa_range.lr_start, 0);
8,383✔
320
    }
321
}
322

323
void
324
db_label_source::set_col_as_graphable(int lpc)
704✔
325
{
326
    static auto& vc = view_colors::singleton();
704✔
327

328
    auto& hm = this->dls_headers[lpc];
704✔
329
    auto name_for_ident_attrs = hm.hm_name;
704✔
330
    auto attrs = vc.attrs_for_ident(name_for_ident_attrs);
704✔
331
    for (size_t attempt = 0; hm.hm_chart.attrs_in_use(attrs) && attempt < 3;
704✔
332
         attempt++)
333
    {
UNCOV
334
        name_for_ident_attrs += " ";
×
UNCOV
335
        attrs = vc.attrs_for_ident(name_for_ident_attrs);
×
336
    }
337
    hm.hm_graphable = true;
704✔
338
    hm.hm_chart.with_attrs_for_ident(hm.hm_name, attrs);
704✔
339
    hm.hm_title_attrs = attrs | text_attrs::with_reverse();
704✔
340
    hm.hm_column_size = std::max(hm.hm_column_size, size_t{10});
704✔
341
}
704✔
342

343
void
344
db_label_source::push_header(const std::string& colstr, int type)
3,000✔
345
{
346
    this->dls_headers.emplace_back(colstr);
3,000✔
347
    this->dls_cell_width.push_back(0);
3,000✔
348

349
    auto& hm = this->dls_headers.back();
3,000✔
350

351
    hm.hm_column_size = utf8_string_length(colstr).unwrapOr(colstr.length());
3,000✔
352
    hm.hm_column_type = type;
3,000✔
353
    if (colstr == "log_time" || colstr == "min(log_time)"
5,932✔
354
        || colstr == "log_time_msecs")
5,932✔
355
    {
356
        this->dls_time_column_index = this->dls_headers.size() - 1;
74✔
357
    }
358
    if (colstr == "__lnav_style__") {
3,000✔
359
        this->dls_row_style_column = this->dls_headers.size() - 1;
3✔
360
    }
361
    if (colstr == "log_level") {
3,000✔
362
        this->dls_level_column = this->dls_headers.size() - 1;
54✔
363
    }
364
    hm.hm_chart.with_show_state(stacked_bar_chart_base::show_all{});
3,000✔
365
}
3,000✔
366

367
void
368
db_label_source::update_time_column(const timeval& tv)
319✔
369
{
370
    if (!this->dls_time_column.empty() && tv < this->dls_time_column.back()) {
319✔
371
        this->dls_time_column_invalidated_at = this->dls_time_column.size();
1✔
372
        this->dls_time_column_index = SIZE_MAX;
1✔
373
        this->dls_time_column.clear();
1✔
374
    } else {
375
        this->dls_time_column.push_back(tv);
318✔
376
    }
377
}
319✔
378

379
void
380
db_label_source::update_time_column(const string_fragment& sf)
298✔
381
{
382
    date_time_scanner dts;
298✔
383
    timeval tv;
384

385
    if (!dts.convert_to_timeval(sf.data(), sf.length(), nullptr, tv)) {
298✔
UNCOV
386
        tv.tv_sec = -1;
×
UNCOV
387
        tv.tv_usec = -1;
×
388
    }
389
    this->update_time_column(tv);
298✔
390
}
298✔
391

392
void
393
db_label_source::push_column(const column_value_t& sv)
9,081✔
394
{
395
    auto row_index = this->dls_row_cursors.size() - 1;
9,081✔
396
    auto& vc = view_colors::singleton();
9,081✔
397
    auto col = this->dls_push_column++;
9,081✔
398
    auto& hm = this->dls_headers[col];
9,081✔
399
    size_t width = 1;
9,081✔
400
    auto cv_sf = string_fragment::invalid();
9,081✔
401

402
    sv.match(
9,081✔
UNCOV
403
        [this, &col, &width, &cv_sf, &hm, &row_index](
×
404
            const string_fragment& sf) {
405
            if (this->dls_row_style_column == col) {
4,077✔
406
                return;
9✔
407
            }
408
            if (col == this->dls_time_column_index) {
4,068✔
409
                this->update_time_column(sf);
298✔
410
            } else if (this->dls_level_column
7,540✔
411
                       && this->dls_level_column.value() == col
5,097✔
412
                       && this->tss_view != nullptr)
5,097✔
413
            {
414
                auto& bm = this->tss_view->get_bookmarks();
218✔
415
                auto lev = string2level(sf.data(), sf.length());
218✔
416
                switch (lev) {
218✔
417
                    case log_level_t::LEVEL_FATAL:
37✔
418
                    case log_level_t::LEVEL_CRITICAL:
419
                    case log_level_t::LEVEL_ERROR:
420
                        bm[&textview_curses::BM_ERRORS].insert_once(
74✔
421
                            vis_line_t(row_index));
37✔
422
                        break;
37✔
423
                    case log_level_t::LEVEL_WARNING:
6✔
424
                        bm[&textview_curses::BM_WARNINGS].insert_once(
12✔
425
                            vis_line_t(row_index));
6✔
426
                        break;
6✔
427
                    default:
175✔
428
                        break;
175✔
429
                }
430
            }
431
            width = utf8_string_length(sf.data(), sf.length())
4,068✔
432
                        .unwrapOr(sf.length());
4,068✔
433
            if (hm.is_graphable()
4,068✔
434
                && sf.length() < lnav::cell_container::SHORT_TEXT_LENGTH)
4,068✔
435
            {
436
                auto from_res = humanize::try_from<double>(sf);
385✔
437
                if (from_res.has_value()) {
385✔
438
                    this->dls_cell_container.push_float_with_units_cell(
770✔
439
                        from_res.value(), sf);
385✔
440
                } else {
UNCOV
441
                    this->dls_cell_container.push_text_cell(sf);
×
442
                }
443
            } else {
444
                this->dls_cell_container.push_text_cell(sf);
3,683✔
445
            }
446
            cv_sf = sf;
4,068✔
447
        },
UNCOV
448
        [this, col, &width](int64_t i) {
×
449
            width = count_digits(i);
2,623✔
450
            this->dls_cell_container.push_int_cell(i);
2,623✔
451
            if (col == this->dls_time_column_index) {
2,623✔
452
                auto ms = std::chrono::milliseconds{i};
21✔
453
                auto us
454
                    = std::chrono::duration_cast<std::chrono::microseconds>(ms);
21✔
455

456
                this->update_time_column(to_timeval(us));
21✔
457
            }
458
        },
2,623✔
UNCOV
459
        [this, &width](double d) {
×
460
            char buffer[1];
461
            auto fmt_res = fmt::format_to_n(buffer, 0, FMT_STRING("{}"), d);
693✔
462
            width = fmt_res.size;
231✔
463
            this->dls_cell_container.push_float_cell(d);
231✔
464
        },
231✔
465
        [this, &width](null_value_t) {
9,081✔
466
            width = 6;
2,150✔
467
            this->dls_cell_container.push_null_cell();
2,150✔
468
        });
2,150✔
469

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

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

583
            col_sf
UNCOV
584
                = string_fragment::from_str("expecting a JSON object for style")
×
UNCOV
585
                      .to_owned(this->dls_cell_allocator);
×
UNCOV
586
            this->dls_row_styles_have_errors = true;
×
587
        }
588

589
        if (col_sf.empty()) {
9✔
590
            this->dls_cell_container.push_null_cell();
6✔
591
        } else {
592
            this->dls_cell_container.push_text_cell(col_sf);
3✔
593
            width = utf8_string_length(col_sf.data(), col_sf.length())
3✔
594
                        .unwrapOr(col_sf.length());
3✔
595
            this->dls_cell_allocator.reset();
3✔
596
        }
597
    }
598

599
    hm.hm_column_size = std::max(this->dls_headers[col].hm_column_size, width);
9,081✔
600
    if (hm.is_graphable()) {
9,081✔
601
        if (sv.is<int64_t>()) {
2,001✔
602
            hm.hm_chart.add_value(hm.hm_name, sv.get<int64_t>());
1,391✔
603
        } else if (sv.is<double>()) {
610✔
604
            hm.hm_chart.add_value(hm.hm_name, sv.get<double>());
221✔
605
        } else if (sv.is<string_fragment>()) {
389✔
606
            auto sf = sv.get<string_fragment>();
385✔
607
            auto num_from_res = humanize::try_from<double>(sf);
385✔
608
            if (num_from_res) {
385✔
609
                hm.hm_chart.add_value(hm.hm_name, num_from_res.value());
385✔
610
            }
611
        }
612
    } else if (cv_sf.is_valid() && cv_sf.length() > 2
10,763✔
613
               && ((cv_sf.startswith("{") && cv_sf.endswith("}"))
13,795✔
614
                   || (cv_sf.startswith("[") && cv_sf.endswith("]"))))
3,032✔
615
    {
616
        auto cb = [this, &hm, &vc](const std::string& ptr,
1,051✔
617
                                   const scoped_value_t& sv) {
618
            auto ptr_sf = string_fragment::from_str(ptr);
1,051✔
619
            auto iter = hm.hm_json_columns.find(ptr_sf);
1,051✔
620
            if (iter == hm.hm_json_columns.end()) {
1,051✔
621
                auto owned_ptr_sf = ptr_sf.to_owned(this->dls_header_allocator);
869✔
622
                iter = hm.hm_json_columns
1,738✔
623
                           .emplace(owned_ptr_sf, hm.hm_json_columns.size())
869✔
624
                           .first;
625
            }
626
            if (sv.is<int64_t>()) {
1,051✔
627
                auto val = sv.get<int64_t>();
334✔
628
                auto& ci = hm.hm_chart.add_value(ptr, val);
334✔
629
                if (ci.ci_attrs.empty()) {
334✔
630
                    ci.ci_attrs = vc.attrs_for_ident(ptr);
312✔
631
                }
632
            } else if (sv.is<double>()) {
717✔
633
                auto val = sv.get<double>();
35✔
634
                auto& ci = hm.hm_chart.add_value(ptr, val);
35✔
635
                if (ci.ci_attrs.empty()) {
35✔
636
                    ci.ci_attrs = vc.attrs_for_ident(ptr);
35✔
637
                }
638
            }
639
        };
1,051✔
640
        json_ptr_walk jpw(cb);
386✔
641

642
        jpw.parse_fully(cv_sf);
386✔
643
    }
386✔
644
    hm.hm_chart.next_row();
9,081✔
645
}
9,081✔
646

647
void
648
db_label_source::clear()
2,687✔
649
{
650
    this->dls_generation += 1;
2,687✔
651
    this->dls_query_start = std::nullopt;
2,687✔
652
    this->dls_query_end = std::nullopt;
2,687✔
653
    this->dls_headers.clear();
2,687✔
654
    this->dls_row_cursors.clear();
2,687✔
655
    this->dls_cell_container.reset();
2,687✔
656
    this->dls_time_column.clear();
2,687✔
657
    this->dls_time_column_index = SIZE_MAX;
2,687✔
658
    this->dls_cell_width.clear();
2,687✔
659
    this->dls_row_styles.clear();
2,687✔
660
    this->dls_row_styles_have_errors = false;
2,687✔
661
    this->dls_row_style_column = SIZE_MAX;
2,687✔
662
    this->dls_level_column = std::nullopt;
2,687✔
663
    this->dls_cell_allocator.reset();
2,687✔
664
    this->dls_header_allocator.reset();
2,687✔
665
    if (this->tss_view != nullptr) {
2,687✔
666
        this->tss_view->get_bookmarks().clear();
327✔
667
    }
668
}
2,687✔
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)
302✔
690
{
691
    if ((row < 0_vl) || (((size_t) row) >= this->dls_time_column.size())) {
302✔
692
        return std::nullopt;
245✔
693
    }
694

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

698
bool
699
db_label_source::text_handle_mouse(
×
700
    textview_curses& tc,
701
    const listview_curses::display_line_content_t&,
702
    mouse_event& me)
703
{
704
    if (tc.get_overlay_selection()) {
×
705
        auto nci = ncinput{};
×
706
        if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 0, 3)) {
×
707
            nci.id = ' ';
×
708
            nci.eff_text[0] = ' ';
×
709
            this->list_input_handle_key(tc, nci);
×
710
        }
711
    }
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
719
db_label_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
×
720
{
721
    switch (ch.eff_text[0]) {
×
722
        case ' ': {
×
723
            auto ov_sel = lv.get_overlay_selection();
×
724
            if (ov_sel) {
×
725
                std::vector<attr_line_t> rows;
×
726
                auto* ov_source = lv.get_overlay_source();
×
727
                ov_source->list_value_for_overlay(
×
728
                    lv, lv.get_selection().value(), rows);
×
729
                if (ov_sel.value() < (ssize_t) rows.size()) {
×
730
                    auto& row_al = rows[ov_sel.value()];
×
731
                    auto col_attr
732
                        = get_string_attr(row_al.al_attrs, DBA_COLUMN_NAME);
×
733
                    if (col_attr) {
×
734
                        auto col_name = col_attr.value().get();
×
735
                        auto col_opt = this->column_name_to_index(col_name);
×
736
                        if (col_opt) {
×
737
                            this->dls_headers[col_opt.value()].hm_hidden
×
738
                                = !this->dls_headers[col_opt.value()].hm_hidden;
×
739
                        }
740
                    }
741
                }
742
                lv.set_needs_update();
×
743

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

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✔
757
        log_trace("db_label_source::text_row_details: empty");
×
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✔
768
        std::vector<attr_line_t> rows;
×
769
        auto* ov_source = tc.get_overlay_source();
×
770
        ov_source->list_value_for_overlay(tc, tc.get_selection().value(), rows);
×
771
        if (ov_sel.value() < (ssize_t) rows.size()) {
×
772
            auto& row_al = rows[ov_sel.value()];
×
773
            auto deets_attr = get_string_attr(row_al.al_attrs, DBA_DETAILS);
×
774
            if (deets_attr) {
×
775
                auto deets = deets_attr->get();
×
776
                if (!deets.empty()) {
×
777
                    return json_string(
×
778
                        auto_buffer::from(deets.c_str(), deets.length()));
×
779
                }
780
            }
781
        }
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✔
810
                            case lnav::cell_type::CT_FLOAT:
×
811
                                if (cursor->get_sub_value() == 0) {
×
812
                                    value_map.gen(cursor->get_float());
×
813
                                } else {
814
                                    value_map.gen(cursor->get_float_as_text());
×
815
                                }
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

830
    return std::nullopt;
×
831
}
832

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

840
    if (this->dls_headers.size() == 1) {
1,480✔
841
        return this->dls_row_cursors[row]
1,472✔
842
            .sync()
1,472✔
843
            .value()
1,472✔
844
            .to_string_fragment(this->dls_cell_allocator)
2,944✔
845
            .to_string();
1,472✔
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
871
db_label_source::get_cell_as_string(vis_line_t row, size_t col)
×
872
{
873
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())
×
874
        || col >= this->dls_headers.size())
×
875
    {
876
        return "";
×
877
    }
878

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

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

892
    return "";
×
893
}
894

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

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

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

918
    return std::nullopt;
×
919
}
920

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

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

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

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✔
955
        hm.hm_hidden = false;
×
956
    }
957
}
6✔
958

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

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

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

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

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

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

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

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

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

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

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

1033
            if (parse_res.isOk()) {
×
1034
                auto jwc = parse_res.unwrap();
×
1035
                {
UNCOV
1036
                    yajlpp_gen gen;
×
1037

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

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

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

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

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

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

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

UNCOV
1082
                    al.append(indent + 2, ' ')
×
1083
                        .append(lnav::roles::h5(jpw_value.first))
×
1084
                        .append(" = ")
×
1085
                        .append(fmt::to_string(jpw_value.second));
×
1086

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

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

UNCOV
1098
                    auto val_opt = to_double(jpw_value.second);
×
1099
                    if (val_opt) {
×
UNCOV
1100
                        auto attrs = vc.attrs_for_ident(jpw_value.first);
×
1101

UNCOV
1102
                        chart.add_value(jpw_value.first, val_opt.value());
×
1103
                        chart.with_attrs_for_ident(jpw_value.first, attrs);
×
1104
                        sa.emplace_back(lr, VC_ROLE.value(role_t::VCR_NUMBER));
×
1105
                    }
1106
                    value_out.emplace_back(al);
×
1107
                    al.clear();
×
1108
                }
1109

1110
                int curr_line = start_line;
×
UNCOV
1111
                for (auto iter = jwc.jwc_values.begin();
×
1112
                     iter != jwc.jwc_values.end();
×
1113
                     ++iter, curr_line++)
×
1114
                {
UNCOV
1115
                    auto val_opt = to_double(iter->second);
×
1116
                    if (!val_opt) {
×
1117
                        continue;
×
1118
                    }
1119

UNCOV
1120
                    auto& sa = value_out[curr_line].get_attrs();
×
1121
                    int left = indent + 2;
×
1122
                    chart.chart_attrs_for_value(
×
UNCOV
1123
                        lv, left, width, iter->first, val_opt.value(), sa);
×
1124
                }
UNCOV
1125
            } else {
×
1126
                yajlpp_gen gen;
×
1127

1128
                {
1129
                    yajlpp_map root(gen);
×
1130

1131
                    root.gen("key");
×
UNCOV
1132
                    root.gen(hm.hm_name);
×
UNCOV
1133
                    root.gen("value");
×
1134
                    root.gen(sf);
×
1135
                }
UNCOV
1136
                al.append(": ").append(sf);
×
UNCOV
1137
                al.al_attrs.emplace_back(
×
UNCOV
1138
                    line_range{0, -1},
×
1139
                    DBA_DETAILS.value(gen.to_string_fragment().to_string()));
×
1140
            }
UNCOV
1141
        } else {
×
UNCOV
1142
            yajlpp_gen gen;
×
1143

1144
            {
1145
                yajlpp_map root(gen);
×
1146

1147
                root.gen("key");
×
1148
                root.gen(hm.hm_name);
×
UNCOV
1149
                root.gen("value");
×
1150
                switch (cursor->get_type()) {
×
1151
                    case lnav::cell_type::CT_NULL:
×
1152
                        root.gen();
×
1153
                        break;
×
UNCOV
1154
                    case lnav::cell_type::CT_INTEGER:
×
1155
                        root.gen(cursor->get_int());
×
1156
                        break;
×
UNCOV
1157
                    case lnav::cell_type::CT_FLOAT:
×
UNCOV
1158
                        if (cursor->get_sub_value() == 0) {
×
1159
                            root.gen(cursor->get_float());
×
1160
                        } else {
1161
                            root.gen(cursor->get_float_as_text());
×
1162
                        }
1163
                        break;
×
1164
                    case lnav::cell_type::CT_TEXT:
×
1165
                        root.gen(cursor->get_text());
×
1166
                        break;
×
1167
                }
1168
            }
1169

1170
            auto value_al = attr_line_t::from_table_cell_content(sf, 1000);
×
1171
            al.append(": ").append(value_al);
×
1172
            al.al_attrs.emplace_back(
×
1173
                line_range{0, -1},
×
UNCOV
1174
                DBA_DETAILS.value(gen.to_string_fragment().to_string()));
×
1175
        }
1176

1177
        if (!al.empty()) {
×
1178
            value_out.emplace_back(al);
×
1179
        }
1180
        cursor = cursor->next();
×
1181
    }
1182

UNCOV
1183
    this->dos_labels->dls_cell_allocator.reset();
×
1184
}
1185

1186
bool
1187
db_overlay_source::list_static_overlay(const listview_curses& lv,
301✔
1188
                                       media_t media,
1189
                                       int y,
1190
                                       int bottom,
1191
                                       attr_line_t& value_out)
1192
{
1193
    if (y != 0) {
301✔
1194
        return false;
95✔
1195
    }
1196

1197
    auto& line = value_out.get_string();
206✔
1198
    const auto* dls = this->dos_labels;
206✔
1199
    auto& sa = value_out.get_attrs();
206✔
1200

1201
    for (size_t lpc = 0; lpc < this->dos_labels->dls_headers.size(); lpc++) {
1,163✔
1202
        if (lpc == this->dos_labels->dls_row_style_column
957✔
1203
            && !this->dos_labels->dls_row_styles_have_errors)
3✔
1204
        {
1205
            continue;
3✔
1206
        }
1207

1208
        const auto& hm = dls->dls_headers[lpc];
955✔
1209
        if (hm.hm_hidden) {
955✔
1210
            continue;
1✔
1211
        }
1212
        auto actual_col_size
1213
            = std::min(dls->dls_max_column_width, hm.hm_column_size);
954✔
1214
        auto cell_title = hm.hm_name;
954✔
1215
        string_attrs_t cell_attrs;
954✔
1216
        scrub_ansi_string(cell_title, &cell_attrs);
954✔
1217
        truncate_to(cell_title, dls->dls_max_column_width);
954✔
1218

1219
        auto cell_length
1220
            = utf8_string_length(cell_title).unwrapOr(actual_col_size);
954✔
1221
        int total_fill = actual_col_size - cell_length;
954✔
1222
        auto line_len_before = line.length();
954✔
1223

1224
        int before = total_fill / 2;
954✔
1225
        total_fill -= before;
954✔
1226
        line.append(before, ' ');
954✔
1227
        shift_string_attrs(cell_attrs, 0, line.size());
954✔
1228
        line.append(cell_title);
954✔
1229
        line.append(total_fill, ' ');
954✔
1230
        auto header_range = line_range(line_len_before, line.length());
954✔
1231

1232
        line.append(1, ' ');
954✔
1233

1234
        require_ge(header_range.lr_start, 0);
954✔
1235

1236
        sa.emplace_back(header_range, VC_STYLE.value(hm.hm_title_attrs));
954✔
1237
        sa.insert(sa.end(), cell_attrs.begin(), cell_attrs.end());
954✔
1238
    }
954✔
1239

1240
    line_range lr(0);
206✔
1241

1242
    sa.emplace_back(
206✔
1243
        lr,
1244
        VC_STYLE.value(text_attrs::with_styles(text_attrs::style::bold,
412✔
1245
                                               text_attrs::style::underline)));
1246
    return true;
206✔
1247
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc