aboutsummaryrefslogtreecommitdiff
path: root/internal/cli/cli.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-24 10:47:48 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-24 10:47:48 +0200
commite50b003d84b091b60c8028468edc633f04e39731 (patch)
treef68b4efc14b133bacd571550d86869aa450cf97d /internal/cli/cli.go
parentc9f3bf46f0de09edb796e35f3dcff349febf75c9 (diff)
downloadlectio-e50b003d84b091b60c8028468edc633f04e39731.tar.gz
lectio-e50b003d84b091b60c8028468edc633f04e39731.zip
bible-lookup: add --ref passage lookup + --list-pl/--list-en book lists; v0.6.0
Diffstat (limited to 'internal/cli/cli.go')
-rw-r--r--internal/cli/cli.go178
1 files changed, 166 insertions, 12 deletions
diff --git a/internal/cli/cli.go b/internal/cli/cli.go
index 5954e23..bd080b9 100644
--- a/internal/cli/cli.go
+++ b/internal/cli/cli.go
@@ -13,6 +13,7 @@ import (
"strings"
"time"
+ "github.com/lukaszkasprzak/lectio/internal/bible"
"github.com/lukaszkasprzak/lectio/internal/config"
"github.com/lukaszkasprzak/lectio/internal/i18n"
"github.com/lukaszkasprzak/lectio/internal/liturgy"
@@ -30,6 +31,7 @@ Flags:
-a, --all all readings, not just the gospel
-b, --bible VER one version's text: bt,wuj,vul,grb,drb
-c, --compare LIST versions side by side (comma-separated)
+ -p, --ref REF look up a passage (e.g. "J 3:16") with -b/-c
-r, --raw text only, no banner/headings (for piping)
-w, --width N wrap width; 0 = detect terminal
-R, --refresh ignore cache, re-download
@@ -40,6 +42,8 @@ Flags:
-C, --clean prune cached readings older than a year
-P, --pager page reading output (like git); default from config
--no-pager never page, even if config sets one
+ --list-pl list all books with Polish names + abbreviations
+ --list-en list all books with English names + abbreviations
-v, --version print the version and exit
-h, --help this help
@@ -53,6 +57,9 @@ Examples:
lectio -b vul -a Vulgate text, all readings
lectio -l trad -a traditional lectionary, all readings
lectio -u harvest sigla to the horizon
+ lectio -p "J 3:16" -b vul look up a passage in one version
+ lectio -p "Ps 23:1" -c wuj,drb compare a passage across versions
+ lectio --list-en list every book (English) + abbreviations
Flags override config. Exit codes: 0 ok, 1 runtime error (fetch/parse),
2 usage error (bad flag, bad date, bad version, bad --lectionary/--lang).
@@ -93,6 +100,8 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
var all, raw, refresh, offline, update, clean, pagerFlag, noPager bool
var bibleVer, compareList, lectionary, lang string
var width int
+ var listPL, listEN bool
+ var ref string
fs := flag.NewFlagSet("lectio", flag.ContinueOnError)
fs.SetOutput(stderr)
@@ -123,6 +132,10 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
fs.BoolVar(&pagerFlag, "P", false, "page reading output (like git)")
fs.BoolVar(&pagerFlag, "pager", false, "page reading output (like git)")
fs.BoolVar(&noPager, "no-pager", false, "never page, even if config sets one")
+ 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(&listPL, "list-pl", false, "list all books with Polish names + abbreviations")
+ fs.BoolVar(&listEN, "list-en", false, "list all books with English names + abbreviations")
if err := fs.Parse(rest); err != nil {
return 2
@@ -178,18 +191,28 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
}
}
- // Validate versions before any pager starts -- don't page an error. When
- // compareList != "", bibleVer is unused (compare wins, as before) so it's
- // deliberately left unvalidated in that case.
- if bibleVer != "" && compareList == "" && !config.ValidVersion(bibleVer) {
- fmt.Fprintf(stderr, "lectio: unknown version %q (want one of bt, wuj, vul, grb, drb)\n", bibleVer)
- return 2
- }
- if compareList != "" {
- for _, v := range strings.Split(compareList, ",") {
- if v = strings.TrimSpace(v); v != "" && !config.ValidVersion(v) {
- fmt.Fprintf(stderr, "lectio: unknown version %q (want one of bt, wuj, vul, grb, drb)\n", v)
- return 2
+ // Validate versions before any pager starts -- don't page an error.
+ var refVersions []string
+ if ref != "" {
+ rv, verr := refLookupVersions(cfg, bibleVer, compareList)
+ if verr != nil {
+ fmt.Fprintln(stderr, "lectio:", verr)
+ return 2
+ }
+ refVersions = rv
+ } else {
+ // Day-reading paths: bt is a valid version here (compareList wins over
+ // bibleVer, as before, so bibleVer is left unvalidated when a list is given).
+ if bibleVer != "" && compareList == "" && !config.ValidVersion(bibleVer) {
+ fmt.Fprintf(stderr, "lectio: unknown version %q (want one of bt, wuj, vul, grb, drb)\n", bibleVer)
+ return 2
+ }
+ if compareList != "" {
+ for _, v := range strings.Split(compareList, ",") {
+ if v = strings.TrimSpace(v); v != "" && !config.ValidVersion(v) {
+ fmt.Fprintf(stderr, "lectio: unknown version %q (want one of bt, wuj, vul, grb, drb)\n", v)
+ return 2
+ }
}
}
}
@@ -205,6 +228,10 @@ func Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
var code int
switch {
+ case listPL || listEN:
+ code = runList(listEN && !listPL, out)
+ case ref != "":
+ code = lookupRef(cfg, ref, refVersions, raw, effWidth, out, stderr)
case compareList != "":
code = renderCompare(cfg, compareList, date, effAll, raw, effWidth, refresh, out, stderr)
case bibleVer != "":
@@ -529,6 +556,133 @@ func renderCompare(cfg config.Config, list, date string, all, raw bool, width in
return 0
}
+// isCorpusVersion reports whether v is a scripture-text version with an
+// embedded corpus (wuj/vul/grb/drb) -- i.e. a valid version that is not "bt"
+// (the niedziela.pl scrape, which has no full text to look a passage up in).
+func isCorpusVersion(v string) bool {
+ return config.ValidVersion(v) && v != "bt"
+}
+
+// refLookupVersions resolves which corpus version(s) `--ref` should look a
+// passage up in, rejecting "bt" (no corpus). Precedence: an explicit -c LIST,
+// then a single -b VER, then a sensible default (the configured default_version
+// if it has a corpus, else the first corpus version in cfg.Versions). It errors
+// (exit 2 in the caller) on any non-corpus version or if nothing usable is
+// configured.
+func refLookupVersions(cfg config.Config, bibleVer, compareList string) ([]string, error) {
+ if compareList != "" {
+ var versions []string
+ for _, v := range strings.Split(compareList, ",") {
+ if v = strings.TrimSpace(v); v == "" {
+ continue
+ }
+ if !isCorpusVersion(v) {
+ return nil, fmt.Errorf("--ref cannot use version %q; pass a corpus version (wuj, vul, grb, drb)", v)
+ }
+ versions = append(versions, v)
+ }
+ if len(versions) == 0 {
+ return nil, fmt.Errorf("--ref needs at least one corpus version in -c")
+ }
+ return versions, nil
+ }
+ if bibleVer != "" {
+ if !isCorpusVersion(bibleVer) {
+ return nil, fmt.Errorf("--ref cannot use version %q; pass a corpus version (wuj, vul, grb, drb)", bibleVer)
+ }
+ return []string{bibleVer}, nil
+ }
+ if isCorpusVersion(cfg.DefaultVersion) {
+ return []string{cfg.DefaultVersion}, nil
+ }
+ for _, v := range cfg.Versions {
+ if isCorpusVersion(v) {
+ return []string{v}, nil
+ }
+ }
+ return nil, fmt.Errorf("--ref needs a corpus version; pass -b wuj|vul|grb|drb")
+}
+
+// lookupRef renders a passage lookup (-p/--ref). A --ref citation is already in
+// target form (a "Book chap:verse" reference), exactly like a traditional
+// (missalemeum) citation, so it reuses the same render path with
+// lectionary="traditional": render.GatherVersion/Compare then resolve the ref
+// literally via bible.Lookup (no Polish->English niedziela conversion). versions
+// is the already-validated corpus set (never "bt"). raw omits the header for
+// piping. Exit 1 if NO requested version yields any verse.
+func lookupRef(cfg config.Config, ref string, versions []string, raw bool, width int, stdout, stderr io.Writer) int {
+ ref = strings.TrimSpace(ref)
+ if ref == "" {
+ fmt.Fprintln(stderr, "lectio: empty --ref")
+ return 2
+ }
+ sec := liturgy.Section{Citation: ref, Heading: ref}
+
+ found := false
+ for _, v := range versions {
+ if vs, _ := bible.Lookup(v, ref); len(vs) > 0 {
+ found = true
+ break
+ }
+ }
+ if !found {
+ fmt.Fprintf(stderr, "lectio: no text found for %q in %s\n", ref, strings.Join(versions, ", "))
+ return 1
+ }
+
+ w := width
+ if w <= 0 {
+ if len(versions) > 1 {
+ w = compareDefaultWidth
+ } else {
+ w = defaultWidth
+ }
+ }
+
+ if !raw {
+ fmt.Fprintln(stdout, ref)
+ fmt.Fprintln(stdout, strings.Repeat("=", minInt(len([]rune(ref)), w)))
+ fmt.Fprintln(stdout)
+ }
+
+ if len(versions) == 1 {
+ label, blocks := render.GatherVersion(versions[0], sec, "traditional", cfg.UILanguage)
+ if !raw {
+ fmt.Fprintln(stdout, label)
+ fmt.Fprintln(stdout)
+ }
+ lines := make([]string, 0, len(blocks))
+ for _, b := range blocks {
+ lines = append(lines, render.Wrap(b, w))
+ }
+ fmt.Fprintln(stdout, strings.Join(lines, "\n"))
+ return 0
+ }
+
+ fmt.Fprintln(stdout, render.Compare([]liturgy.Section{sec}, versions, w, "traditional", cfg.UILanguage))
+ return 0
+}
+
+// runList handles --list-pl/--list-en: print every Biblical book (scriptural
+// order) as "<abbrev> <name>", the name in Polish when pl, English otherwise.
+// The abbreviation column is padded by rune count (not bytes) so diacritics
+// like "Łk"/"Kpł" still line up.
+func runList(en bool, stdout io.Writer) int {
+ const col = 6
+ for _, b := range bible.Books() {
+ name := b.Polish
+ if en {
+ name = b.English
+ }
+ pad := col - len([]rune(b.Abbrev))
+ if pad < 1 {
+ pad = 1
+ }
+ fmt.Fprintf(stdout, "%s%s%s\n", b.Abbrev, strings.Repeat(" ", pad), name)
+ }
+ return 0
+}
+
// resolveWidth applies the --width 0 = detect-terminal rule: an explicit
// positive width wins, then a real terminal width if stdout is a tty, then
// a sane per-mode default.