diff options
Diffstat (limited to 'internal/cli/yearview.go')
| -rw-r--r-- | internal/cli/yearview.go | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/internal/cli/yearview.go b/internal/cli/yearview.go new file mode 100644 index 0000000..6b8375c --- /dev/null +++ b/internal/cli/yearview.go @@ -0,0 +1,124 @@ +package cli + +import ( + "fmt" + "io" + "regexp" + "strconv" + "strings" + "time" + + "github.com/lukaszkasprzak/lectio/internal/caldata" + "github.com/lukaszkasprzak/lectio/internal/calendar" + "github.com/lukaszkasprzak/lectio/internal/config" +) + +// reNumberedSunday matches the generic seasonal Sundays ("ordinary-sunday-11", +// "lent-sunday-1"), which carry solemnity rank but are not what a "key dates" +// list wants -- the season boundary already marks them. +var reNumberedSunday = regexp.MustCompile(`-sunday-\d+$`) + +// seasonalSunday reports whether a slug is a generic numbered Sunday or an +// Octave-of-Easter weekday, which the overview omits from the solemnity list. +func seasonalSunday(slug string) bool { + s := strings.TrimPrefix(slug, "ef-") + return reNumberedSunday.MatchString(s) || strings.HasPrefix(s, "easter-octave-") +} + +// seasonName renders a season slug as a display phrase for the year overview. +func seasonName(s calendar.Season) string { + switch s { + case calendar.Advent: + return "Advent" + case calendar.Christmas: + return "Christmas" + case calendar.Lent: + return "Lent" + case calendar.Triduum: + return "the Paschal Triduum" + case calendar.Easter_: + return "Eastertide" + case calendar.Ordinary: + return "Ordinary Time" + } + switch string(s) { + case "septuagesima": + return "Septuagesima" + case "passiontide": + return "Passiontide" + case "time-after-epiphany": + return "Time after Epiphany" + case "time-after-pentecost": + return "Time after Pentecost" + } + return string(s) +} + +// topRank reports whether a rank is the year's headline tier: a solemnity +// (Ordinary Form) or a class-1 feast (Extraordinary Form). +func topRank(r calendar.Rank) bool { + return r == calendar.RankSolemnity || r == calendar.RankClass1 +} + +// runYearOverview prints the key liturgical dates of a civil year -- season +// boundaries, solemnities and feasts of the Lord -- computed from the engine +// for the configured form (and custom-calendar layers). It is what `--year N` +// does on its own (with --format it emits the machine calendar instead). Output +// goes through the pager when one is configured and stdout is a terminal. +func runYearOverview(cfg config.Config, yearStr, formFlag string, pagerFlag, noPager bool, stdout, stderr io.Writer) int { + year, err := strconv.Atoi(strings.TrimSpace(yearStr)) + if err != nil || year < 1583 || year > 9999 { + fmt.Fprintf(stderr, "lectio: invalid --year %q (want 1583-9999)\n", yearStr) + return 2 + } + sel := cfg.Selection() + if formFlag != "" { + if formFlag != "old" && formFlag != "new" { + fmt.Fprintf(stderr, "lectio: invalid --form %q (want old|new)\n", formFlag) + return 2 + } + sel.Form = formFlag + } + dir, _ := config.CalendarsDir() + layers, errs := caldata.Stack(sel.Form, dir, cfg.Use) + for _, e := range errs { + fmt.Fprintln(stderr, "lectio: warning:", e) + } + + out := stdout + if pagerRequested(pagerFlag, noPager, cfg) && isTerminalWriter(stdout) { + if o, finish, ok := startPager(pagerCommand(cfg), stdout, stderr); ok { + out = o + defer finish() + } + } + + formName := "Ordinary Form" + if sel.Form == "old" { + formName = "Extraordinary Form (1962)" + } + title := fmt.Sprintf("Key liturgical dates %d — %s", year, formName) + fmt.Fprintf(out, "%s\n%s\n\n", title, strings.Repeat("=", len([]rune(title)))) + + prev := calendar.Season("") + for d := time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC); d.Year() == year; d = d.AddDate(0, 0, 1) { + day := calendar.Compute(d, sel, layers) + var tags []string + if day.Season != prev && prev != "" { + tags = append(tags, seasonName(day.Season)+" begins") + } + prev = day.Season + if topRank(day.Observed.Rank) && !seasonalSunday(day.Observed.Slug) { + tags = append(tags, "solemnity") + } else if day.Observed.Class == calendar.ClassLord && day.Observed.Rank == calendar.RankFeast { + tags = append(tags, "feast of the Lord") + } + if len(tags) == 0 { + continue + } + name := celebrationName(cfg, day.Observed) + fmt.Fprintf(out, " %s %s %-46s %s\n", + d.Format("2006-01-02"), d.Weekday().String()[:3], name, strings.Join(tags, " · ")) + } + return 0 +} |
