api documentation for ec_parse
[protos/libecoli.git] / src / ecoli_node_option.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_strvec.h>
15 #include <ecoli_node.h>
16 #include <ecoli_parse.h>
17 #include <ecoli_complete.h>
18 #include <ecoli_node_str.h>
19 #include <ecoli_test.h>
20 #include <ecoli_config.h>
21 #include <ecoli_node_option.h>
22
23 EC_LOG_TYPE_REGISTER(node_option);
24
25 struct ec_node_option {
26         struct ec_node *child;
27 };
28
29 static int
30 ec_node_option_parse(const struct ec_node *node,
31                 struct ec_pnode *pstate,
32                 const struct ec_strvec *strvec)
33 {
34         struct ec_node_option *priv = ec_node_priv(node);
35         int ret;
36
37         ret = ec_parse_child(priv->child, pstate, strvec);
38         if (ret < 0)
39                 return ret;
40
41         if (ret == EC_PARSE_NOMATCH)
42                 return 0;
43
44         return ret;
45 }
46
47 static int
48 ec_node_option_complete(const struct ec_node *node,
49                         struct ec_comp *comp,
50                         const struct ec_strvec *strvec)
51 {
52         struct ec_node_option *priv = ec_node_priv(node);
53
54         return ec_complete_child(priv->child, comp, strvec);
55 }
56
57 static void ec_node_option_free_priv(struct ec_node *node)
58 {
59         struct ec_node_option *priv = ec_node_priv(node);
60
61         ec_node_free(priv->child);
62 }
63
64 static size_t
65 ec_node_option_get_children_count(const struct ec_node *node)
66 {
67         struct ec_node_option *priv = ec_node_priv(node);
68
69         if (priv->child)
70                 return 1;
71         return 0;
72 }
73
74 static int
75 ec_node_option_get_child(const struct ec_node *node, size_t i,
76         struct ec_node **child, unsigned int *refs)
77 {
78         struct ec_node_option *priv = ec_node_priv(node);
79
80         if (i >= 1)
81                 return -1;
82
83         *child = priv->child;
84         *refs = 2;
85         return 0;
86 }
87
88 static const struct ec_config_schema ec_node_option_schema[] = {
89         {
90                 .key = "child",
91                 .desc = "The child node.",
92                 .type = EC_CONFIG_TYPE_NODE,
93         },
94         {
95                 .type = EC_CONFIG_TYPE_NONE,
96         },
97 };
98
99 static int ec_node_option_set_config(struct ec_node *node,
100                                 const struct ec_config *config)
101 {
102         struct ec_node_option *priv = ec_node_priv(node);
103         const struct ec_config *child;
104
105         child = ec_config_dict_get(config, "child");
106         if (child == NULL)
107                 goto fail;
108         if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) {
109                 errno = EINVAL;
110                 goto fail;
111         }
112
113         if (priv->child != NULL)
114                 ec_node_free(priv->child);
115         priv->child = ec_node_clone(child->node);
116
117         return 0;
118
119 fail:
120         return -1;
121 }
122
123 static struct ec_node_type ec_node_option_type = {
124         .name = "option",
125         .schema = ec_node_option_schema,
126         .set_config = ec_node_option_set_config,
127         .parse = ec_node_option_parse,
128         .complete = ec_node_option_complete,
129         .size = sizeof(struct ec_node_option),
130         .free_priv = ec_node_option_free_priv,
131         .get_children_count = ec_node_option_get_children_count,
132         .get_child = ec_node_option_get_child,
133 };
134
135 EC_NODE_TYPE_REGISTER(ec_node_option_type);
136
137 int
138 ec_node_option_set_child(struct ec_node *node, struct ec_node *child)
139 {
140         const struct ec_config *cur_config = NULL;
141         struct ec_config *config = NULL;
142         int ret;
143
144         if (ec_node_check_type(node, &ec_node_option_type) < 0)
145                 goto fail;
146
147         cur_config = ec_node_get_config(node);
148         if (cur_config == NULL)
149                 config = ec_config_dict();
150         else
151                 config = ec_config_dup(cur_config);
152         if (config == NULL)
153                 goto fail;
154
155         if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
156                 child = NULL; /* freed */
157                 goto fail;
158         }
159         child = NULL; /* freed */
160
161         ret = ec_node_set_config(node, config);
162         config = NULL; /* freed */
163         if (ret < 0)
164                 goto fail;
165
166         return 0;
167
168 fail:
169         ec_config_free(config);
170         ec_node_free(child);
171         return -1;
172 }
173
174 struct ec_node *ec_node_option(const char *id, struct ec_node *child)
175 {
176         struct ec_node *node = NULL;
177
178         if (child == NULL)
179                 goto fail;
180
181         node = ec_node_from_type(&ec_node_option_type, id);
182         if (node == NULL)
183                 goto fail;
184
185         ec_node_option_set_child(node, child);
186         child = NULL;
187
188         return node;
189
190 fail:
191         ec_node_free(child);
192         return NULL;
193 }
194
195 /* LCOV_EXCL_START */
196 static int ec_node_option_testcase(void)
197 {
198         struct ec_node *node;
199         int testres = 0;
200
201         node = ec_node_option(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"));
202         if (node == NULL) {
203                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
204                 return -1;
205         }
206         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
207         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
208         testres |= EC_TEST_CHECK_PARSE(node, 0, "bar");
209         testres |= EC_TEST_CHECK_PARSE(node, 0);
210         ec_node_free(node);
211
212         /* test completion */
213         node = ec_node_option(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"));
214         if (node == NULL) {
215                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
216                 return -1;
217         }
218         testres |= EC_TEST_CHECK_COMPLETE(node,
219                 "", EC_VA_END,
220                 "foo", EC_VA_END);
221         testres |= EC_TEST_CHECK_COMPLETE(node,
222                 "f", EC_VA_END,
223                 "foo", EC_VA_END);
224         testres |= EC_TEST_CHECK_COMPLETE(node,
225                 "b", EC_VA_END,
226                 EC_VA_END);
227         ec_node_free(node);
228
229         return testres;
230 }
231 /* LCOV_EXCL_STOP */
232
233 static struct ec_test ec_node_option_test = {
234         .name = "node_option",
235         .test = ec_node_option_testcase,
236 };
237
238 EC_TEST_REGISTER(ec_node_option_test);