1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2018, Olivier MATZ <zer0@droids-corp.org>
11 #include <ecoli_string.h>
12 #include <ecoli_malloc.h>
13 #include <ecoli_keyval.h>
14 #include <ecoli_node.h>
15 #include <ecoli_log.h>
16 #include <ecoli_test.h>
17 #include <ecoli_config.h>
19 EC_LOG_TYPE_REGISTER(config);
21 #ifndef EC_COUNT_OF //XXX
22 #define EC_COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
23 ((size_t)(!(sizeof(x) % sizeof(0[x])))))
27 __ec_config_dump(FILE *out, const char *key, const struct ec_config *config,
30 ec_config_dict_validate(const struct ec_keyval *dict,
31 const struct ec_config_schema *schema,
34 /* return ec_value type as a string */
36 ec_config_type_str(enum ec_config_type type)
39 case EC_CONFIG_TYPE_BOOL: return "bool";
40 case EC_CONFIG_TYPE_INT64: return "int64";
41 case EC_CONFIG_TYPE_UINT64: return "uint64";
42 case EC_CONFIG_TYPE_STRING: return "string";
43 case EC_CONFIG_TYPE_NODE: return "node";
44 case EC_CONFIG_TYPE_LIST: return "list";
45 case EC_CONFIG_TYPE_DICT: return "dict";
46 default: return "unknown";
51 __ec_config_schema_validate(const struct ec_config_schema *schema,
52 size_t schema_len, enum ec_config_type type)
57 if (type == EC_CONFIG_TYPE_LIST) {
58 if (schema[0].key != NULL) {
60 EC_LOG(EC_LOG_ERR, "list schema key must be NULL\n");
63 } else if (type == EC_CONFIG_TYPE_DICT) {
64 for (i = 0; i < schema_len; i++) {
65 if (schema[i].key == NULL) {
67 EC_LOG(EC_LOG_ERR, "dict schema key should not be NULL\n");
73 EC_LOG(EC_LOG_ERR, "invalid schema type\n");
77 for (i = 0; i < schema_len; i++) {
78 /* check for duplicate name if more than one element */
79 for (j = i + 1; j < schema_len; j++) {
80 if (!strcmp(schema[i].key, schema[j].key)) {
83 "duplicate key <%s> in schema\n",
89 switch (schema[i].type) {
90 case EC_CONFIG_TYPE_BOOL:
91 case EC_CONFIG_TYPE_INT64:
92 case EC_CONFIG_TYPE_UINT64:
93 case EC_CONFIG_TYPE_STRING:
94 case EC_CONFIG_TYPE_NODE:
95 if (schema[i].subschema != NULL ||
96 schema[i].subschema_len != 0) {
99 "key <%s> should not have subtype/subschema\n",
104 case EC_CONFIG_TYPE_LIST:
105 if (schema[i].subschema == NULL ||
106 schema[i].subschema_len != 1) {
109 "key <%s> must have subschema of length 1\n",
114 case EC_CONFIG_TYPE_DICT:
115 if (schema[i].subschema == NULL ||
116 schema[i].subschema_len == 0) {
119 "key <%s> must have subschema\n",
125 EC_LOG(EC_LOG_ERR, "invalid type for key <%s>\n",
131 if (schema[i].subschema == NULL)
134 ret = __ec_config_schema_validate(schema[i].subschema,
135 schema[i].subschema_len,
138 EC_LOG(EC_LOG_ERR, "cannot parse subschema %s%s\n",
139 schema[i].key ? "key=" : "",
140 schema[i].key ? : "");
149 ec_config_schema_validate(const struct ec_config_schema *schema,
152 return __ec_config_schema_validate(schema, schema_len,
153 EC_CONFIG_TYPE_DICT);
157 __ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema,
158 size_t schema_len, size_t indent)
162 for (i = 0; i < schema_len; i++) {
163 fprintf(out, "%*s" "%s%s(%s): %s\n",
165 schema[i].key ? : "",
166 schema[i].key ? " ": "",
167 ec_config_type_str(schema[i].type),
169 if (schema[i].subschema == NULL)
171 __ec_config_schema_dump(out, schema[i].subschema,
172 schema[i].subschema_len, indent + 1);
177 ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema,
180 fprintf(out, "------------------- schema dump:\n");
182 if (schema == NULL || schema_len == 0) {
183 fprintf(out, "no schema\n");
187 __ec_config_schema_dump(out, schema, schema_len, 0);
191 ec_config_bool(bool boolean)
193 struct ec_config *value = NULL;
195 value = ec_calloc(1, sizeof(*value));
199 value->type = EC_CONFIG_TYPE_BOOL;
200 value->boolean = boolean;
206 ec_config_i64(int64_t i64)
208 struct ec_config *value = NULL;
210 value = ec_calloc(1, sizeof(*value));
214 value->type = EC_CONFIG_TYPE_INT64;
221 ec_config_u64(uint64_t u64)
223 struct ec_config *value = NULL;
225 value = ec_calloc(1, sizeof(*value));
229 value->type = EC_CONFIG_TYPE_UINT64;
235 /* duplicate string */
237 ec_config_string(const char *string)
239 struct ec_config *value = NULL;
245 s = ec_strdup(string);
249 value = ec_calloc(1, sizeof(*value));
253 value->type = EC_CONFIG_TYPE_STRING;
264 /* "consume" the node */
266 ec_config_node(struct ec_node *node)
268 struct ec_config *value = NULL;
273 value = ec_calloc(1, sizeof(*value));
277 value->type = EC_CONFIG_TYPE_NODE;
291 struct ec_config *value = NULL;
292 struct ec_keyval *dict = NULL;
298 value = ec_calloc(1, sizeof(*value));
302 value->type = EC_CONFIG_TYPE_DICT;
308 ec_keyval_free(dict);
316 struct ec_config *value = NULL;
318 value = ec_calloc(1, sizeof(*value));
322 value->type = EC_CONFIG_TYPE_LIST;
323 TAILQ_INIT(&value->list);
329 ec_config_set_schema(struct ec_config *dict,
330 const struct ec_config_schema *schema,
333 if (dict->type != EC_CONFIG_TYPE_DICT) {
338 if (ec_config_schema_validate(schema, schema_len) < 0)
341 dict->schema = schema;
342 dict->schema_len = schema_len;
350 const struct ec_config_schema *
351 ec_config_schema_lookup(const struct ec_config_schema *schema,
352 size_t schema_len, const char *key,
353 enum ec_config_type type)
357 for (i = 0; i < schema_len; i++) {
358 if (!strcmp(key, schema[i].key) &&
359 type == schema[i].type)
368 ec_config_free(struct ec_config *value)
373 switch (value->type) {
374 case EC_CONFIG_TYPE_STRING:
375 ec_free(value->string);
377 case EC_CONFIG_TYPE_NODE:
378 ec_node_free(value->node);
380 case EC_CONFIG_TYPE_LIST:
381 while (!TAILQ_EMPTY(&value->list)) {
383 v = TAILQ_FIRST(&value->list);
384 TAILQ_REMOVE(&value->list, v, next);
388 case EC_CONFIG_TYPE_DICT:
389 ec_keyval_free(value->dict);
399 ec_config_list_cmp(const struct ec_config_list *list1,
400 const struct ec_config_list *list2)
402 const struct ec_config *v1, *v2;
404 for (v1 = TAILQ_FIRST(list1), v2 = TAILQ_FIRST(list2);
405 v1 != NULL && v2 != NULL;
406 v1 = TAILQ_NEXT(v1, next), v2 = TAILQ_NEXT(v2, next)) {
407 if (ec_config_cmp(v1, v2))
410 if (v1 != NULL || v2 != NULL)
416 /* XXX -> ec_keyval_cmp() */
418 ec_config_dict_cmp(const struct ec_keyval *d1,
419 const struct ec_keyval *d2)
421 const struct ec_config *v1, *v2;
422 struct ec_keyval_iter *iter = NULL;
425 if (ec_keyval_len(d1) != ec_keyval_len(d2))
428 for (iter = ec_keyval_iter(d1);
429 ec_keyval_iter_valid(iter);
430 ec_keyval_iter_next(iter)) {
431 key = ec_keyval_iter_get_key(iter);
432 v1 = ec_keyval_iter_get_val(iter);
433 v2 = ec_keyval_get(d2, key);
435 if (ec_config_cmp(v1, v2))
439 ec_keyval_iter_free(iter);
443 ec_keyval_iter_free(iter);
448 ec_config_cmp(const struct ec_config *value1,
449 const struct ec_config *value2)
451 if (value1->type != value2->type)
454 switch (value1->type) {
455 case EC_CONFIG_TYPE_BOOL:
456 if (value1->boolean == value2->boolean)
458 case EC_CONFIG_TYPE_INT64:
459 if (value1->i64 == value2->i64)
461 case EC_CONFIG_TYPE_UINT64:
462 if (value1->u64 == value2->u64)
464 case EC_CONFIG_TYPE_STRING:
465 if (!strcmp(value1->string, value2->string))
467 case EC_CONFIG_TYPE_NODE:
468 if (value1->node == value2->node)
470 case EC_CONFIG_TYPE_LIST:
471 return ec_config_list_cmp(&value1->list, &value2->list);
472 case EC_CONFIG_TYPE_DICT:
473 return ec_config_dict_cmp(value1->dict, value2->dict);
482 ec_config_list_validate(const struct ec_config_list *list,
483 const struct ec_config_schema *sch)
485 const struct ec_config *value;
487 TAILQ_FOREACH(value, list, next) {
488 if (value->type != sch->type) {
493 if (value->type == EC_CONFIG_TYPE_LIST) {
494 if (ec_config_list_validate(&value->list,
497 } else if (value->type == EC_CONFIG_TYPE_DICT) {
498 if (ec_config_dict_validate(value->dict,
499 sch->subschema, sch->subschema_len) < 0)
508 ec_config_dict_validate(const struct ec_keyval *dict,
509 const struct ec_config_schema *schema,
512 const struct ec_config *value;
513 struct ec_keyval_iter *iter = NULL;
514 const struct ec_config_schema *sch;
517 for (iter = ec_keyval_iter(dict);
518 ec_keyval_iter_valid(iter);
519 ec_keyval_iter_next(iter)) {
521 key = ec_keyval_iter_get_key(iter);
522 value = ec_keyval_iter_get_val(iter);
523 sch = ec_config_schema_lookup(schema, schema_len,
530 if (value->type == EC_CONFIG_TYPE_LIST) {
531 if (ec_config_list_validate(&value->list,
534 } else if (value->type == EC_CONFIG_TYPE_DICT) {
535 if (ec_config_dict_validate(value->dict,
536 sch->subschema, sch->subschema_len) < 0)
541 ec_keyval_iter_free(iter);
545 ec_keyval_iter_free(iter);
550 ec_config_validate(const struct ec_config *dict)
552 if (dict->type != EC_CONFIG_TYPE_DICT) {
556 if (dict->schema == NULL || dict->schema_len == 0) {
561 if (ec_config_dict_validate(dict->dict, dict->schema,
562 dict->schema_len) < 0)
572 const struct ec_config *
573 ec_config_get(const struct ec_config *config, const char *key)
578 return ec_keyval_get(config->dict, key);
581 /* value is consumed */
582 int ec_config_set(struct ec_config *config, const char *key,
583 struct ec_config *value)
585 void (*free_cb)(struct ec_config *) = ec_config_free;
587 if (config == NULL || key == NULL || value == NULL) {
591 if (config->type != EC_CONFIG_TYPE_DICT) {
596 return ec_keyval_set(config->dict, key, value,
597 (void (*)(void *))free_cb);
600 ec_config_free(value);
604 /* value is consumed */
606 ec_config_add(struct ec_config *list,
607 struct ec_config *value)
609 if (list->type != EC_CONFIG_TYPE_LIST) {
614 TAILQ_INSERT_TAIL(&list->list, value, next);
619 ec_config_free(value);
625 ec_config_list_dump(FILE *out, const struct ec_config_list *list,
628 const struct ec_config *v;
630 fprintf(out, "%*s" "type=list:\n", (int)indent * 4, "");
632 TAILQ_FOREACH(v, list, next) {
633 if (__ec_config_dump(out, NULL, v, indent + 1) < 0)
641 ec_config_dict_dump(FILE *out, const struct ec_keyval *dict,
644 const struct ec_config *value;
645 struct ec_keyval_iter *iter;
648 fprintf(out, "%*s" "type=dict:\n", (int)indent * 4, "");
649 for (iter = ec_keyval_iter(dict);
650 ec_keyval_iter_valid(iter);
651 ec_keyval_iter_next(iter)) {
652 key = ec_keyval_iter_get_key(iter);
653 value = ec_keyval_iter_get_val(iter);
654 if (__ec_config_dump(out, key, value, indent + 1) < 0)
657 ec_keyval_iter_free(iter);
661 ec_keyval_iter_free(iter);
666 __ec_config_dump(FILE *out, const char *key, const struct ec_config *value,
669 char *val_str = NULL;
671 switch (value->type) {
672 case EC_CONFIG_TYPE_BOOL:
674 ec_asprintf(&val_str, "true");
676 ec_asprintf(&val_str, "false");
678 case EC_CONFIG_TYPE_INT64:
679 ec_asprintf(&val_str, "%"PRIu64, value->u64);
681 case EC_CONFIG_TYPE_UINT64:
682 ec_asprintf(&val_str, "%"PRIi64, value->i64);
684 case EC_CONFIG_TYPE_STRING:
685 ec_asprintf(&val_str, "%s", value->string);
687 case EC_CONFIG_TYPE_NODE:
688 ec_asprintf(&val_str, "%p", value->node);
690 case EC_CONFIG_TYPE_LIST:
691 return ec_config_list_dump(out, &value->list, indent);
692 case EC_CONFIG_TYPE_DICT:
693 return ec_config_dict_dump(out, value->dict, indent);
699 /* errno is already set on error */
703 fprintf(out, "%*s" "%s%s%stype=%s val=%s\n", (int)indent * 4, "",
707 ec_config_type_str(value->type), val_str);
718 ec_config_dump(FILE *out, const struct ec_config *config)
720 fprintf(out, "------------------- config dump:\n");
722 if (config == NULL) {
723 fprintf(out, "no config\n");
727 if (__ec_config_dump(out, NULL, config, 0) < 0)
728 fprintf(out, "error while dumping\n");
731 /* LCOV_EXCL_START */
732 static const struct ec_config_schema intlist_elt[] = {
734 .desc = "This is a description for int",
735 .type = EC_CONFIG_TYPE_INT64,
739 static const struct ec_config_schema dict[] = {
742 .desc = "This is a description for int",
743 .type = EC_CONFIG_TYPE_INT64,
747 .desc = "This is a description for int2",
748 .type = EC_CONFIG_TYPE_INT64,
752 static const struct ec_config_schema dictlist_elt[] = {
754 .desc = "This is a description for dict",
755 .type = EC_CONFIG_TYPE_DICT,
757 .subschema_len = EC_COUNT_OF(dict),
761 static const struct ec_config_schema baseconfig[] = {
764 .desc = "This is a description for bool",
765 .type = EC_CONFIG_TYPE_BOOL,
769 .desc = "This is a description for int",
770 .type = EC_CONFIG_TYPE_INT64,
774 .desc = "This is a description for string",
775 .type = EC_CONFIG_TYPE_STRING,
779 .desc = "This is a description for node",
780 .type = EC_CONFIG_TYPE_NODE,
784 .desc = "This is a description for list",
785 .type = EC_CONFIG_TYPE_LIST,
786 .subschema = intlist_elt,
787 .subschema_len = EC_COUNT_OF(intlist_elt),
791 .desc = "This is a description for list",
792 .type = EC_CONFIG_TYPE_LIST,
793 .subschema = dictlist_elt,
794 .subschema_len = EC_COUNT_OF(dictlist_elt),
798 static int ec_config_testcase(void)
800 struct ec_node *node = NULL;
801 struct ec_keyval *dict = NULL;
802 const struct ec_config *value = NULL;
803 struct ec_config *config = NULL, *list = NULL, *subconfig = NULL;
804 //const struct ec_config *pvalue;
808 node = ec_node("empty", EC_NO_ID);
812 if (ec_config_schema_validate(baseconfig,
813 EC_COUNT_OF(baseconfig)) < 0) {
814 EC_LOG(EC_LOG_ERR, "invalid config schema\n");
818 ec_config_schema_dump(stdout, baseconfig, EC_COUNT_OF(baseconfig));
820 config = ec_config_dict();
823 if (ec_config_set_schema(config, baseconfig,
824 EC_COUNT_OF(baseconfig)) < 0)
827 ret = ec_config_set(config, "bool", ec_config_bool(true));
828 testres |= EC_TEST_CHECK(ret == 0, "cannot set boolean");
829 value = ec_config_get(config, "bool");
830 testres |= EC_TEST_CHECK(
832 value->type == EC_CONFIG_TYPE_BOOL &&
833 value->boolean == true,
834 "unexpected boolean value");
836 ret = ec_config_set(config, "int", ec_config_i64(1234));
837 testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
838 value = ec_config_get(config, "int");
839 testres |= EC_TEST_CHECK(
841 value->type == EC_CONFIG_TYPE_INT64 &&
843 "unexpected int value");
845 testres |= EC_TEST_CHECK(
846 ec_config_validate(config) == 0,
847 "cannot validate config\n");
849 ret = ec_config_set(config, "string", ec_config_string("toto"));
850 testres |= EC_TEST_CHECK(ret == 0, "cannot set string");
851 value = ec_config_get(config, "string");
852 testres |= EC_TEST_CHECK(
854 value->type == EC_CONFIG_TYPE_STRING &&
855 !strcmp(value->string, "toto"),
856 "unexpected string value");
858 list = ec_config_list();
862 subconfig = ec_config_dict();
863 if (subconfig == NULL)
866 ret = ec_config_set(subconfig, "int", ec_config_i64(1));
867 testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
868 value = ec_config_get(subconfig, "int");
869 testres |= EC_TEST_CHECK(
871 value->type == EC_CONFIG_TYPE_INT64 &&
873 "unexpected int value");
875 ret = ec_config_set(subconfig, "int2", ec_config_i64(2));
876 testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
877 value = ec_config_get(subconfig, "int2");
878 testres |= EC_TEST_CHECK(
880 value->type == EC_CONFIG_TYPE_INT64 &&
882 "unexpected int value");
884 ret = ec_config_add(list, subconfig);
885 subconfig = NULL; /* freed */
886 testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
888 subconfig = ec_config_dict();
889 if (subconfig == NULL)
892 ret = ec_config_set(subconfig, "int", ec_config_i64(3));
893 testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
894 value = ec_config_get(subconfig, "int");
895 testres |= EC_TEST_CHECK(
897 value->type == EC_CONFIG_TYPE_INT64 &&
899 "unexpected int value");
901 ret = ec_config_set(subconfig, "int2", ec_config_i64(4));
902 testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
903 value = ec_config_get(subconfig, "int2");
904 testres |= EC_TEST_CHECK(
906 value->type == EC_CONFIG_TYPE_INT64 &&
908 "unexpected int value");
910 ret = ec_config_add(list, subconfig);
911 subconfig = NULL; /* freed */
912 testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
914 ret = ec_config_set(config, "dictlist", list);
916 testres |= EC_TEST_CHECK(ret == 0, "cannot set list");
918 testres |= EC_TEST_CHECK(
919 ec_config_validate(config) == 0,
920 "cannot validate config\n");
924 config = ec_config(schema);
925 value = ec_config_bool(true);
926 ec_config_set(config, "key", value);
927 value = ec_config_get(config, "key");
928 /* check value->type and value->bool */
930 value = ec_config_string("xxx");
931 ec_config_set(config, "key", value);
932 value = ec_config_get(config, "key");
933 /* check value->type and value->string */
935 list = ec_config_list();
936 value = ec_config_string("xxx");
937 ec_config_add(list, value);
938 value = ec_config_string("yyy");
939 ec_config_add(list, value);
940 value = ec_config_remove(list, 0);
941 ec_config_set(config, "key", list);
946 value.type = EC_CONFIG_TYPE_NODE;
947 value.node = ec_node_clone(node);
948 ret = ec_config_set(config, "node", value);
949 testres |= EC_TEST_CHECK(ret == 0, "cannot set node");
950 pvalue = ec_config_get(config, "node");
951 testres |= EC_TEST_CHECK(
953 ec_config_cmp(pvalue, &value) == 0,
954 "unexpected node value");
956 subconfig = ec_config(dict, EC_COUNT_OF(dict));
957 if (subconfig == NULL)
960 value.type = EC_CONFIG_TYPE_INT64;
962 ret = ec_config_set(subconfig, "int", value);
963 testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
964 pvalue = ec_config_get(subconfig, "int");
965 testres |= EC_TEST_CHECK(
967 ec_config_cmp(pvalue, &value) == 0,
968 "unexpected int value");
970 value.type = EC_CONFIG_TYPE_DICT;
971 value.dict = subconfig;
972 subconfig = NULL; /* will be freed when freeing config */
973 ret = ec_config_set(config, "dict", value);
974 testres |= EC_TEST_CHECK(ret == 0, "cannot set dict");
975 pvalue = ec_config_get(config, "dict");
976 testres |= EC_TEST_CHECK(
978 ec_config_cmp(pvalue, &value) == 0,
979 "unexpected dict value");
981 value.type = EC_CONFIG_TYPE_INT64;
983 pvalue = ec_config_get(
984 ec_config_get(config, "dict")->dict, "int");
985 testres |= EC_TEST_CHECK(
987 ec_config_cmp(pvalue, &value) == 0,
988 "unexpected int value");
990 value.type = EC_CONFIG_TYPE_INT64;
992 ret = ec_config_add(config, "intlist", value);
993 testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
994 value.type = EC_CONFIG_TYPE_INT64;
996 ret = ec_config_add(config, "intlist", value);
997 testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
998 value.type = EC_CONFIG_TYPE_INT64;
1000 ret = ec_config_add(config, "intlist", value);
1001 testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
1003 value.type = EC_CONFIG_TYPE_INT64;
1005 ret = ec_config_set(config, "invalid", value);
1006 testres |= EC_TEST_CHECK(ret < 0,
1007 "should not be able to set invalid key");
1008 pvalue = ec_config_get(config, "invalid");
1009 testres |= EC_TEST_CHECK(pvalue == NULL,
1010 "invalid key returned a value");
1013 ec_config_dump(stdout, config);
1015 ec_config_free(list);
1016 ec_config_free(subconfig);
1017 ec_config_free(config);
1018 ec_keyval_free(dict);
1024 ec_config_free(list);
1025 ec_config_free(subconfig);
1026 ec_config_free(config);
1027 ec_keyval_free(dict);
1032 /* LCOV_EXCL_STOP */
1034 static struct ec_test ec_config_test = {
1036 .test = ec_config_testcase,
1039 EC_TEST_REGISTER(ec_config_test);