aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 12:25:46 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 12:25:46 +0200
commit56f3b8161f73c33fe6e25f6461d3f061af427e9e (patch)
tree7abe54aa45b3b4d9e3db0a75564d75c0d44b06ce /internal
parenta8232892b0ec94f8d077e3730143a09efcac452d (diff)
downloadlectio-56f3b8161f73c33fe6e25f6461d3f061af427e9e.tar.gz
lectio-56f3b8161f73c33fe6e25f6461d3f061af427e9e.zip
feat(caldata): embed + parse the owned universal Roman calendar (initial coverage)
Diffstat (limited to 'internal')
-rw-r--r--internal/caldata/caldata.go69
-rw-r--r--internal/caldata/caldata_test.go25
-rw-r--r--internal/caldata/roman-calendar.ini231
3 files changed, 325 insertions, 0 deletions
diff --git a/internal/caldata/caldata.go b/internal/caldata/caldata.go
new file mode 100644
index 0000000..5deb895
--- /dev/null
+++ b/internal/caldata/caldata.go
@@ -0,0 +1,69 @@
+// Package caldata embeds and parses lectio's owned universal General Roman
+// Calendar data into a calendar.Layer. It imports only internal/ini and
+// internal/calendar (types).
+package caldata
+
+import (
+ _ "embed"
+ "fmt"
+
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+ "github.com/lukaszkasprzak/lectio/internal/ini"
+)
+
+//go:embed roman-calendar.ini
+var romanCalendar []byte
+
+// Universal parses the embedded universal calendar. It panics on a malformed
+// embedded file (a build-time bug caught by tests), never at the request layer.
+func Universal() calendar.Layer {
+ l, err := parse(romanCalendar)
+ if err != nil {
+ panic(fmt.Sprintf("caldata: embedded roman-calendar.ini invalid: %v", err))
+ }
+ return l
+}
+
+func parse(data []byte) (calendar.Layer, error) {
+ secs, err := ini.Parse(data)
+ if err != nil {
+ return calendar.Layer{}, err
+ }
+ layer := calendar.Layer{Cels: map[string]calendar.RawCelebration{}}
+ for _, s := range secs {
+ fields := map[string]string{}
+ for _, p := range s.Pairs {
+ fields[p.Key] = p.Val
+ }
+ switch {
+ case s.Name == "" && len(fields) == 0:
+ continue
+ case s.Name == "layer":
+ layer.ID, layer.Name, layer.Type = fields["id"], fields["name"], fields["type"]
+ default:
+ base, variant, isVariant := cutVariant(s.Name)
+ rc := layer.Cels[base]
+ if rc.Fields == nil {
+ rc = calendar.RawCelebration{Fields: map[string]string{}, Variants: map[string]map[string]string{}}
+ }
+ if isVariant {
+ rc.Variants[variant] = fields
+ } else {
+ for k, v := range fields {
+ rc.Fields[k] = v
+ }
+ }
+ layer.Cels[base] = rc
+ }
+ }
+ return layer, nil
+}
+
+func cutVariant(name string) (base, variant string, ok bool) {
+ for i := 0; i < len(name); i++ {
+ if name[i] == '/' {
+ return name[:i], name[i+1:], true
+ }
+ }
+ return name, "", false
+}
diff --git a/internal/caldata/caldata_test.go b/internal/caldata/caldata_test.go
new file mode 100644
index 0000000..ab7a314
--- /dev/null
+++ b/internal/caldata/caldata_test.go
@@ -0,0 +1,25 @@
+package caldata
+
+import (
+ "testing"
+
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+)
+
+func TestUniversalLoads(t *testing.T) {
+ l := Universal()
+ if l.ID != "universal" {
+ t.Fatalf("layer id = %q", l.ID)
+ }
+ rc, ok := l.Cels["assumption"]
+ if !ok || rc.Fields["rank"] != "solemnity" || rc.Fields["date"] != "08-15" {
+ t.Fatalf("assumption = %+v ok=%v", rc, ok)
+ }
+ // every entry must build into a typed Celebration whose rank parsed.
+ for slug, rc := range l.Cels {
+ c := calendar.BuildCelebrationForTest(slug, rc)
+ if c.Rank == calendar.RankFerial && rc.Fields["rank"] != "" {
+ t.Errorf("%s: rank did not parse (%q)", slug, rc.Fields["rank"])
+ }
+ }
+}
diff --git a/internal/caldata/roman-calendar.ini b/internal/caldata/roman-calendar.ini
new file mode 100644
index 0000000..dfaeb96
--- /dev/null
+++ b/internal/caldata/roman-calendar.ini
@@ -0,0 +1,231 @@
+; General Roman Calendar (Ordinary Form) — universal sanctoral.
+; The temporal cycle (Sundays, seasons, Easter, Christmas, Epiphany, Ascension,
+; Pentecost, Trinity, Corpus Christi, Sacred Heart, Christ the King, Baptism,
+; Holy Family) is computed by internal/calendar and is NOT listed here.
+; Initial coverage; expanded and verified against the oracle (see the plan).
+; Bootstrapped with reference to romcal (MIT) — see NOTICE.
+
+[layer]
+id = universal
+name = General Roman Calendar
+type = universal
+
+; --- Solemnities ---
+
+[mary-mother-of-god]
+date = 01-01
+rank = solemnity
+class = bvm
+colour = white
+name.en = Mary, the Holy Mother of God
+name.pl = Świętej Bożej Rodzicielki Maryi
+name.la = Sanctae Dei Genetricis Mariae
+
+[st-joseph]
+date = 03-19
+rank = solemnity
+class = saint
+colour = white
+name.en = Saint Joseph, Spouse of the Blessed Virgin Mary
+name.pl = Świętego Józefa, Oblubieńca Najświętszej Maryi Panny
+
+[annunciation]
+date = 03-25
+rank = solemnity
+class = lord
+colour = white
+name.en = The Annunciation of the Lord
+name.pl = Zwiastowanie Pańskie
+
+[nativity-john-baptist]
+date = 06-24
+rank = solemnity
+class = saint
+colour = white
+name.en = The Nativity of Saint John the Baptist
+name.pl = Narodzenie świętego Jana Chrzciciela
+
+[peter-and-paul]
+date = 06-29
+rank = solemnity
+class = saint
+colour = red
+name.en = Saints Peter and Paul, Apostles
+name.pl = Świętych Apostołów Piotra i Pawła
+
+[assumption]
+date = 08-15
+rank = solemnity
+class = bvm
+colour = white
+name.en = The Assumption of the Blessed Virgin Mary
+name.pl = Wniebowzięcie Najświętszej Maryi Panny
+name.la = In Assumptione Beatae Mariae Virginis
+
+[all-saints]
+date = 11-01
+rank = solemnity
+class = saint
+colour = white
+name.en = All Saints
+name.pl = Wszystkich Świętych
+
+[all-souls]
+date = 11-02
+rank = solemnity
+class = saint
+colour = violet
+name.en = The Commemoration of All the Faithful Departed (All Souls)
+name.pl = Wspomnienie wszystkich wiernych zmarłych
+
+[immaculate-conception]
+date = 12-08
+rank = solemnity
+class = bvm
+colour = white
+name.en = The Immaculate Conception of the Blessed Virgin Mary
+name.pl = Niepokalane Poczęcie Najświętszej Maryi Panny
+
+; --- Feasts of the Lord (class lord → replace Ordinary Sundays) ---
+
+[presentation-of-the-lord]
+date = 02-02
+rank = feast
+class = lord
+colour = white
+name.en = The Presentation of the Lord
+name.pl = Ofiarowanie Pańskie
+
+[transfiguration]
+date = 08-06
+rank = feast
+class = lord
+colour = white
+name.en = The Transfiguration of the Lord
+name.pl = Przemienienie Pańskie
+
+[exaltation-of-the-cross]
+date = 09-14
+rank = feast
+class = lord
+colour = red
+name.en = The Exaltation of the Holy Cross
+name.pl = Podwyższenie Krzyża Świętego
+
+[dedication-of-the-lateran]
+date = 11-09
+rank = feast
+class = lord
+colour = white
+name.en = The Dedication of the Lateran Basilica
+
+; --- Feasts (saints) ---
+
+[conversion-of-paul]
+date = 01-25
+rank = feast
+class = saint
+colour = white
+name.en = The Conversion of Saint Paul the Apostle
+
+[st-mark]
+date = 04-25
+rank = feast
+class = saint
+colour = red
+name.en = Saint Mark, Evangelist
+
+[st-james]
+date = 07-25
+rank = feast
+class = saint
+colour = red
+name.en = Saint James, Apostle
+
+[archangels]
+date = 09-29
+rank = feast
+class = saint
+colour = white
+name.en = Saints Michael, Gabriel and Raphael, Archangels
+
+[st-luke]
+date = 10-18
+rank = feast
+class = saint
+colour = red
+name.en = Saint Luke, Evangelist
+
+[st-andrew]
+date = 11-30
+rank = feast
+class = saint
+colour = red
+name.en = Saint Andrew, Apostle
+
+[st-stephen]
+date = 12-26
+rank = feast
+class = saint
+colour = red
+name.en = Saint Stephen, the First Martyr
+
+[st-john-evangelist]
+date = 12-27
+rank = feast
+class = saint
+colour = white
+name.en = Saint John, Apostle and Evangelist
+
+[holy-innocents]
+date = 12-28
+rank = feast
+class = saint
+colour = red
+name.en = The Holy Innocents, Martyrs
+
+; --- Memorials ---
+
+[st-thomas-aquinas]
+date = 01-28
+rank = memorial
+class = saint
+colour = white
+name.en = Saint Thomas Aquinas, Priest and Doctor
+
+[st-benedict]
+date = 07-11
+rank = memorial
+class = saint
+colour = white
+name.en = Saint Benedict, Abbot
+
+[st-anthony-padua]
+date = 06-13
+rank = memorial
+class = saint
+colour = white
+name.en = Saint Anthony of Padua, Priest and Doctor
+
+[st-augustine]
+date = 08-28
+rank = memorial
+class = saint
+colour = white
+name.en = Saint Augustine, Bishop and Doctor
+
+[st-therese-lisieux]
+date = 10-01
+rank = memorial
+class = saint
+colour = white
+name.en = Saint Thérèse of the Child Jesus, Virgin and Doctor
+
+; --- Optional memorials ---
+
+[st-nicholas]
+date = 12-06
+rank = optional
+class = saint
+colour = white
+name.en = Saint Nicholas, Bishop