diff options
Diffstat (limited to 'bin')
| -rw-r--r-- | bin/main.ml | 411 |
1 files changed, 44 insertions, 367 deletions
diff --git a/bin/main.ml b/bin/main.ml index 7565090..ecc763e 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1984,383 +1984,60 @@ let publish_report ~rite ~lang ~sigla ~from_y ~to_y ~out ~overlays ~dtstamp ~pru same reason. Deliberately NOT embedded in [help_text]: the cram test pins help's first line, and a version in it would make every release edit a test expectation for no gain. *) -let version = "1.2.0" +let version = "1.2.1" +(* --help is a REMINDER, not a reference. It answers "what are the commands + and which flag do I want", in one screen; every rule, format, hazard and + worked example lives in colitur(1), which is installed beside the binary + and is where a reader who needs more should be sent. + + It used to print 374 lines -- the whole manual, including the RFC 4180 + quoting rule and the template scope hazard. A help text nobody can read + at the terminal is not more helpful than a short one; it is less, because + the reader stops looking. *) let help_text = - {|colitur -- deterministic liturgical calendar engine (Roman rite, 1962) + {|colitur -- deterministic liturgical calendar engine (Roman rite, EF 1962 and OF) usage: - colitur easter <year> Easter, and the movable feasts anchored to it - colitur temporal <year> [--rite ef|of] [--pretty] - [--month N | --date YYYY-MM-DD | --today] - the temporal cycle, one line per day - colitur day <year> the resolved day identity, one line per day - colitur readings <year> the Mass reading citations, one line per day - colitur rubrics <year> the Mass formulary said, one line per day - colitur day|readings|rubrics [<year>] [--year Y] [--rite ef|of] - [--overlay FILE ...] [--lang CODE|FILE] [--raw] [--pretty] - [--month N | --date YYYY-MM-DD | --today] - <year> may be given positionally or as --year (both, if they - agree); rubrics's own --lang/--raw govern its trailing - formulary-name column, --sigla-* stay refused there (see - "naming" below); --pretty and the narrowing flags are described - under "reading it yourself" below - colitur emit --format csv|json|sexp|xml|ics --from Y --to Y [--rite ef|of] - [--overlay FILE ...] [--dtstamp S] [--lang CODE|FILE] [--raw] - render a resolved year range through one of five emitters - colitur table [<year>] [--year Y] --template FILE [--rite ef|of] - [--flavour X] [--overlay FILE ...] [--lang CODE|FILE] [--raw] - colitur render --template FILE [<year>] [--year Y] [--rite ef|of] - [--flavour X] [--overlay FILE ...] [--lang CODE|FILE] [--raw] - compute year Y and render it through FILE, a logic-less - Mustache-family template; table and render are the same - operation, two names (see "rendering" below) - colitur publish --from Y --to Y --out DIR [--rite ef|of] - [--overlay FILE ...] [--prune] [--dtstamp S] - [--lang CODE|FILE] [--raw] - write the static tree: per-year csv/json/xml/ics, one JSON - file per day, the schema and a generated index (see - "publish" below) - colitur lang --list which language files this build can find - colitur lang --dump CODE a language's full key set, in INI form, on stdout - colitur lang --check FILE what a language file is missing, and any typo'd key - colitur config --show every setting, its value and where it came from - colitur new-overlay print a starter overlay file to stdout - colitur convert FILE.ini flat INI overlay -> S-expression, on stdout - colitur check FILE ... load an overlay, say what it does, exit 2 if not - colitur -h, --help this help - colitur -V, --version print the version and exit - -<year> is a civil year, 1583..9999 inclusive. Each report covers 1 January to -31 December of that year, not a liturgical year. `emit`/`publish` take a -RANGE instead (--from Y --to Y, inclusive) and do not also accept a single ---year or positional year -- deliberate: they may compute many years in one -run, and a third, single-year spelling on top of the range form would add -parsing surface for no real workflow gain. - -reading it yourself: - --pretty lay the rows out as boxes for a person rather - than for awk; accepted on day/readings/rubrics/ - temporal, refused elsewhere. The box format is - for eyes only and may change between releases -- - parse the default rows, which will not. - --month N print only that month, 1..12 - --date YYYY-MM-DD print only that day - --today print only today - - The three narrowing flags are ALTERNATIVES -- naming two is an error, not a - silent win for one. They work in the default format too, and matter most - under --pretty, whose boxes span several lines and so survive no line-wise - grep at all. - - --date and --today NAME a year, so on those two the year may be omitted: - `colitur day --today` is complete. Give one anyway and it must agree, the - same rule a positional year and --year already follow. --month names no - year, so it still needs one. On `temporal`, which refuses --year, --date - and --today may still supply the year: they select a day and merely happen - to determine the year, which --year does not do. - -output formats: - day date weekday season week slug rank colour [+commemoration ...] [name] - 2026-04-05 sunday paschaltide 1 ef-easter-sunday class-1 white - readings date slug | Epistle | Gospel [| name] - 2026-12-25 ef-nativity | Heb 1:1-12 | John 1:1-14 - rubrics date, formulary slug, source, creed, gloria, preface [name] -- TAB-separated - 2026-01-01[TAB]ef-circumcision[TAB]own[TAB]true[TAB]true[TAB]nativity[TAB]In Octava Nativitatis Domini - - A citation contains spaces, so readings uses " | " between its fields while - day stays space-separated; that is why they are separate commands rather - than extra columns. The same reasoning is why the resolved display NAME, - present by default (see "naming" below), is appended as the LAST field - rather than substituted for the slug shown above: a name may itself - contain spaces, and inserting it earlier in the row would break every - fixed-position field that follows it. It is present only when it differs - from the slug already shown -- under --raw, or any language with no entry - for that particular day, the trailing field is simply absent, which is - what makes --raw byte-identical to this program's pre-naming output. - - rubrics prints which Mass is actually said and how that was decided - (source: proper/own/preceding-sunday/common/votive) -- not always the - day's own: a weekday with no proper resumes the preceding Sunday's, a - saint with no proper says his assigned Common -- followed by whether the - Creed is said (RG 475-476), whether the Gloria in excelsis is said - (RG 431-432, deferring to the Breviary's own Te Deum rule, nn. 237-238, - for RG 431(a)), and which preface is said (RG 482-499) -- Creed/Gloria - both "true"/"false", OCaml's own literal, not "yes"/"no" or "1"/"0"; - preface one of nativity/epiphany/lent/holy-cross/easter/ascension/ - sacred-heart/christ-the-king/holy-spirit/trinity/bvm/st-joseph/apostles/ - common/requiem, or "-" when this engine resolves no Mass at all that day - (Good Friday). TAB-separated rather than space or " | ": the resolved - formulary NAME (below) can carry both spaces and punctuation a citation - never does, which rules out either alternative already in use above. - --overlay is accepted (the observed celebration it changes decides the - formulary, the Creed, the Gloria and the preface); --sigla-* are refused - -- this row prints no citation for them to affect. - - --lang/--raw ARE accepted (unlike --sigla-*): `said` (the formulary slug) - is a machine key exactly like `day`'s own slug, and this row resolves it - to a display name under the identical append-only rule day/readings use - -- present as a trailing 8th field only when it differs from the slug - already printed, so --raw (or a language with no entry for that day) is - byte-identical to the seven-field row shown above. - - emit one schema (season, week, slug, rank, colour, subject, names, - citations, commemorations), rendered five ways: csv (RFC 4180, - one header for the whole run), json, sexp, xml (schema/colitur- - v1.xsd) and ics (RFC 5545). --from/--to give a civil-year range, - inclusive. --dtstamp fixes the ics DTSTAMP so two runs over the - same data are byte-identical -- the engine reads no clock. - -rite: - --rite ef|of selects which rite module a command computes against; - default ef (so every invocation written before this flag - existed is unaffected). `of` is the 1970 Missale Romanum - (editio typica tertia, 2002). Accepted on `day`, `readings`, - `rubrics`, `temporal`, `emit`, `table`, `render` and `publish`; - refused, not silently ignored, on `check`/`convert` (operate - on an overlay FILE, not a computed year), `new-overlay` - (prints a static starter, no calendar computation), and - `lang`/`config` (answer naming/config questions orthogonal to - any rite). `easter` is refused too, but PROVEN rite-invariant - rather than merely unbuilt for `of`: EF and OF reckon Easter - on the identical Gregorian computus, so no second value could - ever change the six dates it prints. - - `emit --format csv --rite of` and `publish --rite of` widen - their CSV output with a 17th column, "second", between - "first" and "gospel" -- present, and usually empty, because a - Sunday or solemnity genuinely carries a Second reading (OLM - 1981 Praenotanda n. 66.1) and a feria/feast/memorial does not - (n. 69.1); EF's own 16-column CSV header is unaffected, byte - for byte, because EF's `citations` never contains one. json/ - xml/sexp/ics need no such widening -- each already had a place - for a variable-length reading list. - - A single `--out` directory can hold both rites' own `publish` - trees side by side ("ef/", "of/"), but not safely combined - with --prune on either: publish's own manifest and index.html - describe the WHOLE tree, not one rite's slice of it, so a - later run for the other rite would not know the first run's - files exist and --prune would delete them as stale. Publish a - single rite per --out, or omit --prune when deliberately - layering both. - -overlays: - --overlay FILE (repeatable, ordered; -o) applies a user calendar ON TOP of - the shipped universal one, never instead of it, so local feasts - add to it rather than replacing it. Later files win over earlier - ones, and over the universal calendar, when they name the same - slug. Accepted on `day`, `readings`, `rubrics`, `emit`, `table`, - `render` and `publish` -- `easter` and `temporal` read no - sanctoral data, so the flag is refused there rather than - silently ignored. - - An overlay is applied, NOT validated: colitur's test layers assert - things about the shipped calendar and cannot vouch for a file you - supply. A directive naming a slug that does not exist warns on - stderr and the run continues; a file that fails to load is fatal. - - To write one: - - colitur new-overlay > my-parish.sexp # a commented starter - $EDITOR my-parish.sexp - colitur check my-parish.sexp # parses? every directive hit? - colitur day 2026 --overlay my-parish.sexp - - `check` reports what each directive targets and exits 2 if a file - will not load or a directive matched nothing, so it fits a - Makefile or a pre-commit hook. It does not check a calendar - against the rubrics -- nothing here can. - - A flatter INI form exists for simple calendars, converted with - `colitur convert`, which verifies its own output before emitting - it. See colitur-overlay(5) for both forms. + colitur easter <year> Easter and the feasts anchored to it + colitur temporal <year> the temporal cycle, one line per day + colitur day <year> the resolved day, one line per day + colitur readings <year> the Mass reading citations + colitur rubrics <year> the Mass said, Creed, Gloria, preface + colitur emit --format FMT --from Y --to Y csv|json|sexp|xml|ics + colitur table|render --template FILE --year Y render through a template + colitur publish --from Y --to Y --out DIR write a static tree + colitur check FILE ... load an overlay and say what it does + colitur convert FILE.ini INI overlay -> S-expression, on stdout + colitur new-overlay print a starter overlay to stdout + colitur lang --list|--dump CODE|--check FILE + colitur config --show every setting and where it came from - In an added celebration, `citations` and `layer` may be omitted: - they default to empty and to the overlay's own id. Dates may be - (Fixed (month M) (day D)), (Easter_offset N) signed, or - (Nth_weekday (month M) (nth N) (weekday W)) with N negative to - count from the end of the month. +common flags: + --rite ef|of which form (default ef) + --year Y the year, as an alternative to the positional one + --month N print only that month; --date YYYY-MM-DD, --today + --pretty boxes for a person rather than rows for awk + --lang CODE|FILE name things in this language; --raw for bare slugs + --overlay FILE apply a local calendar (repeatable, ordered) + -h, --help this help -V, --version the version -year: - `day`, `readings`, `rubrics`, `table` and `render` each take a single - civil year, sayable two ways -- positionally (`colitur day - 2026`) or as --year (`colitur day --year 2026`) -- additively: - neither form was removed when the other was added, so every - invocation that worked before still works unchanged. Naming - both is fine as long as they agree (`colitur day 2026 --year - 2026`); naming both with DIFFERENT years is a hard usage error - rather than one silently winning. +<year> is 1583..9999 and means 1 January to 31 December, not a liturgical +year. emit and publish take --from/--to instead. A flag a command cannot +honour is refused, never silently ignored. - `emit`/`publish` deliberately do NOT gain a --year: they take - --from Y --to Y instead (see the usage block above), and stay - that way even for a single-year run (`--from 2026 --to 2026`) - -- a third spelling meaning exactly the same thing as the two - above would add parsing surface, and a range command's own - natural single-year form is already `--from Y --to Y`, not a - new flag. +exit status: 0 success; 2 bad usage, year out of range, or unreadable data. -naming: - --lang CODE|FILE applies to `day`, `readings`, `rubrics`, `emit`, - `table`, `render` and `publish`. A CODE (e.g. `la`, `en`) is - looked up as - <lang-dir>/CODE.ini; a value containing '/' or ending ".ini" is - read as a literal path instead. Default `la`, overridable by a - config file (see below). An unknown language is a hard ERROR - naming what is available (try `colitur lang --list`), never a - silent fallback to Latin -- a booklet quietly printed in the - wrong language is worse than one that refuses to print. A - language file may declare `fallback = CODE` in its [meta] - section, so a partial translation shows its fallback language - for the keys it does not itself carry, rather than bare slugs. - - --raw restores every command's pre-naming output: `name` (and every - other localised field `emit`'s schema carries) equals the bare - machine slug, exactly as if no language had ever been resolved. - This is not a special case threaded through the naming code -- - it is `colitur lang`'s own identity table, under which every - lookup echoes its key back unchanged. - - --sigla-style CODE|FILE, --sigla-book full|abbr, --sigla-tradition NAME - settings for how a Mass reading CITATION is written -- WHICH - punctuation/abbreviation style, WHICH book form, and WHICH - numbering tradition. Each has a config-key counterpart - (`sigla_style`/`sigla_book`/`sigla_tradition`) resolved with - the identical flag > config > default precedence as --lang, and - each is reported by `colitur config --show` with its source. - Defaults: `sigla_style` the resolved language code (so a - booklet's citations follow its own --lang unless told - otherwise), `sigla_book` `abbr`, `sigla_tradition` `vulgate`. - An unrecognised `--sigla-book` is a hard ERROR (want `full` or - `abbr`), the same discipline as an unknown `--lang`; an - unrecognised `--sigla-tradition` is not -- it degrades to the - Vulgate with a stderr warning, because asking for a renumbering - is optional the way asking for a language is not. `--raw` - bypasses all of it and emits each citation exactly as stored, - byte for byte, so it stays usable for diffing and does not - depend on the citation parser being correct. - - colitur lang --list which language files this build can find, and - each one's own declared fallback, if any. - colitur lang --dump CODE the named language's full key set, in INI - form, on stdout -- a starting point for a new - translation, or a way to diff two versions of - one. - colitur lang --check FILE what a language file is MISSING (a real slug - with no entry) and, separately, any entry - naming a slug that does not exist at all (a - typo, silently dead otherwise) -- exits 1 if - anything is unknown, so it fits a Makefile or - a pre-commit hook. - colitur config --show every effective setting -- lang, overlay, - template, format, sigla_style, sigla_book, - sigla_tradition -- its resolved value, and - where it came from: `flag`, `config` or - `default`. See colitur-config(5) for the - config file's location and full precedence. - -rendering: - --template FILE (required on `table`/`render`) is a logic-less Mustache- - family template: {{placeholder}}, {{#section}}...{{/section}}, - {{^inverted}}...{{/inverted}}, {{!comment}} -- nothing else. It is - DATA, never a program: no partials, no lambdas, no expression - evaluation, no filesystem or process access, and no "raw" or - triple-brace form that could opt out of escaping. The value it - renders against is the same schema `emit` uses (season, week, - slug, rank, colour, subject, names, citations, commemorations), - reshaped into a booklet (`days`) and a month grid (`weeks`, with - padding cells for the leading/trailing blanks); see - colitur-templates(5) for the full field list, the syntax, and - the one remaining scope hazard (`num` -- both a month and a week - carry it, and only the innermost one is ever meant). `name` is a - plain resolved string, not a lang-keyed object, so it carries no - equivalent hazard of its own. - - --flavour X selects how interpolated VALUES are escaped (never the - template's own literal markup, which is the author's). One of: - - latex typst groff html xml ics none - - Inferred from --template's extension when --flavour is omitted: - - .tex -> latex - .typ -> typst - .ms .mom .me -> groff - .html .htm -> html - .xml -> xml - .ics -> ics - .md .adoc .txt -> none (no metacharacters are escaped; - Markdown/AsciiDoc/plain text have no fixed - metacharacter set, so escaping them here - would produce worse output than leaving - them alone) - - An extension colitur does not recognise is a hard ERROR naming - the seven flavours above, never a silent fallback to `none`: - guessing wrong produces output that looks fine until the - metacharacters it silently failed to escape show up. - - `table` and `render` are the SAME operation under two names. The design - this project followed originally sketched `compute | render` as a - Unix pipe, with `render` reading a serialised view back from - stdin. That is deliberately not built: honouring the pipe would - need a JSON *parser*, purely so this program could re-read a view - it had just serialised itself -- a second hand-rolled component, - and a second place for the published contract to drift, for no - benefit over calling the view builder directly in the same - process. There is therefore no stdin-fed `render`; `colitur emit - --format json | jq` still composes for real pipe use, because - that JSON is the OUTPUT, never something colitur itself parses - back in. - -publish: - --out DIR (required) writes the static tree that IS this program's API: - any web server or git repo can serve it as-is, and nothing runs - at request time. - - <rite>/<year>.{json,csv,xml,ics} one civil year, all days - <rite>/<year>/<mm>/<dd>.json one file per day - schema/day-v1.json the published JSON contract - index.html a generated index page - .colitur-manifest every path this run wrote - - <rite> is "ef" or "of", selected by --rite exactly as on every - other command (default ef) -- see "rite" above for what a - single --out directory holding both rites' own trees needs. - - Deterministic: publishing the same --from/--to range twice - produces a byte-identical tree (--dtstamp behaves exactly as on - `emit`). That is what makes publishing into a git repo safe -- - `git status` shows only real change, and you review an actual - diff before pushing. - - Non-destructive: publish writes only files it owns, and records - every one in .colitur-manifest. A file you put in the output - directory yourself is never in that manifest, so it is never - touched, whether or not --prune is given. --prune additionally - removes manifest entries from a PREVIOUS run that this run did - not rewrite (e.g. an earlier year's per-day files, when you - publish a different range into the same directory) -- never - anything the manifest does not name. - -environment: - COLITUR_DATA_DIR - Read the calendar data from this directory instead of the - installed (<prefix>/share/colitur/ef) or build-tree location. - If it is set and holds no sanctoral.sexp, colitur exits 2 rather - than silently falling back to a different copy of the data. +Reading references only (e.g. "Jn 3:16"); never scripture text. -exit status: - 0 success - 2 bad usage, year out of range, or the calendar data could not be read +Full documentation -- every output format, the overlay and template file +formats, citation naming and numbering, the sources this computes against +and its stated limits: -Reading references only (e.g. "Jn 3:16"); never scripture text. -See colitur(1) for the full description and the sources it computes against, -colitur-overlay(5) for the overlay file format in full, -colitur-templates(5) for the template format in full -- the syntax, the -remaining scope hazard, the seven flavours' escaping, and the full view-model -field reference -- and colitur-config(5) for the config file's location and -precedence in full.|} + man colitur the program, in full + man 5 colitur-overlay the overlay format + man 5 colitur-templates the template format + man 5 colitur-config the config file|} let print_help () = print_endline help_text; |
