aboutsummaryrefslogtreecommitdiff
path: root/mobile/mobile.go
blob: 6312ee091921d606da1fbb838a13285d870abb2f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// 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/i18n"
	"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"`
	Rank      string    `json:"rank"`
	RankLabel string    `json:"rank_label"`
	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
		out.Rank = info.Rank
		out.RankLabel = i18n.Get(lang).Rank[info.Rank]
	} 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" }