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
|
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(userBooksTOML())
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"
}
// readingText resolves an English citation and returns its text from version,
// or "" if it does not resolve.
func readingText(tbl *bible.BookTable, version, citation string) string {
engRef, ok := tbl.ParseRef("en", citation)
if !ok {
return ""
}
vs, _ := bible.Lookup(version, engRef)
parts := make([]string, 0, len(vs))
for _, v := range vs {
parts = append(parts, strings.TrimSpace(v.Text))
}
return 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))
}
}
vern := vernacularVersion(cfg)
for _, m := range day.Observed.Masses {
for _, r := range m.Readings {
label := r.Part
if m.Variant != "" {
label = m.Variant + " " + label
}
fmt.Fprintf(out, "\n %s (%s):\n", label, r.Citation)
if txt := readingText(tbl, vern, r.Citation); 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"
}
}
|