aboutsummaryrefslogtreecommitdiff
path: root/lib/kernel
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-14 16:43:00 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-14 16:43:00 +0200
commitbca1a206589bc41bbcb367a7ae9b217c02f0e892 (patch)
treeb5c7ee9972c91bab2da7b888cb64796cd47d934b /lib/kernel
parent148729e731370f56a50c3819a7d81808c6e602ca (diff)
downloadcolitur-bca1a206589bc41bbcb367a7ae9b217c02f0e892.tar.gz
colitur-bca1a206589bc41bbcb367a7ae9b217c02f0e892.zip
kernel(lectionary): slug-keyed reading citations
Data only, the same shape and discipline as Layer: slug-canonical, duplicates rejected at construction naming the offending slug, sexp round-trips. Which slug a day falls back to is a rubric and belongs to the rite module, so nothing here knows about ferias or Sundays.
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/lectionary.ml33
-rw-r--r--lib/kernel/lectionary.mli21
2 files changed, 54 insertions, 0 deletions
diff --git a/lib/kernel/lectionary.ml b/lib/kernel/lectionary.ml
new file mode 100644
index 0000000..1da3d1e
--- /dev/null
+++ b/lib/kernel/lectionary.ml
@@ -0,0 +1,33 @@
+open Sexplib0.Sexp_conv
+
+type entry = Slug.t * Citation.t list [@@deriving sexp]
+type t = entry list [@@deriving sexp]
+
+let empty = []
+
+let of_entries es =
+ let sorted = List.stable_sort (fun (a, _) (b, _) -> Slug.compare a b) es in
+ let rec dup = function
+ | (a, _) :: ((b, _) :: _ as rest) ->
+ if Slug.equal a b then Some a else dup rest
+ | _ -> None
+ in
+ match dup sorted with
+ | Some s ->
+ Error (Printf.sprintf "lectionary: duplicate slug %S" (Slug.to_string s))
+ | None -> Ok sorted
+
+let find t s = List.assoc_opt s t
+let mem t s = List.mem_assoc s t
+let entries t = t
+
+let load path =
+ match Sexplib.Sexp.load_sexp path with
+ | exception Sexplib.Sexp.Parse_error e ->
+ Error (Printf.sprintf "lectionary: %s: %s" path e.err_msg)
+ | exception Sys_error e -> Error (Printf.sprintf "lectionary: %s" e)
+ | sexp -> (
+ match t_of_sexp sexp with
+ | exception Sexplib0.Sexp_conv_error.Of_sexp_error (exn, _) ->
+ Error (Printf.sprintf "lectionary: %s: %s" path (Printexc.to_string exn))
+ | parsed -> of_entries parsed)
diff --git a/lib/kernel/lectionary.mli b/lib/kernel/lectionary.mli
new file mode 100644
index 0000000..1595377
--- /dev/null
+++ b/lib/kernel/lectionary.mli
@@ -0,0 +1,21 @@
+(** Reading citations keyed by liturgical slug (parent spec ยง2: "keyed by
+ liturgical slug + cycle -> citation set"). Data only: which slug a given
+ day falls back to is a rubric, and lives in the rite module.
+
+ Same discipline as {!Layer}: slug-canonical, validated at construction,
+ sexp round-trips. *)
+type t [@@deriving sexp]
+
+val empty : t
+
+(** Canonically sorts by slug. [Error] on a duplicate slug, naming it --
+ a lectionary with two answers for one day is not a lectionary. *)
+val of_entries : (Slug.t * Citation.t list) list -> (t, string) result
+
+val find : t -> Slug.t -> Citation.t list option
+val mem : t -> Slug.t -> bool
+val entries : t -> (Slug.t * Citation.t list) list
+
+(** Loads from a sexp file. Parse and validation failures come back as
+ [Error], never as an exception. *)
+val load : string -> (t, string) result