28c69b9192d7af2ed06449cb45eb30ec44fe8d14
[protos/libecoli.git] / lib / ecoli_node_str.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 <errno.h>
9
10 #include <ecoli_log.h>
11 #include <ecoli_malloc.h>
12 #include <ecoli_test.h>
13 #include <ecoli_strvec.h>
14 #include <ecoli_node.h>
15 #include <ecoli_config.h>
16 #include <ecoli_parse.h>
17 #include <ecoli_complete.h>
18 #include <ecoli_node_str.h>
19
20 EC_LOG_TYPE_REGISTER(node_str);
21
22 struct ec_node_str {
23         struct ec_node gen;
24         char *string;
25         unsigned len;
26 };
27
28 static int
29 ec_node_str_parse(const struct ec_node *gen_node,
30                 struct ec_parse *state,
31                 const struct ec_strvec *strvec)
32 {
33         struct ec_node_str *node = (struct ec_node_str *)gen_node;
34         const char *str;
35
36         (void)state;
37
38         if (node->string == NULL) {
39                 errno = EINVAL;
40                 return -1;
41         }
42
43         if (ec_strvec_len(strvec) == 0)
44                 return EC_PARSE_NOMATCH;
45
46         str = ec_strvec_val(strvec, 0);
47         if (strcmp(str, node->string) != 0)
48                 return EC_PARSE_NOMATCH;
49
50         return 1;
51 }
52
53 static int
54 ec_node_str_complete(const struct ec_node *gen_node,
55                 struct ec_comp *comp,
56                 const struct ec_strvec *strvec)
57 {
58         struct ec_node_str *node = (struct ec_node_str *)gen_node;
59         const char *str;
60         size_t n = 0;
61
62         if (ec_strvec_len(strvec) != 1)
63                 return 0;
64
65         str = ec_strvec_val(strvec, 0);
66         for (n = 0; n < node->len; n++) {
67                 if (str[n] != node->string[n])
68                         break;
69         }
70
71         /* no completion */
72         if (str[n] != '\0')
73                 return EC_PARSE_NOMATCH;
74
75         if (ec_comp_add_item(comp, gen_node, NULL, EC_COMP_FULL,
76                                         str, node->string) < 0)
77                 return -1;
78
79         return 0;
80 }
81
82 static const char *ec_node_str_desc(const struct ec_node *gen_node)
83 {
84         struct ec_node_str *node = (struct ec_node_str *)gen_node;
85
86         return node->string;
87 }
88
89 static void ec_node_str_free_priv(struct ec_node *gen_node)
90 {
91         struct ec_node_str *node = (struct ec_node_str *)gen_node;
92
93         ec_free(node->string);
94 }
95
96 static const struct ec_config_schema ec_node_str_schema[] = {
97         {
98                 .key = "string",
99                 .desc = "The string to match.",
100                 .type = EC_CONFIG_TYPE_STRING,
101         },
102 };
103
104 static int ec_node_str_set_config(struct ec_node *gen_node,
105                                 const struct ec_config *config)
106 {
107         struct ec_node_str *node = (struct ec_node_str *)gen_node;
108         const struct ec_config *value = NULL;
109         char *s = NULL;
110
111         value = ec_config_get(config, "string");
112         if (value == NULL) {
113                 errno = EINVAL;
114                 goto fail;
115         }
116
117         s = ec_strdup(value->string);
118         if (s == NULL)
119                 goto fail;
120
121         ec_free(node->string);
122         node->string = s;
123         node->len = strlen(node->string);
124
125         return 0;
126
127 fail:
128         ec_free(s);
129         return -1;
130 }
131
132 static struct ec_node_type ec_node_str_type = {
133         .name = "str",
134         .schema = ec_node_str_schema,
135         .schema_len = EC_COUNT_OF(ec_node_str_schema),
136         .set_config = ec_node_str_set_config,
137         .parse = ec_node_str_parse,
138         .complete = ec_node_str_complete,
139         .desc = ec_node_str_desc,
140         .size = sizeof(struct ec_node_str),
141         .free_priv = ec_node_str_free_priv,
142 };
143
144 EC_NODE_TYPE_REGISTER(ec_node_str_type);
145
146 int ec_node_str_set_str(struct ec_node *gen_node, const char *str)
147 {
148         struct ec_config *config = NULL;
149
150         if (ec_node_check_type(gen_node, &ec_node_str_type) < 0)
151                 goto fail;
152
153         if (str == NULL) {
154                 errno = EINVAL;
155                 goto fail;
156         }
157
158         config = ec_config_dict();
159         if (config == NULL)
160                 goto fail;
161
162         if (ec_config_dict_set(config, "string", ec_config_string(str)) < 0)
163                 goto fail;
164
165         if (ec_node_set_config(gen_node, config) < 0)
166                 goto fail;
167
168         return 0;
169
170 fail:
171         ec_config_free(config);
172         return -1;
173 }
174
175 struct ec_node *ec_node_str(const char *id, const char *str)
176 {
177         struct ec_node *gen_node = NULL;
178
179         gen_node = __ec_node(&ec_node_str_type, id);
180         if (gen_node == NULL)
181                 goto fail;
182
183         if (ec_node_str_set_str(gen_node, str) < 0)
184                 goto fail;
185
186         return gen_node;
187
188 fail:
189         ec_node_free(gen_node);
190         return NULL;
191 }
192
193 /* LCOV_EXCL_START */
194 static int ec_node_str_testcase(void)
195 {
196         struct ec_node *node;
197         int testres = 0;
198
199         node = ec_node_str(EC_NO_ID, "foo");
200         if (node == NULL) {
201                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
202                 return -1;
203         }
204         testres |= EC_TEST_CHECK(!strcmp(ec_node_desc(node), "foo"),
205                 "Invalid node description.");
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, -1, "foobar");
209         testres |= EC_TEST_CHECK_PARSE(node, -1, " foo");
210         testres |= EC_TEST_CHECK_PARSE(node, -1, "");
211         ec_node_free(node);
212
213         node = ec_node_str(EC_NO_ID, "Здравствуйте");
214         if (node == NULL) {
215                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
216                 return -1;
217         }
218         testres |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте");
219         testres |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте",
220                 "John!");
221         testres |= EC_TEST_CHECK_PARSE(node, -1, "foo");
222         testres |= EC_TEST_CHECK_PARSE(node, -1, "");
223         ec_node_free(node);
224
225         /* an empty string node always matches */
226         node = ec_node_str(EC_NO_ID, "");
227         if (node == NULL) {
228                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
229                 return -1;
230         }
231         testres |= EC_TEST_CHECK_PARSE(node, 1, "");
232         testres |= EC_TEST_CHECK_PARSE(node, 1, "", "foo");
233         testres |= EC_TEST_CHECK_PARSE(node, -1, "foo");
234         ec_node_free(node);
235
236         /* test completion */
237         node = ec_node_str(EC_NO_ID, "foo");
238         if (node == NULL) {
239                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
240                 return -1;
241         }
242         testres |= EC_TEST_CHECK_COMPLETE(node,
243                 EC_NODE_ENDLIST,
244                 EC_NODE_ENDLIST);
245         testres |= EC_TEST_CHECK_COMPLETE(node,
246                 "", EC_NODE_ENDLIST,
247                 "foo", EC_NODE_ENDLIST);
248         testres |= EC_TEST_CHECK_COMPLETE(node,
249                 "f", EC_NODE_ENDLIST,
250                 "foo", EC_NODE_ENDLIST);
251         testres |= EC_TEST_CHECK_COMPLETE(node,
252                 "foo", EC_NODE_ENDLIST,
253                 "foo", EC_NODE_ENDLIST);
254         testres |= EC_TEST_CHECK_COMPLETE(node,
255                 "x", EC_NODE_ENDLIST,
256                 EC_NODE_ENDLIST);
257         ec_node_free(node);
258
259         return testres;
260 }
261 /* LCOV_EXCL_STOP */
262
263 static struct ec_test ec_node_str_test = {
264         .name = "node_str",
265         .test = ec_node_str_testcase,
266 };
267
268 EC_TEST_REGISTER(ec_node_str_test);