aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 12:18:27 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 12:18:27 +0200
commit9839ea9f2257d843c08497f07e9a5d23dc47e0fb (patch)
tree69cef5760ce2a097f0c26df542a9050c3937676a /internal
parent3677ad490c9f18020d68349c9e3a71bcf389178f (diff)
downloadlectio-9839ea9f2257d843c08497f07e9a5d23dc47e0fb.tar.gz
lectio-9839ea9f2257d843c08497f07e9a5d23dc47e0fb.zip
feat(calendar): ordered-stack layer merge (override/suppress/add)
Diffstat (limited to 'internal')
-rw-r--r--internal/calendar/merge.go52
-rw-r--r--internal/calendar/merge_test.go34
2 files changed, 86 insertions, 0 deletions
diff --git a/internal/calendar/merge.go b/internal/calendar/merge.go
new file mode 100644
index 0000000..ce8fe8f
--- /dev/null
+++ b/internal/calendar/merge.go
@@ -0,0 +1,52 @@
+package calendar
+
+// mergeLayers folds an ordered layer stack into one raw celebration set. Later
+// layers override earlier ones field-by-field; a slug with Fields["suppress"]
+// == "true" is removed; variant sub-sections merge per variant.
+func mergeLayers(layers []Layer) map[string]RawCelebration {
+ out := map[string]RawCelebration{}
+ for _, layer := range layers {
+ for slug, rc := range layer.Cels {
+ if rc.Fields["suppress"] == "true" {
+ delete(out, slug)
+ continue
+ }
+ cur, ok := out[slug]
+ if !ok {
+ cur = RawCelebration{Fields: map[string]string{}, Variants: map[string]map[string]string{}}
+ } else {
+ cur = cloneRaw(cur)
+ }
+ for k, v := range rc.Fields {
+ cur.Fields[k] = v
+ }
+ for variant, fields := range rc.Variants {
+ if cur.Variants[variant] == nil {
+ cur.Variants[variant] = map[string]string{}
+ }
+ for k, v := range fields {
+ cur.Variants[variant][k] = v
+ }
+ }
+ cur.Fields["layer"] = layer.ID // provenance: last writer
+ out[slug] = cur
+ }
+ }
+ return out
+}
+
+func cloneRaw(rc RawCelebration) RawCelebration {
+ nf := make(map[string]string, len(rc.Fields))
+ for k, v := range rc.Fields {
+ nf[k] = v
+ }
+ nv := make(map[string]map[string]string, len(rc.Variants))
+ for variant, fields := range rc.Variants {
+ m := make(map[string]string, len(fields))
+ for k, v := range fields {
+ m[k] = v
+ }
+ nv[variant] = m
+ }
+ return RawCelebration{Fields: nf, Variants: nv}
+}
diff --git a/internal/calendar/merge_test.go b/internal/calendar/merge_test.go
new file mode 100644
index 0000000..168c56f
--- /dev/null
+++ b/internal/calendar/merge_test.go
@@ -0,0 +1,34 @@
+package calendar
+
+import "testing"
+
+func raw(fields map[string]string) RawCelebration {
+ return RawCelebration{Fields: fields, Variants: map[string]map[string]string{}}
+}
+
+func TestMergeLayers(t *testing.T) {
+ base := Layer{ID: "universal", Cels: map[string]RawCelebration{
+ "assumption": raw(map[string]string{"rank": "solemnity", "colour": "white", "name.en": "Assumption"}),
+ "st-x": raw(map[string]string{"rank": "optional", "date": "01-02"}),
+ "drop-me": raw(map[string]string{"rank": "memorial", "date": "03-03"}),
+ }}
+ local := Layer{ID: "krakow", Cels: map[string]RawCelebration{
+ "st-x": raw(map[string]string{"rank": "feast"}), // elevate; keep date
+ "drop-me": raw(map[string]string{"suppress": "true"}), // remove
+ "st-new": raw(map[string]string{"rank": "solemnity", "date": "05-08"}), // add
+ }}
+ got := mergeLayers([]Layer{base, local})
+
+ if got["st-x"].Fields["rank"] != "feast" || got["st-x"].Fields["date"] != "01-02" {
+ t.Errorf("st-x merge = %+v", got["st-x"].Fields)
+ }
+ if _, ok := got["drop-me"]; ok {
+ t.Error("drop-me should be suppressed")
+ }
+ if got["st-new"].Fields["date"] != "05-08" {
+ t.Error("st-new should be added")
+ }
+ if got["assumption"].Fields["name.en"] != "Assumption" {
+ t.Error("untouched base entry lost")
+ }
+}