test(hmpl): render interpolation tags

This commit is contained in:
2025-03-22 14:56:03 +00:00
parent aeea41b0e1
commit 546a35f1d2
12 changed files with 146 additions and 30 deletions

View File

@@ -85,9 +85,9 @@
pg-from = pkgs.callPackage ./package/postgres/pg-from/default.nix rust.commonArgs; pg-from = pkgs.callPackage ./package/postgres/pg-from/default.nix rust.commonArgs;
pg-schema = pkgs.callPackage ./package/postgres/pg-schema/default.nix rust.commonArgs; pg-schema = pkgs.callPackage ./package/postgres/pg-schema/default.nix rust.commonArgs;
pg-migration = pkgs.callPackage ./package/postgres/pg-migration/default.nix rust.commonArgs; pg-migration = pkgs.callPackage ./package/postgres/pg-migration/default.nix rust.commonArgs;
chectic = pkgs.callPackage ./package/c/chectic/default.nix {}; hectic = pkgs.callPackage ./package/c/hectic/default.nix {};
hmpl = pkgs.callPackage ./package/c/hmpl/default.nix { hmpl = pkgs.callPackage ./package/c/hmpl/default.nix {
chectic = self.packages.${system}.chectic; hectic = self.packages.${system}.hectic;
}; };
}; };

View File

