From b93de95dd24ff2a1d3126e6d7d00cd77530a03bf Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Fri, 24 Jul 2026 09:15:40 +0200 Subject: tradlit: offline caching + read; update pre-caches traditional; --clean prunes it; v0.2.0 --- internal/liturgy/clean_test.go | 31 +++++++++++++++++-------------- internal/liturgy/fetch.go | 32 +++++++++++++++++++------------- internal/liturgy/fetch_test.go | 2 +- internal/liturgy/store.go | 2 +- 4 files changed, 38 insertions(+), 29 deletions(-) (limited to 'internal/liturgy') diff --git a/internal/liturgy/clean_test.go b/internal/liturgy/clean_test.go index 4dbe0d9..d9ab4fc 100644 --- a/internal/liturgy/clean_test.go +++ b/internal/liturgy/clean_test.go @@ -7,12 +7,13 @@ import ( "time" ) -// TestCleanCache exercises CleanCache's file-selection rules: only -// ".html"/".json" pairs older than the cutoff are removed; a -// recent pair and a non-matching file are left untouched. The cutoff is a -// fixed literal (not time.Now-derived) so the test is deterministic; only -// the "recent" fixture is anchored to today, and only to make sure it lands -// safely on the "keep" side of that fixed cutoff. +// TestCleanCache exercises CleanCache's file-selection rules: date-prefixed +// cache files (modern ".html"/".json" and traditional ".trad..json") +// older than the cutoff are removed; recent files and a non-matching file +// are left untouched. The cutoff is a fixed literal (not time.Now-derived) +// so the test is deterministic; only the "recent" fixtures are anchored to +// today, and only to make sure they land safely on the "keep" side of that +// fixed cutoff. func TestCleanCache(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CACHE_HOME", dir) @@ -23,10 +24,12 @@ func TestCleanCache(t *testing.T) { recent := time.Now().Format("2006-01-02") files := map[string]string{ - "2020-01-01.html": "old", - "2020-01-01.json": `[{"Heading":"old"}]`, - recent + ".html": "recent", - "notes.txt": "not a cache file", + "2020-01-01.html": "old", + "2020-01-01.json": `[{"Heading":"old"}]`, + "2020-01-01.trad.pl.json": `[{"info":{},"sections":[]}]`, + recent + ".html": "recent", + recent + ".trad.pl.json": `[{"info":{},"sections":[]}]`, + "notes.txt": "not a cache file", } for name, content := range files { if err := os.WriteFile(filepath.Join(cacheDir, name), []byte(content), 0o644); err != nil { @@ -39,19 +42,19 @@ func TestCleanCache(t *testing.T) { if err != nil { t.Fatalf("CleanCache: %v", err) } - if removed != 2 { - t.Errorf("removed = %d, want 2", removed) + if removed != 3 { + t.Errorf("removed = %d, want 3", removed) } if freed <= 0 { t.Errorf("freed = %d, want > 0", freed) } - for _, gone := range []string{"2020-01-01.html", "2020-01-01.json"} { + for _, gone := range []string{"2020-01-01.html", "2020-01-01.json", "2020-01-01.trad.pl.json"} { if _, err := os.Stat(filepath.Join(cacheDir, gone)); !os.IsNotExist(err) { t.Errorf("%s still exists after CleanCache, want removed", gone) } } - for _, kept := range []string{recent + ".html", "notes.txt"} { + for _, kept := range []string{recent + ".html", recent + ".trad.pl.json", "notes.txt"} { if _, err := os.Stat(filepath.Join(cacheDir, kept)); err != nil { t.Errorf("%s missing after CleanCache, want kept: %v", kept, err) } diff --git a/internal/liturgy/fetch.go b/internal/liturgy/fetch.go index f4f6992..8c887f4 100644 --- a/internal/liturgy/fetch.go +++ b/internal/liturgy/fetch.go @@ -41,11 +41,13 @@ var publishedRe = regexp.MustCompile(`id="\w*0all"`) // validate its own input first. var dateRe = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`) -// cacheFileRe matches the cache file names Load/Harvest write into -// cacheDir(): ".html" or ".json". CleanCache uses it -// to tell cache entries apart from anything else that might be sitting in -// the directory. -var cacheFileRe = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2})\.(html|json)$`) +// cacheFileRe matches the date-prefixed cache file names Load/Harvest/tradlit +// write into CacheDir(): ".html", ".json", and +// ".trad..json" (the traditional-lectionary cache; see +// internal/tradlit). CleanCache uses it to tell cache entries apart from +// anything else that might be sitting in the directory, and to recover the +// date (group 1) for the age check regardless of which cache file it is. +var cacheFileRe = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2})\.[a-z0-9.]+$`) // Options controls how Load resolves a day's readings. type Options struct { @@ -58,9 +60,11 @@ type Options struct { Offline bool } -// cacheDir is where the HTML/JSON cache layers live: +// CacheDir is where the HTML/JSON cache layers live: // ${XDG_CACHE_HOME:-~/.cache}/lectio/ -func cacheDir() string { +// Exported so internal/tradlit shares the same cache root for the +// traditional lectionary's propers. +func CacheDir() string { base := os.Getenv("XDG_CACHE_HOME") if base == "" { home, err := os.UserHomeDir() @@ -93,7 +97,7 @@ func Load(opts Options) ([]Section, error) { return LoadOffline(opts.Date) } - dir := cacheDir() + dir := CacheDir() jsonPath := filepath.Join(dir, opts.Date+".json") htmlPath := filepath.Join(dir, opts.Date+".html") @@ -142,12 +146,14 @@ func Load(opts Options) ([]Section, error) { } // CleanCache removes cached readings whose date is before `before` from -// cacheDir(). It matches only files named ".html"/".json"; -// anything else in the directory (e.g. a stray notes.txt, or the sigla -// store, which lives elsewhere entirely) is left alone. A missing cache dir -// is not an error -- it just means there is nothing to clean yet. +// CacheDir(). It matches only date-prefixed cache files (see cacheFileRe: +// ".html", ".json", or the traditional lectionary's +// ".trad..json"); anything else in the directory (e.g. a stray +// notes.txt, or the sigla store, which lives elsewhere entirely) is left +// alone. A missing cache dir is not an error -- it just means there is +// nothing to clean yet. func CleanCache(before time.Time) (removed int, freed int64, err error) { - dir := cacheDir() + dir := CacheDir() entries, err := os.ReadDir(dir) if err != nil { if os.IsNotExist(err) { diff --git a/internal/liturgy/fetch_test.go b/internal/liturgy/fetch_test.go index 1cebb86..738126d 100644 --- a/internal/liturgy/fetch_test.go +++ b/internal/liturgy/fetch_test.go @@ -37,7 +37,7 @@ func TestLoadCaches(t *testing.T) { // before it ever builds a filesystem path from it, so every caller (web, // cli, tui) is protected even if a future caller forgets to validate. // -// The planted "passwd.json" sits one level *above* cacheDir() -- reachable +// The planted "passwd.json" sits one level *above* CacheDir() -- reachable // only via a "../" date -- so if Load ever built jsonPath from the raw date // unchecked, loadJSONCache would read it back and return its section instead // of an error. diff --git a/internal/liturgy/store.go b/internal/liturgy/store.go index cc176e8..69e45f2 100644 --- a/internal/liturgy/store.go +++ b/internal/liturgy/store.go @@ -127,7 +127,7 @@ func Harvest(fromDate string, maxDays int) (added int, furthest string, err erro return 0, "", err } - dir := cacheDir() + dir := CacheDir() day := start var harvestErr error for i := 0; maxDays == 0 || i < maxDays; i++ { -- cgit v1.3