aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--go.mod10
-rw-r--r--go.sum30
-rw-r--r--mobile/mobile.go112
4 files changed, 154 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 0e4cf7b..8868333 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,9 @@ sources/getbible/
*.swp
.idea/
.vscode/
+
+# built by cmd/dlectio-gen
+/dlectio-gen
+
+# subagent-driven-development scratch (ledger, briefs, review packages)
+.superpowers/
diff --git a/go.mod b/go.mod
index e2ba908..6c82929 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/lukaszkasprzak/lectio
-go 1.24.0
+go 1.25.0
require (
github.com/charmbracelet/bubbletea v1.3.10
@@ -24,6 +24,12 @@ require (
github.com/muesli/termenv v0.16.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
- golang.org/x/sys v0.36.0 // indirect
+ golang.org/x/mobile v0.0.0-20260730202154-c700fe717e6e // indirect
+ golang.org/x/mod v0.38.0 // indirect
+ golang.org/x/sync v0.22.0 // indirect
+ golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.3.8 // indirect
+ golang.org/x/tools v0.48.0 // indirect
)
+
+tool golang.org/x/mobile/cmd/gobind
diff --git a/go.sum b/go.sum
index 8aa8bc9..e121589 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,8 @@
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
+github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
+github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
@@ -10,12 +13,15 @@ github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
+github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/go-pdf/fpdf v0.9.0 h1:PPvSaUuo1iMi9KkaAn90NuKi+P4gwMedWPHhj8YlJQw=
github.com/go-pdf/fpdf v0.9.0/go.mod h1:oO8N111TkmKb9D7VvWGLvLJlaZUQVPM+6V42pp3iV4Y=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -30,16 +36,36 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
+github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
+golang.org/x/exp/shiny v0.0.0-20260611194520-c48552f49976/go.mod h1:UXxgIHNj0uSRIk9ua62CRJcUpfHN9V+cRZRXHEo4GhE=
+golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY=
+golang.org/x/mobile v0.0.0-20260730202154-c700fe717e6e h1:v5PGTbI7uer7FhpDeGryGytfbI8t7CDWkKukozvIsNU=
+golang.org/x/mobile v0.0.0-20260730202154-c700fe717e6e/go.mod h1:YX+n47s+53POxN3dx9cIGxG3hGUm/lD64hvrRJFbcSA=
+golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
+golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
+golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
+golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
+golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/telemetry v0.0.0-20260708182218-49f421fb7959/go.mod h1:LV7u5Oco+Z/g6XI7PqN+EUUUGGkEcmB1uj2ceI0fOVg=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
+golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
+golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
+golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
+golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
+golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
diff --git a/mobile/mobile.go b/mobile/mobile.go
new file mode 100644
index 0000000..c2c6347
--- /dev/null
+++ b/mobile/mobile.go
@@ -0,0 +1,112 @@
+// Package mobile is lectio's engine facade for the dlectio Android app. It is
+// bound to a native Android library with gomobile:
+//
+// gomobile bind -target=android/arm64 -tags fullbible \
+// -o dlectio-engine.aar github.com/lukaszkasprzak/lectio/mobile
+//
+// The app calls Day() with a plain date/form/version and receives one JSON
+// document with the day's identity and rendered Mass readings. All liturgical
+// computation and text resolution happens here, in the exact validated lectio
+// engine — the app does none of it. Keep every exported signature to types
+// gomobile can marshal (string in, string out): that is why the payload is JSON.
+package mobile
+
+import (
+ "encoding/json"
+ "strings"
+
+ "github.com/lukaszkasprzak/lectio/internal/config"
+ "github.com/lukaszkasprzak/lectio/internal/readings"
+ "github.com/lukaszkasprzak/lectio/internal/render"
+)
+
+// reading is one Mass reading, rendered in the requested corpus.
+type reading struct {
+ Ord int `json:"ord"`
+ Part string `json:"part"` // machine key, e.g. "ewangelium" / "psalm"
+ Heading string `json:"heading"` // human label in the interface language, e.g. "Gospel"
+ Citation string `json:"citation"`
+ Text string `json:"text"`
+}
+
+// dayDoc is the JSON returned by Day.
+type dayDoc struct {
+ Date string `json:"date"`
+ Form string `json:"form"`
+ Lang string `json:"lang"`
+ Version string `json:"version"`
+ Name string `json:"name"`
+ Season string `json:"season"`
+ Colour string `json:"colour"`
+ Readings []reading `json:"readings"`
+ Error string `json:"error,omitempty"`
+}
+
+// lectByForm maps the app's form code to lectio's lectionary value.
+func lectByForm(form string) string {
+ if form == "ef" {
+ return "traditional"
+ }
+ return "new"
+}
+
+// Day computes one day's identity and Mass readings and returns them as JSON.
+//
+// date - "YYYY-MM-DD"
+// form - "of" (Ordinary) or "ef" (1962)
+// version - corpus: "drb" | "wuj" | "vul" | "grb"
+// lang - interface language for the celebration name: "en" | "pl"
+//
+// It never panics: any engine error is reported in the JSON "error" field so the
+// JNI boundary always returns a well-formed string.
+func Day(date, form, version, lang string) string {
+ lect := lectByForm(form)
+ out := dayDoc{Date: date, Form: form, Lang: lang, Version: version}
+
+ // Day identity (name/season/colour) in the interface language.
+ cfgID := config.Config{UILanguage: lang, Lectionary: lect, All: true}
+ if _, info, err := readings.Load(cfgID, readings.Options{Date: date, All: true}); err == nil {
+ out.Name = info.Name
+ out.Season = info.Season
+ out.Colour = info.Colour
+ } else {
+ out.Error = err.Error()
+ }
+
+ // Readings, resolved and rendered in the requested corpus. The interface
+ // language localizes the structural labels (Heading) and citation dialect;
+ // the corpus (version) alone decides the scripture text.
+ cfgR := config.Config{UILanguage: lang, Lectionary: lect, ReadingVersion: version, All: true}
+ secs, _, err := readings.Load(cfgR, readings.Options{Date: date, All: true})
+ if err != nil {
+ if out.Error == "" {
+ out.Error = err.Error()
+ }
+ } else {
+ for i, sec := range secs {
+ // GatherVersion's lang localizes only the label/error wording, never
+ // the verse text (which the corpus alone decides), so the interface
+ // language is correct here. HeadingWithRef gives the same localized
+ // "label (citation)" heading lectio shows in its cli/tui/web.
+ _, blocks := render.GatherVersion(version, sec, lect, lang)
+ out.Readings = append(out.Readings, reading{
+ Ord: i,
+ Part: sec.PartID,
+ Heading: render.HeadingWithRef(sec, lang),
+ Citation: sec.Citation,
+ Text: strings.Join(blocks, "\n"),
+ })
+ }
+ }
+
+ b, err := json.Marshal(out)
+ if err != nil {
+ // Should be impossible with these types; degrade gracefully.
+ return `{"error":"marshal failed"}`
+ }
+ return string(b)
+}
+
+// Ping is a trivial JNI smoke test: it returns "pong" so the app can confirm the
+// native engine loaded before issuing a real query.
+func Ping() string { return "pong" }