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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
package cli
import (
"fmt"
"io"
"strings"
"time"
"github.com/lukaszkasprzak/lectio/internal/bible"
"github.com/lukaszkasprzak/lectio/internal/caldata"
"github.com/lukaszkasprzak/lectio/internal/calendar"
"github.com/lukaszkasprzak/lectio/internal/config"
"github.com/lukaszkasprzak/lectio/internal/render"
)
// 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
}
dir, _ := config.CalendarsDir()
layers, errs := caldata.Stack(cfg.Selection().Form, dir, cfg.Use)
for _, e := range errs {
fmt.Fprintln(stderr, "lectio: warning:", e)
}
day := calendar.Compute(d.UTC(), cfg.Selection(), layers)
tbl, _ := bible.LoadBookTable(userBooksINI())
printLiturgicalDay(stdout, cfg, tbl, day)
return 0
}
// vernacularVersion is the offline corpus used to render reading text: Wujek
// for Polish, Douay-Rheims otherwise (bt is scraped, so it is not used here).
func vernacularVersion(cfg config.Config) string {
if cfg.TraditionalLang == "pl" || cfg.UILanguage == "pl" {
return "wuj"
}
return "drb"
}
// readingLine resolves an English-authored citation and returns (display,
// text): display is the citation re-formatted in the user's configured sigla
// dialect (SiglaLang); text is the verses from version (or "" if unresolved).
// The reading data is authored in English, so it is parsed in the "en" dialect;
// only the display honors the configured dialect's abbreviations.
func readingLine(tbl *bible.BookTable, cfg config.Config, version, citation string) (string, string) {
engRef, ok := tbl.ParseRef("en", citation)
if !ok {
return citation, "" // unparseable: show as authored
}
display := tbl.FormatRef(cfg.SiglaLang(), engRef)
vs, _ := bible.Lookup(version, engRef)
parts := make([]string, 0, len(vs))
for _, v := range vs {
parts = append(parts, strings.TrimSpace(v.Text))
}
return display, strings.Join(parts, " ")
}
func printLiturgicalDay(out io.Writer, cfg config.Config, tbl *bible.BookTable, day calendar.LiturgicalDay) {
fmt.Fprintf(out, "%s %s\n", day.Date.Format("2006-01-02"), day.Weekday.String())
if day.SundayCycle != "" { // OF carries the reading cycles; EF does not
fmt.Fprintf(out, "season: %s (week %d · Sunday cycle %s · weekday cycle %s)\n",
day.Season, day.Week, day.SundayCycle, day.WeekdayCycle)
} else {
fmt.Fprintf(out, "season: %s (week %d)\n", day.Season, day.Week)
}
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))
}
}
// Readings: the observed celebration's inline propers, else the temporal
// lectionary for the day (proper-of-feast overrides the temporal cycle).
var readings []calendar.Reading
for _, m := range day.Observed.Masses {
readings = append(readings, m.Readings...)
}
if len(readings) == 0 {
readings = caldata.TemporalReadings(cfg.Selection().Form, day.Observed.Slug)
}
vern := vernacularVersion(cfg)
for _, r := range readings {
disp, txt := readingLine(tbl, cfg, vern, r.Citation)
fmt.Fprintf(out, "\n %s (%s):\n", r.Part, disp)
if txt != "" {
for _, line := range strings.Split(render.Wrap(txt, 72), "\n") {
fmt.Fprintf(out, " %s\n", line)
}
} else {
fmt.Fprintf(out, " (text not in %s)\n", vern)
}
}
}
// 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(strings.TrimPrefix(c.Slug, "ef-"), "-", " ")
}
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"
case calendar.RankClass1:
return "I class"
case calendar.RankClass2:
return "II class"
case calendar.RankClass3:
return "III class"
case calendar.RankClass4:
return "IV class"
case calendar.RankCommemoration:
return "commemoration"
default:
return "feria"
}
}
|