diff options
author | Maxim Anisimov <maxim.anisimov.ua@gmail.com> | 2019-08-08 11:12:33 +0300 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2019-08-28 18:17:55 +0200 |
commit | d1ac5ca16bfbc2475eb379b48e981e035f908b50 (patch) | |
tree | f94a5b4fb4fa6bcf88834c41ced191a6f9a994c9 | |
parent | b0ec3030a3e5519d75c2268fa20062edd2dacf4e (diff) |
mmcli: add json output support
Signed-off-by: Maxim Anisimov <maxim.anisimov.ua@gmail.com>
-rw-r--r-- | cli/mmcli-output.c | 119 | ||||
-rw-r--r-- | cli/mmcli-output.h | 1 | ||||
-rw-r--r-- | cli/mmcli.c | 16 |
3 files changed, 133 insertions, 3 deletions
diff --git a/cli/mmcli-output.c b/cli/mmcli-output.c index 7699f25e..5b2af008 100644 --- a/cli/mmcli-output.c +++ b/cli/mmcli-output.c @@ -1072,6 +1072,119 @@ dump_output_list_keyvalue (MmcF field) } /******************************************************************************/ +/* Json-friendly output */ + +static gint +list_sort_by_keys (const OutputItem *item_a, + const OutputItem *item_b) +{ + return g_strcmp0(field_infos[item_a->field].key, field_infos[item_b->field].key); +} + +static void +dump_output_json (void) +{ + GList *l; + MmcF current_field = MMC_F_UNKNOWN; + gchar **current_path = NULL; + guint cur_dlen = 0; + GRegex *escape_regex; + + output_items = g_list_sort (output_items, (GCompareFunc) list_sort_by_keys); + escape_regex = g_regex_new("'", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL); + + g_print("{"); + for (l = output_items; l; l = g_list_next (l)) { + OutputItem *item_l = (OutputItem *)(l->data); + + if (current_field != item_l->field) { + guint new_dlen; + guint iter = 0; + gchar **new_path; + + new_path = g_strsplit(field_infos[item_l->field].key, ".", -1); + new_dlen = g_strv_length(new_path) - 1; + if (current_path) { + guint min_dlen = MIN (cur_dlen, new_dlen); + while(iter < min_dlen && g_strcmp0(current_path[iter], new_path[iter]) == 0) + iter++; + + g_strfreev(current_path); + + if (iter < min_dlen || new_dlen < cur_dlen) + for(min_dlen = iter; min_dlen < cur_dlen; min_dlen++) + g_print("}"); + + g_print(","); + } + + while(iter < new_dlen) + g_print("\"%s\":{", new_path[iter++]); + + cur_dlen = new_dlen; + current_path = new_path; + current_field = item_l->field; + } else { + g_print(","); + } + if (item_l->type == VALUE_TYPE_SINGLE) { + OutputItemSingle *single = (OutputItemSingle *) item_l; + gchar *escaped = NULL; + + if (single->value) + escaped = g_regex_replace_literal(escape_regex, single->value, -1, 0, "\"", 0, NULL); + + g_print ("\"%s\":\"%s\"", current_path[cur_dlen], escaped ? escaped : "--"); + g_free (escaped); + } else if (item_l->type == VALUE_TYPE_MULTIPLE) { + OutputItemMultiple *multiple = (OutputItemMultiple *) item_l; + guint i, n = multiple->values ? g_strv_length (multiple->values) : 0; + + g_print ("\"%s\":[", current_path[cur_dlen]); + for(i = 0; i < n; i++) { + gchar *escaped = g_regex_replace_literal(escape_regex, multiple->values[i], -1, 0, "\"", 0, NULL); + g_print("\"%s\"", escaped); + if (i < n - 1) + g_print(","); + g_free (escaped); + } + g_print("]"); + } else { + g_assert_not_reached (); + } + } + while(cur_dlen--) + g_print("}"); + g_print("}\n"); + if (current_path) + g_strfreev(current_path); + g_regex_unref(escape_regex); +} + +static void +dump_output_list_json (MmcF field) +{ + GList *l; + g_assert (field != MMC_F_UNKNOWN); + g_print("{\"%s\":[", field_infos[field].key); + for (l = output_items; l; l = g_list_next (l)) { + OutputItem *item_l; + OutputItemListitem *listitem; + item_l = (OutputItem *)(l->data); + g_assert (item_l->type == VALUE_TYPE_LISTITEM); + listitem = (OutputItemListitem *)item_l; + g_assert (listitem->value); + + /* All items must be of same type */ + g_assert_cmpint (item_l->field, ==, field); + g_print("\"%s\"", listitem->value); + if (g_list_next(l)) + g_print(","); + } + g_print("]}\n"); +} + +/******************************************************************************/ /* Dump output */ void @@ -1086,6 +1199,9 @@ mmcli_output_dump (void) case MMC_OUTPUT_TYPE_KEYVALUE: dump_output_keyvalue (); break; + case MMC_OUTPUT_TYPE_JSON: + dump_output_json (); + break; } g_list_free_full (output_items, (GDestroyNotify) output_item_free); @@ -1106,6 +1222,9 @@ mmcli_output_list_dump (MmcF field) case MMC_OUTPUT_TYPE_KEYVALUE: dump_output_list_keyvalue (field); break; + case MMC_OUTPUT_TYPE_JSON: + dump_output_list_json (field); + break; } g_list_free_full (output_items, (GDestroyNotify) output_item_free); diff --git a/cli/mmcli-output.h b/cli/mmcli-output.h index 024d2440..135d429a 100644 --- a/cli/mmcli-output.h +++ b/cli/mmcli-output.h @@ -285,6 +285,7 @@ typedef enum { MMC_OUTPUT_TYPE_NONE, MMC_OUTPUT_TYPE_HUMAN, MMC_OUTPUT_TYPE_KEYVALUE, + MMC_OUTPUT_TYPE_JSON } MmcOutputType; void mmcli_output_set (MmcOutputType type); diff --git a/cli/mmcli.c b/cli/mmcli.c index 6953672d..ab08f33d 100644 --- a/cli/mmcli.c +++ b/cli/mmcli.c @@ -45,6 +45,7 @@ static GCancellable *cancellable; /* Context */ static gboolean output_keyvalue_flag; +static gboolean output_json_flag; static gboolean verbose_flag; static gboolean version_flag; static gboolean async_flag; @@ -55,6 +56,10 @@ static GOptionEntry main_entries[] = { "Run action with machine-friendly key-value output", NULL }, + { "output-json", 'J', 0, G_OPTION_ARG_NONE, &output_json_flag, + "Run action with machine-friendly json output", + NULL + }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag, "Run action with verbose logs", NULL @@ -237,14 +242,19 @@ main (gint argc, gchar **argv) g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, log_handler, NULL); /* Setup output */ - if (output_keyvalue_flag) { + if (output_keyvalue_flag && output_json_flag) { + g_printerr ("error: only one output type supported at the same time\n"); + exit (EXIT_FAILURE); + } + if (output_keyvalue_flag || output_json_flag) { if (verbose_flag) { g_printerr ("error: cannot set verbose output in keyvalue output type\n"); exit (EXIT_FAILURE); } - mmcli_output_set (MMC_OUTPUT_TYPE_KEYVALUE); - } else + mmcli_output_set (output_keyvalue_flag ? MMC_OUTPUT_TYPE_KEYVALUE : MMC_OUTPUT_TYPE_JSON); + } else { mmcli_output_set (MMC_OUTPUT_TYPE_HUMAN); + } /* Setup signals */ signal (SIGINT, signals_handler); |