@@ -1,7 +1,7 @@
{ stdenv, gcc, lib }: { stdenv, gcc, lib }:
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "chectic"; pname = "hectic";
version = "1.0"; version = "1.0";
src = ./.; src = ./.;
doCheck = true; doCheck = true;
@@ -11,23 +11,23 @@ stdenv.mkDerivation {
${gcc}/bin/cc -Wall -Wextra -g \ ${gcc}/bin/cc -Wall -Wextra -g \
-std=c99 \ -std=c99 \
-pedantic -fsanitize=address \ -pedantic -fsanitize=address \
-c chectic.c -o target/chectic.o -c hectic.c -o target/hectic.o
${gcc}/bin/ar rcs target/libchectic.a target/chectic.o ${gcc}/bin/ar rcs target/libhectic.a target/hectic.o
''; '';
checkPhase = '' checkPhase = ''
mkdir -p target/test mkdir -p target/test
for test_file in test/*.c; do for test_file in test/*.c; do
exe="target/test/$(basename ''${test_file%.c})" exe="target/test/$(basename ''${test_file%.c})"
${gcc}/bin/cc -Wall -Wextra -g -pedantic -fsanitize=address -I. "$test_file" -Ltarget -lchectic -o "$exe" ${gcc}/bin/cc -Wall -Wextra -g -pedantic -fsanitize=address -I. "$test_file" -Ltarget -lhectic -o "$exe"
"$exe" "$exe"
done done
''; '';
installPhase = '' installPhase = ''
mkdir -p $out/lib $out/include mkdir -p $out/lib $out/include
cp target/libchectic.a $out/lib/ cp target/libhectic.a $out/lib/
cp chectic.h $out/include/ cp hectic.h $out/include/
''; '';
meta = { meta = {

View File

@@ -1,4 +1,4 @@
#include "chectic.h" #include "hectic.h"
void set_output_color_mode(ColorMode mode) { void set_output_color_mode(ColorMode mode) {
color_mode = mode; color_mode = mode;

View File

@@ -1,5 +1,5 @@
#ifndef EPRINTF_CHECTIC #ifndef EPRINTF_HECTIC
#define EPRINTF_CHECTIC #define EPRINTF_HECTIC
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>

View File

@@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "chectic.h" #include "hectic.h"
#define TEST_RAISE_GENERIC(LOG_MACRO, LEVEL, LEVEL_STR) do { \ #define TEST_RAISE_GENERIC(LOG_MACRO, LEVEL, LEVEL_STR) do { \
FILE *orig_stderr = stderr; \ FILE *orig_stderr = stderr; \

View File

@@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "chectic.h" #include "hectic.h"
void test_arena_init() { void test_arena_init() {
Arena arena = arena_init(128); Arena arena = arena_init(128);

View File

@@ -3,7 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include "chectic.h" #include "hectic.h"
#define ARENA_SIZE 1024 * 1024 #define ARENA_SIZE 1024 * 1024

View File

@@ -1,4 +1,4 @@
{ stdenv, gcc, lib, chectic }: { stdenv, gcc, lib, hectic }:
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "hmpl"; pname = "hmpl";
@@ -6,7 +6,7 @@ stdenv.mkDerivation {
src = ./.; src = ./.;
doCheck = true; doCheck = true;
buildInputs = [ chectic ]; buildInputs = [ hectic ];
buildPhase = '' buildPhase = ''
mkdir -p target mkdir -p target
@@ -15,7 +15,7 @@ stdenv.mkDerivation {
${gcc}/bin/cc -Wall -Wextra -g \ ${gcc}/bin/cc -Wall -Wextra -g \
-std=c99 \ -std=c99 \
-pedantic -fsanitize=address -c hmpl.c \ -pedantic -fsanitize=address -c hmpl.c \
-lchectic \ -lhectic \
-o target/hmpl.o -o target/hmpl.o
${gcc}/bin/ar rcs target/libhmpl.a target/hmpl.o ${gcc}/bin/ar rcs target/libhmpl.a target/hmpl.o
@@ -23,10 +23,19 @@ stdenv.mkDerivation {
${gcc}/bin/cc -Wall -Wextra -g \ ${gcc}/bin/cc -Wall -Wextra -g \
-pedantic -fsanitize=address main.c \ -pedantic -fsanitize=address main.c \
-Ltarget -lhmpl \ -Ltarget -lhmpl \
-lchectic -o target/hmpl -lhectic -o target/hmpl
''; '';
checkPhase = '' ''; checkPhase = ''
mkdir -p target/test
for test_file in test/*.c; do
exe="target/test/$(basename ''${test_file%.c})"
${gcc}/bin/cc -Wall -Wextra -g -pedantic \
-fsanitize=address -I. "$test_file" \
-Ltarget -lhmpl -lhectic -o "$exe"
"$exe"
done
'';
installPhase = '' installPhase = ''
mkdir -p $out/bin $out/lib $out/include mkdir -p $out/bin $out/lib $out/include
@@ -36,7 +45,7 @@ stdenv.mkDerivation {
''; '';
meta = { meta = {
description = "chectic"; description = "hectic";
license = lib.licenses.mit; license = lib.licenses.mit;
}; };
} }

View File

@@ -27,8 +27,8 @@ char *eval(Arena *arena, const Json * const context, const char * const key) {
} }
/* Modified: text is passed by reference so we can update it and free old allocations */ /* Modified: text is passed by reference so we can update it and free old allocations */
void render_template_placeholders(Arena *arena, char **text_ptr, Json *context, const char * const prefix) { void hmpl_render_interpolation_tags(Arena *arena, char **text_ptr, Json *context, const char * const prefix) {
raise_debug("render_template_placeholders"); raise_debug("hmpl_render_interpolation_tags");
char start_pattern[256]; char start_pattern[256];
snprintf(start_pattern, sizeof(start_pattern), "{{%s", prefix); snprintf(start_pattern, sizeof(start_pattern), "{{%s", prefix);
int start_pattern_length = strlen(start_pattern); int start_pattern_length = strlen(start_pattern);
@@ -67,19 +67,19 @@ void render_template_placeholders(Arena *arena, char **text_ptr, Json *context,
} }
} }
void render_template_with_arena(Arena *arena, char **text, const Json * const context) { void hmpl_render_with_arena(Arena *arena, char **text, const Json * const context) {
if (context->type != JSON_OBJECT) { if (context->type != JSON_OBJECT) {
raise_exception("Malformed context: context is not json"); raise_exception("Malformed context: context is not json");
exit(1); exit(1);
} }
render_template_placeholders(arena, text, context, ""); hmpl_render_interpolation_tags(arena, text, context, "");
} }
void render_template(char **text, const Json * const context) { void hmpl_render(char **text, const Json * const context) {
Arena arena = arena_init(1024 * 1024); Arena arena = arena_init(1024 * 1024);
render_template_with_arena(&arena, text, context); hmpl_render_with_arena(&arena, text, context);
arena_free(&arena); arena_free(&arena);
} }

View File

