LCOV - code coverage report
Current view: top level - templating - mustach-jansson.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 125 183 68.3 %
Date: 2022-08-25 06:15:09 Functions: 9 13 69.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  Copyright (C) 2020 Taler Systems SA
       3             : 
       4             :  Original license:
       5             :  Author: José Bollo <jobol@nonadev.net>
       6             :  Author: José Bollo <jose.bollo@iot.bzh>
       7             : 
       8             :  https://gitlab.com/jobol/mustach
       9             : 
      10             :  Licensed under the Apache License, Version 2.0 (the "License");
      11             :  you may not use this file except in compliance with the License.
      12             :  You may obtain a copy of the License at
      13             : 
      14             :      http://www.apache.org/licenses/LICENSE-2.0
      15             : 
      16             :  Unless required by applicable law or agreed to in writing, software
      17             :  distributed under the License is distributed on an "AS IS" BASIS,
      18             :  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      19             :  See the License for the specific language governing permissions and
      20             :  limitations under the License.
      21             : */
      22             : 
      23             : #include "platform.h"
      24             : #include "mustach-jansson.h"
      25             : 
      26             : struct Context
      27             : {
      28             :   /**
      29             :    * Context object.
      30             :    */
      31             :   json_t *cont;
      32             : 
      33             :   /**
      34             :    * Current object.
      35             :    */
      36             :   json_t *obj;
      37             : 
      38             :   /**
      39             :    * Opaque object iterator.
      40             :    */
      41             :   void *iter;
      42             : 
      43             :   /**
      44             :    * Current index when iterating over an array.
      45             :    */
      46             :   unsigned int index;
      47             : 
      48             :   /**
      49             :    * Count when iterating over an array.
      50             :    */
      51             :   unsigned int count;
      52             : 
      53             :   bool is_objiter;
      54             : };
      55             : 
      56             : enum Bang
      57             : {
      58             :   BANG_NONE,
      59             :   BANG_I18N,
      60             :   BANG_STRINGIFY,
      61             :   BANG_AMOUNT_CURRENCY,
      62             :   BANG_AMOUNT_DECIMAL,
      63             : };
      64             : 
      65             : struct JanssonClosure
      66             : {
      67             :   json_t *root;
      68             :   mustach_jansson_write_cb writecb;
      69             :   int depth;
      70             : 
      71             :   /**
      72             :    * Did the last find(..) call result in an iterable?
      73             :    */
      74             :   struct Context stack[MUSTACH_MAX_DEPTH];
      75             : 
      76             :   /**
      77             :    * The last object we found should be iterated over.
      78             :    */
      79             :   bool found_iter;
      80             : 
      81             :   /**
      82             :    * Last bang we found.
      83             :    */
      84             :   enum Bang found_bang;
      85             :   
      86             :   /**
      87             :    * Language for i18n lookups.
      88             :    */
      89             :   const char *lang;
      90             : };
      91             : 
      92             : 
      93             : static json_t *
      94          18 : walk (json_t *obj, const char *path)
      95             : {
      96          18 :   char *saveptr = NULL;
      97          18 :   char *sp = GNUNET_strdup (path);
      98          18 :   char *p = sp;
      99             :   while (true)
     100          18 :   {
     101          36 :     char *tok = strtok_r (p, ".", &saveptr);
     102          36 :     if (tok == NULL)
     103          16 :       break;
     104          20 :     obj = json_object_get (obj, tok);
     105          20 :     if (obj == NULL)
     106           2 :       break;
     107          18 :     p = NULL;
     108             :   }
     109          18 :   GNUNET_free (sp);
     110          18 :   return obj;
     111             : }
     112             : 
     113             : 
     114             : static json_t *
     115          17 : find (struct JanssonClosure *e, const char *name)
     116             : {
     117          17 :   json_t *obj = NULL;
     118          17 :   char *path = GNUNET_strdup (name);
     119             :   char *bang;
     120             : 
     121          17 :   bang = strchr (path, '!');
     122             : 
     123          17 :   e->found_bang = BANG_NONE;
     124             : 
     125          17 :   if (NULL != bang)
     126             :   {
     127           7 :     *bang = 0;
     128           7 :     bang++;
     129             : 
     130           7 :     if (0 == strcmp (bang, "i18n"))
     131           4 :       e->found_bang = BANG_I18N;
     132           3 :     else if (0 == strcmp(bang, "stringify"))
     133           1 :       e->found_bang = BANG_STRINGIFY;
     134           2 :     else if (0 == strcmp(bang, "amount_decimal"))
     135           1 :       e->found_bang = BANG_AMOUNT_CURRENCY;
     136           1 :     else if (0 == strcmp(bang, "amount_currency"))
     137           1 :       e->found_bang = BANG_AMOUNT_DECIMAL;
     138             :   }
     139             : 
     140          17 :   if (BANG_I18N == e->found_bang && NULL != e->lang)
     141             :   {
     142             :     char *aug_path;
     143           3 :     GNUNET_asprintf (&aug_path, "%s_i18n.%s", path, e->lang);
     144           3 :     obj = walk (e->stack[e->depth].obj, aug_path);
     145           3 :     GNUNET_free (aug_path);
     146             :   }
     147             : 
     148          17 :   if (NULL == obj)
     149             :   {
     150          15 :     obj = walk (e->stack[e->depth].obj, path);
     151             :   }
     152             : 
     153          17 :   GNUNET_free (path);
     154             : 
     155          17 :   return obj;
     156             : }
     157             : 
     158             : 
     159             : static int
     160          12 : start(void *closure)
     161             : {
     162          12 :   struct JanssonClosure *e = closure;
     163          12 :   e->depth = 0;
     164          12 :   e->stack[0].cont = NULL;
     165          12 :   e->stack[0].obj = e->root;
     166          12 :   e->stack[0].index = 0;
     167          12 :   e->stack[0].count = 1;
     168          12 :   e->lang = json_string_value (json_object_get (e->root, "$language"));
     169          12 :   return MUSTACH_OK;
     170             : }
     171             : 
     172             : 
     173             : static int
     174           0 : emituw (void *closure, const char *buffer, size_t size, int escape, FILE *file)
     175             : {
     176           0 :   struct JanssonClosure *e = closure;
     177           0 :   if (!escape)
     178           0 :     e->writecb (file, buffer, size);
     179             :   else
     180             :     do
     181             :     {
     182           0 :       switch (*buffer)
     183             :       {
     184           0 :         case '<':
     185           0 :           e->writecb (file, "&lt;", 4);
     186           0 :           break;
     187           0 :         case '>':
     188           0 :           e->writecb (file, "&gt;", 4);
     189           0 :           break;
     190           0 :         case '&':
     191           0 :           e->writecb (file, "&amp;", 5);
     192           0 :           break;
     193           0 :         default:
     194           0 :           e->writecb (file, buffer, 1);
     195           0 :           break;
     196             :       }
     197           0 :       buffer++;
     198             :     }
     199           0 :     while(--size);
     200           0 :   return MUSTACH_OK;
     201             : }
     202             : 
     203             : 
     204             : static int
     205           3 : enter(void *closure, const char *name)
     206             : {
     207           3 :   struct JanssonClosure *e = closure;
     208           3 :   json_t *o = find(e, name);
     209           3 :   if (++e->depth >= MUSTACH_MAX_DEPTH)
     210           0 :     return MUSTACH_ERROR_TOO_DEEP;
     211             : 
     212           3 :   if (json_is_object (o))
     213             :   {
     214           1 :     if (e->found_iter)
     215             :     {
     216           0 :       void *iter = json_object_iter (o);
     217           0 :       if (NULL == iter)
     218             :       {
     219           0 :         e->depth--;
     220           0 :         return 0;
     221             :       }
     222           0 :       e->stack[e->depth].is_objiter = 1;
     223           0 :       e->stack[e->depth].iter = iter;
     224           0 :       e->stack[e->depth].obj = json_object_iter_value (iter);
     225           0 :       e->stack[e->depth].cont = o;
     226             :     }
     227             :     else
     228             :     {
     229           1 :       e->stack[e->depth].is_objiter = 0;
     230           1 :       e->stack[e->depth].obj = o;
     231           1 :       e->stack[e->depth].cont = o;
     232             :     }
     233           1 :     return 1;
     234             :   }
     235             : 
     236           2 :   if (json_is_array (o))
     237             :   {
     238           2 :     unsigned int size = json_array_size (o);
     239           2 :     if (size == 0)
     240             :     {
     241           1 :       e->depth--;
     242           1 :       return 0;
     243             :     }
     244           1 :     e->stack[e->depth].count = size;
     245           1 :     e->stack[e->depth].cont = o;
     246           1 :     e->stack[e->depth].obj = json_array_get (o, 0);
     247           1 :     e->stack[e->depth].index = 0;
     248           1 :     e->stack[e->depth].is_objiter = 0;
     249           1 :     return 1;
     250             :   }
     251             : 
     252           0 :   e->depth--;
     253           0 :   return 0;
     254             : }
     255             : 
     256             : 
     257             : static int
     258           3 : next (void *closure)
     259             : {
     260           3 :   struct JanssonClosure *e = closure;
     261             :   struct Context *ctx;
     262           3 :   if (e->depth <= 0)
     263           0 :     return MUSTACH_ERROR_CLOSING;
     264           3 :   ctx = &e->stack[e->depth];
     265           3 :   if (ctx->is_objiter)
     266             :   {
     267           0 :     ctx->iter = json_object_iter_next (ctx->obj, ctx->iter);
     268           0 :     if (NULL == ctx->iter)
     269           0 :       return 0;
     270           0 :     ctx->obj = json_object_iter_value (ctx->iter);
     271           0 :     return 1;
     272             :   }
     273           3 :   ctx->index++;
     274           3 :   if (ctx->index >= ctx->count)
     275           2 :     return 0;
     276           1 :   ctx->obj = json_array_get (ctx->cont, ctx->index);
     277           1 :   return 1;
     278             : }
     279             : 
     280             : static int
     281           2 : leave (void *closure)
     282             : {
     283           2 :   struct JanssonClosure *e = closure;
     284           2 :   if (e->depth <= 0)
     285           0 :     return MUSTACH_ERROR_CLOSING;
     286           2 :   e->depth--;
     287           2 :   return 0;
     288             : }
     289             : 
     290             : static void
     291           2 : freecb (void *v)
     292             : {
     293           2 :   free (v);
     294           2 : }
     295             : 
     296             : static int
     297          14 : get (void *closure, const char *name, struct mustach_sbuf *sbuf)
     298             : {
     299          14 :   struct JanssonClosure *e = closure;
     300             :   json_t *obj;
     301             : 
     302          14 :   if ( (0 == strcmp (name, "*") ) &&
     303           0 :        (e->stack[e->depth].is_objiter ) )
     304             :   {
     305           0 :     sbuf->value = json_object_iter_key (e->stack[e->depth].iter);
     306           0 :     return MUSTACH_OK;
     307             :   }
     308          14 :   obj = find (e, name);
     309          14 :   if (NULL != obj)
     310             :   {
     311          13 :     switch (e->found_bang)
     312             :     {
     313          10 :       case BANG_I18N:
     314             :       case BANG_NONE:
     315             :         {
     316          10 :           const char *s = json_string_value (obj);
     317          10 :           if (NULL != s)
     318             :           {
     319          10 :             sbuf->value = s;
     320          10 :             return MUSTACH_OK;
     321             :           }
     322             :         }
     323           0 :         break;
     324           1 :       case BANG_STRINGIFY:
     325           1 :         sbuf->value = json_dumps (obj, JSON_INDENT (2));
     326           1 :         sbuf->freecb = freecb;
     327           1 :         return MUSTACH_OK;
     328           1 :       case BANG_AMOUNT_DECIMAL:
     329             :         {
     330             :           char *s;
     331             :           char *c;
     332           1 :           if (!json_is_string (obj))
     333             :             break;
     334           1 :           s = strdup (json_string_value (obj));
     335           1 :           c = strchr (s, ':');
     336           1 :           if (NULL != c)
     337           1 :             *c = 0;
     338           1 :           sbuf->value = s;
     339           1 :           sbuf->freecb = freecb;
     340           1 :           return MUSTACH_OK;
     341             :         }
     342             :         break;
     343           1 :       case BANG_AMOUNT_CURRENCY:
     344             :         {
     345             :           const char *s;
     346           1 :           if (!json_is_string (obj))
     347             :             break;
     348           1 :           s = json_string_value (obj);
     349           1 :           s = strchr (s, ':');
     350           1 :           if (NULL == s)
     351           0 :             break;
     352           1 :           sbuf->value = s + 1;
     353           1 :           return MUSTACH_OK;
     354             :         }
     355             :         break;
     356           0 :       default:
     357           0 :         break;
     358             :     }
     359           1 :   }
     360           1 :   sbuf->value = "";
     361           1 :   return MUSTACH_OK;
     362             : }
     363             : 
     364             : static struct mustach_itf itf = {
     365             :   .start = start,
     366             :   .put = NULL,
     367             :   .enter = enter,
     368             :   .next = next,
     369             :   .leave = leave,
     370             :   .partial =NULL,
     371             :   .get = get,
     372             :   .emit = NULL,
     373             :   .stop = NULL
     374             : };
     375             : 
     376             : static struct mustach_itf itfuw = {
     377             :   .start = start,
     378             :   .put = NULL,
     379             :   .enter = enter,
     380             :   .next = next,
     381             :   .leave = leave,
     382             :   .partial = NULL,
     383             :   .get = get,
     384             :   .emit = emituw,
     385             :   .stop = NULL
     386             : };
     387             : 
     388           0 : int fmustach_jansson (const char *template, json_t *root, FILE *file)
     389             : {
     390           0 :   struct JanssonClosure e = { 0 };
     391           0 :   e.root = root;
     392           0 :   return fmustach(template, &itf, &e, file);
     393             : }
     394             : 
     395           0 : int fdmustach_jansson (const char *template, json_t *root, int fd)
     396             : {
     397           0 :   struct JanssonClosure e = { 0 };
     398           0 :   e.root = root;
     399           0 :   return fdmustach(template, &itf, &e, fd);
     400             : }
     401             : 
     402          12 : int mustach_jansson (const char *template, json_t *root, char **result, size_t *size)
     403             : {
     404          12 :   struct JanssonClosure e = { 0 };
     405          12 :   e.root = root;
     406          12 :   e.writecb = NULL;
     407          12 :   return mustach(template, &itf, &e, result, size);
     408             : }
     409             : 
     410           0 : int umustach_jansson (const char *template, json_t *root, mustach_jansson_write_cb writecb, void *closure)
     411             : {
     412           0 :   struct JanssonClosure e = { 0 };
     413           0 :   e.root = root;
     414           0 :   e.writecb = writecb;
     415           0 :   return fmustach(template, &itfuw, &e, closure);
     416             : }
     417             : 

Generated by: LCOV version 1.14