format-table: add tristate field

This commit is contained in:
Lennart Poettering
2026-04-26 21:21:49 +02:00
parent 45ceb97f6a
commit f0abcd5e0f
3 changed files with 100 additions and 0 deletions
+33
View File
@@ -87,6 +87,7 @@ typedef struct TableData {
union {
uint8_t data[0]; /* data is generic array */
bool boolean;
int tristate;
usec_t timestamp;
usec_t timespan;
uint64_t size;
@@ -342,6 +343,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
case TABLE_PERCENT:
case TABLE_IFINDEX:
case TABLE_SIGNAL:
case TABLE_TRISTATE:
return sizeof(int);
case TABLE_IN_ADDR:
@@ -935,6 +937,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
uint64_t uint64;
int percent;
int ifindex;
int tristate;
bool b;
union in_addr_union address;
sd_id128_t id128;
@@ -972,6 +975,11 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
data = &buffer.b;
break;
case TABLE_TRISTATE:
buffer.tristate = va_arg(ap, int);
data = &buffer.tristate;
break;
case TABLE_TIMESTAMP:
case TABLE_TIMESTAMP_UTC:
case TABLE_TIMESTAMP_RELATIVE:
@@ -1434,12 +1442,25 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
return strv_compare(a->strv, b->strv);
case TABLE_BOOLEAN:
case TABLE_BOOLEAN_CHECKMARK:
if (!a->boolean && b->boolean)
return -1;
if (a->boolean && !b->boolean)
return 1;
return 0;
case TABLE_TRISTATE:
/* NB: we do not use CMP() here, since we want to collapse all negative and all
* positive into one bucket each. */
if ((a->tristate < 0 && b->tristate >= 0) ||
(a->tristate == 0 && b->tristate > 0))
return -1;
if ((b->tristate < 0 && a->tristate >= 0) ||
(b->tristate == 0 && a->tristate > 0))
return 1;
return 0;
case TABLE_TIMESTAMP:
case TABLE_TIMESTAMP_UTC:
case TABLE_TIMESTAMP_RELATIVE:
@@ -1691,6 +1712,12 @@ static const char* table_data_format(
case TABLE_BOOLEAN_CHECKMARK:
return glyph(d->boolean ? GLYPH_CHECK_MARK : GLYPH_CROSS_MARK);
case TABLE_TRISTATE:
if (d->tristate < 0)
return table_ersatz_string(t);
return yes_no(d->tristate);
case TABLE_TIMESTAMP:
case TABLE_TIMESTAMP_UTC:
case TABLE_TIMESTAMP_RELATIVE:
@@ -2776,6 +2803,12 @@ static int table_data_to_json(TableData *d, sd_json_variant **ret) {
case TABLE_BOOLEAN:
return sd_json_variant_new_boolean(ret, d->boolean);
case TABLE_TRISTATE:
if (d->tristate < 0)
return sd_json_variant_new_null(ret);
return sd_json_variant_new_boolean(ret, d->tristate);
case TABLE_TIMESTAMP:
case TABLE_TIMESTAMP_UTC:
case TABLE_TIMESTAMP_RELATIVE:
+1
View File
@@ -20,6 +20,7 @@ typedef enum TableDataType {
TABLE_VERSION, /* just like TABLE_STRING, but uses version comparison when sorting */
TABLE_BOOLEAN,
TABLE_BOOLEAN_CHECKMARK,
TABLE_TRISTATE,
TABLE_TIMESTAMP,
TABLE_TIMESTAMP_UTC,
TABLE_TIMESTAMP_RELATIVE,
+66
View File
@@ -581,6 +581,72 @@ TEST(table) {
"5min 5min \n");
}
TEST(tristate) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *formatted = NULL;
ASSERT_NOT_NULL((t = table_new("name", "flag")));
ASSERT_OK(table_add_many(t,
TABLE_STRING, "neg",
TABLE_TRISTATE, -1));
ASSERT_OK(table_add_many(t,
TABLE_STRING, "zero",
TABLE_TRISTATE, 0));
ASSERT_OK(table_add_many(t,
TABLE_STRING, "pos",
TABLE_TRISTATE, 1));
ASSERT_OK(table_format(t, &formatted));
printf("%s\n", formatted);
ASSERT_STREQ(formatted,
"NAME FLAG\n"
"neg \n"
"zero no\n"
"pos yes\n");
formatted = mfree(formatted);
/* Try a non-default ersatz string. */
table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
ASSERT_OK(table_format(t, &formatted));
printf("%s\n", formatted);
ASSERT_STREQ(formatted,
"NAME FLAG\n"
"neg -\n"
"zero no\n"
"pos yes\n");
formatted = mfree(formatted);
/* Sorting: -1 < 0 < 1 */
ASSERT_OK(table_set_sort(t, (size_t) 1, SIZE_MAX));
ASSERT_OK(table_format(t, &formatted));
printf("%s\n", formatted);
ASSERT_STREQ(formatted,
"NAME FLAG\n"
"neg -\n"
"zero no\n"
"pos yes\n");
formatted = mfree(formatted);
/* JSON: -1 → null, 0 → false, positive → true */
ASSERT_OK(table_to_json(t, &v));
ASSERT_OK(sd_json_build(&w,
SD_JSON_BUILD_ARRAY(
SD_JSON_BUILD_OBJECT(
SD_JSON_BUILD_PAIR("name", JSON_BUILD_CONST_STRING("neg")),
SD_JSON_BUILD_PAIR("flag", SD_JSON_BUILD_NULL)),
SD_JSON_BUILD_OBJECT(
SD_JSON_BUILD_PAIR("name", JSON_BUILD_CONST_STRING("zero")),
SD_JSON_BUILD_PAIR_BOOLEAN("flag", false)),
SD_JSON_BUILD_OBJECT(
SD_JSON_BUILD_PAIR("name", JSON_BUILD_CONST_STRING("pos")),
SD_JSON_BUILD_PAIR_BOOLEAN("flag", true)))));
ASSERT_TRUE(sd_json_variant_equal(v, w));
}
TEST(signed_integers) {
_cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *formatted = NULL;