diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 12:18:27 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 12:18:27 +0200 |
| commit | 9839ea9f2257d843c08497f07e9a5d23dc47e0fb (patch) | |
| tree | 69cef5760ce2a097f0c26df542a9050c3937676a | |
| parent | 3677ad490c9f18020d68349c9e3a71bcf389178f (diff) | |
| download | lectio-9839ea9f2257d843c08497f07e9a5d23dc47e0fb.tar.gz lectio-9839ea9f2257d843c08497f07e9a5d23dc47e0fb.zip | |
feat(calendar): ordered-stack layer merge (override/suppress/add)
| -rw-r--r-- | internal/calendar/merge.go | 52 | ||||
| -rw-r--r-- | internal/calendar/merge_test.go | 34 |
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") + } +} |
