summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 01:35:35 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 01:35:35 +0200
commit2386a4551aec94252168a8554269ee423d516882 (patch)
tree9d4e50014e66ffe49a9ef3110c1808244d40fb82 /cmd
parent4855ef2a667b7ea71092e0582db5aff1838a0d63 (diff)
downloadlectio-2386a4551aec94252168a8554269ee423d516882.tar.gz
lectio-2386a4551aec94252168a8554269ee423d516882.zip
cmd(lectio-ef-dump): new EF calendar dumper for colitur's differential oracle
Prints one line per day, sorted ascending, for a civil-year range: date, weekday, season, week, observed slug, rank, colour, then zero or more +slug tokens for the day's other (losing) candidates. The line format matches colitur's `colitur day` output field-for-field so the two streams diff directly; every column carries lectio's own vocabulary (season names, slugs, rank/colour spellings) with no translation toward colitur's — that mapping belongs to the differential comparator, not this dumper. Uses calendar.Compute with Selection{Form: "old"} over the embedded tridentine layer (caldata.Tridentine()), mirroring the setup already used by oracle_ef_test.go. Lives in cmd/ because internal/calendar cannot be imported outside this module; colitur's build is untouched. Note for the comparator: lectio has no RG 111 commemoration-admission logic, so the trailing +slug tokens are the day's losing candidates, not admitted commemorations.
Diffstat (limited to 'cmd')
-rw-r--r--cmd/lectio-ef-dump/main.go106
1 files changed, 106 insertions, 0 deletions
diff --git a/cmd/lectio-ef-dump/main.go b/cmd/lectio-ef-dump/main.go
new file mode 100644
index 0000000..08ff547
--- /dev/null
+++ b/cmd/lectio-ef-dump/main.go
@@ -0,0 +1,106 @@
+// Command lectio-ef-dump prints lectio's Extraordinary Form (1962) calendar,
+// one line per day, for use as colitur's differential oracle.
+//
+// Output format matches colitur's `colitur day <year>` line for line (see
+// colitur's bin/main.ml, day_line), so the two streams are diffable directly:
+//
+// YYYY-MM-DD weekday season week slug rank colour [+other-slug]...
+//
+// Every column carries lectio's OWN vocabulary (season names, slugs, rank and
+// colour spellings) -- this dumper does not translate lectio's values into
+// colitur's. The mapping between the two vocabularies is the differential
+// comparator's job, not this dumper's; translating here would hide real
+// divergences behind an already-reconciled view.
+//
+// The trailing "+other-slug" tokens are lectio's Others: the sanctoral
+// candidates that lost the day's precedence contest. lectio has no RG 111
+// commemoration-admission logic, so this is the set of losing candidates, not
+// the set of admitted commemorations -- the two are different things. See the
+// task report for detail; the comparator does not diff this column.
+//
+// Usage: lectio-ef-dump <from-year> <to-year>
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/lukaszkasprzak/lectio/internal/caldata"
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+)
+
+func main() {
+ os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
+}
+
+// run is lectio-ef-dump's testable entry point: parse args, compute, and
+// write. Returns a process exit code (0 ok, 1 runtime error, 2 usage error).
+func run(args []string, stdout, stderr io.Writer) int {
+ if len(args) != 2 {
+ fmt.Fprintln(stderr, "usage: lectio-ef-dump <from-year> <to-year>")
+ return 2
+ }
+ from, errFrom := strconv.Atoi(args[0])
+ to, errTo := strconv.Atoi(args[1])
+ if errFrom != nil || errTo != nil {
+ fmt.Fprintln(stderr, "lectio-ef-dump: from-year and to-year must be integers")
+ return 2
+ }
+ if from > to {
+ fmt.Fprintf(stderr, "lectio-ef-dump: from-year %d is after to-year %d\n", from, to)
+ return 2
+ }
+
+ sel := calendar.DefaultSelection()
+ sel.Form = "old"
+ layers := []calendar.Layer{caldata.Tridentine()}
+
+ w := bufio.NewWriter(stdout)
+ for y := from; y <= to; y++ {
+ start := time.Date(y, time.January, 1, 0, 0, 0, 0, time.UTC)
+ end := time.Date(y, time.December, 31, 0, 0, 0, 0, time.UTC)
+ for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {
+ day := calendar.Compute(d, sel, layers)
+ if _, err := w.WriteString(dumpLine(day)); err != nil {
+ fmt.Fprintf(stderr, "lectio-ef-dump: %v\n", err)
+ return 1
+ }
+ }
+ }
+ if err := w.Flush(); err != nil {
+ fmt.Fprintf(stderr, "lectio-ef-dump: %v\n", err)
+ return 1
+ }
+ return 0
+}
+
+// dumpLine renders one LiturgicalDay as a colitur-format line. Week is
+// lectio's own int (0 where no season week applies, e.g. named I class feasts
+// and per annum green-season ferias); printed as "-" there so the field count
+// stays fixed, matching colitur's own convention for an absent week.
+func dumpLine(day calendar.LiturgicalDay) string {
+ week := "-"
+ if day.Week != 0 {
+ week = strconv.Itoa(day.Week)
+ }
+ var b strings.Builder
+ fmt.Fprintf(&b, "%s %s %s %s %s %s %s",
+ day.Date.Format("2006-01-02"),
+ strings.ToLower(day.Weekday.String()),
+ string(day.Season),
+ week,
+ day.Observed.Slug,
+ string(day.Observed.Rank),
+ string(day.Colour),
+ )
+ for _, o := range day.Others {
+ fmt.Fprintf(&b, " +%s", o.Slug)
+ }
+ b.WriteByte('\n')
+ return b.String()
+}