qdict: Introduce qdict_rename_keys()
A few block drivers will need to rename .bdrv_create options for their QAPIfication, so let's have a helper function for that. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
37974a97d4
commit
bcebf102cc
@ -81,4 +81,10 @@ QObject *qdict_crumple(const QDict *src, Error **errp);
|
||||
|
||||
void qdict_join(QDict *dest, QDict *src, bool overwrite);
|
||||
|
||||
typedef struct QDictRenames {
|
||||
const char *from;
|
||||
const char *to;
|
||||
} QDictRenames;
|
||||
bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
|
||||
|
||||
#endif /* QDICT_H */
|
||||
|
@ -1072,3 +1072,37 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite)
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qdict_rename_keys(): Rename keys in qdict according to the replacements
|
||||
* specified in the array renames. The array must be terminated by an entry
|
||||
* with from = NULL.
|
||||
*
|
||||
* The renames are performed individually in the order of the array, so entries
|
||||
* may be renamed multiple times and may or may not conflict depending on the
|
||||
* order of the renames array.
|
||||
*
|
||||
* Returns true for success, false in error cases.
|
||||
*/
|
||||
bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp)
|
||||
{
|
||||
QObject *qobj;
|
||||
|
||||
while (renames->from) {
|
||||
if (qdict_haskey(qdict, renames->from)) {
|
||||
if (qdict_haskey(qdict, renames->to)) {
|
||||
error_setg(errp, "'%s' and its alias '%s' can't be used at the "
|
||||
"same time", renames->to, renames->from);
|
||||
return false;
|
||||
}
|
||||
|
||||
qobj = qdict_get(qdict, renames->from);
|
||||
qobject_incref(qobj);
|
||||
qdict_put_obj(qdict, renames->to, qobj);
|
||||
qdict_del(qdict, renames->from);
|
||||
}
|
||||
|
||||
renames++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -665,6 +665,133 @@ static void qdict_crumple_test_empty(void)
|
||||
QDECREF(dst);
|
||||
}
|
||||
|
||||
static int qdict_count_entries(QDict *dict)
|
||||
{
|
||||
const QDictEntry *e;
|
||||
int count = 0;
|
||||
|
||||
for (e = qdict_first(dict); e; e = qdict_next(dict, e)) {
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void qdict_rename_keys_test(void)
|
||||
{
|
||||
QDict *dict = qdict_new();
|
||||
QDict *copy;
|
||||
QDictRenames *renames;
|
||||
Error *local_err = NULL;
|
||||
|
||||
qdict_put_str(dict, "abc", "foo");
|
||||
qdict_put_str(dict, "abcdef", "bar");
|
||||
qdict_put_int(dict, "number", 42);
|
||||
qdict_put_bool(dict, "flag", true);
|
||||
qdict_put_null(dict, "nothing");
|
||||
|
||||
/* Empty rename list */
|
||||
renames = (QDictRenames[]) {
|
||||
{ NULL, "this can be anything" }
|
||||
};
|
||||
copy = qdict_clone_shallow(dict);
|
||||
qdict_rename_keys(copy, renames, &error_abort);
|
||||
|
||||
g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
|
||||
g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
|
||||
g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
|
||||
g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
|
||||
g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
|
||||
g_assert_cmpint(qdict_count_entries(copy), ==, 5);
|
||||
|
||||
QDECREF(copy);
|
||||
|
||||
/* Simple rename of all entries */
|
||||
renames = (QDictRenames[]) {
|
||||
{ "abc", "str1" },
|
||||
{ "abcdef", "str2" },
|
||||
{ "number", "int" },
|
||||
{ "flag", "bool" },
|
||||
{ "nothing", "null" },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
copy = qdict_clone_shallow(dict);
|
||||
qdict_rename_keys(copy, renames, &error_abort);
|
||||
|
||||
g_assert(!qdict_haskey(copy, "abc"));
|
||||
g_assert(!qdict_haskey(copy, "abcdef"));
|
||||
g_assert(!qdict_haskey(copy, "number"));
|
||||
g_assert(!qdict_haskey(copy, "flag"));
|
||||
g_assert(!qdict_haskey(copy, "nothing"));
|
||||
|
||||
g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo");
|
||||
g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar");
|
||||
g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42);
|
||||
g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true);
|
||||
g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL);
|
||||
g_assert_cmpint(qdict_count_entries(copy), ==, 5);
|
||||
|
||||
QDECREF(copy);
|
||||
|
||||
/* Renames are processed top to bottom */
|
||||
renames = (QDictRenames[]) {
|
||||
{ "abc", "tmp" },
|
||||
{ "abcdef", "abc" },
|
||||
{ "number", "abcdef" },
|
||||
{ "flag", "number" },
|
||||
{ "nothing", "flag" },
|
||||
{ "tmp", "nothing" },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
copy = qdict_clone_shallow(dict);
|
||||
qdict_rename_keys(copy, renames, &error_abort);
|
||||
|
||||
g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo");
|
||||
g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar");
|
||||
g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42);
|
||||
g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true);
|
||||
g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL);
|
||||
g_assert(!qdict_haskey(copy, "tmp"));
|
||||
g_assert_cmpint(qdict_count_entries(copy), ==, 5);
|
||||
|
||||
QDECREF(copy);
|
||||
|
||||
/* Conflicting rename */
|
||||
renames = (QDictRenames[]) {
|
||||
{ "abcdef", "abc" },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
copy = qdict_clone_shallow(dict);
|
||||
qdict_rename_keys(copy, renames, &local_err);
|
||||
|
||||
g_assert(local_err != NULL);
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
|
||||
g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
|
||||
g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
|
||||
g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
|
||||
g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
|
||||
g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
|
||||
g_assert_cmpint(qdict_count_entries(copy), ==, 5);
|
||||
|
||||
QDECREF(copy);
|
||||
|
||||
/* Renames in an empty dict */
|
||||
renames = (QDictRenames[]) {
|
||||
{ "abcdef", "abc" },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
|
||||
QDECREF(dict);
|
||||
dict = qdict_new();
|
||||
|
||||
qdict_rename_keys(dict, renames, &error_abort);
|
||||
g_assert(qdict_first(dict) == NULL);
|
||||
|
||||
QDECREF(dict);
|
||||
}
|
||||
|
||||
static void qdict_crumple_test_bad_inputs(void)
|
||||
{
|
||||
QDict *src;
|
||||
@ -880,6 +1007,8 @@ int main(int argc, char **argv)
|
||||
g_test_add_func("/public/crumple/bad_inputs",
|
||||
qdict_crumple_test_bad_inputs);
|
||||
|
||||
g_test_add_func("/public/rename_keys", qdict_rename_keys_test);
|
||||
|
||||
/* The Big one */
|
||||
if (g_test_slow()) {
|
||||
g_test_add_func("/stress/test", qdict_stress_test);
|
||||
|
Loading…
Reference in New Issue
Block a user