024a8db99b7f2396b4630a46245786f3ea241fee
[src/app-framework-binder.git] / src / genskel / genskel.c
1 /*
2  * Copyright (C) 2016, 2017 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 /*
18  * This simple program expands the object { "$ref": "#/path/to/a/target" }
19  *
20  * For example:
21  *
22  *  {
23  *    "type":{
24  *      "a": "int",
25  *      "b": { "$ref": "#/type/a" }
26  *    }
27  *  }
28  *
29  * will be exapanded to
30  *
31  *  {
32  *    "type":{
33  *      "a": "int",
34  *      "b": "int"
35  *    }
36  *  }
37  * 
38  * Invocation:   program  [file|-]...
39  *
40  * without arguments, it reads the input.
41  */
42
43 #define _GNU_SOURCE
44 #include <string.h>
45 #include <stdio.h>
46 #include <unistd.h>
47
48 #include <json-c/json.h>
49
50 #define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0)
51
52 /**
53  * records path to the expanded node
54  */
55 struct path
56 {
57         struct json_object *object;     /**< node being expanded */
58         struct path *upper;             /**< link to upper expanded nodes */
59 };
60
61 /**
62  * root of the JSON being parsed
63  */
64 struct json_object *root;
65 const char *init = NULL;
66 const char *start = NULL;
67 const char *onevent = NULL;
68 const char *api = NULL;
69 const char *scope = NULL;
70 const char *prefix = NULL;
71 const char *postfix = NULL;
72
73 /**
74  * Search for a reference of type "#/a/b/c" int the
75  * parsed JSON object
76  */
77 struct json_object *search(const char *path)
78 {
79         char *d;
80         struct json_object *i;
81
82         /* does it match #/ at the beginning? */
83         if (path[0] != '#' || (path[0] && path[1] != '/'))
84                 return NULL;
85
86         /* search from root to target */
87         i = root;
88         d = strdupa(path+2);
89         d = strtok(d, "/");
90         while(i && d) {
91                 if (!json_object_object_get_ex(i, d, &i))
92                         return NULL; 
93                 d = strtok(NULL, "/");
94         }
95         return i;
96 }
97
98 /**
99  * Expands the node designated by path and returns its expanded form
100  */
101 struct json_object *expand_$ref(struct path path)
102 {
103         struct path *p;
104         struct json_object *o, *x;
105         int n, i;
106         struct json_object_iterator ji, jn; 
107
108         /* expansion depends of the type of the node */
109         switch (json_object_get_type(path.object)) {
110         case json_type_object:
111                 /* for object, look if it contains a property "$ref" */
112                 if (json_object_object_get_ex(path.object, "$ref", &o)) {
113                         /* yes, reference, try to substitute its target */
114                         if (!json_object_is_type(o, json_type_string)) {
115                                 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
116                                 exit(1);
117                         }
118                         x = search(json_object_get_string(o));
119                         if (!x) {
120                                 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
121                                 exit(1);
122                         }
123                         p = &path;
124                         while(p) {
125                                 if (x == p->object) {
126                                         fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
127                                         exit(1);
128                                 }
129                                 p = p->upper;
130                         }
131                         /* cool found, return a new instance of the target */
132                         return json_object_get(x);
133                 }
134                 /* no, expand the values */
135                 ji = json_object_iter_begin(path.object);
136                 jn = json_object_iter_end(path.object);
137                 while (!json_object_iter_equal(&ji, &jn)) {
138                         o = json_object_iter_peek_value(&ji);
139                         x = expand_$ref((struct path){ .object = o, .upper = &path });
140                         if (x != o)
141                                 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
142                         json_object_iter_next(&ji);
143                 }
144                 break;
145         case json_type_array:
146                 /* expand the values of arrays */
147                 i = 0;
148                 n = json_object_array_length(path.object);
149                 while (i != n) {
150                         o = json_object_array_get_idx(path.object, i);
151                         x = expand_$ref((struct path){ .object = o, .upper = &path });
152                         if (x != o)
153                                 json_object_array_put_idx(path.object, i, x);
154                         i++;
155                 }
156                 break;
157         default:
158                 /* otherwise no expansion */
159                 break;
160         }
161
162         /* return the given node */
163         return path.object;
164 }
165
166 char *get_perm_string(struct json_object *o, int *pl)
167 {
168         struct json_object *x, *y;
169         char *a, *b, *c;
170         int l, n, i, L;
171
172         /* value for permission */
173         if (json_object_object_get_ex(o, "permission", &x)) {
174                 if (!json_object_is_type(x, json_type_string)) {
175                         fprintf(stderr, "permission must be a string. Was: %s\n", json_object_get_string(o));
176                         exit(1);
177                 }
178                 a = strdup(json_object_get_string(x));
179                 oom(a);
180                 *pl = 0;
181                 return a;
182         }
183
184         /* value for not */
185         if (json_object_object_get_ex(o, "not", &x)) {
186                 a = get_perm_string(x, &l);
187                 b = malloc(6 + strlen(a));
188                 oom(b);
189                 if (l)
190                         stpcpy(stpcpy(stpcpy(b,"not("),a),")");
191                 else
192                         stpcpy(stpcpy(b,"not "),a);
193                 free(a);
194                 *pl = 0;
195                 return b;
196         }
197
198         /* value for and or or */
199         if (json_object_object_get_ex(o, "allOf", &x))
200                 L = 1;
201         else if (json_object_object_get_ex(o, "anyOf", &x))
202                 L = 2;
203         else {
204                 fprintf(stderr, "unrecognized permission. Was: %s\n", json_object_get_string(o));
205                 exit(1);
206         }
207
208         /* check the array */
209         if (!json_object_is_type(x, json_type_array)) {
210                 fprintf(stderr, "sequence must be an array. Was: %s\n", json_object_get_string(o));
211                 exit(1);
212         }
213         n = json_object_array_length(x);
214         if (n == 0) {
215                 fprintf(stderr, "invalid empty sequence. Was: %s\n", json_object_get_string(o));
216                 exit(1);
217         }
218
219         /* process the array */
220         if (n == 1)
221                 return get_perm_string(json_object_array_get_idx(x, 0), pl);
222         b = NULL;
223         i = 0;
224         while (i != n) {
225                 y = json_object_array_get_idx(x, i);
226                 a = get_perm_string(y, &l);
227                 if (l > L) {
228                         c = malloc(3 + strlen(a));
229                         oom(c);
230                         stpcpy(stpcpy(stpcpy(c,"("),a),")");
231                         free(a);
232                         a = c;
233                 }
234                 if (!b)
235                         b = a;
236                 else {
237                         c = malloc(6 + strlen(a) + strlen(b));
238                         oom(c);
239                         stpcpy(stpcpy(stpcpy(c,b),L==2?" or ":" and "),a);
240                         free(a);
241                         free(b);
242                         b = c;
243                 }
244                 i++;
245         }
246         *pl = L;
247         return b;
248 }
249
250 struct json_object *make_perm(struct json_object *o)
251 {
252         int l;
253         char *permstr = get_perm_string(o, &l);
254         return json_object_new_string(permstr);
255 }
256
257 void make_permissions(struct path path)
258 {
259         struct json_object *o, *x, *y;
260         struct json_object_iterator ji, jn; 
261
262         if (json_object_object_get_ex(path.object, "permissions", &o)) {
263
264                 /* expand $refs of permissions */
265                 x = expand_$ref((struct path){ .object = o, .upper = &path });
266                 if (x != o)
267                         json_object_object_add(path.object, "permissions", x);
268
269                 /* makes the permissions */
270                 ji = json_object_iter_begin(o);
271                 jn = json_object_iter_end(o);
272                 while (!json_object_iter_equal(&ji, &jn)) {
273                         x = json_object_iter_peek_value(&ji);
274                         y = make_perm(x);
275                         if (x != y)
276                                 json_object_object_add(o, json_object_iter_peek_name(&ji), y);
277                         json_object_iter_next(&ji);
278                 }
279         }
280 }
281
282 char *make_desc(struct json_object *o)
283 {
284         const char *a, *b;
285         char *desc, c, buf[3];
286         size_t len;
287         int i, pos;
288
289         a = b = json_object_to_json_string_ext(root, 0);
290         len = 1;
291         while((c = *b++)) {
292                 len += 1 + ('"' == c);
293         }
294
295         len += 7 * (1 + len / 72);
296         desc = malloc(len);
297         oom(desc);
298
299         len = pos = 0;
300         b = a;
301         while((c = *b++)) {
302                 if (c == '"') {
303                         buf[0] = '\\';
304                         buf[1] = '"';
305                         buf[2] = 0;
306                 }
307                 else if (c == '\\') {
308                         switch ((c = *b++)) {
309                         case 0:
310                                 b--;
311                                 break;
312                         case '/':
313                                 buf[0] = '/';
314                                 buf[1] = 0;
315                                 break;
316                         default:
317                                 buf[0] = '\\';
318                                 buf[1] = c;
319                                 buf[2] = 0;
320                                 break;
321                         }
322                 }
323                 else {
324                         buf[0] = c;
325                         buf[1] = 0;
326                 }
327                 i = 0;
328                 while (buf[i]) {
329                         if (pos == 77) {
330                                 desc[len++] = '"';
331                                 desc[len++] = '\n';
332                                 pos = 0;
333                         }
334                         if (pos == 0) {
335                                 desc[len++] = ' ';
336                                 desc[len++] = ' ';
337                                 desc[len++] = ' ';
338                                 desc[len++] = ' ';
339                                 desc[len++] = '"';
340                                 pos = 5;
341                         }
342                         desc[len++] = buf[i++];
343                         pos++;
344                 }
345         }
346         desc[len++] = '"';
347         desc[len++] = '\n';
348         desc[len] = 0;
349         return desc;
350 }
351
352 void print_verbs(int real)
353 {
354         struct json_object_iterator ji, jn;
355         struct json_object *o, *v, *x, *y;
356         const char *verb, *perm, *loa;
357         int i, n, l;
358         const char **verbs;
359
360         /* search the verbs */
361         o = search("#/verbs");
362         if (!o)
363                 return;
364
365         /* list the verbs and sort it */
366         n = json_object_object_length(o);
367         verbs = malloc(n * sizeof *verbs);
368         oom(verbs);
369         n = 0;
370         ji = json_object_iter_begin(o);
371         jn = json_object_iter_end(o);
372         while (!json_object_iter_equal(&ji, &jn)) {
373                 verb = json_object_iter_peek_name(&ji);
374                 i = 0;
375                 while (i < n && strcasecmp(verb, verbs[i]) > 0)
376                         i++;
377                 if (i < n)
378                         memmove(verbs + i + 1, verbs + i, (n - i) * sizeof *verbs);
379                 verbs[i] = verb;
380                 n++;
381                 json_object_iter_next(&ji);
382         }
383
384         /* emit the verbs */
385         for (i = 0 ; i < n ; i++) {
386                 verb = verbs[i];
387                 json_object_object_get_ex(o, verb, &v);
388
389                 if (real) {
390                         if (!json_object_object_get_ex(v, "permissions", &x))
391                                 perm = "NULL";
392                         else {
393                                 perm = json_object_to_json_string(x);
394                         }
395                         l = 0;
396                         loa = "";
397                         if (json_object_object_get_ex(v, "LOA", &x)) {
398                                 if (json_object_is_type(x, json_type_int)) {
399                                         loa = "AFB_SESSION_LOA_EQ_";
400                                         y = x;
401                                 } else {
402                                         if (json_object_object_get_ex(x, "minimum", &y))
403                                                 loa = "AFB_SESSION_LOA_GE_";
404                                         else if (json_object_object_get_ex(x, "maximum", &y))
405                                                 loa = "AFB_SESSION_LOA_LE_";
406                                         else
407                                                 y = NULL;
408                                         if (y && !json_object_is_type(y, json_type_int))
409                                                 y = NULL;
410                                 }
411                                 l = json_object_get_int(y);
412                                 if (y == NULL || l < 0 || l > 3) {
413                                         fprintf(stderr, "invalid LOA spec. Was: %s",  json_object_get_string(x));
414                                         exit(1);
415                                 }
416                         }
417
418                         printf(
419                                 "    {\n"
420                                 "        .verb = \"%s\",\n"
421                                 "        .callback = %s%s%s,\n"
422                                 "        .permissions = %s,\n"
423                                 "        .session = %s%d,\n"
424                                 "    },\n"
425                                 , verb, prefix, verb, postfix, perm, loa, l
426                         );
427                 } else {
428                         printf(
429                                 "%s void %s%s%s(struct afb_req req);\n"
430                                 , scope, prefix, verb, postfix
431                         );
432                 }
433         }
434
435         free(verbs);
436 }
437
438 void getvar(const char **var, const char *path, const char *defval)
439 {
440         struct json_object *o;
441
442         if (!*var) {
443                 o = search(path);
444                 if (o && json_object_is_type(o, json_type_string))
445                         *var = json_object_get_string(o);
446                 else
447                         *var = defval;
448         }
449 }
450
451 /**
452  * process a file and prints its expansion on stdout
453  */
454 void process(char *filename)
455 {
456         char *desc;
457
458         /* translate - */
459         if (!strcmp(filename, "-"))
460                 filename = "/dev/stdin";
461
462         /* check access */
463         if (access(filename, R_OK)) {
464                 fprintf(stderr, "can't access file %s\n", filename);
465                 exit(1);
466         }
467
468         /* read the file */
469         root = json_object_from_file(filename);
470         if (!root) {
471                 fprintf(stderr, "reading file %s produced null\n", filename);
472                 exit(1);
473         }
474
475         /* create the description */
476         desc = make_desc(root);
477
478         /* generate permissions strings */
479         make_permissions((struct path){ .object = root, .upper = NULL });
480
481         /* expand references */
482         root = expand_$ref((struct path){ .object = root, .upper = NULL });
483
484         /* get some names */
485         getvar(&api, "#/api", "?");
486         getvar(&init, "#/meta-binding/init", NULL);
487         getvar(&start, "#/meta-binding/start", NULL);
488         getvar(&onevent, "#/meta-binding/onevent", NULL);
489         getvar(&scope, "#/meta-binding/scope", "static");
490         getvar(&prefix, "#/meta-binding/prefix", "afb_verb_");
491         getvar(&postfix, "#/meta-binding/postfix", "_cb");
492
493         /* get the API name */
494         printf(
495                 "\n"
496                 "static const char _afb_description_v2_[] =\n"
497                 "%s"
498                 ";\n"
499                 "\n"
500                 , desc
501         );
502         print_verbs(0);
503         printf(
504                 "\n"
505                 "static const struct afb_verb_v2 _afb_verbs_v2_[] = {\n"
506         );
507         print_verbs(1);
508         printf(
509                 "    { .verb = NULL }\n"
510                 "};\n"
511                 "\n"
512                 "const struct afb_binding_v2 afbBindingV2 = {\n"
513                 "    .api = \"%s\",\n"
514                 "    .specification = _afb_description_v2_,\n"
515                 "    .verbs = _afb_verbs_v2_,\n"
516                 "    .init = %s,\n"
517                 "    .start = %s,\n"
518                 "    .onevent = %s,\n"
519                 "};\n"
520                 "\n"
521                 , api?:"?", init?:"NULL", start?:"NULL", onevent?:"NULL"
522         );
523
524         /* clean up */
525         json_object_put(root);
526         free(desc);
527 }
528
529 /** process the list of files or stdin if none */
530 int main(int ac, char **av)
531 {
532         if (!*++av)
533                 process("-");
534         else {
535                 do { process(*av); } while(*++av);
536         }       
537         return 0;
538 }
539