From 2386a4551aec94252168a8554269ee423d516882 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 01:35:35 +0200 Subject: cmd(lectio-ef-dump): new EF calendar dumper for colitur's differential oracle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- cmd/lectio-ef-dump/main.go | 106 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 cmd/lectio-ef-dump/main.go (limited to 'cmd') 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 ` 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 +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 ") + 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() +} -- cgit v1.3