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

libbitcoin / libbitcoin-system / 9766436202

02 Jul 2024 06:51PM UTC coverage: 82.709%. Remained the same
9766436202

push

github

web-flow
Merge pull request #1487 from evoskuil/master

Factor block.check "bypass" into block.identify overrides.

7 of 29 new or added lines in 8 files covered. (24.14%)

4 existing lines in 1 file now uncovered.

9854 of 11914 relevant lines covered (82.71%)

4786847.07 hits per line

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

65.13
/src/config/printer.cpp
1
/**
2
 * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS)
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/system/config/printer.hpp>
20

21
////#include <format>
22
#include <boost/format.hpp>
23
#include <bitcoin/system/config/parameter.hpp>
24
#include <bitcoin/system/data/data.hpp>
25
#include <bitcoin/system/define.hpp>
26
#include <bitcoin/system/unicode/ascii.hpp>
27
#include <bitcoin/system/unicode/code_points.hpp>
28

29
// We built this because po::options_description.print() sucks.
30
// C++20: en.cppreference.com/w/cpp/utility/format/format
31

32
namespace libbitcoin {
33
namespace system {
34
namespace config {
35

36
// TODO: parameterize these localized values.
37
// Various shared localizable strings.
38
#define BC_PRINTER_ARGUMENT_TABLE_HEADER "Arguments (positional):"
39
#define BC_PRINTER_DESCRIPTION_FORMAT "Info: %1%"
40
#define BC_PRINTER_OPTION_TABLE_HEADER "Options (named):"
41
#define BC_PRINTER_USAGE_FORMAT "Usage: %1% %2% %3%"
42
#define BC_PRINTER_VALUE_TEXT "value"
43

44
// Not localizable formatters.
45
#define BC_PRINTER_USAGE_OPTION_MULTIPLE_FORMAT " [--%1% %2%]..."
46
#define BC_PRINTER_USAGE_OPTION_OPTIONAL_FORMAT " [--%1% %2%]"
47
#define BC_PRINTER_USAGE_OPTION_REQUIRED_FORMAT " --%1% %2%"
48
#define BC_PRINTER_USAGE_OPTION_TOGGLE_SHORT_FORMAT " [-%1%]"
49
#define BC_PRINTER_USAGE_OPTION_TOGGLE_LONG_FORMAT " [--%1%]"
50

51
#define BC_PRINTER_USAGE_ARGUMENT_MULTIPLE_FORMAT " [%1%]..."
52
#define BC_PRINTER_USAGE_ARGUMENT_OPTIONAL_FORMAT " [%1%]"
53
#define BC_PRINTER_USAGE_ARGUMENT_REQUIRED_FORMAT " %1%"
54

55
#define BC_PRINTER_TABLE_OPTION_FORMAT "-%1% [--%2%]"
56
#define BC_PRINTER_TABLE_OPTION_LONG_FORMAT "--%1%"
57
#define BC_PRINTER_TABLE_OPTION_SHORT_FORMAT "-%1%"
58

59
#define BC_PRINTER_TABLE_ARGUMENT_FORMAT "%1%"
60

61
#define BC_PRINTER_SETTING_SECTION_FORMAT "[%1%]\n"
62
#define BC_PRINTER_SETTING_COMMENT_FORMAT "# %1%\n"
63
#define BC_PRINTER_SETTING_MULTIPLE_FORMAT "%1% = <%2%>\n%1% = <%2%>\n...\n"
64
#define BC_PRINTER_SETTING_OPTIONAL_FORMAT "%1% = <%2%>\n"
65
#define BC_PRINTER_SETTING_REQUIRED_FORMAT "%1% = %2%\n"
66

67
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
68

69
using boost::format;
70
const int printer::max_arguments = 256;
71

72
printer::printer(const options_metadata& options,
34✔
73
    const arguments_metadata& arguments, const std::string& application,
74
    const std::string& description, const std::string& command) NOEXCEPT
34✔
75
  : options_(options), arguments_(arguments), application_(application),
34✔
76
    description_(description), command_(command)
34✔
77
{
78
}
34✔
79

80
printer::printer(const options_metadata& settings,
×
81
    const std::string& application, const std::string& description) NOEXCEPT
×
82
  : options_(settings), application_(application), description_(description)
×
83
{
84
}
×
85

86
// Formatters
87
// ----------------------------------------------------------------------------
88

89
static void enqueue_fragment(const std::string& fragment,
65✔
90
    std::vector<std::string>& column) NOEXCEPT
91
{
92
    if (!fragment.empty())
65✔
93
        column.push_back(fragment);
38✔
94
}
65✔
95

96
std::vector<std::string> printer::columnize(const std::string& paragraph,
25✔
97
    size_t width) NOEXCEPT
98
{
99
    const auto words = split(paragraph, ascii_space, false);
25✔
100

101
    std::string fragment;
25✔
102
    std::vector<std::string> column;
25✔
103

104
    for (const auto& word: words)
163✔
105
    {
106
        if (!fragment.empty() && (word.length() + fragment.length() < width))
138✔
107
        {
108
            fragment += " " + word;
98✔
109
            continue;
98✔
110
        }
111

112
        enqueue_fragment(fragment, column);
40✔
113
        fragment = word;
40✔
114
    }
115

116
    enqueue_fragment(fragment, column);
25✔
117
    return column;
25✔
118
}
25✔
119

120
static std::string format_row_name(const parameter& value) NOEXCEPT
16✔
121
{
122
    // We hack in upper case for all positional args because of a bug in
123
    // boost that requires environment variable options to be lower case
124
    // in order to match any case environment variable, at least on Windows.
125
    // This is a problem when composing with a command-line argument that
126
    // wants to be upper case but must match in case with the env var option.
127

128
    if (value.position() != parameter::not_positional)
16✔
129
        return (format(BC_PRINTER_TABLE_ARGUMENT_FORMAT) %
18✔
130
            ascii_to_upper(value.long_name())).str();
18✔
131
    else if (value.short_name() == parameter::no_short_name)
10✔
132
        return (format(BC_PRINTER_TABLE_OPTION_LONG_FORMAT) %
6✔
133
            value.long_name()).str();
6✔
134
    else if (value.long_name().empty())
14✔
135
        return (format(BC_PRINTER_TABLE_OPTION_SHORT_FORMAT) %
4✔
136
            value.short_name()).str();
6✔
137
    else
138
        return (format(BC_PRINTER_TABLE_OPTION_FORMAT) %
10✔
139
            value.short_name() % value.long_name()).str();
15✔
140
}
141

142
static bool match_positional(bool positional, const parameter& value) NOEXCEPT
24✔
143
{
144
    const auto positioned = value.position() != parameter::not_positional;
24✔
145
    return positioned == positional;
24✔
146
}
147

148
// This formats to 73 char width as: [ 20 | ' ' | 52 | '\n' ]
149
// GitHub code examples start horizontal scroll after 73 characters.
150
std::string printer::format_parameters_table(bool positional) NOEXCEPT
10✔
151
{
152
    std::ostringstream output;
10✔
153
    const auto& parameters = get_parameters();
10✔
154
    format table_format("%-20s %-52s\n");
10✔
155

156
    for (const auto& parameter: parameters)
34✔
157
    {
158
        // Skip positional arguments if not positional.
159
        if (!match_positional(positional, parameter))
24✔
160
            continue;
8✔
161

162
        // Get the formatted parameter name.
163
        auto name = format_row_name(parameter);
16✔
164

165
        // Build a column for the description.
166
        const auto rows = columnize(parameter.description(), 52);
16✔
167

168
        // If there is no description the command is not output!
169
        for (const auto& row: rows)
40✔
170
        {
171
            output << table_format % name % row;
48✔
172

173
            // The name is only set in the first row.
174
            name.clear();
24✔
175
        }
176
    }
16✔
177

178
    return output.str();
10✔
179
}
10✔
180

181
// This formats to 73 char width: [ 73 | '\n' ]
182
// GitHub code examples start horizontal scroll after 73 characters.
183
std::string printer::format_paragraph(const std::string& paragraph) NOEXCEPT
×
184
{
185
    std::ostringstream output;
×
186
    format paragraph_format("%-73s\n");
×
187

188
    const auto lines = columnize(paragraph, 73);
×
189

190
    for (const auto& line: lines)
×
191
        output << paragraph_format % line;
×
192

193
    return output.str();
×
194
}
×
195

196
static std::string format_setting(const parameter& value,
×
197
    const std::string& name) NOEXCEPT
198
{
199
    // A required argument may only be preceded by required arguments.
200
    // Requiredness may be in error if the metadata is inconsistent.
201
    auto required = value.required();
×
202

203
    // In terms of formatting we also treat multivalued as not required.
NEW
204
    auto optional = is_one(value.args_limit());
×
205

206
    std::string formatter;
×
207
    if (required)
×
208
        formatter = BC_PRINTER_SETTING_REQUIRED_FORMAT;
×
209
    else if (optional)
×
210
        formatter = BC_PRINTER_SETTING_OPTIONAL_FORMAT;
×
211
    else
212
        formatter = BC_PRINTER_SETTING_MULTIPLE_FORMAT;
×
213

214
    return (format(formatter) % name % BC_PRINTER_VALUE_TEXT).str();
×
215
}
216

217
// Requires a single period in each setting (i.e. no unnamed sections).
218
static void split_setting_name(const parameter& value, std::string& name,
×
219
    std::string& section) NOEXCEPT
220
{
221
    const auto tokens = split(value.long_name(), ".", false);
×
222
    if (tokens.size() != 2)
×
223
    {
224
        section.clear();
×
225
        name.clear();
×
226
        return;
×
227
    }
228

229
    section = tokens[0];
×
230
    name = tokens[1];
×
231
}
×
232

233
std::string printer::format_settings_table() NOEXCEPT
×
234
{
235
    std::string name;
×
236
    std::string section;
×
237
    std::ostringstream output;
×
238
    std::string preceding_section;
×
239

240
    const auto& parameters = get_parameters();
×
241
    for (const auto& parameter: parameters)
×
242
    {
243
        split_setting_name(parameter, name, section);
×
244
        if (section.empty())
×
245
        {
246
            BC_ASSERT_MSG(false, "Invalid config setting metadata.");
×
247
            continue;
×
248
        }
249

250
        if (section != preceding_section)
×
251
        {
252
            output << std::endl;
×
253
            if (!section.empty())
×
254
            {
255
                output << format(BC_PRINTER_SETTING_SECTION_FORMAT) % section;
×
256
                preceding_section = section;
×
257
            }
258
        }
259

260
        output << format(BC_PRINTER_SETTING_COMMENT_FORMAT) %
×
261
            parameter.description();
×
262

263
        output << format_setting(parameter, name);
×
264
    }
265

266
    return output.str();
×
267
}
×
268

269
std::string printer::format_usage() NOEXCEPT
×
270
{
271
    // USAGE: bx COMMAND [-hvt] -n VALUE [-m VALUE] [-w VALUE]... REQUIRED
272
    // [OPTIONAL] [MULTIPLE]...
273
    const auto usage = format(BC_PRINTER_USAGE_FORMAT) % application() %
×
274
        command() % format_usage_parameters();
×
275

276
    return format_paragraph(usage.str());
×
277
}
×
278

279
std::string printer::format_description() NOEXCEPT
×
280
{
281
    // Info: %1%
282
    const auto described = format(BC_PRINTER_DESCRIPTION_FORMAT) %
×
283
        description();
×
284

285
    return format_paragraph(described.str());
×
286
}
×
287

288
std::string printer::format_usage_parameters() NOEXCEPT
2✔
289
{
290
    std::string toggle_short_options;
2✔
291
    std::vector<std::string> toggle_long_options;
2✔
292
    std::vector<std::string> required_options;
2✔
293
    std::vector<std::string> optional_options;
2✔
294
    std::vector<std::string> multiple_options;
2✔
295
    std::vector<std::string> required_arguments;
2✔
296
    std::vector<std::string> optional_arguments;
2✔
297
    std::vector<std::string> multiple_arguments;
2✔
298

299
    const auto& parameters = get_parameters();
2✔
300

301
    for (const auto& parameter: parameters)
15✔
302
    {
303
        // A required argument may only be preceded by required arguments.
304
        // Requiredness may be in error if the metadata is inconsistent.
305
        const auto required = parameter.required();
13✔
306

307
        // Options are named and args are positional.
308
        const auto option = parameter.position() == parameter::not_positional;
13✔
309

310
        // In terms of formatting we also treat multivalued as not required.
311
        const auto optional = is_one(parameter.args_limit());
13✔
312

313
        // This will capture only options set to zero_tokens().
314
        const auto toggle = is_zero(parameter.args_limit());
13✔
315

316
        // A toggle with a short name gets mashed up in group.
317
        const auto is_short = parameter.short_name() != parameter::no_short_name;
13✔
318

319
        const auto& long_name = parameter.long_name();
13✔
320

321
        if (toggle)
13✔
322
        {
323
            if (is_short)
6✔
324
                toggle_short_options.push_back(parameter.short_name());
5✔
325
            else
326
                toggle_long_options.push_back(long_name);
1✔
327
        }
328
        else if (option)
7✔
329
        {
330
            if (required)
3✔
331
                required_options.push_back(long_name);
1✔
332
            else if (optional)
2✔
333
                optional_options.push_back(long_name);
1✔
334
            else
335
                multiple_options.push_back(long_name);
1✔
336
        }
337
        else
338
        {
339
            // to_upper_copy is a hack for boost bug, see format_row_name.
340
            if (required)
4✔
341
                required_arguments.push_back(ascii_to_upper(long_name));
2✔
342
            else if (optional)
3✔
343
                optional_arguments.push_back(ascii_to_upper(long_name));
2✔
344
            else
345
                multiple_arguments.push_back(ascii_to_upper(long_name));
4✔
346
        }
347
    }
348

349
    std::ostringstream usage;
2✔
350

351
    if (!toggle_short_options.empty())
2✔
352
        usage << format(BC_PRINTER_USAGE_OPTION_TOGGLE_SHORT_FORMAT) %
4✔
353
            toggle_short_options;
2✔
354

355
    for (const auto& required_option: required_options)
3✔
356
        usage << format(BC_PRINTER_USAGE_OPTION_REQUIRED_FORMAT) %
2✔
357
            required_option % BC_PRINTER_VALUE_TEXT;
1✔
358

359
    for (const auto& toggle_long_option: toggle_long_options)
3✔
360
        usage << format(BC_PRINTER_USAGE_OPTION_TOGGLE_LONG_FORMAT) %
2✔
361
            toggle_long_option;
1✔
362

363
    for (const auto& optional_option: optional_options)
3✔
364
        usage << format(BC_PRINTER_USAGE_OPTION_OPTIONAL_FORMAT) %
2✔
365
            optional_option % BC_PRINTER_VALUE_TEXT;
1✔
366

367
    for (const auto& multiple_option: multiple_options)
3✔
368
        usage << format(BC_PRINTER_USAGE_OPTION_MULTIPLE_FORMAT) %
2✔
369
            multiple_option % BC_PRINTER_VALUE_TEXT;
1✔
370

371
    for (const auto& required_argument: required_arguments)
3✔
372
        usage << format(BC_PRINTER_USAGE_ARGUMENT_REQUIRED_FORMAT) %
2✔
373
            required_argument;
1✔
374

375
    for (const auto& optional_argument: optional_arguments)
3✔
376
        usage << format(BC_PRINTER_USAGE_ARGUMENT_OPTIONAL_FORMAT) %
2✔
377
            optional_argument;
1✔
378

379
    for (const auto& multiple_argument: multiple_arguments)
4✔
380
        usage << format(BC_PRINTER_USAGE_ARGUMENT_MULTIPLE_FORMAT) %
4✔
381
            multiple_argument;
2✔
382

383
    return trim_copy(usage.str());
2✔
384
}
2✔
385

386
// Initialization
387

388
static void enqueue_name(int count, std::string& name,
37✔
389
    argument_list& names) NOEXCEPT
390
{
391
    if (count <= 0)
37✔
392
        return;
393

394
    if (count > printer::max_arguments)
32✔
395
        count = -1;
11✔
396

397
    names.push_back(argument_pair(name, count));
64✔
398
}
399

400
// This method just gives us a copy of arguments_metadata private state.
401
// It would be nice if instead that state was public.
402
void printer::generate_argument_names() NOEXCEPT
20✔
403
{
404
    // Member values
405
    const auto& args = arguments();
20✔
406
    auto& argument_names = get_argument_names();
20✔
407

408
    argument_names.clear();
20✔
409
    const auto max_total_arguments = args.max_total_count();
20✔
410

411
    // Temporary values
412
    std::string argument_name;
20✔
413
    std::string previous_argument_name;
20✔
414
    int max_previous_argument = 0;
20✔
415

416
    // We must enumerate all arguments to get the full set of names and counts.
417
    for (unsigned int position = 0; position < max_total_arguments &&
4,043✔
418
        max_previous_argument <= max_arguments; ++position)
4,043✔
419
    {
420
        argument_name = args.name_for_position(position);
4,023✔
421

422
        // Initialize the first name as having a zeroth instance.
423
        if (max_previous_argument == 0)
4,023✔
424
            previous_argument_name = argument_name;
15✔
425

426
        // This is a duplicate of the previous name, so increment the count.
427
        if (argument_name == previous_argument_name)
4,023✔
428
        {
429
            ++max_previous_argument;
4,006✔
430
            continue;
4,006✔
431
        }
432

433
        enqueue_name(max_previous_argument, previous_argument_name,
17✔
434
            argument_names);
435
        previous_argument_name = argument_name;
17✔
436
        max_previous_argument = 1;
17✔
437
    }
438

439
    // Save the previous name (if there is one).
440
    enqueue_name(max_previous_argument, previous_argument_name,
20✔
441
        argument_names);
442
}
20✔
443

444
static bool compare_parameters(const parameter left,
34✔
445
    const parameter right) NOEXCEPT
446
{
447
    return left.format_name() < right.format_name();
68✔
448
}
449

450
void printer::generate_parameters() NOEXCEPT
14✔
451
{
452
    const auto& argument_names = get_argument_names();
14✔
453
    const auto& opts = options();
14✔
454
    auto& parameters = get_parameters();
14✔
455

456
    parameters.clear();
14✔
457

458
    parameter param;
14✔
459
    for (auto option_ptr: opts.options())
58✔
460
    {
461
        param.initialize(*option_ptr, argument_names);
44✔
462

463
        // Sort non-positional parameters (i.e. options).
464
        if (param.position() == parameter::not_positional)
44✔
465
            insert_sorted(parameters, param, compare_parameters);
32✔
466
        else
467
            parameters.push_back(param);
12✔
468
    }
469
}
14✔
470

471
void printer::initialize() NOEXCEPT
11✔
472
{
473
    generate_argument_names();
11✔
474
    generate_parameters();
11✔
475
}
11✔
476

477
// Printers
478
// ----------------------------------------------------------------------------
479

480
void printer::commandline(std::ostream& output) NOEXCEPT
×
481
{
482
    const auto& option_table = format_parameters_table(false);
×
483
    const auto& argument_table = format_parameters_table(true);
×
484

485
    // Don't write a header if a table is empty.
486
    std::string option_table_header(option_table.empty() ? "" :
×
487
        BC_PRINTER_OPTION_TABLE_HEADER "\n");
×
488
    std::string argument_table_header(argument_table.empty() ? "" :
×
489
        BC_PRINTER_ARGUMENT_TABLE_HEADER "\n");
×
490

491
    output
×
492
        << std::endl << format_usage()
×
493
        << std::endl << format_description()
×
494
        << std::endl << option_table_header
×
495
        << std::endl << option_table
×
496
        << std::endl << argument_table_header
×
497
        << std::endl << argument_table;
×
498
}
×
499

500
void printer::settings(std::ostream& output) NOEXCEPT
×
501
{
502
    const auto& setting_table = format_settings_table();
×
503

504
    if (!description_.empty())
×
505
        output << std::endl << description_;
×
506

507
    output << std::endl << setting_table;
×
508
}
×
509

510
BC_POP_WARNING()
511

512
} // namespace config
513
} // namespace system
514
} // namespace libbitcoin
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

© 2025 Coveralls, Inc