File: templatecache.go

package info (click to toggle)
changie 1.24.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 2,068 kB
  • sloc: makefile: 40; ruby: 38; javascript: 32
file content (211 lines) | stat: -rw-r--r-- 5,155 bytes parent folder | download
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package core

import (
	"io"
	"strings"
	"text/template"
	"time"

	"github.com/Masterminds/sprig/v3"
)

// Batch data is a common structure for templates when generating change fragments.
type BatchData struct {
	// Time of the change
	Time time.Time
	// Version of the change, will include "v" prefix if used
	Version string
	// Version of the release without the "v" prefix if used
	VersionNoPrefix string
	// Previous released version
	PreviousVersion string
	// Major value of the version
	Major int
	// Minor value of the version
	Minor int
	// Patch value of the version
	Patch int
	// Prerelease value of the version
	Prerelease string
	// Metadata value of the version
	Metadata string
	// Changes included in the batch
	Changes []Change
	// Env vars configured by the system.
	// See [envPrefix](#config-envprefix) for configuration.
	Env map[string]string
}

// Component data stores data related to writing component headers.
type ComponentData struct {
	// Name of the component
	Component string `required:"true"`
	// Env vars configured by the system.
	// See [envPrefix](#config-envprefix) for configuration.
	Env map[string]string
}

// Kind data stores data related to writing kind headers.
type KindData struct {
	// Name of the kind
	Kind string `required:"true"`
	// Env vars configured by the system.
	// See [envPrefix](#config-envprefix) for configuration.
	Env map[string]string
}

// Template cache handles running all the templates for change fragments.
// Included options include the default [go template](https://golang.org/pkg/text/template/)
// and [sprig functions](https://masterminds.github.io/sprig/) for formatting.
// Additionally, custom template functions are listed below for working with changes.
// example: yaml
// format: |
// ### Contributors
// {{- range (customs .Changes "Author" | uniq) }}
// * [{{.}}](https://github.com/{{.}})
// {{- end}}
type TemplateCache struct {
	cache map[string]*template.Template
}

func NewTemplateCache() *TemplateCache {
	return &TemplateCache{
		cache: make(map[string]*template.Template),
	}
}

func (tc *TemplateCache) Load(text string) (*template.Template, error) {
	cachedTemplate, ok := tc.cache[text]
	if ok {
		return cachedTemplate, nil
	}

	templ, err := template.New(text).Funcs(tc.buildFuncMap()).Parse(text)
	if err != nil {
		// do not save our template if it had an error
		return nil, err
	}

	tc.cache[text] = templ

	return templ, err
}

func (tc *TemplateCache) Execute(text string, wr io.Writer, data any) error {
	templ, err := tc.Load(text)
	if err != nil {
		return err
	}

	return templ.Execute(wr, data)
}

func (tc *TemplateCache) ExecuteString(text string, data any) (string, error) {
	sb := strings.Builder{}

	templ, err := tc.Load(text)
	if err != nil {
		return "", err
	}

	err = templ.Execute(&sb, data)
	if err != nil {
		return "", err
	}

	return sb.String(), nil
}

func (tc *TemplateCache) buildFuncMap() map[string]any {
	funcs := sprig.TxtFuncMap()

	funcs["count"] = tc.Count
	funcs["components"] = tc.Components
	funcs["kinds"] = tc.Kinds
	funcs["bodies"] = tc.Bodies
	funcs["times"] = tc.Times
	funcs["customs"] = tc.Customs

	return funcs
}

// Count will return the number of occurrences of a string in a slice.
// example: yaml
// format: "{{ kinds .Changes | count \"added\" }} kinds"
func (tc *TemplateCache) Count(value string, items []string) (int, error) {
	count := 0

	for _, i := range items {
		if i == value {
			count++
		}
	}

	return count, nil
}

// Components will return all the components from the provided changes.
// example: yaml
// format: "{{components .Changes }} components"
func (tc *TemplateCache) Components(changes []Change) ([]string, error) {
	comps := make([]string, len(changes))

	for i, c := range changes {
		comps[i] = c.Component
	}

	return comps, nil
}

// Kinds will return all the kindsi from the provided changes.
// example: yaml
// format: "{{ kinds .Changes }} kinds"
func (tc *TemplateCache) Kinds(changes []Change) ([]string, error) {
	kinds := make([]string, len(changes))

	for i, c := range changes {
		kinds[i] = c.Kind
	}

	return kinds, nil
}

// Bodies will return all the bodies from the provided changes.
// example: yaml
// format: "{{ bodies .Changes }} bodies"
func (tc *TemplateCache) Bodies(changes []Change) ([]string, error) {
	bodies := make([]string, len(changes))

	for i, c := range changes {
		bodies[i] = c.Body
	}

	return bodies, nil
}

// Times will return all the times from the provided changes.
// example: yaml
// format: "{{ times .Changes }} times"
func (tc *TemplateCache) Times(changes []Change) ([]time.Time, error) {
	times := make([]time.Time, len(changes))

	for i, c := range changes {
		times[i] = c.Time
	}

	return times, nil
}

// Customs will return all the values from the custom map by a key.
// If a key is missing from a change, it will be an empty string.
// example: yaml
// format: "{{ customs .Changes \"Author\" }} authors"
func (tc *TemplateCache) Customs(changes []Change, key string) ([]string, error) {
	values := make([]string, len(changes))

	for i, c := range changes {
		values[i] = c.Custom[key]
	}

	return values, nil
}