aboutsummaryrefslogtreecommitdiff
path: root/internal/cli/liturgy.go
blob: b1a88743d01717285473bf6d92da7d4d38e845a0 (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
package cli

import (
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/lukaszkasprzak/lectio/internal/caldata"
	"github.com/lukaszkasprzak/lectio/internal/calendar"
	"github.com/lukaszkasprzak/lectio/internal/config"
)

// 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)
	printLiturgicalDay(stdout, cfg, day)
	return 0
}

func printLiturgicalDay(out io.Writer, cfg config.Config, 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))
		}
	}
	for _, m := range day.Observed.Masses {
		for _, r := range m.Readings {
			label := r.Part
			if m.Variant != "" {
				label = m.Variant + " " + label
			}
			fmt.Fprintf(out, "  %-16s %s\n", label+":", r.Citation)
		}
	}
}

// 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"
	}
}