sq config
[protos/libecoli.git] / lib / ecoli_config.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2018, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <sys/queue.h>
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <inttypes.h>
10
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>
18
19 EC_LOG_TYPE_REGISTER(config);
20
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])))))
24 #endif
25
26 static int
27 __ec_config_dump(FILE *out, const char *key, const struct ec_config *config,
28         size_t indent);
29 static int
30 ec_config_dict_validate(const struct ec_keyval *dict,
31                         const struct ec_config_schema *schema,
32                         size_t schema_len);
33
34 /* return ec_value type as a string */
35 static const char *
36 ec_config_type_str(enum ec_config_type type)
37 {
38         switch (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";
47         }
48 }
49
50 static int
51 __ec_config_schema_validate(const struct ec_config_schema *schema,
52                         size_t schema_len, enum ec_config_type type)
53 {
54         size_t i, j;
55         int ret;
56
57         if (type == EC_CONFIG_TYPE_LIST) {
58                 if (schema[0].key != NULL) {
59                         errno = EINVAL;
60                         EC_LOG(EC_LOG_ERR, "list schema key must be NULL\n");
61                         return -1;
62                 }
63         } else if (type == EC_CONFIG_TYPE_DICT) {
64                 for (i = 0; i < schema_len; i++) {
65                         if (schema[i].key == NULL) {
66                                 errno = EINVAL;
67                                 EC_LOG(EC_LOG_ERR,
68                                         "dict schema key should not be NULL\n");
69                                 return -1;
70                         }
71                 }
72         } else {
73                 errno = EINVAL;
74                 EC_LOG(EC_LOG_ERR, "invalid schema type\n");
75                 return -1;
76         }
77
78         for (i = 0; i < schema_len; i++) {
79                 /* check for duplicate name if more than one element */
80                 for (j = i + 1; j < schema_len; j++) {
81                         if (!strcmp(schema[i].key, schema[j].key)) {
82                                 errno = EEXIST;
83                                 EC_LOG(EC_LOG_ERR,
84                                         "duplicate key <%s> in schema\n",
85                                         schema[i].key);
86                                 return -1;
87                         }
88                 }
89
90                 switch (schema[i].type) {
91                 case EC_CONFIG_TYPE_BOOL:
92                 case EC_CONFIG_TYPE_INT64:
93                 case EC_CONFIG_TYPE_UINT64:
94                 case EC_CONFIG_TYPE_STRING:
95                 case EC_CONFIG_TYPE_NODE:
96                         if (schema[i].subschema != NULL ||
97                                         schema[i].subschema_len != 0) {
98                                 errno = EINVAL;
99                                 EC_LOG(EC_LOG_ERR,
100                                         "key <%s> should not have subtype/subschema\n",
101                                         schema[i].key);
102                                 return -1;
103                         }
104                         break;
105                 case EC_CONFIG_TYPE_LIST:
106                         if (schema[i].subschema == NULL ||
107                                         schema[i].subschema_len != 1) {
108                                 errno = EINVAL;
109                                 EC_LOG(EC_LOG_ERR,
110                                         "key <%s> must have subschema of length 1\n",
111                                         schema[i].key);
112                                 return -1;
113                         }
114                         break;
115                 case EC_CONFIG_TYPE_DICT:
116                         if (schema[i].subschema == NULL ||
117                                         schema[i].subschema_len == 0) {
118                                 errno = EINVAL;
119                                 EC_LOG(EC_LOG_ERR,
120                                         "key <%s> must have subschema\n",
121                                         schema[i].key);
122                                 return -1;
123                         }
124                         break;
125                 default:
126                         EC_LOG(EC_LOG_ERR, "invalid type for key <%s>\n",
127                                 schema[i].key);
128                         errno = EINVAL;
129                         return -1;
130                 }
131
132                 if (schema[i].subschema == NULL)
133                         continue;
134
135                 ret = __ec_config_schema_validate(schema[i].subschema,
136                                                 schema[i].subschema_len,
137                                                 schema[i].type);
138                 if (ret < 0) {
139                         EC_LOG(EC_LOG_ERR, "cannot parse subschema %s%s\n",
140                                 schema[i].key ? "key=" : "",
141                                 schema[i].key ? : "");
142                         return ret;
143                 }
144         }
145
146         return 0;
147 }
148
149 int
150 ec_config_schema_validate(const struct ec_config_schema *schema,
151                         size_t schema_len)
152 {
153         return __ec_config_schema_validate(schema, schema_len,
154                                         EC_CONFIG_TYPE_DICT);
155 }
156
157 static void
158 __ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema,
159                         size_t schema_len, size_t indent)
160 {
161         size_t i;
162
163         for (i = 0; i < schema_len; i++) {
164                 fprintf(out, "%*s" "%s%s%stype=%s desc='%s'\n",
165                         (int)indent * 4, "",
166                         schema[i].key ? "key=": "",
167                         schema[i].key ? : "",
168                         schema[i].key ? " ": "",
169                         ec_config_type_str(schema[i].type),
170                         schema[i].desc);
171                 if (schema[i].subschema == NULL)
172                         continue;
173                 __ec_config_schema_dump(out, schema[i].subschema,
174                                         schema[i].subschema_len, indent + 1);
175         }
176 }
177
178 void
179 ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema,
180                 size_t schema_len)
181 {
182         fprintf(out, "------------------- schema dump:\n");
183
184         if (schema == NULL || schema_len == 0) {
185                 fprintf(out, "no schema\n");
186                 return;
187         }
188
189         __ec_config_schema_dump(out, schema, schema_len, 0);
190 }
191
192 struct ec_config *
193 ec_config_bool(bool boolean)
194 {
195         struct ec_config *value = NULL;
196
197         value = ec_calloc(1, sizeof(*value));
198         if (value == NULL)
199                 return NULL;
200
201         value->type = EC_CONFIG_TYPE_BOOL;
202         value->boolean = boolean;
203
204         return value;
205 }
206
207 struct ec_config *
208 ec_config_i64(int64_t i64)
209 {
210         struct ec_config *value = NULL;
211
212         value = ec_calloc(1, sizeof(*value));
213         if (value == NULL)
214                 return NULL;
215
216         value->type = EC_CONFIG_TYPE_INT64;
217         value->i64 = i64;
218
219         return value;
220 }
221
222 struct ec_config *
223 ec_config_u64(uint64_t u64)
224 {
225         struct ec_config *value = NULL;
226
227         value = ec_calloc(1, sizeof(*value));
228         if (value == NULL)
229                 return NULL;
230
231         value->type = EC_CONFIG_TYPE_UINT64;
232         value->u64 = u64;
233
234         return value;
235 }
236
237 /* duplicate string */
238 struct ec_config *
239 ec_config_string(const char *string)
240 {
241         struct ec_config *value = NULL;
242         char *s = NULL;
243
244         if (string == NULL)
245                 goto fail;
246
247         s = ec_strdup(string);
248         if (s == NULL)
249                 goto fail;
250
251         value = ec_calloc(1, sizeof(*value));
252         if (value == NULL)
253                 goto fail;
254
255         value->type = EC_CONFIG_TYPE_STRING;
256         value->string = s;
257
258         return value;
259
260 fail:
261         ec_free(value);
262         ec_free(s);
263         return NULL;
264 }
265
266 /* "consume" the node */
267 struct ec_config *
268 ec_config_node(struct ec_node *node)
269 {
270         struct ec_config *value = NULL;
271
272         if (node == NULL)
273                 goto fail;
274
275         value = ec_calloc(1, sizeof(*value));
276         if (value == NULL)
277                 goto fail;
278
279         value->type = EC_CONFIG_TYPE_NODE;
280         value->node = node;
281
282         return value;
283
284 fail:
285         ec_node_free(node);
286         ec_free(value);
287         return NULL;
288 }
289
290 static int
291 ec_config_set_schema(struct ec_config *dict,
292                 const struct ec_config_schema *schema,
293                 size_t schema_len)
294 {
295         if (dict->type != EC_CONFIG_TYPE_DICT) {
296                 errno = EINVAL;
297                 goto fail;
298         }
299
300         if (ec_config_schema_validate(schema, schema_len) < 0)
301                 goto fail;
302
303         dict->schema = schema;
304         dict->schema_len = schema_len;
305
306         return 0;
307
308 fail:
309         return -1;
310 }
311
312 struct ec_config *
313 ec_config_dict(const struct ec_config_schema *schema,
314         size_t schema_len)
315 {
316         struct ec_config *value = NULL;
317         struct ec_keyval *dict = NULL;
318
319         dict = ec_keyval();
320         if (dict == NULL)
321                 goto fail;
322
323         value = ec_calloc(1, sizeof(*value));
324         if (value == NULL)
325                 goto fail;
326
327         value->type = EC_CONFIG_TYPE_DICT;
328         value->dict = dict;
329
330         if (ec_config_set_schema(value, schema, schema_len) < 0)
331                 goto fail;
332
333         return value;
334
335 fail:
336         ec_keyval_free(dict);
337         ec_free(value);
338         return NULL;
339 }
340
341 struct ec_config *
342 ec_config_list(void)
343 {
344         struct ec_config *value = NULL;
345
346         value = ec_calloc(1, sizeof(*value));
347         if (value == NULL)
348                 return NULL;
349
350         value->type = EC_CONFIG_TYPE_LIST;
351         TAILQ_INIT(&value->list);
352
353         return value;
354 }
355
356 static const struct ec_config_schema *
357 ec_config_schema_lookup(const struct ec_config_schema *schema,
358                         size_t schema_len, const char *key,
359                         enum ec_config_type type)
360 {
361         size_t i;
362
363         for (i = 0; i < schema_len; i++) {
364                 if (!strcmp(key, schema[i].key) &&
365                                 type == schema[i].type)
366                         return &schema[i];
367         }
368
369         errno = ENOENT;
370         return NULL;
371 }
372
373 void
374 ec_config_free(struct ec_config *value)
375 {
376         if (value == NULL)
377                 return;
378
379         switch (value->type) {
380         case EC_CONFIG_TYPE_STRING:
381                 ec_free(value->string);
382                 break;
383         case EC_CONFIG_TYPE_NODE:
384                 ec_node_free(value->node);
385                 break;
386         case EC_CONFIG_TYPE_LIST:
387                 while (!TAILQ_EMPTY(&value->list)) {
388                         struct ec_config *v;
389                         v = TAILQ_FIRST(&value->list);
390                         TAILQ_REMOVE(&value->list, v, next);
391                         ec_config_free(v);
392                 }
393                 break;
394         case EC_CONFIG_TYPE_DICT:
395                 ec_keyval_free(value->dict);
396                 break;
397         default:
398                 break;
399         }
400
401         ec_free(value);
402 }
403
404 static int
405 ec_config_list_cmp(const struct ec_config_list *list1,
406                 const struct ec_config_list *list2)
407 {
408         const struct ec_config *v1, *v2;
409
410         for (v1 = TAILQ_FIRST(list1), v2 = TAILQ_FIRST(list2);
411              v1 != NULL && v2 != NULL;
412              v1 = TAILQ_NEXT(v1, next), v2 = TAILQ_NEXT(v2, next)) {
413                 if (ec_config_cmp(v1, v2))
414                         return -1;
415         }
416         if (v1 != NULL || v2 != NULL)
417                 return -1;
418
419         return 0;
420 }
421
422 /* XXX -> ec_keyval_cmp() */
423 static int
424 ec_config_dict_cmp(const struct ec_keyval *d1,
425                 const struct ec_keyval *d2)
426 {
427         const struct ec_config *v1, *v2;
428         struct ec_keyval_iter *iter = NULL;
429         const char *key;
430
431         if (ec_keyval_len(d1) != ec_keyval_len(d2))
432                 return -1;
433
434         for (iter = ec_keyval_iter(d1);
435              ec_keyval_iter_valid(iter);
436              ec_keyval_iter_next(iter)) {
437                 key = ec_keyval_iter_get_key(iter);
438                 v1 = ec_keyval_iter_get_val(iter);
439                 v2 = ec_keyval_get(d2, key);
440
441                 if (ec_config_cmp(v1, v2))
442                         goto fail;
443         }
444
445         ec_keyval_iter_free(iter);
446         return 0;
447
448 fail:
449         ec_keyval_iter_free(iter);
450         return -1;
451 }
452
453 int
454 ec_config_cmp(const struct ec_config *value1,
455                 const struct ec_config *value2)
456 {
457         if (value1->type != value2->type)
458                 return -1;
459
460         switch (value1->type) {
461         case EC_CONFIG_TYPE_BOOL:
462                 if (value1->boolean == value2->boolean)
463                         return 0;
464         case EC_CONFIG_TYPE_INT64:
465                 if (value1->i64 == value2->i64)
466                         return 0;
467         case EC_CONFIG_TYPE_UINT64:
468                 if (value1->u64 == value2->u64)
469                         return 0;
470         case EC_CONFIG_TYPE_STRING:
471                 if (!strcmp(value1->string, value2->string))
472                         return 0;
473         case EC_CONFIG_TYPE_NODE:
474                 if (value1->node == value2->node)
475                         return 0;
476         case EC_CONFIG_TYPE_LIST:
477                 return ec_config_list_cmp(&value1->list, &value2->list);
478         case EC_CONFIG_TYPE_DICT:
479                 return ec_config_dict_cmp(value1->dict, value2->dict);
480         default:
481                 break;
482         }
483
484         return -1;
485 }
486
487 static int
488 ec_config_list_validate(const struct ec_config_list *list,
489                         const struct ec_config_schema *sch)
490 {
491         const struct ec_config *value;
492
493         TAILQ_FOREACH(value, list, next) {
494                 if (value->type != sch->type) {
495                         errno = EBADMSG;
496                         return -1;
497                 }
498
499                 if (value->type == EC_CONFIG_TYPE_LIST) {
500                         if (ec_config_list_validate(&value->list,
501                                                         sch->subschema) < 0)
502                                 return -1;
503                 } else if (value->type == EC_CONFIG_TYPE_DICT) {
504                         if (ec_config_dict_validate(value->dict,
505                                         sch->subschema, sch->subschema_len) < 0)
506                                 return -1;
507                 }
508         }
509
510         return 0;
511 }
512
513 static int
514 ec_config_dict_validate(const struct ec_keyval *dict,
515                         const struct ec_config_schema *schema,
516                         size_t schema_len)
517 {
518         const struct ec_config *value;
519         struct ec_keyval_iter *iter = NULL;
520         const struct ec_config_schema *sch;
521         const char *key;
522
523         for (iter = ec_keyval_iter(dict);
524              ec_keyval_iter_valid(iter);
525              ec_keyval_iter_next(iter)) {
526
527                 key = ec_keyval_iter_get_key(iter);
528                 value = ec_keyval_iter_get_val(iter);
529                 sch = ec_config_schema_lookup(schema, schema_len,
530                                         key, value->type);
531                 if (sch == NULL) {
532                         errno = EBADMSG;
533                         goto fail;
534                 }
535
536                 if (value->type == EC_CONFIG_TYPE_LIST) {
537                         if (ec_config_list_validate(&value->list,
538                                                         sch->subschema) < 0)
539                                 goto fail;
540                 } else if (value->type == EC_CONFIG_TYPE_DICT) {
541                         if (ec_config_dict_validate(value->dict,
542                                         sch->subschema, sch->subschema_len) < 0)
543                                 goto fail;
544                 }
545         }
546
547         ec_keyval_iter_free(iter);
548         return 0;
549
550 fail:
551         ec_keyval_iter_free(iter);
552         return -1;
553 }
554
555 int
556 ec_config_validate(const struct ec_config *dict)
557 {
558         if (dict->type != EC_CONFIG_TYPE_DICT) {
559                 errno = EINVAL;
560                 goto fail;
561         }
562         if (dict->schema == NULL || dict->schema_len == 0) {
563                 errno = ENOENT;
564                 goto fail;
565         }
566
567         if (ec_config_dict_validate(dict->dict, dict->schema,
568                                         dict->schema_len) < 0)
569                 goto fail;
570
571         return 0
572 ;
573 fail:
574         return -1;
575 }
576
577 struct ec_config *
578 ec_config_get(struct ec_config *config, const char *key)
579 {
580         if (config == NULL)
581                 return NULL;
582
583         return ec_keyval_get(config->dict, key);
584 }
585
586 struct ec_config *
587 ec_config_list_first(struct ec_config *list)
588 {
589         if (list  == NULL || list->type != EC_CONFIG_TYPE_LIST) {
590                 errno = EINVAL;
591                 return NULL;
592         }
593
594         return TAILQ_FIRST(&list->list);
595 }
596
597 struct ec_config *
598 ec_config_list_next(struct ec_config *list, struct ec_config *config)
599 {
600         (void)list;
601         return TAILQ_NEXT(config, next);
602 }
603
604 /* value is consumed */
605 int ec_config_set(struct ec_config *config, const char *key,
606                 struct ec_config *value)
607 {
608         void (*free_cb)(struct ec_config *) = ec_config_free;
609
610         if (config == NULL || key == NULL || value == NULL) {
611                 errno = EINVAL;
612                 goto fail;
613         }
614         if (config->type != EC_CONFIG_TYPE_DICT) {
615                 errno = EINVAL;
616                 goto fail;
617         }
618
619         return ec_keyval_set(config->dict, key, value,
620                         (void (*)(void *))free_cb);
621
622 fail:
623         ec_config_free(value);
624         return -1;
625 }
626
627 /* value is consumed */
628 int
629 ec_config_add(struct ec_config *list,
630                 struct ec_config *value)
631 {
632         if (list->type != EC_CONFIG_TYPE_LIST) {
633                 errno = EINVAL;
634                 goto fail;
635         }
636
637         TAILQ_INSERT_TAIL(&list->list, value, next);
638
639         return 0;
640
641 fail:
642         ec_config_free(value);
643         return -1;
644 }
645
646 void ec_config_del(struct ec_config *list, struct ec_config *config)
647 {
648         TAILQ_REMOVE(&list->list, config, next);
649         ec_config_free(config);
650 }
651
652 static int
653 ec_config_list_dump(FILE *out, const struct ec_config_list *list,
654                 size_t indent)
655 {
656         const struct ec_config *v;
657
658         fprintf(out, "%*s" "type=list:\n", (int)indent * 4, "");
659
660         TAILQ_FOREACH(v, list, next) {
661                 if (__ec_config_dump(out, NULL, v, indent + 1) < 0)
662                         return -1;
663         }
664
665         return 0;
666 }
667
668 static int
669 ec_config_dict_dump(FILE *out, const struct ec_keyval *dict,
670                 size_t indent)
671 {
672         const struct ec_config *value;
673         struct ec_keyval_iter *iter;
674         const char *key;
675
676         fprintf(out, "%*s" "type=dict:\n", (int)indent * 4, "");
677         for (iter = ec_keyval_iter(dict);
678              ec_keyval_iter_valid(iter);
679              ec_keyval_iter_next(iter)) {
680                 key = ec_keyval_iter_get_key(iter);
681                 value = ec_keyval_iter_get_val(iter);
682                 if (__ec_config_dump(out, key, value, indent + 1) < 0)
683                         goto fail;
684         }
685         ec_keyval_iter_free(iter);
686         return 0;
687
688 fail:
689         ec_keyval_iter_free(iter);
690         return -1;
691 }
692
693 static int
694 __ec_config_dump(FILE *out, const char *key, const struct ec_config *value,
695                 size_t indent)
696 {
697         char *val_str = NULL;
698
699         switch (value->type) {
700         case EC_CONFIG_TYPE_BOOL:
701                 if (value->boolean)
702                         ec_asprintf(&val_str, "true");
703                 else
704                         ec_asprintf(&val_str, "false");
705                 break;
706         case EC_CONFIG_TYPE_INT64:
707                 ec_asprintf(&val_str, "%"PRIu64, value->u64);
708                 break;
709         case EC_CONFIG_TYPE_UINT64:
710                 ec_asprintf(&val_str, "%"PRIi64, value->i64);
711                 break;
712         case EC_CONFIG_TYPE_STRING:
713                 ec_asprintf(&val_str, "%s", value->string);
714                 break;
715         case EC_CONFIG_TYPE_NODE:
716                 ec_asprintf(&val_str, "%p", value->node);
717                 break;
718         case EC_CONFIG_TYPE_LIST:
719                 return ec_config_list_dump(out, &value->list, indent);
720         case EC_CONFIG_TYPE_DICT:
721                 return ec_config_dict_dump(out, value->dict, indent);
722         default:
723                 errno = EINVAL;
724                 break;
725         }
726
727         /* errno is already set on error */
728         if (val_str == NULL)
729                 goto fail;
730
731         fprintf(out, "%*s" "%s%s%stype=%s val=%s\n", (int)indent * 4, "",
732                 key ? "key=": "",
733                 key ? key: "",
734                 key ? " ": "",
735                 ec_config_type_str(value->type), val_str);
736
737         ec_free(val_str);
738         return 0;
739
740 fail:
741         ec_free(val_str);
742         return -1;
743 }
744
745 void
746 ec_config_dump(FILE *out, const struct ec_config *config)
747 {
748         fprintf(out, "------------------- config dump:\n");
749
750         if (config == NULL) {
751                 fprintf(out, "no config\n");
752                 return;
753         }
754
755         if (__ec_config_dump(out, NULL, config, 0) < 0)
756                 fprintf(out, "error while dumping\n");
757 }
758
759 /* LCOV_EXCL_START */
760 static const struct ec_config_schema sch_intlist_elt[] = {
761         {
762                 .desc = "This is a description for int",
763                 .type = EC_CONFIG_TYPE_INT64,
764         },
765 };
766
767 static const struct ec_config_schema sch_dict[] = {
768         {
769                 .key = "my_int",
770                 .desc = "This is a description for int",
771                 .type = EC_CONFIG_TYPE_INT64,
772         },
773         {
774                 .key = "my_int2",
775                 .desc = "This is a description for int2",
776                 .type = EC_CONFIG_TYPE_INT64,
777         },
778 };
779
780 static const struct ec_config_schema sch_dictlist_elt[] = {
781         {
782                 .desc = "This is a description for dict",
783                 .type = EC_CONFIG_TYPE_DICT,
784                 .subschema = sch_dict,
785                 .subschema_len = EC_COUNT_OF(sch_dict),
786         },
787 };
788
789 static const struct ec_config_schema sch_baseconfig[] = {
790         {
791                 .key = "my_bool",
792                 .desc = "This is a description for bool",
793                 .type = EC_CONFIG_TYPE_BOOL,
794         },
795         {
796                 .key = "my_int",
797                 .desc = "This is a description for int",
798                 .type = EC_CONFIG_TYPE_INT64,
799         },
800         {
801                 .key = "my_string",
802                 .desc = "This is a description for string",
803                 .type = EC_CONFIG_TYPE_STRING,
804         },
805         {
806                 .key = "my_node",
807                 .desc = "This is a description for node",
808                 .type = EC_CONFIG_TYPE_NODE,
809         },
810         {
811                 .key = "my_intlist",
812                 .desc = "This is a description for list",
813                 .type = EC_CONFIG_TYPE_LIST,
814                 .subschema = sch_intlist_elt,
815                 .subschema_len = EC_COUNT_OF(sch_intlist_elt),
816         },
817         {
818                 .key = "my_dictlist",
819                 .desc = "This is a description for list",
820                 .type = EC_CONFIG_TYPE_LIST,
821                 .subschema = sch_dictlist_elt,
822                 .subschema_len = EC_COUNT_OF(sch_dictlist_elt),
823         },
824 };
825
826 static int ec_config_testcase(void)
827 {
828         struct ec_node *node = NULL;
829         struct ec_keyval *dict = NULL;
830         const struct ec_config *value = NULL;
831         struct ec_config *config = NULL, *list = NULL, *subconfig = NULL;
832         struct ec_config *list_, *config_;
833         int testres = 0;
834         int ret;
835
836         node = ec_node("empty", EC_NO_ID);
837         if (node == NULL)
838                 goto fail;
839
840         if (ec_config_schema_validate(sch_baseconfig,
841                                         EC_COUNT_OF(sch_baseconfig)) < 0) {
842                 EC_LOG(EC_LOG_ERR, "invalid config schema\n");
843                 goto fail;
844         }
845
846         ec_config_schema_dump(stdout, sch_baseconfig,
847                         EC_COUNT_OF(sch_baseconfig));
848
849         config = ec_config_dict(sch_baseconfig, EC_COUNT_OF(sch_baseconfig));
850         if (config == NULL)
851                 goto fail;
852
853         ret = ec_config_set(config, "my_bool", ec_config_bool(true));
854         testres |= EC_TEST_CHECK(ret == 0, "cannot set boolean");
855         value = ec_config_get(config, "my_bool");
856         testres |= EC_TEST_CHECK(
857                 value != NULL &&
858                 value->type == EC_CONFIG_TYPE_BOOL &&
859                 value->boolean == true,
860                 "unexpected boolean value");
861
862         ret = ec_config_set(config, "my_int", ec_config_i64(1234));
863         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
864         value = ec_config_get(config, "my_int");
865         testres |= EC_TEST_CHECK(
866                 value != NULL &&
867                 value->type == EC_CONFIG_TYPE_INT64 &&
868                 value->i64 == 1234,
869                 "unexpected int value");
870
871         testres |= EC_TEST_CHECK(
872                 ec_config_validate(config) == 0,
873                 "cannot validate config\n");
874
875         ret = ec_config_set(config, "my_string", ec_config_string("toto"));
876         testres |= EC_TEST_CHECK(ret == 0, "cannot set string");
877         value = ec_config_get(config, "my_string");
878         testres |= EC_TEST_CHECK(
879                 value != NULL &&
880                 value->type == EC_CONFIG_TYPE_STRING &&
881                 !strcmp(value->string, "toto"),
882                 "unexpected string value");
883
884         list = ec_config_list();
885         if (list == NULL)
886                 goto fail;
887
888         subconfig = ec_config_dict(sch_dict, EC_COUNT_OF(sch_dict));
889         if (subconfig == NULL)
890                 goto fail;
891
892         ret = ec_config_set(subconfig, "my_int", ec_config_i64(1));
893         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
894         value = ec_config_get(subconfig, "my_int");
895         testres |= EC_TEST_CHECK(
896                 value != NULL &&
897                 value->type == EC_CONFIG_TYPE_INT64 &&
898                 value->i64 == 1,
899                 "unexpected int value");
900
901         ret = ec_config_set(subconfig, "my_int2", ec_config_i64(2));
902         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
903         value = ec_config_get(subconfig, "my_int2");
904         testres |= EC_TEST_CHECK(
905                 value != NULL &&
906                 value->type == EC_CONFIG_TYPE_INT64 &&
907                 value->i64 == 2,
908                 "unexpected int value");
909
910         testres |= EC_TEST_CHECK(
911                 ec_config_validate(subconfig) == 0,
912                 "cannot validate subconfig\n");
913
914         ret = ec_config_add(list, subconfig);
915         subconfig = NULL; /* freed */
916         testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
917
918         subconfig = ec_config_dict(sch_dict, EC_COUNT_OF(sch_dict));
919         if (subconfig == NULL)
920                 goto fail;
921
922         ret = ec_config_set(subconfig, "my_int", ec_config_i64(3));
923         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
924         value = ec_config_get(subconfig, "my_int");
925         testres |= EC_TEST_CHECK(
926                 value != NULL &&
927                 value->type == EC_CONFIG_TYPE_INT64 &&
928                 value->i64 == 3,
929                 "unexpected int value");
930
931         ret = ec_config_set(subconfig, "my_int2", ec_config_i64(4));
932         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
933         value = ec_config_get(subconfig, "my_int2");
934         testres |= EC_TEST_CHECK(
935                 value != NULL &&
936                 value->type == EC_CONFIG_TYPE_INT64 &&
937                 value->i64 == 4,
938                 "unexpected int value");
939
940         testres |= EC_TEST_CHECK(
941                 ec_config_validate(subconfig) == 0,
942                 "cannot validate subconfig\n");
943
944         ret = ec_config_add(list, subconfig);
945         subconfig = NULL; /* freed */
946         testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
947
948         ret = ec_config_set(config, "my_dictlist", list);
949         list = NULL;
950         testres |= EC_TEST_CHECK(ret == 0, "cannot set list");
951
952         testres |= EC_TEST_CHECK(
953                 ec_config_validate(config) == 0,
954                 "cannot validate config\n");
955
956         list_ = ec_config_get(config, "my_dictlist");
957         printf("list = %p\n", list_);
958         for (config_ = ec_config_list_first(list_); config_ != NULL;
959              config_ = ec_config_list_next(list_, config_)) {
960                 ec_config_dump(stdout, config_);
961         }
962
963         ec_config_dump(stdout, config);
964
965         /* remove the first element */
966         ec_config_del(list_, ec_config_list_first(list_));
967         testres |= EC_TEST_CHECK(
968                 ec_config_validate(config) == 0,
969                 "cannot validate config\n");
970
971 #if 0
972         value.type = EC_CONFIG_TYPE_NODE;
973         value.node = ec_node_clone(node);
974         ret = ec_config_set(config, "node", value);
975         testres |= EC_TEST_CHECK(ret == 0, "cannot set node");
976         pvalue = ec_config_get(config, "node");
977         testres |= EC_TEST_CHECK(
978                 pvalue != NULL &&
979                 ec_config_cmp(pvalue, &value) == 0,
980                 "unexpected node value");
981
982         subconfig = ec_config(dict, EC_COUNT_OF(dict));
983         if (subconfig == NULL)
984                 goto fail;
985
986         value.type = EC_CONFIG_TYPE_INT64;
987         value.i64 = 4321;
988         ret = ec_config_set(subconfig, "int", value);
989         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
990         pvalue = ec_config_get(subconfig, "int");
991         testres |= EC_TEST_CHECK(
992                 pvalue != NULL &&
993                 ec_config_cmp(pvalue, &value) == 0,
994                 "unexpected int value");
995
996         value.type = EC_CONFIG_TYPE_DICT;
997         value.dict = subconfig;
998         subconfig = NULL; /* will be freed when freeing config */
999         ret = ec_config_set(config, "dict", value);
1000         testres |= EC_TEST_CHECK(ret == 0, "cannot set dict");
1001         pvalue = ec_config_get(config, "dict");
1002         testres |= EC_TEST_CHECK(
1003                 pvalue != NULL &&
1004                 ec_config_cmp(pvalue, &value) == 0,
1005                 "unexpected dict value");
1006
1007         value.type = EC_CONFIG_TYPE_INT64;
1008         value.i64 = 4321;
1009         pvalue = ec_config_get(
1010                 ec_config_get(config, "dict")->dict, "int");
1011         testres |= EC_TEST_CHECK(
1012                 pvalue != NULL &&
1013                 ec_config_cmp(pvalue, &value) == 0,
1014                 "unexpected int value");
1015
1016         value.type = EC_CONFIG_TYPE_INT64;
1017         value.i64 = 1;
1018         ret = ec_config_add(config, "intlist", value);
1019         testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
1020         value.type = EC_CONFIG_TYPE_INT64;
1021         value.i64 = 2;
1022         ret = ec_config_add(config, "intlist", value);
1023         testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
1024         value.type = EC_CONFIG_TYPE_INT64;
1025         value.i64 = 3;
1026         ret = ec_config_add(config, "intlist", value);
1027         testres |= EC_TEST_CHECK(ret == 0, "cannot add int in list");
1028
1029         value.type = EC_CONFIG_TYPE_INT64;
1030         value.i64 = 4321;
1031         ret = ec_config_set(config, "invalid", value);
1032         testres |= EC_TEST_CHECK(ret < 0,
1033                         "should not be able to set invalid key");
1034         pvalue = ec_config_get(config, "invalid");
1035         testres |= EC_TEST_CHECK(pvalue == NULL,
1036                         "invalid key returned a value");
1037
1038 #endif
1039         ec_config_dump(stdout, config);
1040
1041         ec_config_free(list);
1042         ec_config_free(subconfig);
1043         ec_config_free(config);
1044         ec_keyval_free(dict);
1045         ec_node_free(node);
1046
1047         return testres;
1048
1049 fail:
1050         ec_config_free(list);
1051         ec_config_free(subconfig);
1052         ec_config_free(config);
1053         ec_keyval_free(dict);
1054         ec_node_free(node);
1055
1056         return -1;
1057 }
1058 /* LCOV_EXCL_STOP */
1059
1060 static struct ec_test ec_config_test = {
1061         .name = "config",
1062         .test = ec_config_testcase,
1063 };
1064
1065 EC_TEST_REGISTER(ec_config_test);