Merge pull request #33609 from AdrianVovk/table-json

Improve table JSON name mangling
This commit is contained in:
Lennart Poettering
2024-07-17 23:03:54 +02:00
committed by GitHub
6 changed files with 109 additions and 12 deletions
+5
View File
@@ -912,6 +912,11 @@ static int list_images(int argc, char *argv[], void *userdata) {
(void) table_hide_column_from_display(t, 8);
(void) table_hide_column_from_display(t, 10);
/* Starting in v257, these fields would be automatically formatted with underscores. However, this
* command was introduced in v256, so changing the field name would be a breaking change. */
(void) table_set_json_field_name(t, 8, "usage-exclusive");
(void) table_set_json_field_name(t, 10, "limit-exclusive");
for (;;) {
uint64_t crtime, mtime, usage, usage_exclusive, limit, limit_exclusive;
const char *class, *name, *type, *path;
+4
View File
@@ -3043,6 +3043,10 @@ static int context_dump_partitions(Context *context) {
if (!t)
return log_oom();
/* Starting in v257, these fields would be automatically formatted with underscores. This would have
* been a breaking change, so to avoid that let's hard-code their original names. */
table_set_json_field_name(t, 15, "drop-in_files");
if (!DEBUG_LOGGING) {
if (arg_json_format_flags & SD_JSON_FORMAT_OFF)
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
+42 -7
View File
@@ -1184,6 +1184,20 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
goto check;
}
case TABLE_SET_JSON_FIELD_NAME: {
const char *n = va_arg(ap, const char*);
size_t idx;
if (t->vertical) {
assert(TABLE_CELL_TO_INDEX(last_cell) >= t->n_columns);
idx = TABLE_CELL_TO_INDEX(last_cell) / t->n_columns - 1;
} else {
idx = TABLE_CELL_TO_INDEX(last_cell);
assert(idx < t->n_columns);
}
r = table_set_json_field_name(t, idx, n);
goto check;
}
case _TABLE_DATA_TYPE_MAX:
/* Used as end marker */
va_end(ap);
@@ -2881,18 +2895,39 @@ static int table_data_to_json(TableData *d, sd_json_variant **ret) {
}
}
static char* string_to_json_field_name(const char *f) {
char* table_mangle_to_json_field_name(const char *str) {
/* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a
* field name can be hence this is a bit vague and black magic. Right now we only convert spaces to
* underscores and leave everything as is. */
* field name can be hence this is a bit vague and black magic. Here's what we do:
* - Convert spaces to underscores
* - Convert dashes to underscores (some JSON parsers don't like dealing with dashes)
* - Convert most other symbols to underscores (for similar reasons)
* - Make the first letter of each word lowercase (unless it looks like the whole word is uppercase)
*/
char *c = strdup(f);
bool new_word = true;
char *c;
assert(str);
c = strdup(str);
if (!c)
return NULL;
for (char *x = c; *x; x++)
if (isspace(*x))
for (char *x = c; *x; x++) {
if (!strchr(ALPHANUMERICAL, *x)) {
*x = '_';
new_word = true;
continue;
}
if (new_word) {
if (ascii_tolower(*(x + 1)) == *(x + 1)) /* Heuristic: if next char is upper-case
* then we assume the whole word is all-caps
* and avoid lowercasing it. */
*x = ascii_tolower(*x);
new_word = false;
}
}
return c;
}
@@ -2913,7 +2948,7 @@ static int table_make_json_field_name(Table *t, TableData *d, char **ret) {
return -ENOMEM;
}
mangled = string_to_json_field_name(n);
mangled = table_mangle_to_json_field_name(n);
if (!mangled)
return -ENOMEM;
+2
View File
@@ -74,6 +74,7 @@ typedef enum TableDataType {
TABLE_SET_BOTH_UNDERLINES,
TABLE_SET_URL,
TABLE_SET_UPPERCASE,
TABLE_SET_JSON_FIELD_NAME,
_TABLE_DATA_TYPE_INVALID = -EINVAL,
} TableDataType;
@@ -165,6 +166,7 @@ int table_print_json(Table *t, FILE *f, sd_json_format_flags_t json_flags);
int table_print_with_pager(Table *t, sd_json_format_flags_t json_format_flags, PagerFlags pager_flags, bool show_header);
char* table_mangle_to_json_field_name(const char *str);
int table_set_json_field_name(Table *t, size_t idx, const char *name);
#define table_log_add_error(r) \
+5
View File
@@ -470,6 +470,11 @@ static int context_show_version(Context *c, const char *version) {
(void) table_set_align_percent(t, table_get_cell(t, 0, 8), 100);
table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
/* Starting in v257, these fields would be automatically formatted with underscores. This would have
* been a breaking change, so to avoid that let's hard-code their original names. */
(void) table_set_json_field_name(t, 7, "tries-done");
(void) table_set_json_field_name(t, 8, "tries-left");
/* Determine if the target will make use of partition/fs attributes for any of the transfers */
FOREACH_ARRAY(transfer, c->transfers, c->n_transfers) {
Transfer *tr = *transfer;
+51 -5
View File
@@ -364,18 +364,27 @@ TEST(json) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
assert_se(t = table_new("foo bar", "quux", "piep miau"));
assert_se(t = table_new_raw(4));
assert_se(table_add_many(t,
TABLE_HEADER, "foo bar",
TABLE_HEADER, "quux",
TABLE_HEADER, "piep miau",
TABLE_HEADER, "asdf",
TABLE_SET_JSON_FIELD_NAME, "asdf-custom") >= 0);
assert_se(table_set_json_field_name(t, 2, "zzz") >= 0);
assert_se(table_add_many(t,
TABLE_STRING, "v1",
TABLE_UINT64, UINT64_C(4711),
TABLE_BOOLEAN, true) >= 0);
TABLE_BOOLEAN, true,
TABLE_EMPTY) >= 0);
assert_se(table_add_many(t,
TABLE_STRV, STRV_MAKE("a", "b", "c"),
TABLE_EMPTY,
TABLE_MODE, 0755) >= 0);
TABLE_MODE, 0755,
TABLE_EMPTY) >= 0);
assert_se(table_to_json(t, &v) >= 0);
@@ -384,15 +393,49 @@ TEST(json) {
SD_JSON_BUILD_OBJECT(
SD_JSON_BUILD_PAIR("foo_bar", JSON_BUILD_CONST_STRING("v1")),
SD_JSON_BUILD_PAIR("quux", SD_JSON_BUILD_UNSIGNED(4711)),
SD_JSON_BUILD_PAIR("zzz", SD_JSON_BUILD_BOOLEAN(true))),
SD_JSON_BUILD_PAIR("zzz", SD_JSON_BUILD_BOOLEAN(true)),
SD_JSON_BUILD_PAIR("asdf-custom", SD_JSON_BUILD_NULL)),
SD_JSON_BUILD_OBJECT(
SD_JSON_BUILD_PAIR("foo_bar", SD_JSON_BUILD_STRV(STRV_MAKE("a", "b", "c"))),
SD_JSON_BUILD_PAIR("quux", SD_JSON_BUILD_NULL),
SD_JSON_BUILD_PAIR("zzz", SD_JSON_BUILD_UNSIGNED(0755))))) >= 0);
SD_JSON_BUILD_PAIR("zzz", SD_JSON_BUILD_UNSIGNED(0755)),
SD_JSON_BUILD_PAIR("asdf-custom", SD_JSON_BUILD_NULL)))) >= 0);
assert_se(sd_json_variant_equal(v, w));
}
TEST(json_mangling) {
static const struct {
const char *arg;
const char *exp;
} cases[] = {
/* Not Mangled */
{ "foo", "foo" },
{ "foo_bar", "foo_bar" },
{ "fooBar", "fooBar" },
{ "fooBar123", "fooBar123" },
{ "foo_bar123", "foo_bar123" },
{ ALPHANUMERICAL, ALPHANUMERICAL },
{ "_123", "_123" },
/* Mangled */
{ "Foo Bar", "foo_bar" },
{ "Foo-Bar", "foo_bar" },
{ "Foo@Bar", "foo_bar" },
{ "Foo (Bar)", "foo__bar_"},
{ "MixedCase ALLCAPS", "mixedCase_ALLCAPS" },
{ "_X", "_x" },
{ "_Foo", "_foo" },
};
FOREACH_ELEMENT(i, cases) {
_cleanup_free_ char *ret = NULL;
assert_se(ret = table_mangle_to_json_field_name(i->arg));
printf("\"%s\" -> \"%s\"\n", i->arg, ret);
assert_se(streq(ret, i->exp));
}
}
TEST(table) {
_cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *formatted = NULL;
@@ -544,6 +587,7 @@ TEST(vertical) {
assert_se(table_add_many(t,
TABLE_FIELD, "pfft aa", TABLE_STRING, "foo",
TABLE_FIELD, "uuu o", TABLE_SIZE, UINT64_C(1024),
TABLE_FIELD, "quux", TABLE_STRING, "asdf", TABLE_SET_JSON_FIELD_NAME, "custom-quux",
TABLE_FIELD, "lllllllllllo", TABLE_STRING, "jjjjjjjjjjjjjjjjj") >= 0);
assert_se(table_set_json_field_name(t, 1, "dimpfelmoser") >= 0);
@@ -553,6 +597,7 @@ TEST(vertical) {
assert_se(streq(formatted,
" pfft aa: foo\n"
" uuu o: 1K\n"
" quux: asdf\n"
"lllllllllllo: jjjjjjjjjjjjjjjjj\n"));
_cleanup_(sd_json_variant_unrefp) sd_json_variant *a = NULL, *b = NULL;
@@ -561,6 +606,7 @@ TEST(vertical) {
assert_se(sd_json_build(&b, SD_JSON_BUILD_OBJECT(
SD_JSON_BUILD_PAIR("pfft_aa", SD_JSON_BUILD_STRING("foo")),
SD_JSON_BUILD_PAIR("dimpfelmoser", SD_JSON_BUILD_UNSIGNED(1024)),
SD_JSON_BUILD_PAIR("custom-quux", SD_JSON_BUILD_STRING("asdf")),
SD_JSON_BUILD_PAIR("lllllllllllo", SD_JSON_BUILD_STRING("jjjjjjjjjjjjjjjjj")))) >= 0);
assert_se(sd_json_variant_equal(a, b));