aboutsummaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 19:05:13 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 19:05:13 +0200
commit7dbd16cdf5880c0006dffacd09214ee607050b64 (patch)
tree01e1ee37a2be97a0b06fad70de975d8007beb702 /bin
parent1aeca948c2a2a82bffaaec65077140aa05f7b3f4 (diff)
parent1da70dc7ac03fe33fb92b172a0e26932764170d6 (diff)
downloadcolitur-7dbd16cdf5880c0006dffacd09214ee607050b64.tar.gz
colitur-7dbd16cdf5880c0006dffacd09214ee607050b64.zip
Merge branch 'citations-and-sigla'
Citations are parsed into structure and re-rendered, so book names, abbreviations, punctuation style and numbering tradition become files a user edits rather than strings frozen in the data. New lib/citation (Book, Parse, Render, Sigla); [bible] and [sigla] sections in language files; lang/traditions.ini for numbering; three config keys and CLI flags. --raw emits every citation byte-for-byte as stored, bypassing the whole pipeline, so output stays diffable against lectio and the raw view does not depend on the parser being correct.
Diffstat (limited to 'bin')
-rw-r--r--bin/dune7
-rw-r--r--bin/main.ml382
2 files changed, 357 insertions, 32 deletions
diff --git a/bin/dune b/bin/dune
index 28c04d7..0702a84 100644
--- a/bin/dune
+++ b/bin/dune
@@ -6,4 +6,9 @@
; colitur.opam's frozen depends, only a new library this executable links
; against. Used by Task 12's [mkdir_p] (colitur publish, recursive
; directory creation) and nowhere else.
- (libraries colitur_kernel rite_ef colitur_render colitur_naming unix))
+ ;
+ ; [colitur_citation] backs [load_tradition]'s own reading of
+ ; lang/traditions.ini (which book a reference DENOTES, {!Colitur_citation.Book}) --
+ ; a sibling library to [colitur_naming] (what a book is CALLED), not a
+ ; replacement for it.
+ (libraries colitur_kernel rite_ef colitur_render colitur_naming colitur_citation unix))
diff --git a/bin/main.ml b/bin/main.ml
index cf6cb33..1207b1d 100644
--- a/bin/main.ml
+++ b/bin/main.ml
@@ -299,8 +299,14 @@ let day_line ~lang (d : (Rite_ef.Vocab_ef.season, Rite_ef.Vocab_ef.rank) Colitur
can equally contain spaces, and " | " is what already keeps this row's
fields unambiguous. Present only when the resolved name differs from the
slug, for the identical reason [day_line] gives: under [--raw] this row
- is therefore byte-identical to what it printed before. *)
-let readings_line ~lang (d : (Rite_ef.Vocab_ef.season, Rite_ef.Vocab_ef.rank) Colitur_kernel.Liturgical_day.t)
+ is therefore byte-identical to what it printed before.
+
+ [~sigla] (Task 9) is what [--raw]/[--sigla-style]/[--sigla-book]/
+ [--sigla-tradition] actually reach: each stored reference renders
+ through {!Colitur_citation.Sigla.format} rather than printing verbatim.
+ Under {!Colitur_citation.Sigla.verbatim} (what [--raw] passes) this is
+ the identity, so the byte-exact claim above still holds. *)
+let readings_line ~lang ~sigla (d : (Rite_ef.Vocab_ef.season, Rite_ef.Vocab_ef.rank) Colitur_kernel.Liturgical_day.t)
=
let cel = d.Colitur_kernel.Liturgical_day.observed in
let part_ref p =
@@ -309,7 +315,7 @@ let readings_line ~lang (d : (Rite_ef.Vocab_ef.season, Rite_ef.Vocab_ef.rank) Co
(fun (c : Colitur_kernel.Citation.t) -> c.Colitur_kernel.Citation.part = p)
d.Colitur_kernel.Liturgical_day.citations
with
- | Some c -> c.Colitur_kernel.Citation.reference
+ | Some c -> Colitur_citation.Sigla.format sigla c.Colitur_kernel.Citation.reference
| None -> "-"
in
let slug_s = Colitur_kernel.Slug.to_string cel.Colitur_kernel.Celebration.slug in
@@ -414,7 +420,8 @@ let resolved_year_report ~line ~overlays y =
List.iter line (resolved_year_days ~overlays y)
let day_report ~lang ~overlays y = resolved_year_report ~line:(day_line ~lang) ~overlays y
-let readings_report ~lang ~overlays y = resolved_year_report ~line:(readings_line ~lang) ~overlays y
+let readings_report ~lang ~sigla ~overlays y =
+ resolved_year_report ~line:(readings_line ~lang ~sigla) ~overlays y
(* Task 8: `colitur emit` -- the five template-family emitters built in
Tasks 5-7, wired to a year RANGE rather than a single year, because a
@@ -465,7 +472,7 @@ let check_dtstamp = function
s;
exit 2
-let emit_report ~lang ~format ~overlays ~dtstamp ~from_y ~to_y =
+let emit_report ~lang ~sigla ~format ~overlays ~dtstamp ~from_y ~to_y =
check_dtstamp dtstamp;
if from_y > to_y then begin
Printf.eprintf "colitur: --from %d is after --to %d\n" from_y to_y;
@@ -474,7 +481,7 @@ let emit_report ~lang ~format ~overlays ~dtstamp ~from_y ~to_y =
for y = from_y to to_y do
let days = resolved_year_days ~overlays y in
let v =
- Colitur_render.View.of_days ~lang ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y days
+ Colitur_render.View.of_days ~lang ~sigla ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y days
in
match format with
| "csv" ->
@@ -641,6 +648,160 @@ let load_lang ~raw ~flag ~config =
| Error _ -> t
| Ok base -> Colitur_naming.Lang.with_fallback t base))))
+(* lang/traditions.ini answers a different question from la.ini/en.ini's own
+ [\[bible\]] section: which book a reference DENOTES, not what it is
+ CALLED. Naming varies by language (a [\[bible\]] section per language
+ file); denoting does not -- "modern numbering" is the same decision in
+ Latin, Polish and English -- so it gets its own file, read once here
+ rather than duplicated per language.
+
+ Reuses {!lang_dir} rather than adding a second probe: a tradition file
+ must resolve the same way a language file does, or an installed binary
+ could find one and not the other. Note carefully what that reuse implies
+ -- [lang_dir]'s own installed-vs-source-tree choice is gated on [la.ini]
+ existing, so it can legitimately return a directory that HAS [la.ini] but
+ NOT [traditions.ini] (an older install upgraded in place, from before
+ this file shipped). That case, like an unknown [name] naming no section
+ in the file, degrades to {!Colitur_citation.Book.vulgate} with a WARNING
+ on stderr -- never fatal. This deliberately does NOT mirror [load_lang]'s
+ own PRIMARY-file behaviour (a missing/unparsing la.ini is fatal, [exit 2])
+ -- it mirrors [load_lang]'s own FALLBACK-chain behaviour just above,
+ which already degrades silently rather than taking a good language table
+ down over a defect in a file it merely NAMES. Asking for a renumbering is
+ optional the way asking for a language is not: a run should not be lost
+ over a typo in [--sigla-tradition] (wired in Task 8) the way it is lost
+ over a typo in [--lang].
+
+ [name = "vulgate"] takes no shortcut around the file: [\[vulgate\]] is
+ shipped as a deliberately empty section (see traditions.ini's own
+ comment), and an empty field list is exactly {!Colitur_citation.Book.vulgate}
+ ([[]]) via the same {!Colitur_citation.Book.tradition_of_fields} path
+ every other tradition uses -- one code path, not a special case for the
+ default.
+
+ Called from [config_show] below (Task 8's own [--sigla-tradition]
+ plumbing), which discards the returned [tradition] and keeps only the
+ validation side effect -- `config --show` reports the RESOLVED STRING,
+ exactly like every other row, not a loaded object. Also called from
+ [load_sigla] below (Task 9), which DOES thread the loaded [tradition]
+ through to actually renumber a reference at both places one reaches
+ output, [View.citation_ref] and [part_ref]. *)
+let load_tradition name =
+ let path = Filename.concat (lang_dir ()) "traditions.ini" in
+ let vulgate () = Colitur_citation.Book.vulgate in
+ match read_file path with
+ | Error _ ->
+ Printf.eprintf "colitur: no %s; tradition %S falls back to the Vulgate\n" path name;
+ vulgate ()
+ | Ok text -> (
+ match Colitur_kernel.Overlay_ini.parse_sections text with
+ | Error e ->
+ Printf.eprintf "colitur: %s: %s; tradition %S falls back to the Vulgate\n" path e name;
+ vulgate ()
+ | Ok sections -> (
+ match List.find_opt (fun (s : Colitur_kernel.Overlay_ini.section) -> s.name = name) sections with
+ | None ->
+ Printf.eprintf "colitur: %s: no tradition %S; falling back to the Vulgate\n" path name;
+ vulgate ()
+ | Some sec ->
+ List.iter
+ (fun k ->
+ Printf.eprintf "colitur: %s: [%s]: unknown book %S (ignored)\n" path name k)
+ (Colitur_citation.Book.unknown_fields sec.fields);
+ Colitur_citation.Book.tradition_of_fields sec.fields))
+
+(* A book id's display NAME, for {!Colitur_citation.Sigla.make}'s own
+ [names] parameter. {!Colitur_naming.Lang.bible} is a TOTAL lookup that
+ returns THE KEY on a miss (["luke.abbr"]), because that is [Lang]'s own
+ miss contract, kept uniformly across every lookup it offers -- but a
+ caller that rendered a miss verbatim would print "luke.abbr 5:12-14"
+ straight into a citation. Degrade instead to
+ {!Colitur_citation.Book.default_spelling}, the data's own spelling for
+ the id -- exactly what colitur printed before this feature existed.
+ This path is invisible once [la.ini]/[en.ini] gain a real [\[bible\]]
+ section (Task 10); [test_cli.t] pins it against a language file that
+ deliberately has none, so the fallback stays under test after that. *)
+let names_of lang id form =
+ let key =
+ Colitur_citation.Book.to_string id
+ ^ (match form with `Full -> ".full" | `Abbr -> ".abbr")
+ in
+ let v = Colitur_naming.Lang.bible lang key in
+ if v = key then Colitur_citation.Book.default_spelling id else v
+
+(* Builds the [Sigla.t] every citation-rendering command applies to what it
+ emits -- the single place [--raw]/[--sigla-style]/[--sigla-book]/
+ [--sigla-tradition] actually take effect (Task 9), a sibling to
+ [load_lang] above and reusing it directly for [sigla_style]'s own file
+ lookup (config.mli: "a language CODE, looked up the same way [lang] is,
+ or a path").
+
+ [raw] short-circuits exactly as [load_lang] does: [Sigla.verbatim] is
+ handed back directly, NEVER a styled [Sigla.t] built over [Lang.raw] --
+ the latter would still parse every citation and reformat its
+ punctuation, which is precisely what [--raw] exists to avoid (byte-exact
+ diffing against lectio, and independence from the parser: a parser bug
+ must not corrupt the very output used to diagnose it). Every other
+ [--sigla-*] flag is silently unconsulted under [--raw] too, the same way
+ [--lang] itself is once [--raw] is given.
+
+ [sigla_style] DEFAULTS to [lang_t]'s own resolved code (config.mli's own
+ comment on [sigla_style]: a booklet that asked for a different [--lang]
+ gets its citations in that language's convention too, not a silent
+ reversion to Latin punctuation) -- when the resolved style code IS
+ [lang_t]'s own code, [lang_t] is reused directly rather than reading its
+ file a second time. Only the style file's OWN [\[sigla\]] section is
+ read here ({!Colitur_naming.Lang.sigla_fields} /
+ {!Colitur_citation.Render.style_of_fields}); book NAMES always resolve
+ through [lang_t] via [names_of] above, never through the style file --
+ naming and citation style are deliberately independent axes (a booklet
+ may want Polish names but Latin-convention citations, config.mli again).
+
+ [sigla_book] validation mirrors [config_show]'s own check exactly (a
+ closed [full]/[abbr] set, [Render.with_book] takes a variant, not a
+ string) -- unrecognised is a hard usage error, the same discipline an
+ unrecognised [--lang] gets. [sigla_tradition] resolves through
+ [load_tradition] above, which is BY DESIGN never fatal: asking for a
+ renumbering is optional, unlike asking for a language. *)
+let load_sigla ~raw ~lang_t ~sigla_style_flag ~sigla_book_flag ~sigla_tradition_flag ~config =
+ if raw then Colitur_citation.Sigla.verbatim
+ else
+ let lang_code = Colitur_naming.Lang.code lang_t in
+ let style_code, _ =
+ Colitur_naming.Config.resolve ~flag:sigla_style_flag
+ ~config:(Colitur_naming.Config.sigla_style config) ~default:lang_code
+ in
+ let style_lang =
+ if style_code = lang_code then lang_t
+ else load_lang ~raw:false ~flag:(Some style_code) ~config:None
+ in
+ let style =
+ Colitur_citation.Render.style_of_fields (Colitur_naming.Lang.sigla_fields style_lang)
+ in
+ let sigla_book_value, _ =
+ Colitur_naming.Config.resolve ~flag:sigla_book_flag
+ ~config:(Colitur_naming.Config.sigla_book config)
+ (* The STYLE's own [book] key is the default, so a style file can
+ set it and a flag/config still overrides. A hardcoded "abbr"
+ here made the documented [sigla] book key unreachable. *)
+ ~default:(Colitur_citation.Render.book_string style)
+ in
+ let book_form =
+ match sigla_book_value with
+ | "full" -> `Full
+ | "abbr" -> `Abbr
+ | _ ->
+ Printf.eprintf "colitur: unknown --sigla-book %S (want \"full\" or \"abbr\")\n" sigla_book_value;
+ exit 2
+ in
+ let style = Colitur_citation.Render.with_book book_form style in
+ let sigla_tradition_value, _ =
+ Colitur_naming.Config.resolve ~flag:sigla_tradition_flag
+ ~config:(Colitur_naming.Config.sigla_tradition config) ~default:"vulgate"
+ in
+ let tradition = load_tradition sigla_tradition_value in
+ Colitur_citation.Sigla.make ~style ~tradition ~names:(names_of lang_t)
+
let extension path =
match String.rindex_opt path '.' with
| Some i -> String.sub path i (String.length path - i)
@@ -671,7 +832,7 @@ let flavour_names_comma () =
let flavour_names_bar () =
String.concat "|" (List.map Colitur_render.Escape.to_string Colitur_render.Escape.all)
-let table_report ~lang ~template ~flavour_opt ~overlays y =
+let table_report ~lang ~sigla ~template ~flavour_opt ~overlays y =
let flavour =
match flavour_opt with
| Some name -> (
@@ -694,7 +855,7 @@ let table_report ~lang ~template ~flavour_opt ~overlays y =
exit 2
| Ok src -> (
let days = resolved_year_days ~overlays y in
- let v = Colitur_render.View.of_days ~lang ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y days in
+ let v = Colitur_render.View.of_days ~lang ~sigla ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y days in
match Colitur_render.Template.render_string ~flavour src v with
| Error e ->
(* The template is user input; a parse failure is reported with the
@@ -881,7 +1042,7 @@ let index_html ~from_y ~to_y =
</body></html>\n";
Buffer.contents b
-let publish_report ~lang ~from_y ~to_y ~out ~overlays ~dtstamp ~prune =
+let publish_report ~lang ~sigla ~from_y ~to_y ~out ~overlays ~dtstamp ~prune =
check_dtstamp dtstamp;
if from_y > to_y then begin
Printf.eprintf "colitur: --from %d is after --to %d\n" from_y to_y;
@@ -913,7 +1074,7 @@ let publish_report ~lang ~from_y ~to_y ~out ~overlays ~dtstamp ~prune =
in
for y = from_y to to_y do
let days = resolved_year_days ~overlays y in
- let v = Colitur_render.View.of_days ~lang ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y days in
+ let v = Colitur_render.View.of_days ~lang ~sigla ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y days in
let ys = string_of_int y in
emit ("ef/" ^ ys ^ ".json") (Colitur_render.Emit_json.year v);
emit ("ef/" ^ ys ^ ".csv") (Colitur_render.Emit_csv.year v);
@@ -926,7 +1087,7 @@ let publish_report ~lang ~from_y ~to_y ~out ~overlays ~dtstamp ~prune =
(fun d ->
let iso = D.to_iso8601 d.Colitur_kernel.Liturgical_day.date in
let mm = String.sub iso 5 2 and dd = String.sub iso 8 2 in
- let one = Colitur_render.View.of_days ~lang ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y [ d ] in
+ let one = Colitur_render.View.of_days ~lang ~sigla ~vocab:Rite_ef.Vocab_ef.vocab ~rite:"ef" ~year:y [ d ] in
emit (Printf.sprintf "ef/%s/%s/%s.json" ys mm dd) (Colitur_render.Emit_json.year one))
days
done;
@@ -1096,6 +1257,25 @@ naming:
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
@@ -1109,7 +1289,8 @@ naming:
anything is unknown, so it fits a Makefile or
a pre-commit hook.
colitur config --show every effective setting -- lang, overlay,
- template, format -- its resolved value, and
+ 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.
@@ -1261,6 +1442,10 @@ let with_year ys f =
the same shape as [prune]. [dump]/[check]/[list]/[show] (Task 7,
`colitur lang`/`colitur config`) follow the identical two shapes --
[dump]/[check] each take one value, [list]/[show] are bare. *)
+(* [sigla_style]/[sigla_book]/[sigla_tradition] (Task 8) are three more
+ single-valued optional settings, the same shape as [lang] -- each has a
+ config-key counterpart in [Colitur_naming.Config] and is resolved through
+ the identical [Config.resolve] precedence (flag > config > default). *)
type parsed_args = {
overlays : string list;
format : string option;
@@ -1278,6 +1463,9 @@ type parsed_args = {
check : string option;
list : bool;
show : bool;
+ sigla_style : string option;
+ sigla_book : string option;
+ sigla_tradition : string option;
positional : string list;
}
@@ -1312,6 +1500,12 @@ let parse_args argv =
| [ "--check" ] -> Error "--check needs a file path"
| "--list" :: rest -> go { acc with list = true } rest
| "--show" :: rest -> go { acc with show = true } rest
+ | "--sigla-style" :: v :: rest -> go { acc with sigla_style = Some v } rest
+ | [ "--sigla-style" ] -> Error "--sigla-style needs a language code or file path"
+ | "--sigla-book" :: v :: rest -> go { acc with sigla_book = Some v } rest
+ | [ "--sigla-book" ] -> Error "--sigla-book needs a value (full or abbr)"
+ | "--sigla-tradition" :: v :: rest -> go { acc with sigla_tradition = Some v } rest
+ | [ "--sigla-tradition" ] -> Error "--sigla-tradition needs a section name from lang/traditions.ini"
(* The recognised bare flags pass through as positional words for the
dispatch below to match; anything else beginning with '-' is rejected
rather than silently treated as a command or a year. *)
@@ -1325,7 +1519,8 @@ let parse_args argv =
go
{ overlays = []; format = None; from_y = None; to_y = None; dtstamp = None; year = None;
template = None; flavour = None; out = None; prune = false; lang = None; raw = false;
- dump = None; check = None; list = false; show = false; positional = [] }
+ dump = None; check = None; list = false; show = false; sigla_style = None; sigla_book = None;
+ sigla_tradition = None; positional = [] }
argv
(* Sibling to [reject_overlays_for]: `emit`'s own four flags have no meaning
@@ -1406,6 +1601,24 @@ let reject_lang_subcommand_flags_for cmd ~dump ~check ~list ~show =
exit 2
end
+(* Sibling again: `--sigla-style`/`--sigla-book`/`--sigla-tradition` (Task 8
+ plumbing, Task 9 wiring) actually render a citation on every command
+ that emits one -- `readings`, `table`/`render`, `emit`, `publish`, each
+ of which builds its own [Sigla.t] via [load_sigla] rather than calling
+ this rejector. Every OTHER command -- `day` included, which prints no
+ `first`/`gospel` field of its own -- still refuses the three flags
+ rather than silently accepting and ignoring them, the same discipline
+ every rejector above keeps; `config --show` is the one place that
+ previews their resolution without rendering anything. *)
+let reject_sigla_for cmd ~sigla_style ~sigla_book ~sigla_tradition =
+ if sigla_style <> None || sigla_book <> None || sigla_tradition <> None then begin
+ Printf.eprintf
+ "colitur: --sigla-style/--sigla-book/--sigla-tradition have no effect on `%s`; refusing rather \
+ than ignoring them\n"
+ cmd;
+ exit 2
+ end
+
(* `colitur check FILE...` -- load a user overlay, apply it to the real
shipped calendar, and say what it did, without printing a year of output.
@@ -1514,7 +1727,16 @@ let convert_report path =
[lang_dir ()], the same probe [load_lang] itself uses. Each is opened and
parsed (not merely listed by filename) so a malformed file is flagged
here rather than only failing later when someone actually tries to use
- it. *)
+ it.
+
+ [traditions.ini] lives in the same directory but is not a language file
+ -- it answers which book a reference DENOTES, not what it is CALLED (see
+ [load_tradition] above) -- and does not even parse as one ([Lang.of_string]
+ requires its own [\[meta\]] section, which traditions.ini has no reason to
+ carry). Without this exclusion it would list here as "(unreadable: ...)",
+ which is not a defect in the file, only a mismatch between what this scan
+ assumes every [.ini] in the directory is and what is actually shipped
+ there now. *)
let lang_list () =
let dir = lang_dir () in
match Sys.readdir dir with
@@ -1525,7 +1747,7 @@ let lang_list () =
Array.sort compare files;
Array.iter
(fun f ->
- if Filename.check_suffix f ".ini" then begin
+ if Filename.check_suffix f ".ini" && f <> "traditions.ini" then begin
let code = Filename.remove_extension f in
match read_file (Filename.concat dir f) with
| Ok t -> (
@@ -1652,11 +1874,36 @@ let lang_check path =
in
let missing = List.filter (fun s -> not (List.mem s have)) known in
let unknown = List.filter (fun s -> not (List.mem s known)) have in
+ (* Book names are checked the same way, and separately. Without
+ this the reference set gained a [bible] half that nothing ever
+ consulted: a file with [bible] entirely absent reported a clean
+ bill of health while every citation silently fell back to the
+ data's own Latin spelling. A miss is the TOTAL-lookup contract's
+ own signature -- [Lang.bible] returns the KEY when there is no
+ entry -- so equality with the key IS the test. *)
+ let missing_books =
+ List.concat_map
+ (fun id ->
+ let n = Colitur_citation.Book.to_string id in
+ List.filter_map
+ (fun form ->
+ let key = n ^ "." ^ form in
+ if Colitur_naming.Lang.bible t key = key then Some key
+ else None)
+ [ "full"; "abbr" ])
+ Colitur_citation.Book.all
+ in
List.iter (fun s -> Printf.printf "missing: %s\n" s) (List.sort compare missing);
+ List.iter (fun s -> Printf.printf "missing book: %s\n" s)
+ (List.sort compare missing_books);
List.iter (fun s -> Printf.printf "unknown slug: %s\n" s) (List.sort compare unknown);
+ let books_total = 2 * List.length Colitur_citation.Book.all in
Printf.printf "%s: %d of %d celebrations named, %d missing, %d unknown\n" path
(List.length known - List.length missing)
(List.length known) (List.length missing) (List.length unknown);
+ Printf.printf "%s: %d of %d book names, %d missing\n" path
+ (books_total - List.length missing_books) books_total
+ (List.length missing_books);
if unknown <> [] then exit 1)
(* `colitur config --show` -- each effective setting, its resolved value,
@@ -1669,24 +1916,68 @@ let lang_check path =
given these SAME command-line flags (so `config --show --lang fr` shows
exactly what a real `--lang fr` run would use); [overlay] is a list, so
it has no single "value" to resolve -- shown as one line per effective
- entry instead, with its own source. *)
-let config_show ~lang_flag ~template_flag ~format_flag ~overlays_flag config =
+ entry instead, with its own source.
+
+ [sigla_style]/[sigla_book]/[sigla_tradition] (Task 8) join the same
+ scalar rows, resolved through the identical [Config.resolve]. Two are
+ NOT arbitrary strings, though, so "report" also means "validate", the
+ same way an unknown [--lang] is an error rather than a silent Latin
+ fallback:
+ - [sigla_book] is a closed two-value setting ([full]/[abbr]) --
+ [Colitur_citation.Render.with_book] takes a variant, not a string, so
+ an unrecognised value could never mean anything downstream. Checked
+ HERE too, not only inside [load_sigla]'s own real renderer (Task 9) --
+ this preview must reject exactly what a real render would.
+ - [sigla_tradition] names a section of [lang/traditions.ini]; resolving
+ it for real (via [load_tradition], the same reader [load_sigla] below
+ uses) rather than only printing the string means a typo is caught
+ right here too -- though, unlike [sigla_book], [load_tradition] is
+ BY DESIGN never fatal (see its own comment: asking for a renumbering
+ is optional, unlike asking for a language), so an unknown tradition
+ degrades to a stderr warning and the Vulgate, exactly as it does on a
+ real render, not a [config --show] failure.
+ [sigla_style] gets no such check: like [lang] itself, it is an open
+ language code or path, not a closed set, and [config --show] does not
+ validate [lang] either (that only happens when a command actually loads
+ it via [load_lang]). *)
+let config_show ~lang_flag ~template_flag ~format_flag ~overlays_flag ~sigla_style_flag ~sigla_book_flag
+ ~sigla_tradition_flag config =
let cpath = config_path () in
Printf.printf "config file: %s (%s)\n" cpath
(if cpath <> "" && Sys.file_exists cpath then "exists" else "not found");
let scalar name flag cfgval default =
let v, src = Colitur_naming.Config.resolve ~flag ~config:cfgval ~default in
- Printf.printf "%-10s %-24s (%s)\n" name v src
+ Printf.printf "%-16s %-24s (%s)\n" name v src;
+ v
+ in
+ let lang_value = scalar "lang" lang_flag (Colitur_naming.Config.lang config) "la" in
+ let _ = scalar "template" template_flag (Colitur_naming.Config.template config) "(none)" in
+ let _ = scalar "format" format_flag (Colitur_naming.Config.format config) "(none)" in
+ (* Default is the resolved LANGUAGE, not a literal "la": a booklet that
+ asked for --lang fr and named no --sigla-style of its own gets French
+ citations too, not a silent switch back to Latin punctuation. *)
+ let _ = scalar "sigla_style" sigla_style_flag (Colitur_naming.Config.sigla_style config) lang_value in
+ let sigla_book_value, sigla_book_src =
+ Colitur_naming.Config.resolve ~flag:sigla_book_flag ~config:(Colitur_naming.Config.sigla_book config)
+ ~default:"abbr"
in
- scalar "lang" lang_flag (Colitur_naming.Config.lang config) "la";
- scalar "template" template_flag (Colitur_naming.Config.template config) "(none)";
- scalar "format" format_flag (Colitur_naming.Config.format config) "(none)";
+ if sigla_book_value <> "full" && sigla_book_value <> "abbr" then begin
+ Printf.eprintf "colitur: unknown --sigla-book %S (want \"full\" or \"abbr\")\n" sigla_book_value;
+ exit 2
+ end;
+ Printf.printf "%-16s %-24s (%s)\n" "sigla_book" sigla_book_value sigla_book_src;
+ let sigla_tradition_value, sigla_tradition_src =
+ Colitur_naming.Config.resolve ~flag:sigla_tradition_flag
+ ~config:(Colitur_naming.Config.sigla_tradition config) ~default:"vulgate"
+ in
+ let _ = load_tradition sigla_tradition_value in
+ Printf.printf "%-16s %-24s (%s)\n" "sigla_tradition" sigla_tradition_value sigla_tradition_src;
(match overlays_flag with
- | _ :: _ as l -> List.iter (fun o -> Printf.printf "%-10s %-24s (%s)\n" "overlay" o "flag") l
+ | _ :: _ as l -> List.iter (fun o -> Printf.printf "%-16s %-24s (%s)\n" "overlay" o "flag") l
| [] -> (
match Colitur_naming.Config.overlays config with
- | [] -> Printf.printf "%-10s %-24s (%s)\n" "overlay" "(none)" "default"
- | l -> List.iter (fun o -> Printf.printf "%-10s %-24s (%s)\n" "overlay" o "config") l))
+ | [] -> Printf.printf "%-16s %-24s (%s)\n" "overlay" "(none)" "default"
+ | l -> List.iter (fun o -> Printf.printf "%-16s %-24s (%s)\n" "overlay" o "config") l))
(* `colitur new-overlay` -- a starter file on stdout, for redirection.
Deliberately printed rather than written: the user picks the path, and a
@@ -1749,12 +2040,13 @@ let () =
Printf.eprintf "colitur: %s\n" msg;
usage ()
| Ok { overlays; format; from_y; to_y; dtstamp; year; template; flavour; out; prune; lang;
- raw; dump; check; list; show; positional } -> (
+ raw; dump; check; list; show; sigla_style; sigla_book; sigla_tradition; positional } -> (
let reject_emit = reject_emit_flags_for ~format ~from_y ~to_y ~dtstamp in
let reject_table = reject_table_flags_for ~year ~template ~flavour in
let reject_publish = reject_publish_flags_for ~out ~prune in
let reject_lang = reject_lang_for ~lang ~raw in
let reject_lang_sub = reject_lang_subcommand_flags_for ~dump ~check ~list ~show in
+ let reject_sigla = reject_sigla_for ~sigla_style ~sigla_book ~sigla_tradition in
(* Loaded once, unconditionally: a config file the user wrote and
colitur cannot honour (missing HOME aside, [load_config] treats
that as "no config" rather than an error) is worth surfacing on
@@ -1785,6 +2077,7 @@ let () =
reject_publish "--help";
reject_lang "--help";
reject_lang_sub "--help";
+ reject_sigla "--help";
print_help ()
| [ ("-V" | "--version" | "version") ] ->
reject_overlays_for "--version" overlays;
@@ -1793,6 +2086,7 @@ let () =
reject_publish "--version";
reject_lang "--version";
reject_lang_sub "--version";
+ reject_sigla "--version";
print_endline version;
exit 0
| [ "easter"; ys ] ->
@@ -1802,6 +2096,7 @@ let () =
reject_publish "easter";
reject_lang "easter";
reject_lang_sub "easter";
+ reject_sigla "easter";
with_year ys easter_report
| [ "temporal"; ys ] ->
reject_overlays_for "temporal" overlays;
@@ -1810,6 +2105,7 @@ let () =
reject_publish "temporal";
reject_lang "temporal";
reject_lang_sub "temporal";
+ reject_sigla "temporal";
with_year ys temporal_report
| "check" :: (_ :: _ as files) ->
reject_overlays_for "check" overlays;
@@ -1818,6 +2114,7 @@ let () =
reject_publish "check";
reject_lang "check";
reject_lang_sub "check";
+ reject_sigla "check";
check_report files
| [ "convert"; path ] ->
reject_overlays_for "convert" overlays;
@@ -1826,6 +2123,7 @@ let () =
reject_publish "convert";
reject_lang "convert";
reject_lang_sub "convert";
+ reject_sigla "convert";
convert_report path
| [ "new-overlay" ] ->
reject_overlays_for "new-overlay" overlays;
@@ -1834,6 +2132,7 @@ let () =
reject_publish "new-overlay";
reject_lang "new-overlay";
reject_lang_sub "new-overlay";
+ reject_sigla "new-overlay";
print_string new_overlay_template;
exit 0
| [ "lang" ] -> (
@@ -1842,6 +2141,7 @@ let () =
reject_table "lang";
reject_publish "lang";
reject_lang "lang";
+ reject_sigla "lang";
if show then begin
Printf.eprintf "colitur: --show has no effect on `lang`; refusing rather than ignoring it\n";
exit 2
@@ -1886,19 +2186,26 @@ let () =
exit 2
end;
config_show ~lang_flag:lang ~template_flag:template ~format_flag:format ~overlays_flag:overlays
+ ~sigla_style_flag:sigla_style ~sigla_book_flag:sigla_book ~sigla_tradition_flag:sigla_tradition
config
| [ "day"; ys ] ->
reject_emit "day";
reject_table "day";
reject_publish "day";
reject_lang_sub "day";
+ reject_sigla "day";
with_year ys (day_report ~lang:(resolved_lang ()) ~overlays:effective_overlays)
| [ "readings"; ys ] ->
reject_emit "readings";
reject_table "readings";
reject_publish "readings";
reject_lang_sub "readings";
- with_year ys (readings_report ~lang:(resolved_lang ()) ~overlays:effective_overlays)
+ let lang_t = resolved_lang () in
+ let sigla =
+ load_sigla ~raw ~lang_t ~sigla_style_flag:sigla_style ~sigla_book_flag:sigla_book
+ ~sigla_tradition_flag:sigla_tradition ~config
+ in
+ with_year ys (readings_report ~lang:lang_t ~sigla ~overlays:effective_overlays)
| [ "emit" ] -> (
reject_table "emit";
reject_publish "emit";
@@ -1914,10 +2221,14 @@ let () =
exit 2
| Some from_ys, Some to_ys ->
let lang_t = resolved_lang () in
+ let sigla =
+ load_sigla ~raw ~lang_t ~sigla_style_flag:sigla_style ~sigla_book_flag:sigla_book
+ ~sigla_tradition_flag:sigla_tradition ~config
+ in
with_year from_ys (fun from_y ->
with_year to_ys (fun to_y ->
- emit_report ~lang:lang_t ~format ~overlays:effective_overlays ~dtstamp ~from_y
- ~to_y))))
+ emit_report ~lang:lang_t ~sigla ~format ~overlays:effective_overlays ~dtstamp
+ ~from_y ~to_y))))
| [ ("table" | "render") as cmd ] -> (
reject_emit cmd;
reject_publish cmd;
@@ -1933,8 +2244,13 @@ let () =
exit 2
| Some ys ->
let lang_t = resolved_lang () in
+ let sigla =
+ load_sigla ~raw ~lang_t ~sigla_style_flag:sigla_style ~sigla_book_flag:sigla_book
+ ~sigla_tradition_flag:sigla_tradition ~config
+ in
with_year ys (fun y ->
- table_report ~lang:lang_t ~template ~flavour_opt:flavour ~overlays:effective_overlays y)
+ table_report ~lang:lang_t ~sigla ~template ~flavour_opt:flavour
+ ~overlays:effective_overlays y)
))
| [ "publish" ] -> (
reject_table "publish";
@@ -1951,6 +2267,10 @@ let () =
exit 2
| Some from_ys, Some to_ys ->
let lang_t = resolved_lang () in
+ let sigla =
+ load_sigla ~raw ~lang_t ~sigla_style_flag:sigla_style ~sigla_book_flag:sigla_book
+ ~sigla_tradition_flag:sigla_tradition ~config
+ in
with_year from_ys (fun from_y ->
with_year to_ys (fun to_y ->
(* [publish_report] writes many files across a whole
@@ -1969,8 +2289,8 @@ let () =
underlying errno in [Sys_error] instead -- both
are real on this path, so both are caught. *)
try
- publish_report ~lang:lang_t ~from_y ~to_y ~out ~overlays:effective_overlays
- ~dtstamp ~prune
+ publish_report ~lang:lang_t ~sigla ~from_y ~to_y ~out
+ ~overlays:effective_overlays ~dtstamp ~prune
with
| Unix.Unix_error (e, fn, arg) ->
Printf.eprintf "colitur: %s: %s: %s\n" fn arg (Unix.error_message e);