diff options
author | Christian Grothoff <christian@grothoff.org> | 2022-08-16 13:56:04 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2022-08-16 13:57:26 +0200 |
commit | d6f12190c0d953dd3090153a45ecdd10f01cd9c3 (patch) | |
tree | 722a7e55f34c6b457cad213464ca8a9372044db3 /src/templating/mustach-jansson.c | |
parent | 1e2fdea5a977a9fdbb7bcc0632d9fb1c8ef82987 (diff) | |
download | exchange-d6f12190c0d953dd3090153a45ecdd10f01cd9c3.tar.xz |
-move templating library into exchange.git
Diffstat (limited to 'src/templating/mustach-jansson.c')
-rw-r--r-- | src/templating/mustach-jansson.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/src/templating/mustach-jansson.c b/src/templating/mustach-jansson.c new file mode 100644 index 000000000..2aed58291 --- /dev/null +++ b/src/templating/mustach-jansson.c @@ -0,0 +1,417 @@ +/* + Copyright (C) 2020 Taler Systems SA + + Original license: + Author: José Bollo <jobol@nonadev.net> + Author: José Bollo <jose.bollo@iot.bzh> + + https://gitlab.com/jobol/mustach + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "platform.h" +#include "mustach-jansson.h" + +struct Context +{ + /** + * Context object. + */ + json_t *cont; + + /** + * Current object. + */ + json_t *obj; + + /** + * Opaque object iterator. + */ + void *iter; + + /** + * Current index when iterating over an array. + */ + unsigned int index; + + /** + * Count when iterating over an array. + */ + unsigned int count; + + bool is_objiter; +}; + +enum Bang +{ + BANG_NONE, + BANG_I18N, + BANG_STRINGIFY, + BANG_AMOUNT_CURRENCY, + BANG_AMOUNT_DECIMAL, +}; + +struct JanssonClosure +{ + json_t *root; + mustach_jansson_write_cb writecb; + int depth; + + /** + * Did the last find(..) call result in an iterable? + */ + struct Context stack[MUSTACH_MAX_DEPTH]; + + /** + * The last object we found should be iterated over. + */ + bool found_iter; + + /** + * Last bang we found. + */ + enum Bang found_bang; + + /** + * Language for i18n lookups. + */ + const char *lang; +}; + + +static json_t * +walk (json_t *obj, const char *path) +{ + char *saveptr = NULL; + char *sp = GNUNET_strdup (path); + char *p = sp; + while (true) + { + char *tok = strtok_r (p, ".", &saveptr); + if (tok == NULL) + break; + obj = json_object_get (obj, tok); + if (obj == NULL) + break; + p = NULL; + } + GNUNET_free (sp); + return obj; +} + + +static json_t * +find (struct JanssonClosure *e, const char *name) +{ + json_t *obj = NULL; + char *path = GNUNET_strdup (name); + char *bang; + + bang = strchr (path, '!'); + + e->found_bang = BANG_NONE; + + if (NULL != bang) + { + *bang = 0; + bang++; + + if (0 == strcmp (bang, "i18n")) + e->found_bang = BANG_I18N; + else if (0 == strcmp(bang, "stringify")) + e->found_bang = BANG_STRINGIFY; + else if (0 == strcmp(bang, "amount_decimal")) + e->found_bang = BANG_AMOUNT_CURRENCY; + else if (0 == strcmp(bang, "amount_currency")) + e->found_bang = BANG_AMOUNT_DECIMAL; + } + + if (BANG_I18N == e->found_bang && NULL != e->lang) + { + char *aug_path; + GNUNET_asprintf (&aug_path, "%s_i18n.%s", path, e->lang); + obj = walk (e->stack[e->depth].obj, aug_path); + GNUNET_free (aug_path); + } + + if (NULL == obj) + { + obj = walk (e->stack[e->depth].obj, path); + } + + GNUNET_free (path); + + return obj; +} + + +static int +start(void *closure) +{ + struct JanssonClosure *e = closure; + e->depth = 0; + e->stack[0].cont = NULL; + e->stack[0].obj = e->root; + e->stack[0].index = 0; + e->stack[0].count = 1; + e->lang = json_string_value (json_object_get (e->root, "$language")); + return MUSTACH_OK; +} + + +static int +emituw (void *closure, const char *buffer, size_t size, int escape, FILE *file) +{ + struct JanssonClosure *e = closure; + if (!escape) + e->writecb (file, buffer, size); + else + do + { + switch (*buffer) + { + case '<': + e->writecb (file, "<", 4); + break; + case '>': + e->writecb (file, ">", 4); + break; + case '&': + e->writecb (file, "&", 5); + break; + default: + e->writecb (file, buffer, 1); + break; + } + buffer++; + } + while(--size); + return MUSTACH_OK; +} + + +static int +enter(void *closure, const char *name) +{ + struct JanssonClosure *e = closure; + json_t *o = find(e, name); + if (++e->depth >= MUSTACH_MAX_DEPTH) + return MUSTACH_ERROR_TOO_DEEP; + + if (json_is_object (o)) + { + if (e->found_iter) + { + void *iter = json_object_iter (o); + if (NULL == iter) + { + e->depth--; + return 0; + } + e->stack[e->depth].is_objiter = 1; + e->stack[e->depth].iter = iter; + e->stack[e->depth].obj = json_object_iter_value (iter); + e->stack[e->depth].cont = o; + } + else + { + e->stack[e->depth].is_objiter = 0; + e->stack[e->depth].obj = o; + e->stack[e->depth].cont = o; + } + return 1; + } + + if (json_is_array (o)) + { + unsigned int size = json_array_size (o); + if (size == 0) + { + e->depth--; + return 0; + } + e->stack[e->depth].count = size; + e->stack[e->depth].cont = o; + e->stack[e->depth].obj = json_array_get (o, 0); + e->stack[e->depth].index = 0; + e->stack[e->depth].is_objiter = 0; + return 1; + } + + e->depth--; + return 0; +} + + +static int +next (void *closure) +{ + struct JanssonClosure *e = closure; + struct Context *ctx; + if (e->depth <= 0) + return MUSTACH_ERROR_CLOSING; + ctx = &e->stack[e->depth]; + if (ctx->is_objiter) + { + ctx->iter = json_object_iter_next (ctx->obj, ctx->iter); + if (NULL == ctx->iter) + return 0; + ctx->obj = json_object_iter_value (ctx->iter); + return 1; + } + ctx->index++; + if (ctx->index >= ctx->count) + return 0; + ctx->obj = json_array_get (ctx->cont, ctx->index); + return 1; +} + +static int +leave (void *closure) +{ + struct JanssonClosure *e = closure; + if (e->depth <= 0) + return MUSTACH_ERROR_CLOSING; + e->depth--; + return 0; +} + +static void +freecb (void *v) +{ + free (v); +} + +static int +get (void *closure, const char *name, struct mustach_sbuf *sbuf) +{ + struct JanssonClosure *e = closure; + json_t *obj; + + if ( (0 == strcmp (name, "*") ) && + (e->stack[e->depth].is_objiter ) ) + { + sbuf->value = json_object_iter_key (e->stack[e->depth].iter); + return MUSTACH_OK; + } + obj = find (e, name); + if (NULL != obj) + { + switch (e->found_bang) + { + case BANG_I18N: + case BANG_NONE: + { + const char *s = json_string_value (obj); + if (NULL != s) + { + sbuf->value = s; + return MUSTACH_OK; + } + } + break; + case BANG_STRINGIFY: + sbuf->value = json_dumps (obj, JSON_INDENT (2)); + sbuf->freecb = freecb; + return MUSTACH_OK; + case BANG_AMOUNT_DECIMAL: + { + char *s; + char *c; + if (!json_is_string (obj)) + break; + s = strdup (json_string_value (obj)); + c = strchr (s, ':'); + if (NULL != c) + *c = 0; + sbuf->value = s; + sbuf->freecb = freecb; + return MUSTACH_OK; + } + break; + case BANG_AMOUNT_CURRENCY: + { + const char *s; + if (!json_is_string (obj)) + break; + s = json_string_value (obj); + s = strchr (s, ':'); + if (NULL == s) + break; + sbuf->value = s + 1; + return MUSTACH_OK; + } + break; + default: + break; + } + } + sbuf->value = ""; + return MUSTACH_OK; +} + +static struct mustach_itf itf = { + .start = start, + .put = NULL, + .enter = enter, + .next = next, + .leave = leave, + .partial =NULL, + .get = get, + .emit = NULL, + .stop = NULL +}; + +static struct mustach_itf itfuw = { + .start = start, + .put = NULL, + .enter = enter, + .next = next, + .leave = leave, + .partial = NULL, + .get = get, + .emit = emituw, + .stop = NULL +}; + +int fmustach_jansson (const char *template, json_t *root, FILE *file) +{ + struct JanssonClosure e = { 0 }; + e.root = root; + return fmustach(template, &itf, &e, file); +} + +int fdmustach_jansson (const char *template, json_t *root, int fd) +{ + struct JanssonClosure e = { 0 }; + e.root = root; + return fdmustach(template, &itf, &e, fd); +} + +int mustach_jansson (const char *template, json_t *root, char **result, size_t *size) +{ + struct JanssonClosure e = { 0 }; + e.root = root; + e.writecb = NULL; + return mustach(template, &itf, &e, result, size); +} + +int umustach_jansson (const char *template, json_t *root, mustach_jansson_write_cb writecb, void *closure) +{ + struct JanssonClosure e = { 0 }; + e.root = root; + e.writecb = writecb; + return fmustach(template, &itfuw, &e, closure); +} + |