format-table: add tristate field
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user