diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 13:19:29 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 13:19:29 +0200 |
| commit | acbba6b53df93de694abf1feb90ab401fd6a4580 (patch) | |
| tree | 55a80c803f3699fd66526fa48bcb5ed2c73b40a6 /internal/caldata/caldata.go | |
| parent | f96c7fcd319d7fe94a4dd8debe5d4d56a77ab2c9 (diff) | |
| download | lectio-acbba6b53df93de694abf1feb90ab401fd6a4580.tar.gz lectio-acbba6b53df93de694abf1feb90ab401fd6a4580.zip | |
feat(caldata): export ParseLayer + LoadLayer + Stack (user calendar layers)
Diffstat (limited to 'internal/caldata/caldata.go')
| -rw-r--r-- | internal/caldata/caldata.go | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/internal/caldata/caldata.go b/internal/caldata/caldata.go index 5deb895..9272fbb 100644 --- a/internal/caldata/caldata.go +++ b/internal/caldata/caldata.go @@ -6,6 +6,8 @@ package caldata import ( _ "embed" "fmt" + "os" + "path/filepath" "github.com/lukaszkasprzak/lectio/internal/calendar" "github.com/lukaszkasprzak/lectio/internal/ini" @@ -17,14 +19,51 @@ 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) + l, err := ParseLayer(romanCalendar) if err != nil { panic(fmt.Sprintf("caldata: embedded roman-calendar.ini invalid: %v", err)) } return l } -func parse(data []byte) (calendar.Layer, error) { +// LoadLayer reads and parses a user calendar layer file. id (the filename stem) +// is used as the layer's ID when the file's [layer] header omits one. +func LoadLayer(path, id string) (calendar.Layer, error) { + data, err := os.ReadFile(path) + if err != nil { + return calendar.Layer{}, err + } + l, err := ParseLayer(data) + if err != nil { + return calendar.Layer{}, fmt.Errorf("%s: %w", path, err) + } + if l.ID == "" { + l.ID = id + } + return l, nil +} + +// Stack returns the ordered layer stack for the engine: the universal base +// first, then each user layer named in use (matched to "<dir>/<id>.ini"), in +// order. A layer that fails to load is skipped and reported in the error slice +// so a single bad file never breaks calendar computation. +func Stack(dir string, use []string) ([]calendar.Layer, []error) { + layers := []calendar.Layer{Universal()} + var errs []error + for _, id := range use { + l, err := LoadLayer(filepath.Join(dir, id+".ini"), id) + if err != nil { + errs = append(errs, err) + continue + } + layers = append(layers, l) + } + return layers, errs +} + +// ParseLayer parses INI bytes (a [layer] header + [slug]/[slug/variant] +// celebration sections) into a calendar.Layer. +func ParseLayer(data []byte) (calendar.Layer, error) { secs, err := ini.Parse(data) if err != nil { return calendar.Layer{}, err |
