aboutsummaryrefslogtreecommitdiff
path: root/cmd/lectio-ef-dump/main.go
blob: 08ff5477c5554ba1310574e968d8a917327ee6c9 (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
// Command lectio-ef-dump prints lectio's Extraordinary Form (1962) calendar,
// one line per day, for use as colitur's differential oracle.
//
// Output format matches colitur's `colitur day <year>` line for line (see
// colitur's bin/main.ml, day_line), so the two streams are diffable directly:
//
//	YYYY-MM-DD weekday season week slug rank colour [+other-slug]...
//
// Every column carries lectio's OWN vocabulary (season names, slugs, rank and
// colour spellings) -- this dumper does not translate lectio's values into
// colitur's. The mapping between the two vocabularies is the differential
// comparator's job, not this dumper's; translating here would hide real
// divergences behind an already-reconciled view.
//
// The trailing "+other-slug" tokens are lectio's Others: the sanctoral
// candidates that lost the day's precedence contest. lectio has no RG 111
// commemoration-admission logic, so this is the set of losing candidates, not
// the set of admitted commemorations -- the two are different things. See the
// task report for detail; the comparator does not diff this column.
//
// Usage: lectio-ef-dump <from-year> <to-year>
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"time"

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

func main() {
	os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
}

// run is lectio-ef-dump's testable entry point: parse args, compute, and
// write. Returns a process exit code (0 ok, 1 runtime error, 2 usage error).
func run(args []string, stdout, stderr io.Writer) int {
	if len(args) != 2 {
		fmt.Fprintln(stderr, "usage: lectio-ef-dump <from-year> <to-year>")
		return 2
	}
	from, errFrom := strconv.Atoi(args[0])
	to, errTo := strconv.Atoi(args[1])
	if errFrom != nil || errTo != nil {
		fmt.Fprintln(stderr, "lectio-ef-dump: from-year and to-year must be integers")
		return 2
	}
	if from > to {
		fmt.Fprintf(stderr, "lectio-ef-dump: from-year %d is after to-year %d\n", from, to)
		return 2
	}

	sel := calendar.DefaultSelection()
	sel.Form = "old"
	layers := []calendar.Layer{caldata.Tridentine()}

	w := bufio.NewWriter(stdout)
	for y := from; y <= to; y++ {
		start := time.Date(y, time.January, 1, 0, 0, 0, 0, time.UTC)
		end := time.Date(y, time.December, 31, 0, 0, 0, 0, time.UTC)
		for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {
			day := calendar.Compute(d, sel, layers)
			if _, err := w.WriteString(dumpLine(day)); err != nil {
				fmt.Fprintf(stderr, "lectio-ef-dump: %v\n", err)
				return 1
			}
		}
	}
	if err := w.Flush(); err != nil {
		fmt.Fprintf(stderr, "lectio-ef-dump: %v\n", err)
		return 1
	}
	return 0
}

// dumpLine renders one LiturgicalDay as a colitur-format line. Week is
// lectio's own int (0 where no season week applies, e.g. named I class feasts
// and per annum green-season ferias); printed as "-" there so the field count
// stays fixed, matching colitur's own convention for an absent week.
func dumpLine(day calendar.LiturgicalDay) string {
	week := "-"
	if day.Week != 0 {
		week = strconv.Itoa(day.Week)
	}
	var b strings.Builder
	fmt.Fprintf(&b, "%s %s %s %s %s %s %s",
		day.Date.Format("2006-01-02"),
		strings.ToLower(day.Weekday.String()),
		string(day.Season),
		week,
		day.Observed.Slug,
		string(day.Observed.Rank),
		string(day.Colour),
	)
	for _, o := range day.Others {
		fmt.Fprintf(&b, " +%s", o.Slug)
	}
	b.WriteByte('\n')
	return b.String()
}