add meson support
[protos/libecoli.git] / src / ecoli_node_once.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <stdarg.h>
10 #include <errno.h>
11
12 #include <ecoli_malloc.h>
13 #include <ecoli_log.h>
14 #include <ecoli_test.h>
15 #include <ecoli_strvec.h>
16 #include <ecoli_node.h>
17 #include <ecoli_parse.h>
18 #include <ecoli_complete.h>
19 #include <ecoli_node_str.h>
20 #include <ecoli_node_or.h>
21 #include <ecoli_node_many.h>
22 #include <ecoli_config.h>
23 #include <ecoli_node_once.h>
24
25 EC_LOG_TYPE_REGISTER(node_once);
26
27 struct ec_node_once {
28         struct ec_node gen;
29         struct ec_node *child;
30 };
31
32 static unsigned int
33 count_node(struct ec_parse *parse, const struct ec_node *node)
34 {
35         struct ec_parse *child;
36         unsigned int count = 0;
37
38         if (parse == NULL)
39                 return 0;
40
41         if (ec_parse_get_node(parse) == node)
42                 count++;
43
44         EC_PARSE_FOREACH_CHILD(child, parse)
45                 count += count_node(child, node);
46
47         return count;
48 }
49
50 static int
51 ec_node_once_parse(const struct ec_node *gen_node,
52                 struct ec_parse *state,
53                 const struct ec_strvec *strvec)
54 {
55         struct ec_node_once *node = (struct ec_node_once *)gen_node;
56         unsigned int count;
57
58         /* count the number of occurences of the node: if already parsed,
59          * do not match
60          */
61         count = count_node(ec_parse_get_root(state), node->child);
62         if (count > 0)
63                 return EC_PARSE_NOMATCH;
64
65         return ec_node_parse_child(node->child, state, strvec);
66 }
67
68 static int
69 ec_node_once_complete(const struct ec_node *gen_node,
70                 struct ec_comp *comp,
71                 const struct ec_strvec *strvec)
72 {
73         struct ec_node_once *node = (struct ec_node_once *)gen_node;
74         struct ec_parse *parse = ec_comp_get_state(comp);
75         unsigned int count;
76         int ret;
77
78         /* count the number of occurences of the node: if already parsed,
79          * do not match
80          */
81         count = count_node(ec_parse_get_root(parse), node->child);
82         if (count > 0)
83                 return 0;
84
85         ret = ec_node_complete_child(node->child, comp, strvec);
86         if (ret < 0)
87                 return ret;
88
89         return 0;
90 }
91
92 static void ec_node_once_free_priv(struct ec_node *gen_node)
93 {
94         struct ec_node_once *node = (struct ec_node_once *)gen_node;
95
96         ec_node_free(node->child);
97 }
98
99 static size_t
100 ec_node_once_get_children_count(const struct ec_node *gen_node)
101 {
102         struct ec_node_once *node = (struct ec_node_once *)gen_node;
103
104         if (node->child)
105                 return 1;
106         return 0;
107 }
108
109 static int
110 ec_node_once_get_child(const struct ec_node *gen_node, size_t i,
111                 struct ec_node **child, unsigned int *refs)
112 {
113         struct ec_node_once *node = (struct ec_node_once *)gen_node;
114
115         if (i >= 1)
116                 return -1;
117
118         *child = node->child;
119         *refs = 2;
120         return 0;
121 }
122
123 static const struct ec_config_schema ec_node_once_schema[] = {
124         {
125                 .key = "child",
126                 .desc = "The child node.",
127                 .type = EC_CONFIG_TYPE_NODE,
128         },
129         {
130                 .type = EC_CONFIG_TYPE_NONE,
131         },
132 };
133
134 static int ec_node_once_set_config(struct ec_node *gen_node,
135                                 const struct ec_config *config)
136 {
137         struct ec_node_once *node = (struct ec_node_once *)gen_node;
138         const struct ec_config *child;
139
140         child = ec_config_dict_get(config, "child");
141         if (child == NULL)
142                 goto fail;
143         if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) {
144                 errno = EINVAL;
145                 goto fail;
146         }
147
148         if (node->child != NULL)
149                 ec_node_free(node->child);
150         node->child = ec_node_clone(child->node);
151
152         return 0;
153
154 fail:
155         return -1;
156 }
157
158 static struct ec_node_type ec_node_once_type = {
159         .name = "once",
160         .schema = ec_node_once_schema,
161         .set_config = ec_node_once_set_config,
162         .parse = ec_node_once_parse,
163         .complete = ec_node_once_complete,
164         .size = sizeof(struct ec_node_once),
165         .free_priv = ec_node_once_free_priv,
166         .get_children_count = ec_node_once_get_children_count,
167         .get_child = ec_node_once_get_child,
168 };
169
170 EC_NODE_TYPE_REGISTER(ec_node_once_type);
171
172 int
173 ec_node_once_set_child(struct ec_node *gen_node, struct ec_node *child)
174 {
175         const struct ec_config *cur_config = NULL;
176         struct ec_config *config = NULL;
177         int ret;
178
179         if (ec_node_check_type(gen_node, &ec_node_once_type) < 0)
180                 goto fail;
181
182         cur_config = ec_node_get_config(gen_node);
183         if (cur_config == NULL)
184                 config = ec_config_dict();
185         else
186                 config = ec_config_dup(cur_config);
187         if (config == NULL)
188                 goto fail;
189
190         if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
191                 child = NULL; /* freed */
192                 goto fail;
193         }
194         child = NULL; /* freed */
195
196         ret = ec_node_set_config(gen_node, config);
197         config = NULL; /* freed */
198         if (ret < 0)
199                 goto fail;
200
201         return 0;
202
203 fail:
204         ec_config_free(config);
205         ec_node_free(child);
206         return -1;
207 }
208
209 struct ec_node *ec_node_once(const char *id, struct ec_node *child)
210 {
211         struct ec_node *gen_node = NULL;
212
213         if (child == NULL)
214                 return NULL;
215
216         gen_node = ec_node_from_type(&ec_node_once_type, id);
217         if (gen_node == NULL)
218                 goto fail;
219
220         ec_node_once_set_child(gen_node, child);
221         child = NULL;
222
223         return gen_node;
224
225 fail:
226         ec_node_free(child);
227         return NULL;
228 }
229
230 /* LCOV_EXCL_START */
231 static int ec_node_once_testcase(void)
232 {
233         struct ec_node *node;
234         int testres = 0;
235
236         node = ec_node_many(EC_NO_ID,
237                         EC_NODE_OR(EC_NO_ID,
238                                 ec_node_once(EC_NO_ID, ec_node_str(EC_NO_ID, "foo")),
239                                 ec_node_str(EC_NO_ID, "bar")
240                                 ), 0, 0
241                 );
242         if (node == NULL) {
243                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
244                 return -1;
245         }
246         testres |= EC_TEST_CHECK_PARSE(node, 0);
247         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
248         testres |= EC_TEST_CHECK_PARSE(node, 1, "bar");
249         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
250         testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "bar", "bar");
251         testres |= EC_TEST_CHECK_PARSE(node, 3, "bar", "foo", "bar");
252         testres |= EC_TEST_CHECK_PARSE(node, 2, "bar", "foo", "foo");
253         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "foo");
254         testres |= EC_TEST_CHECK_PARSE(node, 0, "foox");
255
256         testres |= EC_TEST_CHECK_COMPLETE(node,
257                 "", EC_NODE_ENDLIST,
258                 "foo", "bar", EC_NODE_ENDLIST);
259         testres |= EC_TEST_CHECK_COMPLETE(node,
260                 "f", EC_NODE_ENDLIST,
261                 "foo", EC_NODE_ENDLIST);
262         testres |= EC_TEST_CHECK_COMPLETE(node,
263                 "b", EC_NODE_ENDLIST,
264                 "bar", EC_NODE_ENDLIST);
265         testres |= EC_TEST_CHECK_COMPLETE(node,
266                 "foo", "", EC_NODE_ENDLIST,
267                 "bar", EC_NODE_ENDLIST);
268         testres |= EC_TEST_CHECK_COMPLETE(node,
269                 "bar", "", EC_NODE_ENDLIST,
270                 "foo", "bar", EC_NODE_ENDLIST);
271         ec_node_free(node);
272
273         return testres;
274 }
275 /* LCOV_EXCL_STOP */
276
277 static struct ec_test ec_node_once_test = {
278         .name = "node_once",
279         .test = ec_node_once_testcase,
280 };
281
282 EC_TEST_REGISTER(ec_node_once_test);