aboutsummaryrefslogtreecommitdiff
path: root/internal/cli
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-24 13:07:37 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-24 13:07:37 +0200
commita675a983424f1f8d103fc12d55ef4f7129a92d62 (patch)
treeb9483bf09f03cb9047a548289556c73a9343bbb0 /internal/cli
parent55a5c793d04b55892fcddfc753d8526bd5748f03 (diff)
downloadlectio-a675a983424f1f8d103fc12d55ef4f7129a92d62.tar.gz
lectio-a675a983424f1f8d103fc12d55ef4f7129a92d62.zip
qol: tui jump-to-date (d) + lectio --citation/--week + hide bt for traditional web; v0.10.0
Diffstat (limited to 'internal/cli')
-rw-r--r--internal/cli/cli.go94
-rw-r--r--internal/cli/cli_test.go67
2 files changed, 160 insertions, 1 deletions
diff --git a/internal/cli/cli.go b/internal/cli/cli.go
index 76ebd5b..611ce52 100644
--- a/internal/cli/cli.go
+++ b/internal/cli/cli.go
@@ -43,6 +43,8 @@ Flags:
-P, --pager page reading output (like git); default from config
--no-pager never page, even if config sets one
--list list all books + abbreviations (dialect from sigla_style)
+ --citation print the day's gospel reference (scripts/cron) and exit
+ --week list the coming week's gospel references and exit
-v, --version print the version and exit
-h, --help this help
@@ -59,6 +61,8 @@ Examples:
lectio -p "Jn 3:16" -b vul look up a passage (English sigla)
lectio -p "J 3,16" -c wuj,drb Polish sigla when sigla_style=polish/auto+pl UI
lectio --list list every book + abbreviations
+ lectio --citation today's gospel reference
+ lectio --week 2026-07-22 a week of gospel references from a date
Flags override config. Exit codes: 0 ok, 1 runtime error (fetch/parse),
2 usage error (bad flag, bad date, bad version, bad --lectionary/--lang).
@@ -96,7 +100,7 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
return 2
}
- var all, raw, refresh, offline, update, clean, pagerFlag, noPager bool
+ var all, raw, refresh, offline, update, clean, pagerFlag, noPager, citation, week bool
var bibleVer, compareList, lectionary, lang string
var width int
var list bool
@@ -134,6 +138,8 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
fs.StringVar(&ref, "p", "", "look up a passage, e.g. \"J 3:16\" (with -b/-c)")
fs.StringVar(&ref, "ref", "", "look up a passage, e.g. \"J 3:16\" (with -b/-c)")
fs.BoolVar(&list, "list", false, "list all books + abbreviations (in your sigla_style)")
+ fs.BoolVar(&citation, "citation", false, "print the day's gospel reference and exit")
+ fs.BoolVar(&week, "week", false, "list the coming week's gospel references and exit")
if err := fs.Parse(rest); err != nil {
return 2
@@ -175,6 +181,13 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
cfg.TraditionalLang = lang
}
+ if citation {
+ return runCitation(cfg, date, refresh, stdout, stderr)
+ }
+ if week {
+ return runWeek(cfg, date, refresh, stdout, stderr)
+ }
+
effAll := all || cfg.All
effWidth := width
if effWidth == 0 {
@@ -250,6 +263,85 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
return code
}
+// gospelSection returns the gospel section from a (gospel-only, All=false)
+// load: the section tagged "ewangelia" (modern) or "evangelium" (traditional),
+// else the first section present. ok is false only when secs is empty.
+func gospelSection(secs []liturgy.Section) (liturgy.Section, bool) {
+ for _, s := range secs {
+ if s.PartID == "ewangelia" || s.PartID == "evangelium" {
+ return s, true
+ }
+ }
+ if len(secs) > 0 {
+ return secs[0], true
+ }
+ return liturgy.Section{}, false
+}
+
+// gospelCitation returns a section's scripture reference: its Citation field if
+// set, else the reference parsed out of its Heading (e.g. "Ewangelia (Mt 7,
+// 1-5)" -> "Mt 7, 1-5"), else "" (source-form, never translated).
+func gospelCitation(sec liturgy.Section) string {
+ if sec.Citation != "" {
+ return sec.Citation
+ }
+ if c, err := liturgy.ExtractCitation(sec.Heading); err == nil {
+ return c
+ }
+ return ""
+}
+
+// runCitation handles --citation: fetch the day's gospel (gospel-only) and
+// print just its scripture reference, for scripts/cron/prompt use. Honors the
+// resolved cfg (lectionary/lang/offline) and date. Exit 1 if the day has no
+// gospel reference (unpublished date, no network while offline, ...).
+func runCitation(cfg config.Config, date string, refresh bool, stdout, stderr io.Writer) int {
+ secs, _, err := readings.Load(cfg, readings.Options{Date: date, Refresh: refresh, Offline: cfg.Offline, All: false})
+ if err != nil {
+ fmt.Fprintln(stderr, "lectio:", err)
+ return 1
+ }
+ sec, ok := gospelSection(secs)
+ if !ok {
+ fmt.Fprintln(stderr, "lectio: no gospel for", date)
+ return 1
+ }
+ cit := gospelCitation(sec)
+ if cit == "" {
+ fmt.Fprintln(stderr, "lectio: no gospel reference for", date)
+ return 1
+ }
+ fmt.Fprintln(stdout, cit)
+ return 0
+}
+
+// runWeek handles --week: print the gospel reference for each of the seven days
+// starting at date, one "YYYY-MM-DD <reference>" line per day. A day that
+// can't be loaded (unpublished, offline gap, no gospel) shows "—" rather than
+// aborting the run, so the list is always seven lines. Honors cfg
+// (lectionary/lang/offline).
+func runWeek(cfg config.Config, date string, refresh bool, stdout, stderr io.Writer) int {
+ start, err := time.Parse("2006-01-02", date)
+ if err != nil {
+ fmt.Fprintln(stderr, "lectio:", err)
+ return 2
+ }
+ for i := 0; i < 7; i++ {
+ d := start.AddDate(0, 0, i).Format("2006-01-02")
+ cit := "—"
+ secs, _, err := readings.Load(cfg, readings.Options{Date: d, Refresh: refresh, Offline: cfg.Offline, All: false})
+ if err == nil {
+ if sec, ok := gospelSection(secs); ok {
+ if c := gospelCitation(sec); c != "" {
+ cit = c
+ }
+ }
+ }
+ fmt.Fprintf(stdout, "%s %s\n", d, cit)
+ }
+ return 0
+}
+
// userBooksTOML returns the bytes of the optional user books.toml, or nil if
// it is absent/unreadable (built-in defaults are used).
func userBooksTOML() []byte {
diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go
index bc7dfdb..47d8f55 100644
--- a/internal/cli/cli_test.go
+++ b/internal/cli/cli_test.go
@@ -352,3 +352,70 @@ func TestRefCompare(t *testing.T) {
t.Errorf("ref compare missing verse:\n%s", out.String())
}
}
+
+func TestGospelCitationHelpers(t *testing.T) {
+ secs := []liturgy.Section{
+ {Heading: "1. czytanie (Iz 1, 1)", PartID: "pierwsze_czytanie"},
+ {Heading: "Ewangelia (Mt 7, 1-5)", PartID: "ewangelia"},
+ }
+ sec, ok := gospelSection(secs)
+ if !ok || sec.PartID != "ewangelia" {
+ t.Fatalf("gospelSection=%+v ok=%v", sec, ok)
+ }
+ if c := gospelCitation(sec); c != "Mt 7, 1-5" {
+ t.Errorf("citation from heading = %q", c)
+ }
+ // Citation field is preferred over the heading.
+ s2 := liturgy.Section{Heading: "Ewangelia (Mt 7, 1-5)", Citation: "Mt 7, 1-5. 12", PartID: "ewangelia"}
+ if c := gospelCitation(s2); c != "Mt 7, 1-5. 12" {
+ t.Errorf("citation prefers Citation field, got %q", c)
+ }
+ // Empty -> ("", false).
+ if _, ok := gospelSection(nil); ok {
+ t.Error("gospelSection(nil) should be !ok")
+ }
+}
+
+func TestCitation(t *testing.T) {
+ html, err := os.ReadFile("../liturgy/testdata/2026-07-22.html")
+ if err != nil {
+ t.Fatal(err)
+ }
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write(html) }))
+ defer srv.Close()
+ liturgy.SetBaseURL(srv.URL + "/liturgia/%s/Ewangelia")
+ t.Setenv("XDG_CACHE_HOME", t.TempDir())
+ t.Setenv("XDG_CONFIG_HOME", t.TempDir())
+
+ var out, errb bytes.Buffer
+ if code := Run([]string{"--citation", "2026-07-22"}, nil, &out, &errb); code != 0 {
+ t.Fatalf("citation code=%d stderr=%q", code, errb.String())
+ }
+ if s := strings.TrimSpace(out.String()); !strings.Contains(s, "J 20") {
+ t.Errorf("citation = %q, want a gospel ref containing 'J 20'", s)
+ }
+}
+
+func TestWeek(t *testing.T) {
+ html, err := os.ReadFile("../liturgy/testdata/2026-07-22.html")
+ if err != nil {
+ t.Fatal(err)
+ }
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write(html) }))
+ defer srv.Close()
+ liturgy.SetBaseURL(srv.URL + "/liturgia/%s/Ewangelia")
+ t.Setenv("XDG_CACHE_HOME", t.TempDir())
+ t.Setenv("XDG_CONFIG_HOME", t.TempDir())
+
+ var out, errb bytes.Buffer
+ if code := Run([]string{"--week", "2026-07-22"}, nil, &out, &errb); code != 0 {
+ t.Fatalf("week code=%d stderr=%q", code, errb.String())
+ }
+ lines := strings.Split(strings.TrimSpace(out.String()), "\n")
+ if len(lines) != 7 {
+ t.Fatalf("week printed %d lines, want 7:\n%s", len(lines), out.String())
+ }
+ if !strings.HasPrefix(lines[0], "2026-07-22") || !strings.Contains(lines[0], "J 20") {
+ t.Errorf("week[0] = %q", lines[0])
+ }
+}