aboutsummaryrefslogtreecommitdiff
path: root/tools/bootstrap_lectionary.ml
blob: 52c7e50ac98c7b684570ee2e105ea601d5ff522d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
(* Bootstraps data/ef/lectionary.sexp from lectio's tridentine-lectionary.ini.
   Lives in tools/ and not the kernel for the same reason
   bootstrap_sanctoral.ml does: reading someone else's INI needs a reader,
   which does not belong in a pure kernel.

   Only `first` and `gospel` are mapped -- the plan's Global Constraints fix
   the scope at Epistle + Gospel, and lectio's data carries nothing else. *)

open Colitur_kernel

let default_source = "../lectio/internal/caldata/tridentine-lectionary.ini"
let default_dest = "data/ef/lectionary.sexp"

let die fmt =
  Printf.ksprintf
    (fun s -> prerr_endline ("bootstrap_lectionary: " ^ s); exit 1)
    fmt

type section = { name : string; fields : (string * string) list }

let parse_ini path =
  let ic = try open_in path with Sys_error e -> die "%s" e in
  let sections = ref [] and cur = ref None in
  let flush () =
    match !cur with
    | Some (n, fs) -> sections := { name = n; fields = List.rev fs } :: !sections
    | None -> ()
  in
  (try
     while true do
       let line = String.trim (input_line ic) in
       if line = "" || line.[0] = ';' || line.[0] = '#' then ()
       else if line.[0] = '[' then begin
         flush ();
         cur := Some (String.sub line 1 (String.length line - 2), [])
       end
       else
         match String.index_opt line '=' with
         | None -> die "%s: cannot parse line %S" path line
         | Some i ->
             let k = String.trim (String.sub line 0 i) in
             let v =
               String.trim (String.sub line (i + 1) (String.length line - i - 1))
             in
             cur :=
               (match !cur with
               | Some (n, fs) -> Some (n, (k, v) :: fs)
               | None -> die "%s: field %S before any section" path k)
     done
   with End_of_file -> ());
  flush ();
  close_in ic;
  List.rev !sections

let convert sec =
  let slug =
    match Slug.of_string sec.name with
    | Ok s -> s
    | Error e -> die "bad slug %S: %s" sec.name e
  in
  let cite part key =
    match List.assoc_opt key sec.fields with
    | None | Some "" -> None
    | Some reference -> Some { Citation.part; reference }
  in
  let cs = List.filter_map Fun.id [ cite Citation.First "first"; cite Citation.Gospel "gospel" ] in
  if cs = [] then die "%s: no first/gospel field" sec.name;
  if List.length cs = 1 then
    die "%s: has one reading, not two -- an Epistle without a Gospel (or the \
         reverse) is malformed and must be investigated, not silently shipped"
      sec.name;
  (slug, cs)

let sha256 path =
  let ic = Unix.open_process_in (Printf.sprintf "sha256sum %s" (Filename.quote path)) in
  let line = try input_line ic with End_of_file -> die "sha256sum failed" in
  ignore (Unix.close_process_in ic);
  List.hd (String.split_on_char ' ' line)

let () =
  let src = if Array.length Sys.argv > 1 then Sys.argv.(1) else default_source in
  let dst = if Array.length Sys.argv > 2 then Sys.argv.(2) else default_dest in
  let secs = parse_ini src in
  let entries = List.map convert secs in
  let lect =
    match Lectionary.of_entries entries with
    | Ok l -> l
    | Error e -> die "%s" e
  in
  let oc = open_out dst in
  Printf.fprintf oc
    "; data/ef/lectionary.sexp -- EF (1962) temporal lectionary (Epistle +\n\
     ; Gospel citations, never scripture text), bootstrapped from lectio.\n\
     ; Generator: tools/bootstrap_lectionary.ml -- do not hand-edit; re-run the\n\
     ; generator against a newer lectio and commit the diff instead.\n\
     ;\n\
     ; Source: %s\n\
     ; SHA-256: %s\n\
     ; %d entries. Regenerate with:\n\
     ;   eval $(opam env) && dune exec tools/bootstrap_lectionary.exe -- %s %s\n"
    src (sha256 src) (List.length entries) src dst;
  Sexplib.Sexp.output_hum oc (Lectionary.sexp_of_t lect);
  output_char oc '\n';
  close_out oc;
  Printf.printf "bootstrap_lectionary: %d entries -> %s\n" (List.length entries) dst