diff --git a/include/asterisk/json.h b/include/asterisk/json.h index ce0baa452740d94b7f8e2f68daa512fbbc65a0bb..3f485d153d5cbc7e8ab185c8eddb333f67bb9291 100644 --- a/include/asterisk/json.h +++ b/include/asterisk/json.h @@ -538,6 +538,15 @@ int ast_json_array_extend(struct ast_json *array, struct ast_json *tail); */ struct ast_json *ast_json_object_create(void); +/*! + * \brief Create a new JSON object using the given variables + * \param variables A list of Asterisk variables + * \param excludes Comma separated string of variable names to exclude (optional) + * \return Newly allocated object. + * \return \c NULL on error. + */ +struct ast_json *ast_json_object_create_vars(const struct ast_variable *variables, const char *excludes); + /*! * \brief Get size of JSON object. * \since 12.0.0 @@ -572,6 +581,14 @@ struct ast_json *ast_json_object_get(struct ast_json *object, const char *key); */ #define ast_json_object_string_get(object, key) ast_json_string_get(ast_json_object_get(object, key)) +/*! + * \brief Get an integer field from a JSON object. + * \param integer JSON integer. + * \return Value of a JSON integer. + * \return 0 if \a integer is not a JSON integer. + */ +#define ast_json_object_integer_get(object, key) ast_json_integer_get(ast_json_object_get(object, key)) + /*! * \brief Set a field in a JSON object. * \since 12.0.0 diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h index 929cbf525659bff84403bb48cf6c64e98a4b5ba8..881cf8975836e65a38d32a6cd17b6e6d9cb4fab8 100644 --- a/include/asterisk/strings.h +++ b/include/asterisk/strings.h @@ -400,6 +400,19 @@ void ast_copy_string(char *dst, const char *src, size_t size), } ) +/*! + * \brief Check if there is an exact match for 'needle' between delimiters in 'haystack'. + * + * \note This will skip extra leading spaces between delimiters. + * + * \param needle The string to search for + * \param haystack The string searched in + * \param delim The haystack delimiter + * + * \return True if an exact match for needle is in haystack, false otherwise + */ +int ast_in_delimited_string(const char *needle, const char *haystack, char delim); + /*! \brief Build a string in a buffer, designed to be called repeatedly diff --git a/main/json.c b/main/json.c index 09101aa61edd0f3721e86e31013e00ed5c2142f8..616b12e67fd790b966ed6ae53f347b288b772d74 100644 --- a/main/json.c +++ b/main/json.c @@ -852,3 +852,22 @@ struct ast_json *ast_json_channel_vars(struct varshead *channelvars) return ret; } + +struct ast_json *ast_json_object_create_vars(const struct ast_variable *variables, const char *excludes) +{ + const struct ast_variable *i; + struct ast_json *obj; + + obj = ast_json_object_create(); + if (!obj) { + return NULL; + } + + for (i = variables; i; i = i->next) { + if (!excludes || !ast_in_delimited_string(i->name, excludes, ',')) { + ast_json_object_set(obj, i->name, ast_json_string_create(i->value)); + } + } + + return obj; +} diff --git a/main/strings.c b/main/strings.c index 0cd4692a20fe6aba6fac5d441404421477a98000..20769fae1133f1b2b4c48d29068d2699f77ab137 100644 --- a/main/strings.c +++ b/main/strings.c @@ -430,3 +430,28 @@ int ast_vector_string_split(struct ast_vector_string *dest, return 0; } + +int ast_in_delimited_string(const char *needle, const char *haystack, char delim) +{ + const char *end; + unsigned long needle_size; + + ast_assert(haystack != NULL); + + if (!needle) { + return 0; + } + + needle_size = strlen(needle); + haystack = ast_skip_blanks(haystack); + + while ((end = strchr(haystack, delim))) { + if (needle_size == end - haystack && !strncmp(haystack, needle, needle_size)) { + return 1; + } + haystack = ast_skip_blanks(end + 1); + } + + return strcmp(haystack, needle) ? 0 : -1; +} + diff --git a/tests/test_json.c b/tests/test_json.c index 8dbb8727923f9f456c266fd78b386242b87d79c4..a14ac3a217c3e055599fb3cda0dbddf19d08df13 100644 --- a/tests/test_json.c +++ b/tests/test_json.c @@ -37,6 +37,7 @@ #include "asterisk.h" +#include "asterisk/config.h" #include "asterisk/json.h" #include "asterisk/module.h" #include "asterisk/test.h" @@ -1090,6 +1091,63 @@ AST_TEST_DEFINE(json_test_object_iter_null) return AST_TEST_PASS; } +AST_TEST_DEFINE(json_test_object_create_vars) +{ + RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); + RAII_VAR(struct ast_variable *, vars, NULL, ast_variables_destroy); + const char *value; + struct ast_variable *new_var; + + switch (cmd) { + case TEST_INIT: + info->name = "object_create_vars"; + info->category = CATEGORY; + info->summary = "Testing JSON object creation initialized using Asterisk variables."; + info->description = "Test JSON abstraction library."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* NULL case */ + ast_test_validate(test, (uut = ast_json_object_create_vars(NULL, NULL))); + ast_test_validate(test, !(value = ast_json_object_string_get(uut, "foo"))); + + ast_test_validate(test, (new_var = ast_variable_new("foo", "bar", ""))); + ast_variable_list_append(&vars, new_var); + ast_test_validate(test, (new_var = ast_variable_new("bar", "baz", ""))); + ast_variable_list_append(&vars, new_var); + + /* Variables case */ + ast_json_unref(uut); + ast_test_validate(test, (uut = ast_json_object_create_vars(vars, NULL))); + ast_test_validate(test, (value = ast_json_object_string_get(uut, "foo"))); + ast_test_validate(test, !strcmp("bar", value)); + ast_test_validate(test, (value = ast_json_object_string_get(uut, "bar"))); + ast_test_validate(test, !strcmp("baz", value)); + + /* Variables with excludes case */ + ast_json_unref(uut); + ast_test_validate(test, (uut = ast_json_object_create_vars(vars, "foo"))); + ast_test_validate(test, !(value = ast_json_object_string_get(uut, "foo"))); + ast_test_validate(test, (value = ast_json_object_string_get(uut, "bar"))); + ast_test_validate(test, !strcmp("baz", value)); + + ast_json_unref(uut); + ast_test_validate(test, (uut = ast_json_object_create_vars(vars, "foo2"))); + ast_test_validate(test, (value = ast_json_object_string_get(uut, "foo"))); + ast_test_validate(test, (value = ast_json_object_string_get(uut, "bar"))); + ast_test_validate(test, !strcmp("baz", value)); + + ast_json_unref(uut); + ast_test_validate(test, (uut = ast_json_object_create_vars(vars, "foobar,baz"))); + ast_test_validate(test, (value = ast_json_object_string_get(uut, "foo"))); + ast_test_validate(test, (value = ast_json_object_string_get(uut, "bar"))); + ast_test_validate(test, !strcmp("baz", value)); + + return AST_TEST_PASS; +} + AST_TEST_DEFINE(json_test_dump_load_string) { RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); @@ -1738,6 +1796,7 @@ static int unload_module(void) AST_TEST_UNREGISTER(json_test_object_null); AST_TEST_UNREGISTER(json_test_object_iter); AST_TEST_UNREGISTER(json_test_object_iter_null); + AST_TEST_UNREGISTER(json_test_object_create_vars); AST_TEST_UNREGISTER(json_test_dump_load_string); AST_TEST_UNREGISTER(json_test_dump_load_str); AST_TEST_UNREGISTER(json_test_dump_str_fail); @@ -1794,6 +1853,7 @@ static int load_module(void) AST_TEST_REGISTER(json_test_object_null); AST_TEST_REGISTER(json_test_object_iter); AST_TEST_REGISTER(json_test_object_iter_null); + AST_TEST_REGISTER(json_test_object_create_vars); AST_TEST_REGISTER(json_test_dump_load_string); AST_TEST_REGISTER(json_test_dump_load_str); AST_TEST_REGISTER(json_test_dump_str_fail); diff --git a/tests/test_strings.c b/tests/test_strings.c index 3d697e59fa4e2a430c6adc7e98589f2f0f956083..63ac85647b7ab6e352794c50ec9b24a5b3eed034 100644 --- a/tests/test_strings.c +++ b/tests/test_strings.c @@ -620,6 +620,120 @@ AST_TEST_DEFINE(temp_strings) return AST_TEST_PASS; } +AST_TEST_DEFINE(in_delimited_string) +{ + switch (cmd) { + case TEST_INIT: + info->name = "in_delimited_string"; + info->category = "/main/strings/"; + info->summary = "Test ast_in_delimited_string"; + info->description = info->summary; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* Single letter */ + ast_test_validate(test, ast_in_delimited_string("a", "a,b", ',')); + ast_test_validate(test, ast_in_delimited_string("b", "a,b", ',')); + + ast_test_validate(test, !ast_in_delimited_string("c", "a,b", ',')); + ast_test_validate(test, !ast_in_delimited_string("aa", "a,b", ',')); + ast_test_validate(test, !ast_in_delimited_string("bb", "a,b", ',')); + ast_test_validate(test, !ast_in_delimited_string("a,", "a,b", ',')); + ast_test_validate(test, !ast_in_delimited_string(",b", "a,b", ',')); + ast_test_validate(test, !ast_in_delimited_string("a,b", "a,b", ',')); + + /* Bad delimiter (ends up being just a strcmp) */ + ast_test_validate(test, !ast_in_delimited_string("a", "a,b", '#')); + ast_test_validate(test, !ast_in_delimited_string("b", "a,b", '#')); + + ast_test_validate(test, !ast_in_delimited_string("c", "a,b", '#')); + ast_test_validate(test, !ast_in_delimited_string("aa", "a,b", '#')); + ast_test_validate(test, !ast_in_delimited_string("bb", "a,b", '#')); + ast_test_validate(test, !ast_in_delimited_string("a,", "a,b", '#')); + ast_test_validate(test, !ast_in_delimited_string(",b", "a,b", '#')); + + ast_test_validate(test, ast_in_delimited_string("a,b", "a,b", '#')); + + /* Multi letter */ + ast_test_validate(test, ast_in_delimited_string("abc", "abc,def", ',')); + ast_test_validate(test, ast_in_delimited_string("def", "abc,def", ',')); + + ast_test_validate(test, !ast_in_delimited_string("a", "abc,def", ',')); + ast_test_validate(test, !ast_in_delimited_string("b", "abc,def", ',')); + ast_test_validate(test, !ast_in_delimited_string("c", "abc,def", ',')); + + ast_test_validate(test, !ast_in_delimited_string("d", "abc,def", ',')); + ast_test_validate(test, !ast_in_delimited_string("e", "abc,def", ',')); + ast_test_validate(test, !ast_in_delimited_string("f", "abc,def", ',')); + + ast_test_validate(test, !ast_in_delimited_string("abc,", "abc,def", ',')); + ast_test_validate(test, !ast_in_delimited_string(",def", "abc,def", ',')); + ast_test_validate(test, !ast_in_delimited_string("abc,def", "abc,def", ',')); + + /* Embedded */ + ast_test_validate(test, ast_in_delimited_string("abc", "abcdef,abc", ',')); + ast_test_validate(test, ast_in_delimited_string("abcdef", "abcdef,abc", ',')); + + ast_test_validate(test, !ast_in_delimited_string("abc", "abcdef,def", ',')); + ast_test_validate(test, !ast_in_delimited_string("def", "abcdef,abc", ',')); + ast_test_validate(test, !ast_in_delimited_string("def", "abcdefghi,abc", ',')); + + /* NULL and empty values */ + ast_test_validate(test, !ast_in_delimited_string(NULL, "abc,def", ',')); + + ast_test_validate(test, ast_in_delimited_string("abc", ",abc,def", ',')); + ast_test_validate(test, ast_in_delimited_string("abc", "abc,def,", ',')); + ast_test_validate(test, ast_in_delimited_string("abc", "abc,,def,", ',')); + ast_test_validate(test, ast_in_delimited_string("def", "abc,,def", ',')); + ast_test_validate(test, ast_in_delimited_string("def", ",abc,,def,", ',')); + + ast_test_validate(test, ast_in_delimited_string("", ",abc,def", ',')); + ast_test_validate(test, ast_in_delimited_string("", "abc,def,", ',')); + ast_test_validate(test, ast_in_delimited_string("", "abc,,def,", ',')); + ast_test_validate(test, !ast_in_delimited_string("", "abc,def", ',')); + + /* Multi word */ + ast_test_validate(test, ast_in_delimited_string("abc", "abc,def,ghi", ',')); + ast_test_validate(test, ast_in_delimited_string("def", "abc,def,ghi", ',')); + ast_test_validate(test, ast_in_delimited_string("ghi", "abc,def,ghi", ',')); + + ast_test_validate(test, !ast_in_delimited_string("a", "abc,def,ghi", ',')); + ast_test_validate(test, !ast_in_delimited_string("d", "abc,def,ghi", ',')); + ast_test_validate(test, !ast_in_delimited_string("g", "abc,def,ghi", ',')); + + ast_test_validate(test, !ast_in_delimited_string("ab", "abc,def,ghi", ',')); + ast_test_validate(test, !ast_in_delimited_string("de", "abc,def,ghi", ',')); + ast_test_validate(test, !ast_in_delimited_string("gh", "abc,def,ghi", ',')); + + + /* With leading spaces */ + ast_test_validate(test, ast_in_delimited_string("abc", " abc", ',')); + ast_test_validate(test, ast_in_delimited_string("abc", " abc, def", ',')); + ast_test_validate(test, ast_in_delimited_string("def", " abc, def", ',')); + ast_test_validate(test, ast_in_delimited_string("abc", " abc, def, ghi", ',')); + ast_test_validate(test, ast_in_delimited_string("def", " abc, def, ghi", ',')); + ast_test_validate(test, ast_in_delimited_string("ghi", " abc, def, ghi", ',')); + + ast_test_validate(test, ast_in_delimited_string("abc", " abc", ',')); + ast_test_validate(test, ast_in_delimited_string("abc", " abc, def", ',')); + ast_test_validate(test, ast_in_delimited_string("def", " abc, def", ',')); + ast_test_validate(test, ast_in_delimited_string("abc", " abc, def, ghi", ',')); + ast_test_validate(test, ast_in_delimited_string("def", " abc, def, ghi", ',')); + ast_test_validate(test, ast_in_delimited_string("ghi", " abc, def, ghi", ',')); + + /* With leading spaces and space as a delimiter */ + ast_test_validate(test, ast_in_delimited_string("abc", " abc", ' ')); + ast_test_validate(test, ast_in_delimited_string("abc", " abc def", ' ')); + ast_test_validate(test, ast_in_delimited_string("def", " abc def", ' ')); + ast_test_validate(test, ast_in_delimited_string("abc", " abc def ghi", ' ')); + ast_test_validate(test, ast_in_delimited_string("def", " abc def ghi", ' ')); + ast_test_validate(test, ast_in_delimited_string("ghi", " abc def ghi", ' ')); + + return AST_TEST_PASS; +} + static int unload_module(void) { AST_TEST_UNREGISTER(str_test); @@ -630,6 +744,7 @@ static int unload_module(void) AST_TEST_UNREGISTER(escape_test); AST_TEST_UNREGISTER(strings_match); AST_TEST_UNREGISTER(temp_strings); + AST_TEST_UNREGISTER(in_delimited_string); return 0; } @@ -643,6 +758,7 @@ static int load_module(void) AST_TEST_REGISTER(escape_test); AST_TEST_REGISTER(strings_match); AST_TEST_REGISTER(temp_strings); + AST_TEST_REGISTER(in_delimited_string); return AST_MODULE_LOAD_SUCCESS; }