mirror of
https://git.pumpin.online/funkpower/typikon.git
synced 2026-10-22 18:57:13 +00:00
There is one engine and nine doors into it, and nothing until now checked that the doors agree. CI tested each binding against itself: the Python wheel imported, the C demo compiled, the npm package type-checked. None of that would have caught a field that crosses one boundary wrong, which is the failure this library is most exposed to, because a wrong answer about a fast day looks exactly like a right one. So: twelve days, chosen where the bindings could plausibly differ, and all twenty-two fields compared against what the command line says. The days are Pascha itself, the Nativity on both calendars, a Wednesday of the Great Fast under Byzantine Catholic law and again at the Typikon, a weekday of the Nativity Fast where Greek and Slavic usage part, the one Orthodox church on the Gregorian Paschalion, and a day with no feast. A release now runs what a push runs. It had been gated on cargo test alone, so the C ABI, the Lua binding, the Python parity check and the README examples were checked on the branch that led to a release and not on the release. Neither registry will reuse a version, so the gate belongs before the upload. Writing it found three things. The npm exports map did not expose the wasm by package path, so a bundler asked for typikon/web/typikon_bg.wasm was told no such path exists and had to reach into node_modules; the documented init() call was never affected. The suite could hang instead of fail, because the test web server was backgrounded inside a subshell and killed by job spec, leaving bash in do_wait forever. And pointing XDG_CONFIG_HOME at a path that does not exist isolates typikon and breaks every other program that keeps state under it, a browser included. A surface whose toolchain is missing is named as skipped, and fewer than three surfaces running is a failure rather than a pass, so a container with nothing installed cannot come out green.
99 lines
4.1 KiB
C
99 lines
4.1 KiB
C
/* The C ABI, over the same probes, emitting the same JSON.
|
|
*
|
|
* The probe list is written out here rather than read from probes.json,
|
|
* because a JSON parser is exactly what this binding exists to spare its
|
|
* callers and it would be odd to need one to test it. tests/parity.sh checks
|
|
* the two lists have not drifted apart.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <typikon.h>
|
|
|
|
static void esc(FILE *f, const char *s) {
|
|
fputc('"', f);
|
|
for (; *s; s++) {
|
|
if (*s == '"' || *s == '\\') fprintf(f, "\\%c", *s);
|
|
else if ((unsigned char)*s < 0x20) fprintf(f, "\\u%04x", *s);
|
|
else fputc(*s, f);
|
|
}
|
|
fputc('"', f);
|
|
}
|
|
|
|
static void field(FILE *f, const char *k, const char *v, int *first) {
|
|
if (!*first) fputc(',', f);
|
|
*first = 0;
|
|
esc(f, k); fputc(':', f);
|
|
if (v) esc(f, v); else fputs("null", f);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
(void)argc;
|
|
FILE *f = fopen(argv[1], "w");
|
|
fputc('{', f);
|
|
int firstkey = 1;
|
|
|
|
/* date, jurisdiction, discipline: the same twelve as probes.json. */
|
|
struct { int y, m, d; const char *j; int t; } probes[] = {
|
|
{2026,4,12,"moscow",-1}, {2026,4,12,"goarch",-1}, {2026,1,7,"moscow",-1},
|
|
{2025,12,25,"old",-1}, {2026,3,4,"ruthenian",0}, {2026,3,4,"ruthenian",1},
|
|
{2025,12,9,"old",-1}, {2026,2,16,"oca",-1}, {2026,8,15,"antiochian",-1},
|
|
{2026,9,18,"finnish",-1},{2027,5,2,"serbian",-1}, {2026,6,28,"constantinople",-1},
|
|
};
|
|
|
|
for (size_t i = 0; i < sizeof probes / sizeof *probes; i++) {
|
|
int32_t jdn = typikon_jdn_from_ymd(probes[i].y, probes[i].m, probes[i].d);
|
|
int disc = probes[i].t < 0 ? 0 : probes[i].t; /* the CLI oracle passed --no-typikon */
|
|
typikon_day *day = typikon_describe_full(jdn, probes[i].j, NULL, disc);
|
|
if (!day) { fprintf(stderr, "%s\n", typikon_last_error()); return 1; }
|
|
|
|
char key[64];
|
|
snprintf(key, sizeof key, "%04d-%02d-%02d|%s|%d",
|
|
probes[i].y, probes[i].m, probes[i].d, probes[i].j,
|
|
probes[i].t == 1 ? 1 : 0);
|
|
if (!firstkey) fputc(',', f);
|
|
firstkey = 0;
|
|
esc(f, key); fputs(":{", f);
|
|
int first = 1;
|
|
field(f, "date", typikon_day_iso(day), &first);
|
|
field(f, "weekday", typikon_day_weekday(day), &first);
|
|
field(f, "jurisdiction",typikon_day_jurisdiction(day), &first);
|
|
field(f, "calendar", typikon_day_calendar(day), &first);
|
|
field(f, "churchDate", typikon_day_church_date(day), &first);
|
|
field(f, "season", typikon_day_season(day), &first);
|
|
field(f, "cycle", typikon_day_cycle(day), &first);
|
|
field(f, "feast", typikon_day_feast(day), &first);
|
|
field(f, "rank", typikon_day_rank(day), &first);
|
|
field(f, "sign", typikon_day_sign(day), &first);
|
|
field(f, "tradition", typikon_day_tradition(day), &first);
|
|
field(f, "title", typikon_day_title(day), &first);
|
|
field(f, "fast", typikon_day_fast(day), &first);
|
|
field(f, "fastLabel", typikon_day_fast_label(day), &first);
|
|
field(f, "emoji", typikon_day_emoji(day), &first);
|
|
field(f, "reason", typikon_day_reason(day), &first);
|
|
field(f, "obligation", typikon_day_obligation(day), &first);
|
|
field(f, "binds", typikon_day_binds(day), &first);
|
|
|
|
fputs(",\"commemorations\":[", f);
|
|
for (uint32_t c = 0; c < typikon_day_commemoration_count(day); c++) {
|
|
if (c) fputc(',', f);
|
|
esc(f, typikon_day_commemoration(day, c));
|
|
}
|
|
fputc(']', f);
|
|
|
|
int32_t p = typikon_day_pascha(day);
|
|
int32_t py; uint32_t pm, pd;
|
|
typikon_ymd_from_jdn(p, &py, &pm, &pd);
|
|
char iso[16];
|
|
snprintf(iso, sizeof iso, "%04d-%02u-%02u", py, pm, pd);
|
|
fputs(",\"pascha\":", f); esc(f, iso);
|
|
fprintf(f, ",\"daysToPascha\":%d", typikon_day_days_to_pascha(day));
|
|
fprintf(f, ",\"julianOffset\":%d", typikon_day_julian_offset(day));
|
|
fputc('}', f);
|
|
typikon_day_free(day);
|
|
}
|
|
fputs("}\n", f);
|
|
fclose(f);
|
|
return 0;
|
|
}
|