feat(format): expand pow_format modifiers
Extend the pow_format spec parser beyond alignment so configs can shape the
rendered number, not just pad it. A forced scale (#, k, M, G, T, P) pins the SI
prefix instead of auto-selecting it and, since the author then knows both scale
and unit, hides the prefix and unit by default; U brings the unit back, u hides
it in auto mode, and i forces integer display. b and B override the decimal or
binary base independently of the call site, and a trailing width now fixes the
coefficient field when a scale is forced, overflowing to '#' when it does not
fit.
The old '>' and '<' branches rendered via fmt::format("{}", s), which discarded
every new field on the recursive call; they now build from a single render path
that reads the current formatter, so modifiers survive alignment. The auto path
is byte-identical to before, verified against the previous implementation.
Adds a Catch2 suite covering each modifier alone and combined, plus the
backward-compatible alignment cases, and documents the modifiers in the network
and disk man pages.
This commit is contained in:
+116
-44
@@ -23,30 +23,51 @@ class pow_format {
|
|||||||
namespace fmt {
|
namespace fmt {
|
||||||
template <>
|
template <>
|
||||||
struct formatter<pow_format> {
|
struct formatter<pow_format> {
|
||||||
char spec = 0;
|
char spec = 0; // alignment: '>', '<', '=' (0 = none)
|
||||||
int width = 0;
|
int width = 0; // width digits; enforced only when scale_spec != 0
|
||||||
|
char scale_spec = 0; // forced scale: 0 = auto, else one of '#','k','M','G','T','P'
|
||||||
|
char unit_pref = 0; // unit tri-state: 0 = default, 'u' = hide, 'U' = show
|
||||||
|
char base_pref = 0; // base tri-state: 0 = call-site, 'b' = decimal, 'B' = binary
|
||||||
|
bool force_int = false; // 'i': force integer display
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
auto it = ctx.begin(), end = ctx.end();
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
if (it != end && *it == ':') ++it;
|
if (it != end && *it == ':') ++it;
|
||||||
if (it && (*it == '>' || *it == '<' || *it == '=')) {
|
if (it != end && (*it == '>' || *it == '<' || *it == '=')) {
|
||||||
spec = *it;
|
spec = *it;
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
if (it == end || *it == '}') return it;
|
// Consume scale/flag modifiers and the width in any order, until '}' or end.
|
||||||
if ('0' <= *it && *it <= '9') {
|
// The width digits (parsed but only enforced when a scale is forced — see
|
||||||
// We ignore it for now, but keep it for compatibility with
|
// format()) may appear anywhere among the modifiers, so both {:=#3} and the
|
||||||
// existing configs where the format for pow_format'ed numbers was
|
// more natural {:=3#} are accepted. On an unrecognised char we stop and let
|
||||||
// 'string' and specifications such as {:>9} were valid.
|
// fmt raise its usual error.
|
||||||
// The rationale for ignoring it is that the only reason to specify
|
while (it != end && *it != '}') {
|
||||||
// an alignment and a with is to get a fixed width bar, and ">" is
|
char c = *it;
|
||||||
// sufficient in this implementation.
|
if (c == '#' || c == 'k' || c == 'M' || c == 'G' || c == 'T' || c == 'P') {
|
||||||
|
scale_spec = c;
|
||||||
|
++it;
|
||||||
|
} else if (c == 'u' || c == 'U') {
|
||||||
|
unit_pref = c;
|
||||||
|
++it;
|
||||||
|
} else if (c == 'b' || c == 'B') {
|
||||||
|
base_pref = c;
|
||||||
|
++it;
|
||||||
|
} else if (c == 'i') {
|
||||||
|
force_int = true;
|
||||||
|
++it;
|
||||||
|
} else if ('0' <= c && c <= '9') {
|
||||||
|
// Width kept for compatibility with existing configs such as {:>9}; only
|
||||||
|
// enforced (fixed field + '#' overflow) when a scale is forced.
|
||||||
#if FMT_VERSION < 80000
|
#if FMT_VERSION < 80000
|
||||||
width = parse_nonnegative_int(it, end, ctx);
|
width = parse_nonnegative_int(it, end, ctx);
|
||||||
#else
|
#else
|
||||||
width = detail::parse_nonnegative_int(it, end, -1);
|
width = detail::parse_nonnegative_int(it, end, -1);
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
@@ -54,49 +75,100 @@ struct formatter<pow_format> {
|
|||||||
template <class FormatContext>
|
template <class FormatContext>
|
||||||
auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) {
|
auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
const char* units[] = {"", "k", "M", "G", "T", "P", nullptr};
|
const char* units[] = {"", "k", "M", "G", "T", "P", nullptr};
|
||||||
|
const int max_pow = 5; // last valid index in units[]
|
||||||
|
|
||||||
auto base = s.binary_ ? 1024ull : 1000ll;
|
// Effective base: 'b'/'B' override the call-site binary_.
|
||||||
|
bool binary = base_pref == 'B' ? true : base_pref == 'b' ? false : s.binary_;
|
||||||
|
auto base = binary ? 1024ull : 1000ll;
|
||||||
auto div = 1ll;
|
auto div = 1ll;
|
||||||
auto fraction = (double)s.val_;
|
auto fraction = (double)s.val_;
|
||||||
|
|
||||||
int pow;
|
int pow;
|
||||||
|
if (scale_spec != 0) {
|
||||||
|
// Forced scale: map the char to a fixed index into units[].
|
||||||
|
switch (scale_spec) {
|
||||||
|
case 'k':
|
||||||
|
pow = 1;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
pow = 2;
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
pow = 3;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
pow = 4;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
pow = 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pow = 0;
|
||||||
|
break; // '#' -> base scale
|
||||||
|
}
|
||||||
|
if (pow > max_pow) pow = max_pow;
|
||||||
|
for (int i = 0; i < pow; ++i) div *= base;
|
||||||
|
fraction /= div;
|
||||||
|
} else {
|
||||||
for (pow = 0; units[pow + 1] != nullptr && fraction / base >= 1; ++pow) {
|
for (pow = 0; units[pow + 1] != nullptr && fraction / base >= 1; ++pow) {
|
||||||
fraction /= base;
|
fraction /= base;
|
||||||
div *= base;
|
div *= base;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fixed_precision = (s.skip_decimal_ && ((s.val_ % div) == 0)) ? 0 : 1;
|
|
||||||
auto number_width = 3 + fixed_precision // coeff in {:.{fixed_precision}f} format
|
|
||||||
+ (fixed_precision != 0) // float dot
|
|
||||||
+ s.binary_; // potential digit before the decimal point
|
|
||||||
auto max_width = number_width + 1 // prefix from units array
|
|
||||||
+ s.binary_ // for the 'i' in GiB.
|
|
||||||
+ s.unit_.length();
|
|
||||||
|
|
||||||
const char* format;
|
|
||||||
std::string string;
|
|
||||||
switch (spec) {
|
|
||||||
case '>':
|
|
||||||
return fmt::format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width);
|
|
||||||
case '<':
|
|
||||||
return fmt::format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width);
|
|
||||||
case '=':
|
|
||||||
format = "{coefficient:<{number_width}.{fixed_precision}f}{padding}{prefix}{unit}";
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
default:
|
|
||||||
format = pow < s.min_pow_for_decimal_ ? "{coefficient:.0f}{prefix}{unit}"
|
|
||||||
: "{coefficient:.{fixed_precision}f}{prefix}{unit}";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return fmt::format_to(
|
|
||||||
ctx.out(), fmt::runtime(format), fmt::arg("coefficient", fraction),
|
// Precision: 'i' forces 0; otherwise 1 (or 0 when skip_decimal_ divides
|
||||||
fmt::arg("fixed_precision", fixed_precision), fmt::arg("number_width", number_width),
|
// evenly). min_pow_for_decimal_ keeps its default-branch-only effect.
|
||||||
fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")),
|
int precision = force_int ? 0 : (s.skip_decimal_ && ((s.val_ % div) == 0)) ? 0 : 1;
|
||||||
fmt::arg("unit", s.unit_),
|
if (!force_int && scale_spec == 0 && pow < s.min_pow_for_decimal_) precision = 0;
|
||||||
fmt::arg("padding", pow ? ""
|
|
||||||
: s.binary_ ? " "
|
// Unit visibility: default on for auto scale, off for a forced scale; 'u'/'U'
|
||||||
: " "));
|
// override. The binary 'i' is part of the scale prefix, so the unit is just
|
||||||
|
// unit_.
|
||||||
|
bool hide_unit = unit_pref == 'u' || (unit_pref == 0 && scale_spec != 0);
|
||||||
|
|
||||||
|
// Scale prefix (letter + binary 'i'), suppressed entirely when a scale is
|
||||||
|
// forced.
|
||||||
|
std::string prefix =
|
||||||
|
scale_spec != 0 ? "" : std::string(units[pow]) + ((binary && pow) ? "i" : "");
|
||||||
|
std::string unit = hide_unit ? "" : s.unit_;
|
||||||
|
|
||||||
|
auto number_width = 3 + precision // coeff in {:.{precision}f} format
|
||||||
|
+ (precision != 0) // float dot
|
||||||
|
+ binary; // potential digit before the decimal point
|
||||||
|
// In auto mode the prefix column is always reserved (letter + optional 'i'),
|
||||||
|
// matching the historical fixed max_width even at base scale (the '=' padding
|
||||||
|
// fills the gap). A forced scale drops the prefix column entirely.
|
||||||
|
auto prefix_col = scale_spec != 0 ? 0 : 1 + binary;
|
||||||
|
auto max_width = number_width + prefix_col + unit.length();
|
||||||
|
|
||||||
|
// The numeric coefficient string. When a scale is forced with a width and the
|
||||||
|
// number does not fit, it overflows to '#' (spreadsheet-style).
|
||||||
|
bool fixed_num = scale_spec != 0 && width > 0;
|
||||||
|
std::string number = fmt::format("{:.{}f}", fraction, precision);
|
||||||
|
if (fixed_num && (int)number.length() > width) number = std::string(width, '#');
|
||||||
|
|
||||||
|
// Base-scale compensation for the '=' column-align: only in auto mode, where
|
||||||
|
// the absent prefix (and binary 'i') would otherwise shift the unit column.
|
||||||
|
const char* padding = (scale_spec == 0 && pow == 0) ? (binary ? " " : " ") : "";
|
||||||
|
|
||||||
|
switch (spec) {
|
||||||
|
case '=':
|
||||||
|
// Column-align: left-justify the coefficient within its column, then pad
|
||||||
|
// so the prefix/unit line up across values of different magnitude.
|
||||||
|
return fmt::format_to(ctx.out(), "{:<{}}{}{}{}", number, fixed_num ? width : number_width,
|
||||||
|
padding, prefix, unit);
|
||||||
|
case '>':
|
||||||
|
case '<':
|
||||||
|
case 0:
|
||||||
|
default: {
|
||||||
|
// Right-justify the numeric field to the fixed width when forced.
|
||||||
|
std::string body =
|
||||||
|
(fixed_num ? fmt::format("{:>{}}", number, width) : number) + prefix + unit;
|
||||||
|
if (spec == '>') return fmt::format_to(ctx.out(), "{:>{}}", body, max_width);
|
||||||
|
if (spec == '<') return fmt::format_to(ctx.out(), "{:<{}}", body, max_width);
|
||||||
|
return fmt::format_to(ctx.out(), "{}", body);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -146,6 +146,29 @@ Addressed by *disk*
|
|||||||
|
|
||||||
*{specific_free}*: Amount of available disk space for normal users in a specific unit. Defaults to bytes.
|
*{specific_free}*: Amount of available disk space for normal users in a specific unit. Defaults to bytes.
|
||||||
|
|
||||||
|
# NUMBER FORMAT MODIFIERS
|
||||||
|
|
||||||
|
*{total}*, *{used}* and *{free}* auto-scale with a binary prefix (KiB, GiB, …).
|
||||||
|
Their rendering can be tuned with fmt-style modifiers, e.g. *"{free:>}"* or
|
||||||
|
*"{free:G}"*, combined in any order:
|
||||||
|
|
||||||
|
*<*, *=*, *>*: Alignment/padding (left, column-align, right).
|
||||||
|
|
||||||
|
*u* / *U*: Hide (*u*) or show (*U*) the unit suffix. Shown by default with an auto
|
||||||
|
scale, hidden by default when a scale is forced.
|
||||||
|
|
||||||
|
*#*, *k*, *M*, *G*, *T*, *P*: Force a fixed scale instead of auto-selecting
|
||||||
|
(*#* = base scale). Forcing a scale hides the scale prefix and, by default, the
|
||||||
|
unit. E.g. *"{free:G}"* always shows gibibytes.
|
||||||
|
|
||||||
|
*i*: Force integer display (no decimals).
|
||||||
|
|
||||||
|
*b* / *B*: Force decimal base 1000 (*b*) or binary base 1024 (*B*); disk values
|
||||||
|
are binary by default.
|
||||||
|
|
||||||
|
A trailing number is a fixed width for the coefficient when a scale is forced;
|
||||||
|
overflow is shown as *#* characters (e.g. *"{free:=3#}"* → *###*).
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -214,6 +214,34 @@ Addressed by *network*
|
|||||||
|
|
||||||
*{icon}*: Icon, as defined in *format-icons*.
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
|
# NUMBER FORMAT MODIFIERS
|
||||||
|
|
||||||
|
The bandwidth/bitrate replacements above (*{bandwidth\*}*, *{txBitrate}*,
|
||||||
|
*{rxBitrate}*, *{linkSpeed}*) auto-scale the number with an SI prefix (k, M, G,
|
||||||
|
…). Their rendering can be tuned with fmt-style modifiers, e.g.
|
||||||
|
*"{bandwidthDownBits:>}"* or *"{bandwidthDownBits:M}"*. Modifiers may be combined
|
||||||
|
in any order:
|
||||||
|
|
||||||
|
*<*, *=*, *>*: Alignment/padding (left, column-align, right) — as before.
|
||||||
|
|
||||||
|
*u* / *U*: Hide (*u*) or show (*U*) the unit suffix. The unit is shown by default
|
||||||
|
with an auto scale and hidden by default when a scale is forced (see below).
|
||||||
|
|
||||||
|
*#*, *k*, *M*, *G*, *T*, *P*: Force a fixed scale instead of auto-selecting one
|
||||||
|
(*#* = base scale, no prefix). Forcing a scale hides the scale prefix and, by
|
||||||
|
default, the unit (re-enable it with *U*). E.g. *"{bandwidthDownBits:M}"* always
|
||||||
|
shows the value in megabits.
|
||||||
|
|
||||||
|
*i*: Force integer display (no decimals).
|
||||||
|
|
||||||
|
*b* / *B*: Force decimal base 1000 (*b*) or binary base 1024 with an *i* marker
|
||||||
|
(*B*), overriding the module default.
|
||||||
|
|
||||||
|
A trailing number is a fixed width for the coefficient when a scale is forced; if
|
||||||
|
the value does not fit, it is shown as *#* characters (e.g. *"{bandwidthDownBits:=3#}"*
|
||||||
|
→ *###*). Without a forced scale the number is ignored (kept for config
|
||||||
|
compatibility).
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
#include "util/format.hpp"
|
||||||
|
|
||||||
|
#if __has_include(<catch2/catch_test_macros.hpp>)
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#else
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
// Helpers to build the two representative sample values used throughout the
|
||||||
|
// pow_format modifier tests.
|
||||||
|
static std::string fmtA(const char* spec) {
|
||||||
|
// 1536 bytes, binary (base 1024) -> auto "1.5kiB"
|
||||||
|
return fmt::format(fmt::runtime(std::string("{:") + spec + "}"), pow_format(1536, "B", true));
|
||||||
|
}
|
||||||
|
static std::string fmtB(const char* spec) {
|
||||||
|
// 1500000 b/s, decimal (base 1000) -> auto "1.5Mb/s"
|
||||||
|
return fmt::format(fmt::runtime(std::string("{:") + spec + "}"), pow_format(1500000, "b/s"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pow_format default/auto rendering", "[format][pow_format]") {
|
||||||
|
REQUIRE(fmt::format("{}", pow_format(1536, "B", true)) == "1.5kiB");
|
||||||
|
REQUIRE(fmt::format("{}", pow_format(1500000, "b/s")) == "1.5Mb/s");
|
||||||
|
// Sub-scale values and unit-only rendering.
|
||||||
|
REQUIRE(fmt::format("{}", pow_format(500, "B")) == "500.0B");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pow_format hide/show unit (u/U)", "[format][pow_format]") {
|
||||||
|
REQUIRE(fmtA("u") == "1.5ki"); // unit hidden, scale prefix (ki) kept
|
||||||
|
REQUIRE(fmtA("U") == "1.5kiB"); // explicit show = auto default
|
||||||
|
REQUIRE(fmtB("u") == "1.5M");
|
||||||
|
REQUIRE(fmtB("U") == "1.5Mb/s");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pow_format force integer (i)", "[format][pow_format]") {
|
||||||
|
REQUIRE(fmtA("i") == "2kiB"); // 1.5 -> 2
|
||||||
|
REQUIRE(fmtA("iu") == "2ki");
|
||||||
|
REQUIRE(fmtB("i") == "2Mb/s");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pow_format forced scale hides prefix and unit", "[format][pow_format]") {
|
||||||
|
// Base scale (#): no prefix; unit off by default, back on with U.
|
||||||
|
REQUIRE(fmtA("#") == "1536.0");
|
||||||
|
REQUIRE(fmtA("#U") == "1536.0B");
|
||||||
|
REQUIRE(fmtA("#i") == "1536");
|
||||||
|
REQUIRE(fmtB("#") == "1500000.0");
|
||||||
|
REQUIRE(fmtB("#U") == "1500000.0b/s");
|
||||||
|
|
||||||
|
// Force kilo: whole prefix (ki) suppressed, unit off by default.
|
||||||
|
REQUIRE(fmtA("k") == "1.5");
|
||||||
|
REQUIRE(fmtA("kU") == "1.5B"); // unit shown, no 'i' (it lives on the prefix)
|
||||||
|
REQUIRE(fmtA("ki") == "2");
|
||||||
|
REQUIRE(fmtB("k") == "1500.0");
|
||||||
|
REQUIRE(fmtB("kU") == "1500.0b/s");
|
||||||
|
REQUIRE(fmtB("ki") == "1500");
|
||||||
|
|
||||||
|
// Force a scale far above the value's magnitude -> underflow to 0, no bump.
|
||||||
|
REQUIRE(fmtA("M") == "0.0");
|
||||||
|
REQUIRE(fmtA("MU") == "0.0B");
|
||||||
|
REQUIRE(fmtB("G") == "0.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pow_format force base (b/B)", "[format][pow_format]") {
|
||||||
|
// Force decimal on a binary call-site value.
|
||||||
|
REQUIRE(fmtA("b") == "1.5kB"); // base 1000, prefix 'k', no 'i'
|
||||||
|
REQUIRE(fmtA("bu") == "1.5k");
|
||||||
|
REQUIRE(fmtA("b#") == "1536.0");
|
||||||
|
|
||||||
|
// Force binary on a decimal call-site value.
|
||||||
|
REQUIRE(fmtB("B") == "1.4Mib/s"); // base 1024, prefix 'Mi'
|
||||||
|
REQUIRE(fmtB("Bu") == "1.4Mi");
|
||||||
|
REQUIRE(fmtB("Bk") == "1464.8");
|
||||||
|
REQUIRE(fmtB("BkU") == "1464.8b/s");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pow_format fixed width with # overflow", "[format][pow_format]") {
|
||||||
|
// Base scale, numeric field width 3: 1536.0 (6 chars) overflows -> ###.
|
||||||
|
REQUIRE(fmtA("=3#") == "###");
|
||||||
|
REQUIRE(fmtA("=6#") == "1536.0");
|
||||||
|
REQUIRE(fmtA("=3#U") == "###B");
|
||||||
|
REQUIRE(fmtA("=6#U") == "1536.0B");
|
||||||
|
REQUIRE(fmtB("=4k#") == "####");
|
||||||
|
// Width digit position is flexible: {:=3#} and {:=#3} are equivalent.
|
||||||
|
REQUIRE(fmtA("=#3") == "###");
|
||||||
|
// Force kilo, width 6, coefficient "1500" fits, shown with unit; the '='
|
||||||
|
// column left-justifies the coefficient in its field.
|
||||||
|
REQUIRE(fmtB("=6kiU") == "1500 b/s");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pow_format modifiers compose with alignment", "[format][pow_format]") {
|
||||||
|
REQUIRE(fmtA(">u") == " 1.5ki"); // right-align, unit hidden (prefix kept)
|
||||||
|
REQUIRE(fmtB(">ki") == "1500"); // force kilo+int (unit off), no scale width
|
||||||
|
REQUIRE(fmtB("Bu") == "1.4Mi"); // force binary, unit hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pow_format backward compatible alignment", "[format][pow_format]") {
|
||||||
|
// These specs predate the new modifiers and must render as before.
|
||||||
|
REQUIRE(fmt::format("{:>}", pow_format(1536, "B", true)) == " 1.5kiB");
|
||||||
|
REQUIRE(fmt::format("{:<}", pow_format(1536, "B", true)) == "1.5kiB ");
|
||||||
|
REQUIRE(fmt::format("{:=}", pow_format(1536, "B", true)) == "1.5 kiB");
|
||||||
|
// Width without a forced scale is still ignored (only used for compat).
|
||||||
|
REQUIRE(fmt::format("{:>9}", pow_format(1536, "B", true)) == " 1.5kiB");
|
||||||
|
REQUIRE(fmt::format("{}", pow_format(1500000, "b/s")) == "1.5Mb/s");
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ test_src = files(
|
|||||||
'../../src/config.cpp',
|
'../../src/config.cpp',
|
||||||
'JsonParser.cpp',
|
'JsonParser.cpp',
|
||||||
'SafeSignal.cpp',
|
'SafeSignal.cpp',
|
||||||
|
'format.cpp',
|
||||||
'sleeper_thread.cpp',
|
'sleeper_thread.cpp',
|
||||||
'command.cpp',
|
'command.cpp',
|
||||||
'command_line_stream.cpp',
|
'command_line_stream.cpp',
|
||||||
|
|||||||
Reference in New Issue
Block a user