aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 21:30:56 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 21:30:56 +0200
commitbec7c034aca15537900d74a7063d85c0f4deacae (patch)
treebaf4cac4e3a38507b8a0f9de80ac251a5df82908 /internal
parent2901c73fde7ff19e8cdea00f933dc628d11c6ec6 (diff)
downloadlectio-bec7c034aca15537900d74a7063d85c0f4deacae.tar.gz
lectio-bec7c034aca15537900d74a7063d85c0f4deacae.zip
feat(calfeed): shared day builder + reusable reading resolver
Diffstat (limited to 'internal')
-rw-r--r--internal/caldata/readings.go31
-rw-r--r--internal/calfeed/build.go68
-rw-r--r--internal/calfeed/build_test.go46
-rw-r--r--internal/cli/liturgy.go24
4 files changed, 149 insertions, 20 deletions
diff --git a/internal/caldata/readings.go b/internal/caldata/readings.go
new file mode 100644
index 0000000..a11235d
--- /dev/null
+++ b/internal/caldata/readings.go
@@ -0,0 +1,31 @@
+package caldata
+
+import (
+ "time"
+
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+)
+
+// Readings resolves a day's Mass readings: the observed celebration's inline
+// propers, else the temporal lectionary for its slug, else -- for an EF
+// weekday with no proper Mass -- the preceding Sunday's Mass (the 1962 rule
+// that green-season ferias repeat the Sunday). Shared by the CLI (cli.dayReadings
+// delegates here) and calfeed.Build (injected as its readings func).
+func Readings(sel calendar.Selection, layers []calendar.Layer, date time.Time, day calendar.LiturgicalDay) []calendar.Reading {
+ var rs []calendar.Reading
+ for _, m := range day.Observed.Masses {
+ rs = append(rs, m.Readings...)
+ }
+ if len(rs) > 0 {
+ return rs
+ }
+ if r := TemporalReadings(sel.Form, day.Observed.Slug); len(r) > 0 {
+ return r
+ }
+ if sel.Form == "old" && day.Observed.Layer == "temporal" && date.Weekday() != time.Sunday {
+ sun := date.AddDate(0, 0, -int(date.Weekday())) // preceding Sunday (Sunday = 0)
+ sd := calendar.Compute(sun, sel, layers)
+ return TemporalReadings(sel.Form, sd.Observed.Slug)
+ }
+ return nil
+}
diff --git a/internal/calfeed/build.go b/internal/calfeed/build.go
new file mode 100644
index 0000000..d3962aa
--- /dev/null
+++ b/internal/calfeed/build.go
@@ -0,0 +1,68 @@
+package calfeed
+
+import (
+ "strings"
+ "time"
+
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+)
+
+// Build computes the day list from..to inclusive (calendar.Compute per date)
+// and maps it to the wire model. readings is injected -- the caller passes
+// caldata.Readings -- so calfeed never imports internal/caldata.
+func Build(from, to time.Time, uiLang string, sel calendar.Selection, layers []calendar.Layer, readings func(date time.Time, day calendar.LiturgicalDay) []calendar.Reading) []DayView {
+ var days []DayView
+ for d := from; !d.After(to); d = d.AddDate(0, 0, 1) {
+ day := calendar.Compute(d, sel, layers)
+
+ others := make([]CelView, 0, len(day.Others))
+ for _, o := range day.Others {
+ others = append(others, celView(uiLang, o))
+ }
+
+ var rs []ReadingView
+ for _, r := range readings(d, day) {
+ rs = append(rs, ReadingView{Part: r.Part, Citation: r.Citation})
+ }
+
+ days = append(days, DayView{
+ Date: d.Format("2006-01-02"),
+ Season: string(day.Season),
+ Week: day.Week,
+ Weekday: day.Weekday.String(),
+ Colour: string(day.Colour),
+ Observed: celView(uiLang, day.Observed),
+ Others: others,
+ Cycles: Cycles{Sunday: day.SundayCycle, Weekday: day.WeekdayCycle},
+ Readings: rs,
+ })
+ }
+ return days
+}
+
+// celView maps a calendar.Celebration to its wire view.
+func celView(uiLang string, c calendar.Celebration) CelView {
+ return CelView{
+ Slug: c.Slug,
+ Name: celebrationName(uiLang, c),
+ Rank: string(c.Rank),
+ Class: int(c.Class),
+ }
+}
+
+// celebrationName resolves c's name in uiLang, falling back to English, then
+// a humanized slug (with the "ef-" prefix stripped and dashes turned to
+// spaces), then "(feria)" for an unnamed temporal day. Mirrors
+// cli.celebrationName.
+func celebrationName(uiLang string, c calendar.Celebration) string {
+ if n := c.Name[uiLang]; n != "" {
+ return n
+ }
+ if n := c.Name["en"]; n != "" {
+ return n
+ }
+ if c.Slug == "" {
+ return "(feria)"
+ }
+ return strings.ReplaceAll(strings.TrimPrefix(c.Slug, "ef-"), "-", " ")
+}
diff --git a/internal/calfeed/build_test.go b/internal/calfeed/build_test.go
new file mode 100644
index 0000000..333a51b
--- /dev/null
+++ b/internal/calfeed/build_test.go
@@ -0,0 +1,46 @@
+package calfeed
+
+import (
+ "testing"
+ "time"
+
+ "github.com/lukaszkasprzak/lectio/internal/caldata"
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+)
+
+func TestBuildThreeDayRange(t *testing.T) {
+ sel := calendar.DefaultSelection()
+ sel.Form = "old"
+ layers := []calendar.Layer{caldata.Tridentine()}
+
+ from := time.Date(2026, 1, 5, 0, 0, 0, 0, time.UTC)
+ to := time.Date(2026, 1, 7, 0, 0, 0, 0, time.UTC)
+ days := Build(from, to, "en", sel, layers, func(date time.Time, day calendar.LiturgicalDay) []calendar.Reading {
+ return caldata.Readings(sel, layers, date, day)
+ })
+
+ if len(days) != 3 {
+ t.Fatalf("len(days) = %d, want 3", len(days))
+ }
+
+ wantDates := []string{"2026-01-05", "2026-01-06", "2026-01-07"}
+ seenWeekday := false
+ feastFound := false
+ for i, d := range days {
+ if d.Date != wantDates[i] {
+ t.Errorf("day %d: date = %q, want %q", i, d.Date, wantDates[i])
+ }
+ if d.Weekday != "" {
+ seenWeekday = true
+ }
+ if d.Date == "2026-01-06" && d.Observed.Name != "" {
+ feastFound = true
+ }
+ }
+ if !seenWeekday {
+ t.Errorf("expected weekday strings to be set: %+v", days)
+ }
+ if !feastFound {
+ t.Errorf("expected Epiphany (2026-01-06) to have a non-empty Observed.Name: %+v", days)
+ }
+}
diff --git a/internal/cli/liturgy.go b/internal/cli/liturgy.go
index ced23ae..5b6036e 100644
--- a/internal/cli/liturgy.go
+++ b/internal/cli/liturgy.go
@@ -38,27 +38,11 @@ func runLiturgy(cfg config.Config, date string, stdout, stderr io.Writer) int {
return 0
}
-// dayReadings resolves the day's Mass readings: the observed celebration's
-// inline propers, else the temporal lectionary for its slug, else -- for an EF
-// weekday with no proper Mass -- the preceding Sunday's Mass (the 1962 rule
-// that green-season ferias repeat the Sunday).
+// dayReadings resolves the day's Mass readings; the logic lives in
+// caldata.Readings (shared with calfeed.Build) so it stays reusable outside
+// the CLI.
func dayReadings(sel calendar.Selection, layers []calendar.Layer, date time.Time, day calendar.LiturgicalDay) []calendar.Reading {
- var rs []calendar.Reading
- for _, m := range day.Observed.Masses {
- rs = append(rs, m.Readings...)
- }
- if len(rs) > 0 {
- return rs
- }
- if r := caldata.TemporalReadings(sel.Form, day.Observed.Slug); len(r) > 0 {
- return r
- }
- if sel.Form == "old" && day.Observed.Layer == "temporal" && date.Weekday() != time.Sunday {
- sun := date.AddDate(0, 0, -int(date.Weekday())) // preceding Sunday (Sunday = 0)
- sd := calendar.Compute(sun, sel, layers)
- return caldata.TemporalReadings(sel.Form, sd.Observed.Slug)
- }
- return nil
+ return caldata.Readings(sel, layers, date, day)
}
// vernacularVersion is the corpus that renders reading text: the config's