mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 15:14:08 +00:00
282 lines
9.2 KiB
C
282 lines
9.2 KiB
C
#include "fio.h"
|
|
|
|
#define INCLUDE_MUSTACHE_IMPLEMENTATION 1
|
|
#include "mustache_parser.h"
|
|
|
|
static size_t callback_count = 0;
|
|
|
|
static struct {
|
|
enum cb_type_e {
|
|
CB_ERROR,
|
|
CB_ON_TEXT,
|
|
CB_ON_ARG,
|
|
CB_ON_ARG_UNESCAPE,
|
|
CB_ON_TEST,
|
|
CB_ON_START,
|
|
} cb_type;
|
|
void *udata1;
|
|
} callback_expected[] = {
|
|
{CB_ON_TEXT, (void *)0}, {CB_ON_TEST, (void *)0},
|
|
{CB_ON_START, (void *)0}, {CB_ON_ARG, (void *)1},
|
|
{CB_ON_START, (void *)0}, {CB_ON_ARG, (void *)1},
|
|
{CB_ON_ARG_UNESCAPE, (void *)0}, {CB_ON_ARG_UNESCAPE, (void *)0},
|
|
{CB_ON_TEST, (void *)0}, {CB_ON_START, (void *)0},
|
|
{CB_ON_ARG_UNESCAPE, (void *)1}, {CB_ON_ARG_UNESCAPE, (void *)1},
|
|
{CB_ON_TEST, (void *)1}, {CB_ERROR, NULL},
|
|
};
|
|
|
|
static const size_t callback_max =
|
|
sizeof(callback_expected) / sizeof(callback_expected[0]);
|
|
|
|
static void mustache_test_callback(mustache_section_s *section,
|
|
enum cb_type_e expected) {
|
|
switch (expected) {
|
|
case CB_ON_TEXT:
|
|
fprintf(stderr, "* mustache callback for text detected.\n");
|
|
break;
|
|
case CB_ON_ARG:
|
|
fprintf(stderr, "* mustache callback for argument detected.\n");
|
|
break;
|
|
case CB_ON_ARG_UNESCAPE:
|
|
fprintf(stderr, "* mustache callback for unescaped argument detected.\n");
|
|
break;
|
|
case CB_ON_TEST: {
|
|
size_t len;
|
|
const char *txt = mustache_section_text(section, &len);
|
|
if (txt)
|
|
fprintf(stderr,
|
|
"* mustache callback for section testing detected. section "
|
|
"string:\n%.*s\n",
|
|
(int)len, txt);
|
|
else
|
|
fprintf(stderr, "* mustache callback for section testing detected (no "
|
|
"string data available)\n");
|
|
} break;
|
|
case CB_ON_START:
|
|
fprintf(stderr, "* mustache callback for section start detected.\n");
|
|
break;
|
|
case CB_ERROR:
|
|
fprintf(stderr, "* mustache callback for ERROR detected.\n");
|
|
break;
|
|
}
|
|
if (callback_expected[callback_count].cb_type == CB_ERROR) {
|
|
fprintf(stderr, "FAILED: mustache callback count overflow\n");
|
|
exit(-1);
|
|
}
|
|
|
|
if (callback_expected[callback_count].cb_type != expected) {
|
|
fprintf(stderr,
|
|
"FAILED: mustache callback type mismatch (count: %zu, expected %u, "
|
|
"got %u)\n",
|
|
callback_count, callback_expected[callback_count].cb_type,
|
|
expected);
|
|
exit(-1);
|
|
}
|
|
if (callback_expected[callback_count].udata1 != section->udata1) {
|
|
fprintf(stderr,
|
|
"FAILED: mustache callback udata1 mismatch (count: %zu, expected "
|
|
"%p, got %p)\n",
|
|
callback_count, callback_expected[callback_count].udata1,
|
|
section->udata1);
|
|
exit(-1);
|
|
}
|
|
++callback_count;
|
|
}
|
|
|
|
static int mustache_on_arg(mustache_section_s *section, const char *name,
|
|
uint32_t name_len, unsigned char escape) {
|
|
mustache_test_callback(section, escape ? CB_ON_ARG : CB_ON_ARG_UNESCAPE);
|
|
(void)name;
|
|
(void)name_len;
|
|
return 0;
|
|
}
|
|
|
|
static int mustache_on_text(mustache_section_s *section, const char *data,
|
|
uint32_t data_len) {
|
|
mustache_test_callback(section, CB_ON_TEXT);
|
|
(void)data;
|
|
(void)data_len;
|
|
return 0;
|
|
}
|
|
|
|
static int32_t mustache_on_section_test(mustache_section_s *section,
|
|
const char *name, uint32_t name_len,
|
|
uint8_t callable) {
|
|
fprintf(stderr, "* mustache testing section %.*s\n", (int)name_len, name);
|
|
mustache_test_callback(section, CB_ON_TEST);
|
|
(void)name;
|
|
(void)name_len;
|
|
static size_t ret = 0;
|
|
switch (ret++) {
|
|
case 0:
|
|
return 2;
|
|
case 1:
|
|
return 0;
|
|
default:
|
|
return 1;
|
|
}
|
|
(void)callable;
|
|
}
|
|
|
|
static int mustache_on_section_start(mustache_section_s *section,
|
|
char const *name, uint32_t name_len,
|
|
uint32_t index) {
|
|
fprintf(stderr, "* mustache entering section %.*s\n", (int)name_len, name);
|
|
mustache_test_callback(section, CB_ON_START);
|
|
section->udata1 = (void *)((uintptr_t)section->udata1 + 1);
|
|
(void)index;
|
|
(void)name;
|
|
(void)name_len;
|
|
return 0;
|
|
}
|
|
|
|
static void mustache_on_formatting_error(void *udata1, void *udata2) {
|
|
FIO_LOG_ERROR("mustache formatting error.");
|
|
(void)udata1;
|
|
(void)udata2;
|
|
}
|
|
|
|
static inline void save2file(char const *filename, char const *data,
|
|
size_t length) {
|
|
int fd = open(filename, O_CREAT | O_RDWR, 0);
|
|
if (fd == -1) {
|
|
perror("Couldn't open / create file for template testing");
|
|
exit(-1);
|
|
}
|
|
fchmod(fd, 0777);
|
|
if (pwrite(fd, data, length, 0) != (ssize_t)length) {
|
|
perror("Mustache template write error");
|
|
exit(-1);
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
static inline void mustache_print_instructions(mustache_s *m) {
|
|
mustache__instruction_s *ary = (mustache__instruction_s *)(m + 1);
|
|
for (uint32_t i = 0; i < m->u.read_only.intruction_count; ++i) {
|
|
char *name = NULL;
|
|
switch (ary[i].instruction) {
|
|
case MUSTACHE_WRITE_TEXT:
|
|
name = "MUSTACHE_WRITE_TEXT";
|
|
break;
|
|
case MUSTACHE_WRITE_ARG:
|
|
name = "MUSTACHE_WRITE_ARG";
|
|
break;
|
|
case MUSTACHE_WRITE_ARG_UNESCAPED:
|
|
name = "MUSTACHE_WRITE_ARG_UNESCAPED";
|
|
break;
|
|
case MUSTACHE_SECTION_START:
|
|
name = "MUSTACHE_SECTION_START";
|
|
break;
|
|
case MUSTACHE_SECTION_START_INV:
|
|
name = "MUSTACHE_SECTION_START_INV";
|
|
break;
|
|
case MUSTACHE_SECTION_END:
|
|
name = "MUSTACHE_SECTION_END";
|
|
break;
|
|
case MUSTACHE_SECTION_GOTO:
|
|
name = "MUSTACHE_SECTION_GOTO";
|
|
break;
|
|
case MUSTACHE_PADDING_PUSH:
|
|
name = "MUSTACHE_PADDING_PUSH";
|
|
break;
|
|
case MUSTACHE_PADDING_POP:
|
|
name = "MUSTACHE_PADDING_POP";
|
|
break;
|
|
case MUSTACHE_PADDING_WRITE:
|
|
name = "MUSTACHE_PADDING_WRITE";
|
|
break;
|
|
default:
|
|
name = "UNKNOWN!!!";
|
|
break;
|
|
}
|
|
fprintf(stderr, "[%u] %s, start: %u, len %u\n", i, name,
|
|
ary[i].data.name_pos, ary[i].data.name_len);
|
|
}
|
|
}
|
|
|
|
void mustache_test(void) {
|
|
fprintf(stderr, "=== Testing Mustache parser (mustache_parser.h)\n");
|
|
char const *template =
|
|
"Hi there{{#user}}{{name}}{{/user}}{{> mustache_test_partial }}";
|
|
char const *partial = "{{& raw1}}{{{raw2}}}{{^negative}}"
|
|
"{{> mustache_test_partial }}{{=<< >>=}}<</negative>>";
|
|
char const *partial2 = "{{& raw1}}{{{raw2}}}{{^negative}}"
|
|
"{{=<< >>=}}<</negative>>";
|
|
char const *template_name = "mustache_test_template.mustache";
|
|
char const *partial_name = "mustache_test_partial.mustache";
|
|
save2file(template_name, template, strlen(template));
|
|
mustache_error_en err = MUSTACHE_OK;
|
|
mustache_s *m;
|
|
m = mustache_load(.filename = template_name, .err = &err);
|
|
if (m) {
|
|
unlink(template_name);
|
|
FIO_ASSERT(!m,
|
|
"Mustache template loading should have failed without partial "
|
|
"(err = %u)\n",
|
|
err);
|
|
}
|
|
save2file(partial_name, partial, strlen(partial));
|
|
|
|
m = mustache_load(.filename = template_name, .err = &err);
|
|
if (!m) {
|
|
unlink(template_name);
|
|
unlink(partial_name);
|
|
FIO_ASSERT(m, "Mustache template loading from file failed with error %u\n",
|
|
err);
|
|
}
|
|
mustache_free(m);
|
|
m = mustache_load(.data = partial2, .data_len = strlen(partial2),
|
|
.err = &err);
|
|
if (!m) {
|
|
unlink(template_name);
|
|
unlink(partial_name);
|
|
FIO_ASSERT(
|
|
m, "Mustache template loading partial as data failed with error %u\n",
|
|
err);
|
|
}
|
|
mustache_free(m);
|
|
m = mustache_load(.filename = template_name, .data = template,
|
|
.data_len = strlen(template), .err = &err);
|
|
unlink(template_name);
|
|
unlink(partial_name);
|
|
|
|
uint32_t expected[] = {
|
|
MUSTACHE_SECTION_START, MUSTACHE_WRITE_TEXT,
|
|
MUSTACHE_SECTION_START, MUSTACHE_WRITE_ARG,
|
|
MUSTACHE_SECTION_END, MUSTACHE_SECTION_START,
|
|
MUSTACHE_WRITE_ARG_UNESCAPED, MUSTACHE_WRITE_ARG_UNESCAPED,
|
|
MUSTACHE_SECTION_START_INV, MUSTACHE_SECTION_GOTO,
|
|
MUSTACHE_SECTION_END, MUSTACHE_SECTION_END,
|
|
MUSTACHE_SECTION_END,
|
|
};
|
|
|
|
FIO_ASSERT(m, "Mustache template loading failed with error %u\n", err);
|
|
|
|
fprintf(stderr, "* template loaded, testing template instruction array.\n");
|
|
mustache_print_instructions(m);
|
|
mustache__instruction_s *ary = (mustache__instruction_s *)(m + 1);
|
|
|
|
FIO_ASSERT(m->u.read_only.intruction_count == 13,
|
|
"Mustache template instruction count error %u\n",
|
|
m->u.read_only.intruction_count);
|
|
|
|
for (uint16_t i = 0; i < 13; ++i) {
|
|
FIO_ASSERT(ary[i].instruction == expected[i],
|
|
"Mustache instraction[%u] error, type %u != %u\n", i,
|
|
ary[0].instruction, expected[i]);
|
|
}
|
|
fprintf(stderr,
|
|
"* template loaded, testing template build callbacks for data.\n");
|
|
mustache_build(m, .udata1 = NULL, .err = &err);
|
|
FIO_ASSERT(!err, "Mustache template building template failed with error %u\n",
|
|
err);
|
|
FIO_ASSERT(callback_count + 1 == callback_max,
|
|
"Callback count error %zu != %zu", callback_count + 1,
|
|
callback_max);
|
|
FIO_ASSERT(callback_expected[callback_count].cb_type == CB_ERROR,
|
|
"Callback type error on finish");
|
|
/* cleanup */
|
|
mustache_free(m);
|
|
fprintf(stderr, "* passed.\n");
|
|
}
|