@@ -5,14 +5,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "chectic.h" #include "hectic.h"
void init_cjson_with_arenas(Arena *arena); void init_cjson_with_arenas(Arena *arena);
char *eval(Arena *arena, const Json * const context, const char * const key); char *eval(Arena *arena, const Json * const context, const char * const key);
/* Modified: text is passed by reference so we can update it and free old allocations */ /* Modified: text is passed by reference so we can update it and free old allocations */
void render_template_placeholders(Arena *arena, char **text_ptr, Json *context, const char * const prefix); void hmpl_render_interpolation_tags(Arena *arena, char **text_ptr, Json *context, const char * const prefix);
void render_template_with_arena(Arena *arena, char **text, const Json * const ccontext); void render_template_with_arena(Arena *arena, char **text, const Json * const ccontext);

View File

@@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "chectic.h" #include "hectic.h"
#include "hmpl.h" #include "hmpl.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) {
text = arena_strdup(&arena, ""); text = arena_strdup(&arena, "");
} }
render_template_with_arena(&arena, &text, context); hmpl_render_with_arena(&arena, &text, context);
printf("%s", text); printf("%s", text);
arena_free(&arena); arena_free(&arena);

107
package/c/hmpl/test/test.c Normal file
View File

@@ -0,0 +1,107 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "hmpl.h"
#include "hectic.h"
void test_eval_single_level_key(Arena *arena) {
char *context_text = arena_strdup(arena, "{\"name\": \"world\"}");
Json *context = json_parse(arena, &context_text);
if (!context) { raise_exception("Malformed json"); exit(1); }
char *result = eval(arena, context, "name");
raise_debug("eval result: %s", result);
assert(result && strcmp(result, "world") == 0);
}
void test_eval_nested_key(Arena *arena) {
char *context_text = arena_strdup(arena, "{\"person\": {\"name\": \"Alice\"}}");
Json *context = json_parse(arena, &context_text);
if (!context) { raise_exception("Malformed json"); exit(1); }
char *result = eval(arena, context, "person.name");
raise_notice("context: %s, eval result: %s", json_to_string(arena, context), result);
assert(result && strcmp(result, "Alice") == 0);
}
void test_render_interpolation_tags(Arena *arena) {
char *context_text = arena_strdup(arena,
"{\n"
" \"persona\": {\n"
" \"name\": \"John\",\n"
" \"surname\": \"Doe\",\n"
" \"address\": {\n"
" \"home\": {\n"
" \"street\": \"123 Main St\",\n"
" \"city\": \"Springfield\",\n"
" \"zip\": \"12345\"\n"
" },\n"
" \"work\": {\n"
" \"street\": \"456 Business Rd\",\n"
" \"city\": \"Metropolis\",\n"
" \"zip\": \"67890\"\n"
" }\n"
" },\n"
" \"contact\": {\n"
" \"email\": \"john@example.com\",\n"
" \"phone\": {\n"
" \"home\": \"555-1234\",\n"
" \"mobile\": \"555-5678\"\n"
" }\n"
" }\n"
" }\n"
"}");
Json *context = json_parse(arena, &context_text);
if (!context) { raise_exception("Malformed json"); exit(1); }
char *text = arena_strdup(arena,
"Hello {{persona.name}} {{persona.surname}},\n"
"\n"
"Your home address:\n"
"{{persona.address.home.street}},\n"
"{{persona.address.home.city}},\n"
"{{persona.address.home.zip}}\n"
"\n"
"Your work address:\n"
"{{persona.address.work.street}},\n"
"{{persona.address.work.city}},\n"
"{{persona.address.work.zip}}\n"
"\n"
"Contact information:\n"
"Email: {{persona.contact.email}}\n"
"Home Phone: {{persona.contact.phone.home}}\n"
"Mobile Phone: {{persona.contact.phone.mobile}}\n");
hmpl_render_with_arena(arena, &text, context);
assert(strcmp(text,
"Hello John Doe,\n"
"\n"
"Your home address:\n"
"123 Main St,\n"
"Springfield,\n"
"12345\n"
"\n"
"Your work address:\n"
"456 Business Rd,\n"
"Metropolis,\n"
"67890\n"
"\n"
"Contact information:\n"
"Email: john@example.com\n"
"Home Phone: 555-1234\n"
"Mobile Phone: 555-5678\n") == 0);
}
int main(void) {
Arena arena = arena_init(1024 * 1024);
test_eval_single_level_key(&arena);
test_eval_nested_key(&arena);
test_render_interpolation_tags(&arena);
printf("All tests passed.\n");
arena_free(&arena);
return 0;
}