summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 12:58:31 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 12:58:31 +0200
commit6d78c65c58820f79ec5a978a9476d29969da218f (patch)
tree2045bf9f453c407bf8d60053876531420a1c251a /internal
parent27911b8f2574563ddd5d0de75f708503067069c9 (diff)
downloadlectio-6d78c65c58820f79ec5a978a9476d29969da218f.tar.gz
lectio-6d78c65c58820f79ec5a978a9476d29969da218f.zip
feat(cli): 'lectio --liturgy/-L' prints the computed liturgical day offline
Uses the pure engine + embedded universal calendar; UI-language names, rank, colour, cycles, and inline proper readings. Added the Assumption's propers.
Diffstat (limited to 'internal')
-rw-r--r--internal/caldata/roman-calendar.ini4
-rw-r--r--internal/cli/cli.go7
-rw-r--r--internal/cli/liturgy.go84
-rw-r--r--internal/cli/liturgy_test.go33
4 files changed, 128 insertions, 0 deletions
diff --git a/internal/caldata/roman-calendar.ini b/internal/caldata/roman-calendar.ini
index dfaeb96..8ce49e0 100644
--- a/internal/caldata/roman-calendar.ini
+++ b/internal/caldata/roman-calendar.ini
@@ -61,6 +61,10 @@ 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
+reading.first = Rev 11:19; 12:1-6.10
+reading.psalm = Ps 45:10-16
+reading.second = 1 Cor 15:20-27
+reading.gospel = Lk 1:39-56
[all-saints]
date = 11-01
diff --git a/internal/cli/cli.go b/internal/cli/cli.go
index d69bce0..6d09591 100644
--- a/internal/cli/cli.go
+++ b/internal/cli/cli.go
@@ -119,6 +119,7 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
var width int
var list bool
var ref string
+ var liturgy bool
fs := flag.NewFlagSet("lectio", flag.ContinueOnError)
fs.SetOutput(stderr)
@@ -163,6 +164,8 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
fs.StringVar(&output, "out", "", "write --md/--pdf output to FILE (default: stdout)")
fs.StringVar(&output, "output", "", "write --md/--pdf output to FILE (default: stdout)")
fs.StringVar(&calendar, "calendar", "", "export a month (YYYY-MM) as a printable A4 PDF calendar")
+ fs.BoolVar(&liturgy, "L", false, "print the computed liturgical day (offline calendar engine) and exit")
+ fs.BoolVar(&liturgy, "liturgy", false, "print the computed liturgical day (offline calendar engine) and exit")
if err := fs.Parse(rest); err != nil {
return 2
@@ -207,6 +210,10 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
cfg.UILanguage = config.NormalizeUILanguage(uiLang)
}
+ if liturgy {
+ return runLiturgy(cfg, date, stdout, stderr)
+ }
+
if citation || week || randV || randCh {
tbl, _ := bible.LoadBookTable(userBooksTOML())
switch {
diff --git a/internal/cli/liturgy.go b/internal/cli/liturgy.go
new file mode 100644
index 0000000..523531d
--- /dev/null
+++ b/internal/cli/liturgy.go
@@ -0,0 +1,84 @@
+package cli
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "time"
+
+ "github.com/lukaszkasprzak/lectio/internal/caldata"
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+ "github.com/lukaszkasprzak/lectio/internal/config"
+)
+
+// runLiturgy prints the computed liturgical day for date (default today) using
+// the offline calendar engine, and exits. It is the engine's demo/validation
+// surface; the daily-readings paths still use the scraper.
+func runLiturgy(cfg config.Config, date string, stdout, stderr io.Writer) int {
+ if date == "" {
+ date = today()
+ }
+ d, err := time.Parse("2006-01-02", date)
+ if err != nil {
+ fmt.Fprintf(stderr, "lectio: bad date %q (want YYYY-MM-DD)\n", date)
+ return 2
+ }
+ day := calendar.Compute(d.UTC(), cfg.Selection(), []calendar.Layer{caldata.Universal()})
+ printLiturgicalDay(stdout, cfg, day)
+ return 0
+}
+
+func printLiturgicalDay(out io.Writer, cfg config.Config, day calendar.LiturgicalDay) {
+ fmt.Fprintf(out, "%s %s\n", day.Date.Format("2006-01-02"), day.Weekday.String())
+ fmt.Fprintf(out, "season: %s (week %d · Sunday cycle %s · weekday cycle %s)\n",
+ day.Season, day.Week, day.SundayCycle, day.WeekdayCycle)
+ fmt.Fprintf(out, "observed: %s [%s · %s]\n", celebrationName(cfg, day.Observed), rankLabel(day.Observed.Rank), day.Colour)
+ for _, o := range day.Others {
+ if n := celebrationName(cfg, o); n != "" {
+ fmt.Fprintf(out, " also: %s [%s]\n", n, rankLabel(o.Rank))
+ }
+ }
+ for _, m := range day.Observed.Masses {
+ for _, r := range m.Readings {
+ label := r.Part
+ if m.Variant != "" {
+ label = m.Variant + " " + label
+ }
+ fmt.Fprintf(out, " %-16s %s\n", label+":", r.Citation)
+ }
+ }
+}
+
+// celebrationName is the celebration's name in the UI language (falling back to
+// English, then a humanized slug for temporal days that carry no proper name).
+func celebrationName(cfg config.Config, c calendar.Celebration) string {
+ lang := "en"
+ if cfg.UILanguage == "pl" {
+ lang = "pl"
+ }
+ if n := c.Name[lang]; n != "" {
+ return n
+ }
+ if n := c.Name["en"]; n != "" {
+ return n
+ }
+ if c.Slug == "" {
+ return "(feria)"
+ }
+ return strings.ReplaceAll(c.Slug, "-", " ")
+}
+
+func rankLabel(r calendar.Rank) string {
+ switch r {
+ case calendar.RankSolemnity:
+ return "solemnity"
+ case calendar.RankFeast:
+ return "feast"
+ case calendar.RankMemorial:
+ return "memorial"
+ case calendar.RankOptional:
+ return "optional memorial"
+ default:
+ return "feria"
+ }
+}
diff --git a/internal/cli/liturgy_test.go b/internal/cli/liturgy_test.go
new file mode 100644
index 0000000..c3ab0b4
--- /dev/null
+++ b/internal/cli/liturgy_test.go
@@ -0,0 +1,33 @@
+package cli
+
+import (
+ "bytes"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/lukaszkasprzak/lectio/internal/config"
+)
+
+func TestRunLiturgy(t *testing.T) {
+ dir := t.TempDir()
+ t.Setenv("LECTIO_CONFIG", filepath.Join(dir, "config.ini"))
+ cfg, err := config.Load()
+ if err != nil {
+ t.Fatal(err)
+ }
+ var buf bytes.Buffer
+ if code := runLiturgy(cfg, "2025-08-15", &buf, &buf); code != 0 {
+ t.Fatalf("exit code %d", code)
+ }
+ out := buf.String()
+ if !strings.Contains(out, "2025-08-15") {
+ t.Errorf("missing date:\n%s", out)
+ }
+ if !strings.Contains(strings.ToLower(out), "solemnity") || !strings.Contains(out, "Assumption") {
+ t.Errorf("2025-08-15 should be the Assumption solemnity:\n%s", out)
+ }
+ if !strings.Contains(out, "gospel:") {
+ t.Errorf("proper reading not shown:\n%s", out)
+ }
+}