From 49494c3aedf3f1b40c6ee36cb4de5615f097274c Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Tue, 18 Aug 2026 13:16:53 +0200 Subject: feat(cli): colitur check and colitur new-overlay Writing a local calendar had no feedback loop. An overlay is applied, not validated -- that stays true, and the five test layers still cannot vouch for a user's file -- but before this the only way to learn whether yours did what you meant was to generate a year of output and grep for your own slug, and the only way to see that a directive matched nothing was to notice a warning scroll past among 365 lines. `check` loads each overlay, applies it to the real shipped calendar, and reports the directive counts, the slug each one targets, and any directive that found no target. It exits 2 when a file fails to load or a directive matched nothing, so it composes into a Makefile or a pre-commit hook rather than merely being readable. It is applied to the SHIPPED calendar and not to an empty layer on purpose: against an empty one every Suppress would fail trivially and the check would be worthless. It answers three narrow questions -- does the file parse, does every directive find its target, what does the merged result contain. It does not validate a calendar against the rubrics and cannot, and both the help text and the man page say so rather than letting the name imply more than it does. `new-overlay` prints a starter to stdout for redirection, rather than writing a file where it likes. Every value in it is a placeholder that will appear in `day` output if left unedited, so a half-finished overlay is visible rather than silently inert, and it documents the three date shapes and the legal values for each closed field inline. load_ef_layer now returns its diagnostics instead of printing them: day and readings still want them on stderr beside a year of output, while check wants them on stdout, attributed to the overlay that produced them, and counted. Printing at the source made the second impossible. The cram test round-trips new-overlay through check rather than pinning the template line by line -- editing its prose should not fail a test, but a syntax error in it still must. --- test/cli.t | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'test/cli.t') diff --git a/test/cli.t b/test/cli.t index 0c79db4..d735e69 100644 --- a/test/cli.t +++ b/test/cli.t @@ -17,7 +17,7 @@ A year outside the supported domain is rejected (exit 2): No/garbage arguments give a usage error (exit 2): $ colitur - colitur: usage: colitur easter | colitur temporal | colitur day | colitur readings (try: colitur --help) + colitur: usage: colitur easter | colitur temporal | colitur day | colitur readings | colitur check FILE | colitur new-overlay (try: colitur --help) [2] The EF temporal cycle for a year, one line per day: @@ -327,17 +327,49 @@ A flag needing a value, given none: $ colitur day 2026 --overlay colitur: --overlay needs a file path - colitur: usage: colitur easter | colitur temporal | colitur day | colitur readings (try: colitur --help) + colitur: usage: colitur easter | colitur temporal | colitur day | colitur readings | colitur check FILE | colitur new-overlay (try: colitur --help) [2] An unknown option is rejected rather than treated as a positional word: $ colitur day 2026 --diocese colitur: unknown option --diocese - colitur: usage: colitur easter | colitur temporal | colitur day | colitur readings (try: colitur --help) + colitur: usage: colitur easter | colitur temporal | colitur day | colitur readings | colitur check FILE | colitur new-overlay (try: colitur --help) [2] The shipped example overlay is runnable documentation, and it must actually load -- an example that silently rotted would be worse than none. The cram sandbox cannot reach data/, so the assertion that it loads and applies lives in test_lectionary_ef.ml, which reads it from the source tree directly. + +`new-overlay` prints a starter file to stdout for redirection, and what it +prints must itself be valid -- a template that does not load is worse than no +template, because it teaches the wrong shape. Round-tripped here rather than +pinned line by line, so editing the template's prose does not fail this test +while a syntax error in it still does: + + $ colitur new-overlay > starter.sexp + $ colitur check starter.sexp + starter.sexp: ok -- overlay my-parish, 2 directive(s): 2 add, 0 suppress, 0 replace, 0 edit + every directive found its target + add my-local-patron + add my-dedication + +`check` exits 2 when a directive matches nothing, so it is usable in a +Makefile or a pre-commit hook, not merely readable: + + $ printf '((id p) (directives ((Suppress no-such-slug))))' > bad.sexp + $ colitur check bad.sexp + bad.sexp: ok -- overlay p, 1 directive(s): 0 add, 1 suppress, 0 replace, 0 edit + MATCHED NOTHING: overlay p: suppress no-such-slug: slug not present; nothing to suppress + suppress no-such-slug + [2] + +`citations` and `layer` may be omitted from an added celebration; they default +to empty and to the overlay's own id: + + $ printf '((id tiny) (directives ((Add ((date (Fixed (month 5) (day 20))) (cel ((slug tiny-feast) (names ((en "Tiny"))) (rank Class3) (status Feast) (colour White) (subject Saint))))))))' > tiny.sexp + $ colitur check tiny.sexp + tiny.sexp: ok -- overlay tiny, 1 directive(s): 1 add, 0 suppress, 0 replace, 0 edit + every directive found its target + add tiny-feast -- cgit v1.3